R&E 연구노트 ⑰ 얼굴을 어떻게 읽는가, CNN·MTCNN·Xception
연구노트 시리즈.
라이브러리를 "쓴다"와 "왜 그렇게 동작하는지 안다"는 다르다. 출품작을 준비하며 face-api.js가 기대는 이론들을 정리했다.
CNN의 구조
합성곱 신경망(CNN)은 이미지에 특화된 신경망으로, LeNet-5에서 출발해 AlexNet 이후 컴퓨터 비전의 표준이 됐다. 네 가지 요소가 계층적으로 쌓인다.
- 합성곱층: 학습 가능한 필터로 국소적 특징을 뽑는다. 같은 필터를 영상 전체에 적용하는 가중치 공유 덕에 파라미터가 줄고, 위치가 조금 변해도 같은 특징을 잡아내는 변이 불변성을 얻는다.
- ReLU: 음수를 0으로 만드는 단순한 활성화 함수다. 양의 영역에서 기울기가 1로 일정해 기울기 소실 문제를 완화한다.
- 풀링층: 특징 맵의 해상도를 줄여 계산량을 낮추고 작은 위치 변화에 강건해진다.
- 완전연결층: 추출된 특징으로 최종 분류나 회귀를 한다.
얼굴은 눈·코·입이라는 공통 구조를 가지면서도 개인과 연령에 따라 미묘하게 다르다. CNN의 계층적 특징 추출이 이런 문제에 잘 맞는 이유다. 더 깊은 망을 학습하기 위한 ResNet의 잔차 연결도 이 계열의 중요한 진전이다.
MTCNN의 3단계 검출
얼굴을 찾는 데는 MTCNN이 널리 쓰인다. 입력 영상을 여러 크기로 리사이즈한 이미지 피라미드에서 시작해, 세 개의 CNN을 순서대로 적용한다.
- P-Net: 12×12 입력에서 얼굴 후보를 다량 생성하고, 비최대 억제(NMS)로 중복을 제거한다.
- R-Net: 24×24 입력에서 후보를 정제하고 잘못된 것을 더 걸러낸다.
- O-Net: 48×48 입력에서 최종 얼굴 위치와 5개 랜드마크(양 눈, 코, 양 입꼬리)를 함께 출력한다.
학습에는 미니배치 안에서 손실 상위 70%만 역전파하는 온라인 하드 샘플 마이닝을 써서, 어려운 샘플에 집중해 검출 성능을 끌어올린다.
Xception과 깊이별 분리 합성곱
브라우저에서 실시간으로 돌리려면 가벼워야 한다. 여기서 Xception이 등장한다. 표준 합성곱이 채널 간 상관과 공간 상관을 한꺼번에 학습한다면, Xception은 둘을 분리한다.
- 깊이별 합성곱: 각 입력 채널에 대해 따로 공간 합성곱을 한다.
- 점별 합성곱: 1×1 합성곱으로 채널을 선형 결합한다.
이 분리 덕에 3×3 커널·출력 채널 64 기준으로 연산량이 약 8~9배 줄어든다. 같은 정확도를 더 적은 자원으로 낼 수 있다는 뜻이다. face-api.js의 연령·성별 모델은 이 Xception 계열의 TinyXception을 백본으로 삼아, 서버 GPU 없이 엣지에서 실시간 추론을 해낸다.
연령을 추정하는 네 가지 방법
얼굴에서 나이를 뽑는 방법은 크게 넷이다.
- 회귀: 나이를 연속값으로 보고 뉴런 하나가 직접 예측한다. 손실은 MAE나 MSE.
- 분류: 0~100세를 101개 클래스로 나눠 소프트맥스로 분류한다.
- 순서형 회귀: 나이의 순서 관계를 살려 K-1개 이항 분류기 출력을 합산한다.
- DEX: 분류 확률에 나이 값을 가중평균한다. ChaLearn 챌린지 1위 이후 사실상 표준이 됐다.
데이터셋은 분포가 제각각이다. IMDB-WIKI(약 52만 장, 셀러브리티 중심), MORPH-II, AFAD(약 16.5만 장, 아시아인 중심), UTKFace, Adience 등이 있다. 한국인을 주 대상으로 하므로 아시아인 중심의 AFAD가 도메인 적합성에서 유리하다는 점도 함께 고려했다.
실제로 채택한 것
검출에는 약 190KB의 TinyFaceDetector를 썼다. 단일 신경망 순전파로 얼굴 영역과 신뢰도를 함께 예측해 프레임당 약 40~80ms로 동작한다. 연령·성별에는 약 420KB의 AgeGenderNet을 썼다. 검출된 얼굴을 96×96으로 잘라 넣으면 깊이별 분리 합성곱을 거쳐 연령 회귀와 성별 분류를 동시에 낸다. 경계 연령에서 UI가 깜빡이는 것을 막기 위해, 연속 프레임의 추정값을 평균하는 시간적 평활화를 적용했다.