얼굴 임베딩 만들기
1) 얼굴 인식 face detection
Face Recognition 라이브러리 사용하여 얼굴 인식 -> Image cropping(슬라이싱 이용)
2) FaceNet의 얼굴 임베딩 모델 이용하여 얼굴 임베딩 벡터 추출
face_recognition.face_encodings() 이용
L2 Normalization
모델 결과물의 L2 Distance를 구한 후에 이것으로 결과물을 나눠주어 Normalization을 해주는 과정.
L2 Normalization 레이어를 거쳐나온 임베딩 벡터는 두 벡터의 절대적 크기에 무관하게 두 벡터 사이의 각도에만 영향을 받는다.
L2 Distance
Triplet Loss
세 개의 데이터 쌍을 이용해 계산하는 손실함수로 네트워크를 학습시킬 수 있다.
우리 모델에서는 같은 사람의 얼굴 쌍을 임베딩 공간 상에서 가깝게, 다른 사람의 얼굴 쌍은 멀도록 학습시킨다. 이 모델에서 거리가 먼 두 얼굴 이미지의 임베딩은 서로 다른 사람일 확률이 높다고 볼 수 있고 가까우면 같은 사람일 확률이 높다고 볼 수 있다.
3) 임베딩 벡터를 딕셔너리에 넣어주기
얼굴임베딩 사이의 거리 측정
Triplet Loss - L2 distance 사용
얼굴 임베딩 공간의 시각화
차원 축소(PCA, T-SNE 등)
# 필요한 라이브러리 호출
import face_recognition
import os
%matplotlib inline
import matplotlib.pyplot as plt
def get_cropped_face(image_file):
image = face_recognition.load_image_file(image_file) # 이미지 불러오기
face_locations = face_recognition.face_locations(image) # 얼굴 영역 박스
a, b, c, d = face_locations[0] # 얼굴 영역 박스 좌표
cropped_face = image[a:c,d:b,:] # 얼굴 영역 박스 좌표를 이용해 얼굴 잘라내기
return cropped_face
image_path = os.getenv('HOME')+'/aiffel/face_embedding/images/trump.jpg'
# 얼굴 잘라내는 함수 호출
cropped_face = get_cropped_face(image_path)
plt.imshow(cropped_face) # 잘라낸 이미지 출력
image_file = os.path.join(dir_path, file_list)
face = get_cropped_face(image_file) # 얼굴 영역을 구하는 함수(이전 스텝에서 구현)
def get_face_embedding(face):
return face_recognition.face_encodings(face) # FaceNet 얼굴 임베딩 모델 이용
고차원을 128차원으로 줄여줌.
def get_face_embedding_dict(dir_path):
file_list = os.listdir(dir_path)
embedding_dict = {}
for file in file_list:
img_path = os.path.join(dir_path, file) # 경로를 병합하여 새 경로 생성
try:
face = get_cropped_face(img_path) # 얼굴 영역만 자른 이미지
except: # 인식하지 못하는 이미지는 넘어감
continue
embedding = get_face_embedding(face) # 얼굴 영역에서 얼굴 임베딩 벡터를 추출
if len(embedding) > 0: # 얼굴 영역이 제대로 detect되지 않았을 경우를 대비
# os.path.splitext(file)[0]에는 이미지파일명에서 확장자를 제거한 이름이 담긴다.
embedding_dict[os.path.splitext(file)[0]] = embedding[0]
return embedding_dict
key:value = 이름:임베딩 벡터
def get_distance(name1, name2):
return np.linalg.norm(embedding_dict[name1]-embedding_dict[name2], ord=2)
get_distance('obama', 'trump')
# name1과 name2의 거리를 비교하는 함수
def get_sort_key_func(name1): # name1은 미리 지정
def get_distance_from_name1(name2): # name2는 호출시에 인자로 받는다.
return get_distance(name1, name2)
return get_distance_from_name1
# 유사한 이미지 찾는 함수
def get_nearest_face(name, top=5):
sort_key_func = get_sort_key_func(name)
sorted_faces = sorted(embedding_dict.items(), key=lambda x:sort_key_func(x[0])) # 얼굴 임베딩 딕셔너리를 오름차순으로 정렬
for i in range(top+1):
if i == 0:
continue
if sorted_faces[i]:
print('순위 {} : 이름({}), 거리({})'. format(i, sorted_faces[i][0], sort_key_func(sorted_faces[i][0])))
임베딩 벡터 사이의 거리를 L2 distance로 계산한다.
import numpy as np
# 128차원의 벡터를 넘파이로 계산하기 위해서 리스트에서 넘파이 배열로 변환
A = np.array([])
B = np.array([])
C = np.array([])
# np.linalg.norm를 사용하여 벡터간 거리 계산(L2 distance)
distances = np.linalg.norm([A, B] - C, axis=1, ord=2)
print("Distance between A and C: {}".format(distances[0]))
print("Distance between B and C: {}".format(distances[1]))
참고> L1 distance vs L2 distance
import numpy as np
x = np.array([1,2,3,4,5])
y = np.array([2,3,4,5,6])
print(np.linalg.norm(y-x, ord=1)) #L1 distance
print(np.linalg.norm(y-x, ord=2)) #L2 distance
1) PCA(주성분분석)
데이터 분포의 주성분(데이터의 분산이 가장 큰 방향벡터)를 찾아주는 방법.
데이터의 분산을 최대로 보존하면서 서로 직교하는 기저(basis, 새로운 좌표계 역할을 하는 벡터의 모음. 여기서는 분산이 큰 방향벡터의 축)를 찾아 고차원 공간을 저차원 공간으로 사용한다. 기존의 feature를 선형 결합하는 방식을 사용한다. PCA는 주로 선형적인 데이터의 분포를 가지고 있을 때 정보가 가장 잘 보존된다(물리적 정보력을 보존하기 때문0.
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import pandas as pd
embedding_dict1 = pd.DataFrame(embedding_dict)
scaler = StandardScaler()
result = scaler.fit_transform(embedding_dict1)
pca = PCA(n_components=10) # 주성분의 수를 10개, 즉 기저가 되는 방향벡터를 10개로 하는 PCA 알고리즘 수행
pc = pca.fit_transform(data_scaled)
result = pd.DataFrame(pc)
result
2) T-SNE(T-Stochastic Neighbor Embedding)
기존 차원의 공간에서 가까운 점들은, 차원축소된 공간에서도 여전히 가깝게 유지시킨다(상대적인 거리 보존).
128차원 -> 2차원으로 축소
x_list = []
y_list = []
for k, v in embedding_dict.items():
x_list.append(v)
y_list.append(k)
tsne = TSNE(n_components=2, verbose=1, perplexity=40, n_iter-300)
tsne_results = tsne.fit_transform(x_list)
바꿔가면서 실험해보자.
참고: Keras로 구현하기