Tensorflow 얼굴인식 - tensorflow eolgul-insig

안녕하세요, 라온피플 (주)입니다.

지난 시간에는 Tensorflow slim API를 이용하여 유명한 Network 구현을 진행하였습니다.

이번 시간에는 얼굴인식을 진행 하겠습니다.

먼저 얼굴인식이 어떻게 진행되는지 알아 보겠습니다.

얼굴인식은 크게 보면 다음과 같은 과정으로 진행이 됩니다.

Tensorflow 얼굴인식 - tensorflow eolgul-insig

비교할 얼굴 2장을 Backbone Network에 넣은 뒤 최종단에 나오는 Embedding Vector 두 개를 비교하여 같은 사람인지 다른 사람인지 결정하게 됩니다.

Backbone Network는 보통 CNN구조이며, Resnet, Inception, VGG 등등 여러가지 네트워크가 될 수 있습니다.

Embedding Vector는 네트워크 최종단에 있는 길이가 N인 벡터입니다.

요약하면, 얼굴이미지를 1차원 벡터로 정보를 압축하여 두 벡터의 유사도를 비교하는 과정입니다.

이 때 두 벡터의 유사도를 비교하는 방법은 크게 2가지가 있습니다.

첫 번째는, 두 벡터의 L2 거리를 직접 구하는 방법

두 번쨰는, 두 벡터의 Cosine 거리를 구해서 비교하는 방법이 있습니다.

위의 방법은 학습을 어떤 Loss로 했느냐에 따라 결정됩니다.

triplet-loss로 학습하면 두 벡터의 L2거리로 유사도를 비교하게 되고,

softmax-loss로 학습하면 두 벡터의 Cosine거리로 유사도를 비교하게 됩니다.

위의 두가지 로스가 어떤 방식으로 학습되는지 알아봅시다.

먼저 Triplet-loss는 다음과 같습니다.

Tensorflow 얼굴인식 - tensorflow eolgul-insig

Tensorflow 얼굴인식 - tensorflow eolgul-insig

이해를 돕기 위해 그림으로 설명을 하겠습니다.

Anchor는 기준이 되는 얼굴이미지 이며, Positive는 Anchor와 같은 사람 얼굴, Negative는 Anchor와 다른 사람의 얼굴 입니다.

학습시에는 Anchor, Positive, Negative 얼굴 이미지의 Embedding Vector를 구한 후,

Anchor와 Positive 벡터의 거리는 줄어들게, Anchor와 Negative벡터의 거리는 커지게 학습이 진행 됩니다.

이 내용을 수식으로 표현하면 위의 첫번째항, 두번째항으로 나타나 집니다.

이런 방법으로 충분히 학습을 하고나면 실제로 비교를 할때 같은 얼굴이면 거리가 작게, 다른 얼굴이면 거리가 크게 나타남으로써, 얼굴 인식이 가능해 집니다.

두 번째 방법을 살펴봅시다.

Softmax-loss는 흔히 분류할때 쓰이는 Loss 입니다.

많이 알려지고 쓰이는 loss여서 따로 자세히 설명을 안하겠습니다.

내용이 궁금하시면

https://laonple.blog.me/220563347553

위의 링크에 내용이 잘 정리되어 있으니 참고 바랍니다.

중요한건, Softmax로 학습하면 왜 두 벡터 사이의 각도로 얼굴인식이 가능한지입니다.

Softmax라는건 결국 각 클래스를 나타내는 대표 벡터가 있고, 같은 클래스의 Embedding Vector가 해당 대표 벡터에 가까워 지게 학습을 하는 과정입니다.

Tensorflow 얼굴인식 - tensorflow eolgul-insig

이해를 돕기위하여 위의 그림처럼 2차원 Embedding Vector의 상황을 가정해보겠습니다.

처음에 학습이 되기전에는 Anchor, Positive, Negative 벡터가 무분별하게 분포해 있지만,

Softmax-loss로 충분한 학습이 진행된 후에는

같은 클래스인 Positive, Anchor 벡터는 점점 가까워져서 각도가 작아지고

다른 클래스인 Negative, Anchor 벡터는 점점 멀어져서 각도가 커지게됩니다.

이런 방법으로 충분히 학습을 하고나면, 같은 얼굴은 각도가 작게, 다른 얼굴은 각도가 크게 나타남으로써, 얼굴 인식이 가능해집니다.

이번 시간에는 얼굴인식의 바탕이 되는 기본적인 내용을 알아보았습니다.

다음 시간에는 실제 Tensorflow코드로 구현하여 얼굴인식을 진행해 보겠습니다.

감사합니다.

★Sample Code(Github)

인공지능, 딥러닝 하면 떠오르는 텐서플로우(TensorFlow)를 웹앱에서도 개발 및 사용할 수 있는 TensorFlow.js 자바스크립트 머신러닝 라이브러리에 대해 알아봅시다.

TensorFlow.js | 자바스크립트 개발자를 위한 머신러닝

브라우저, Node.js 또는 Google Cloud Platform에서 모델을 학습시키고 배포합니다. TensorFlow.js는 자바스크립트 및 웹 개발을 위한 오픈소스 ML 플랫폼입니다.

www.tensorflow.org

Tensorflow 얼굴인식 - tensorflow eolgul-insig

인공지능 서비스는 특히 영상(Vision)과 음성(Speech) 서비스에 특화되어 있는데요,

AWS AI, Google Cloud AI 등 세계적인 클라우드 기반 AI 서비스를 들여다 보면

얼굴, 포즈, 사물 인식 등의 비전 기술과 음성 인식, Text-to-speech, 자연어 처리 등 음성 기술이 주를 이루고 있어요.

AWS나 Google Cloud는 유료 서비스라서 배우는 입장에서는 부담스러울 수 있는데요,

Tensorflow 같은 오픈 소스 프로젝트를 활용해서 유사한 기술들을 무료로 사용할 수 있습니다.

특히 Tensorflow.js는 브라우저에서 돌아가기 때문에 User Interface와 맞물려 있어 간단한 웹앱을 짜서 쉽게 연동해 볼 수 있지요.

Face Detection 얼굴 인식

웹앱에서 얼굴 인식을 할 수 있는 몇몇 오픈 소스 프로젝트가 있는데요, Github에서 별을 12.4k개나 받은 가장 유명한 face-api.js가 있었어요. 얼굴을 찾기는 물론, 정확도는 조금 떨어지지만, 얼굴 인증(face recognition), 감성/연령 추출 등의 기능도 제공하는데 MIT 라이선스로 무료입니다.

그런데 어느 날 Tensorflow.js 버전 1.x 대에서 업데이트가 중단됩니다. 그래서 지금 face-api.js를 그대로 갖다 쓰면, 다른 Tensorflow.js 버전과 함께 동작할 때 이상한 에러들을 보게 됩니다.

TypeError: t.toFloat is not a function

그래서 Tensorflow.js 버전 2.x 및 3.x에서도 가능하도록 업데이트한 것을 역시나 MIT 라이선스로 배포하기도 했습니다.

  • 링크: https://github.com/vladmandic/face-api

이 face-api.js 외에도 Blazeface라는 가볍고 빠른 알고리즘이 있는데요, Tensorflow.js 모델에 포함되어 Apache 라이선스 2.0으로 공개되어 있네요.

Tensorflow 얼굴인식 - tensorflow eolgul-insig
출처: Tensorflow.js 홈페이지

오늘은 이 Blazeface를 사용해서 React 앱을 만들어보겠습니다.

React Face Detection

★Sample Code(Github)

오늘 만들어 볼 앱은 웹캠 영상을 받아 face detection을 수행하는 앱인데요, 지난 번에 다룬 React 웹캠 앱(아래 튜토리얼 참고)에코드를 추가합니다. React 웹캠 앱에서는 영상을 받아 Draw to Canvas 버튼을 클릭하면 웹캠 영상을 캔버스에 그리는 예제였습니다.

  • React 웹캠 - 3. Canvas
Tensorflow 얼굴인식 - tensorflow eolgul-insig
React 웹캠 앱 실행화면

먼저 npm으로 텐서플로우 라이브러리들을 설치합니다. 우리가 사용할 blazeface도 설치합니다. 

npm i --save @tensorflow/tfjs-backend-webgl @tensorflow/tfjs-converter @tensorflow/tfjs-core
npm i --save @tensorflow-models/blazeface

참고로 공식사이트 안내대로 실행하면 React에서는 오류가 나는데요, @tensorflow/tfjs-backend-webgl을 import하고 tf.setBackend('webgl')을 호출해주면 됩니다.

import * as tf from '@tensorflow/tfjs-core';
import '@tensorflow/tfjs-backend-webgl';
import * as blazeface from '@tensorflow-models/blazeface';

이제 모델을 초기화합니다.

  React.useEffect(() => {
    const initFD = async () => {
      await tf.setBackend('webgl');
      g_var.model = await blazeface.load();
      console.log("model", g_var.model);

      getWebcam((stream => {
        videoRef.current.srcObject = stream;
      }));
    }
    initFD();
  }, []);

React.useEffect(() => {}, []) 구문은 componentDidMount()와 같아서 최초 1회 실행됩니다.

그 안에서 initFD() async 함수를 정의했는데 이 안에서 tf.setBackend()와 blazeface.load()를 await를 사용해서 순차적으로 호출합니다. 전역 변수 g_var.model에 초기화된 모델을 저장하고 웹캠을 엽니다.

웹캠 앱 예제에서 Draw to Canvas 버튼을 누르면 호출되는 drawToCanvas() 함수에서 얼굴 찾기를 수행합니다.

const estimateCanvas = async (canvasRef) => {
  const predictions = await g_var.model.estimateFaces(canvasRef, false);
  return predictions;
}

const drawToCanvas = async () => {
    try {
      const ctx = canvasRef.current.getContext('2d');

      canvasRef.current.width = videoRef.current.videoWidth;
      canvasRef.current.height = videoRef.current.videoHeight;

      if (ctx && ctx !== null) {
        if (videoRef.current) {
          ctx.translate(canvasRef.current.width, 0);
          ctx.scale(-1, 1);
          ctx.drawImage(videoRef.current, 0, 0, canvasRef.current.width, canvasRef.current.height);
          ctx.setTransform(1, 0, 0, 1, 0, 0);
        }

        const preds = await estimateCanvas(canvasRef.current);
        for (let i = 0; i < preds.length; i++) {
          let p = preds[i];
          ctx.strokeStyle = "#FF0000";
          ctx.lineWidth = 5;
          ctx.strokeRect(p.topLeft[0], p.topLeft[1], p.bottomRight[0] - p.topLeft[0], p.bottomRight[1] - p.topLeft[1]);
        }
      }
    } catch (err) {
      console.log(err);
    }
  }

중간에 estimateCanvas()에 캔버스 엘리먼트를 넘겨주는데 이 캔버스를 그대로 blazeface.estimateFaces 파라미터로 넣어주면 됩니다. 간단하죠?

캔버스에 결과를 그리는 drawToCanvas()에서는 먼저 웹캠 영상을 그리고 얼굴을 찾은 뒤, 찾은 얼굴을 빨강 사각형으로 표시합니다. Face detection 결과값 구성은 사이트에도 소개되어 있지만, 간단하게는 개발자도구(F12) 로그로 확인해 볼 수 있습니다.

Tensorflow 얼굴인식 - tensorflow eolgul-insig
결과 값 콘솔 로그

위와 같이 face detection 결과로 배열(찾은 얼굴의 수)이 올라오고 bottomRight[x, y], topLeft[x, y] 값을 이용하여 사각형을 그립니다. 랜드마크는 얼굴의 눈, 코, 입 등을 표시해주는데, 오늘 우리 앱에서는 다루지 않으므로 자세한 내용은 사이트를 참고하세요.

결과 화면을 볼까요? 얼굴은 부끄러워서 가렸습니다.

Tensorflow 얼굴인식 - tensorflow eolgul-insig
실행 결과 - 빨간 사각형으로 얼굴을 찾음

Repeat 버튼을 누르면 주기적으로 얼굴을 찾고 화면에 (사람이든 사진이든) 얼굴이 여러 개면 그만큼 배열이 올라옵니다.

Tensorflow 얼굴인식 - tensorflow eolgul-insig
여러 명을 찾는 경우

오늘은 간단한 얼굴 찾기 웹앱을 만들어봤습니다.

이처럼 Tensorflow.js는 웹앱에서 바로 돌아가기 때문에 사용성에 있어서는 아주 훌륭한 기술이네요!