# Ch13_1 Eigenface(AT&T) 이 장에서는 이미지 데이터를 이용하여 이미지 안의 고유 얼굴 검출, 손글씨로 쓴 숫자 분류 시스템을 살펴본다. <br> Ch13_1 : http://202.31.200.194:8888/notebooks/ml_face/eigenface.ipynb# <br> ___ ### 주성분 분석을 이용한 사람 얼굴 인식 <br> 우선 '주성분 분석'을 이용하여 고유 얼굴 검출을 구현한다. 카테고리의 복수 이미지로부터 주성분을 추출함으로써 그 이미지들이 공통으로 가지는 피처를 추출한다. 사이킷런의 PCA 클래스를 이용하면 쉽게 구현 가능하다. <br> 우선 AT&T의 얼굴 데이터셋을 받아준다. 40명 각자 10개의 그레이스케일 이미지( 가로 92px * 세로 112px )를 포함한다. 따라서 총 400개의 이미지이다. ![](https://i.imgur.com/e8YBsSl.png) 이 400개의 이미지 중 하나를 입력 받아 40명중 누구의 얼굴인지 출력하는 시스템을 만든다. 2단계로 진행되는데 1. 주성분 분석으로 이미지 피처 생성 2. 1에서 생성한 피처와 SVM을 이용하여 모델 학습 위 와 같은 과정을 거친다. <br> 예제에서는 4가지 함수를 사용한다. - read_data : 이미지 데이터를 읽어 numpy 배열형으로 변환한 뒤 반환 - create_train_test_data : 데이터를 학습, 평가 데이터로 나눈다 - extract_features : 학습 데이터에서 PCA 피처를 뽑는다 - train_test_classifier : 모델 학습 및 평가 <br> --- ### read_data ``` def read_data(fin): target_li = [] data_li = [] for line in open(fin): image_path, face_id = line.strip().split(';') image_data = cv2.imread(image_path, cv2.COLOR_BGR2GRAY) data_li.append(image_data) # 이미지 데이터 target_li.append(int(face_id)) # 이미지 얼굴 번호 return(np.array(data_li), np.array(target_li)) ``` 각 이미지의 경로와 해당되는 얼굴 번호를 적어놓은 csv 파일을 불러와 numpy 배열형으로 변환한 뒤 반환 하는 함수 --- ### create_train_test_data ``` def create_train_test_data(image_data, label_li): # 이미지데이터수, 이미지세로픽셀크기, 이미지가로픽셀크기 n_samples, image_h, image_w = image_data.shape # 가로 픽셀 강도벡터와 세로 픽셀 강도벡터를 이어서 하나의 벡터로 만든다. # 길이는 image_h x image_w가 되고 피처 벡터가 된다. X = image_data.reshape(n_samples, -1) n_features = X.shape[1] # 피처 크기 y = label_li # 학습 레이블 n_classes = len(set(y)) # 클래스(인물) 수 print("Total dataset size:") print("n_samples: %d" % n_samples) print("n_features: %d" % n_features) print("n_classes: %d" % n_classes) # 사이킷런의 함수 train_test_split을 이용하여 학습셋과 평가셋을 나눈다. X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42) return(X_train, X_test, y_train, y_test) ``` width * height 의 크기로 강도 벡터를 하나로 만들고 학습 레이블과 함께 사이킷런의 함수를 이용하여 데이터 셋을 나눈다. --- ### extract_features ``` def extract_features_2(X_train, X_test, n_components, image_h, image_w): print("Extracting the top %d eigenfaces from %d faces"% (n_components, X_train.shape[0])) #PCA 클래스 선언 #n_components : 사용할 성분의 개수 int형은 그 수만큼, # 0~1 사이의 float는 분산 퍼센티지에 따른 설명가능한 수 #svd_solver : 주성분 분석에 사용하는 특잇값 분해를 어떻게 풀지 정한다. # 일반적으로 auto를 사용하면 된다. #whiten : bool 형, 기본값은 False. # True면 모든 성분을 척도화하여 분해한다. # 모델이 학습 시 샘플의 전차원 방향을 고려해 정확도를 높인다. # 하지만 각 성분의 상대 편차값이 사라진다. #copy pca = PCA(n_components=n_components, svd_solver='auto', whiten=True).fit(X_train) eigenfaces = pca.components_.reshape((n_components, image_h, image_w)) X_train_pca = pca.transform(X_train) X_test_pca = pca.transform(X_test) return(X_train_pca, X_test_pca, eigenfaces) ``` 다음과 같은 애트리뷰트를 가지며 모델 학습 후 아래 값들을 사용할 수 있다. - components_ : 각 피처 공간에서의 주성분. 주성분 수 * 피처 수 의 크기를 가진다 - explained_variance_ : 각 주성분이 설명할 수 있는 데이터 분산의 크기 - explained_variance_ratio_ : 전체 분산 중에서 해당하는 주성분이 설명하는 분산의 백분율 - mean_ : 학습 데이터에서 샘플의 피처값 평균 - n_components_ : int형의 주성분 수 추정값 - noise_variance_ : float형의 추정된 노이즈 공분산 값 extract_features의 경우 components_를 이후 고유 얼굴을 시각화 하기 위해 사용한다. --- ### train_test_classifier ``` def train_test_classifer(X_train_pca, X_test_pca, y_train, y_test): print("Fitting the classifier to the training set") param_grid = {'C': [1e3, 5e3, 1e4, 5e4, 1e5],'gamma': [0.0001, 0.0005, 0.001, 0.005, 0.01, 0.1]} clf = GridSearchCV(SVC(kernel='rbf', class_weight='balanced'), param_grid) clf = clf.fit(X_train_pca, y_train) print("Best estimator found by grid search:") print(clf.best_estimator_) print("Predicting people's names on the test set") y_pred = clf.predict(X_test_pca) print(classification_report(y_test, y_pred)) ``` 이제 추출한 피처를 SVM의 입력으로 사용하여 학습시킨다. 사이킷 런은 SVC라는 클래스로 구현하고 있다. 위 코드에서 ``` SVC(kernel='rbf', class_weight='balanced') ``` - kernel : string 형으로 기본값은 'rbf' 커널이다. 모델에서 사용할 함수를 정하며, 'linear, poly, rbf, sigmoid, precomputed'를 지원하지만 'linear', 'rbf'를 쓰는게 무난하다. - class_weight : 딕셔너리형 인자 혹은 'balanced'를 받는다. 클래스 가중치를 넘기기 위한 딕셔너리형, 키는 클래스 인덱스, 값은 가중치가 된다. 머신러닝에 사용될 지도학습 모델을 설정하는 것이다. <br> 하지만 SVM은 파라미터에 따라 성능이 많이 달라지는데, 복잡하게 할 필요 없이 GridSearchCV 클래스를 이용해서 직접 코드를 쓰지않고 두 개 파라미터의 조합을 만들어 모델의 성능을 평가할 수 있다. 파라미터 조합을 이용하여 학습된 모델의 성능을 교차검증한다. ``` GridSearchCV(SVC(...), param_grid) ``` 사이킷런 모델이 앞에 들어가있고 뒤에는 딕셔너리형이 있다. SVM의 파라미터 중 C는 에러에 대한 파라미터로 예외 데이터를 가정하게 되며, gamma는 float 형으로 커널의 계수를 뜻한다. 코드의 param_grid 초기화 부분을 보면 C와 gamma에 대한 딕셔너리형을 넣어준다음 교차검증 하는 것을 알 수 있다.