[추Node] 1. Excon (E7) 유사도 측정 모델

데이터 여행자·2021년 2월 7일
0

스터디

목록 보기
12/15

Excon 1회차 스터디

1. 유사도 측정 모델의 구현 순서

  1. 얼굴 임베딩 만들기

    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) 임베딩 벡터를 딕셔너리에 넣어주기

  2. 얼굴임베딩 사이의 거리 측정
    Triplet Loss - L2 distance 사용

  3. 얼굴 임베딩 공간의 시각화
    차원 축소(PCA, T-SNE 등)

2. 노드에서 쓰이는 함수

  1. 이미지에서 얼굴을 인식하여 얼굴 영역을 잘라내는 함수
# 필요한 라이브러리 호출
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)    # 잘라낸 이미지 출력
  1. 얼굴 임베딩 벡터 추출 함수
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차원으로 줄여줌.

  1. 모든 이미지 파일의 임베딩 벡터를 구해 딕셔너리 구조에 담아 리턴하는 함수
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 = 이름:임베딩 벡터

  1. 두 사람의 얼굴 임베딩 사이의 거리(L2 distance) 구하는 함수
def get_distance(name1, name2):
    return np.linalg.norm(embedding_dict[name1]-embedding_dict[name2], ord=2)

get_distance('obama', 'trump')
  1. 가장 유사한 이미지 찾는 함수
# 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])))
  • key=lambda x:sort_key_func(x[0])의 의미는?

3. 임베딩 벡터

  • 임베딩이란?
    고차원의 정보를 저차원으로 변환하면서 필요한 정보를 보존하는 것.
  • 이 노드에서 임베딩을 하는 이유
    필요한 정보를 담고 있는 저차원으로 변환된 벡터(임베딩 벡터)를 비교하면 이미지를 비교할 수 있다.
  • 임베딩의 사례
    유사성 분석(정보 및 패턴 탐색), 검색(비슷한 항목 검색), 기계 전이 학습(사전 학습된 임베딩 사용)

4. 임베딩 벡터의 거리 비교 방식

임베딩 벡터 사이의 거리를 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

시각화 - 차원 축소

  • Fundamental 12(비지도 학습 참고)

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로 구현하기

0개의 댓글