연구노트

R&E 연구노트 ⑰ 얼굴을 어떻게 읽는가, CNN·MTCNN·Xception

3마지막 수정
R&E 연구노트 ⑰ 얼굴을 어떻게 읽는가, CNN·MTCNN·Xception

연구노트 시리즈.

라이브러리를 "쓴다"와 "왜 그렇게 동작하는지 안다"는 다르다. 출품작을 준비하며 face-api.js가 기대는 이론들을 정리했다.

CNN의 구조

합성곱 신경망(CNN)은 이미지에 특화된 신경망으로, LeNet-5에서 출발해 AlexNet 이후 컴퓨터 비전의 표준이 됐다. 네 가지 요소가 계층적으로 쌓인다.

  • 합성곱층: 학습 가능한 필터로 국소적 특징을 뽑는다. 같은 필터를 영상 전체에 적용하는 가중치 공유 덕에 파라미터가 줄고, 위치가 조금 변해도 같은 특징을 잡아내는 변이 불변성을 얻는다.
  • ReLU: 음수를 0으로 만드는 단순한 활성화 함수다. 양의 영역에서 기울기가 1로 일정해 기울기 소실 문제를 완화한다.
  • 풀링층: 특징 맵의 해상도를 줄여 계산량을 낮추고 작은 위치 변화에 강건해진다.
  • 완전연결층: 추출된 특징으로 최종 분류나 회귀를 한다.

얼굴은 눈·코·입이라는 공통 구조를 가지면서도 개인과 연령에 따라 미묘하게 다르다. CNN의 계층적 특징 추출이 이런 문제에 잘 맞는 이유다. 더 깊은 망을 학습하기 위한 ResNet의 잔차 연결도 이 계열의 중요한 진전이다.

MTCNN의 3단계 검출

얼굴을 찾는 데는 MTCNN이 널리 쓰인다. 입력 영상을 여러 크기로 리사이즈한 이미지 피라미드에서 시작해, 세 개의 CNN을 순서대로 적용한다.

  1. P-Net: 12×12 입력에서 얼굴 후보를 다량 생성하고, 비최대 억제(NMS)로 중복을 제거한다.
  2. R-Net: 24×24 입력에서 후보를 정제하고 잘못된 것을 더 걸러낸다.
  3. O-Net: 48×48 입력에서 최종 얼굴 위치와 5개 랜드마크(양 눈, 코, 양 입꼬리)를 함께 출력한다.

학습에는 미니배치 안에서 손실 상위 70%만 역전파하는 온라인 하드 샘플 마이닝을 써서, 어려운 샘플에 집중해 검출 성능을 끌어올린다.

Xception과 깊이별 분리 합성곱

브라우저에서 실시간으로 돌리려면 가벼워야 한다. 여기서 Xception이 등장한다. 표준 합성곱이 채널 간 상관과 공간 상관을 한꺼번에 학습한다면, Xception은 둘을 분리한다.

  • 깊이별 합성곱: 각 입력 채널에 대해 따로 공간 합성곱을 한다.
  • 점별 합성곱: 1×1 합성곱으로 채널을 선형 결합한다.

이 분리 덕에 3×3 커널·출력 채널 64 기준으로 연산량이 약 8~9배 줄어든다. 같은 정확도를 더 적은 자원으로 낼 수 있다는 뜻이다. face-api.js의 연령·성별 모델은 이 Xception 계열의 TinyXception을 백본으로 삼아, 서버 GPU 없이 엣지에서 실시간 추론을 해낸다.

연령을 추정하는 네 가지 방법

얼굴에서 나이를 뽑는 방법은 크게 넷이다.

  1. 회귀: 나이를 연속값으로 보고 뉴런 하나가 직접 예측한다. 손실은 MAE나 MSE.
  2. 분류: 0~100세를 101개 클래스로 나눠 소프트맥스로 분류한다.
  3. 순서형 회귀: 나이의 순서 관계를 살려 K-1개 이항 분류기 출력을 합산한다.
  4. DEX: 분류 확률에 나이 값을 가중평균한다. ChaLearn 챌린지 1위 이후 사실상 표준이 됐다.

데이터셋은 분포가 제각각이다. IMDB-WIKI(약 52만 장, 셀러브리티 중심), MORPH-II, AFAD(약 16.5만 장, 아시아인 중심), UTKFace, Adience 등이 있다. 한국인을 주 대상으로 하므로 아시아인 중심의 AFAD가 도메인 적합성에서 유리하다는 점도 함께 고려했다.

실제로 채택한 것

검출에는 약 190KB의 TinyFaceDetector를 썼다. 단일 신경망 순전파로 얼굴 영역과 신뢰도를 함께 예측해 프레임당 약 40~80ms로 동작한다. 연령·성별에는 약 420KB의 AgeGenderNet을 썼다. 검출된 얼굴을 96×96으로 잘라 넣으면 깊이별 분리 합성곱을 거쳐 연령 회귀와 성별 분류를 동시에 낸다. 경계 연령에서 UI가 깜빡이는 것을 막기 위해, 연속 프레임의 추정값을 평균하는 시간적 평활화를 적용했다.

변경 이력 (2건)
  1. gitprogam 수정feat(binaryhub-blog): tetris 위키 + SEO 정비 + hero 이미지 + 글 다수
  2. gitprogam 최초 작성init: monorepo (nsgh-tools + binaryhub-blog)

관련 글