全然エンジニアじゃないけど機械学習で顔を分類した

##はじめに
IT企業でseleniumをマクロとかPythonで動かす程度の、カスタマーサポート担当やってますー。
たまたま見つけたAI開発コンテスト「Neural Network Console Challenge」に参加してみたので、投稿しますー。

##「Neural Network Console Challenge」とは
https://nnc-challenge.com/

ソニーネットワークコミュニケーションズ株式会社がノンプログラミングでAI開発できるGUIツールである「Neural Network Console(以下NNC)」を、
ピクスタ株式会社が普段扱うことのできない10,000点もの人物画像データを提供することで、初心者にも優しいAI開発チャレンジになっているみたいです。

##テーマ決め
このチャレンジでは画像分類のテーマを各自が決めて、NNCで学習。学習結果をプロセスとともに提出するというもの。

画像を見渡す中で人物が写った画像がやはり多そうだったので、まずは顔を抽出。
OpenCVの分類器でPIXTA画像から大量の顔を切り抜く。

画像の前処理？については下記記事を参考にさせていただきました。初心者にもわかりやすい投稿者様に感謝。

[ディープラーニングでザッカーバーグの顔を識別するAIを作る](https://qiita.com/AkiyoshiOkano/items/72f3e4ba9caf514460ee)
[TensorFlowによるももクロメンバー顔認識](https://qiita.com/kenmaz/items/4b60ea00b159b3e00100)

###顔の抽出
```
# -*- coding:utf-8 -*-

import cv2
import numpy as np

from PIL import Image

# PIXTA画像を保存したフォルダ
input_data_path = './pixta_images/'
# 切り抜いた画像の保存先ディレクトリ
save_path = './cutted_face_images/'
# OpenCVのデフォルトの分類器のpath
cascade_path = './opencv/data/haarcascades/haarcascade_frontalface_default.xml'
faceCascade = cv2.CascadeClassifier(cascade_path)

# 顔検知に成功した数
face_detect_count = 0

# 集めた画像データから顔が検知されたら、切り取り、保存する。
types = ['*.jpg']
paths = []
for t in types:
    paths.extend(glob.glob(os.path.join(input_data_path, t)))
for p in paths:
    img = cv2.imread(p, cv2.IMREAD_COLOR)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    face = faceCascade.detectMultiScale(gray, 1.1, 3)
    
    if len(face) > 0:
        for rect in face:
            x = rect[0]
            y = rect[1]
            w = rect[2]
            h = rect[3]
            cv2.imwrite(save_path + 'cutted_face' + str(face_detect_count) + '.jpg', img[y:y+h, x:x+w])
            face_detect_count = face_detect_count + 1
```

1500枚の画像から約2500くらいの顔が検出されました。検出した顔の中には、顔っぽく見える影やドット柄も含まれており、手作業で削除します。
1000枚程度を削除して、残ったのは1500の顔。

![スクリーンショット 2020-03-31 21.16.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132582/040399cb-4f1f-a1d4-e319-716b3a8eade8.png)
学習用データ提供：PIXTA

運営事務局からのテーマ例には嬉しい/悲しい/恥ずかしいなど、感情によって分類するというものがあったが、顔写真をみていると、笑顔ばっかり。
そこで、笑顔を何種類かに分類することに決めました。

##データセットの作成
NNCにアップロードできる形にするため、1500の顔を笑顔の程度で分別していきます。

まず手始めに笑っているか笑っていないかの２つに分類し、それぞれフォルダにまとめました。さらに、ファイル名とラベルを定義したcsvを準備してNNCにアップロード。

学習のモデルはSONYさんがYouTubeで配信しているNNCに関する説明動画を元に、以下のような物を作ってみました。
![スクリーンショット 2020-03-31 0.44.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132582/d6e42b1c-1e51-22ea-2a62-6f7113c7c3ea.png)

学習結果はというと、、、

![スクリーンショット 2020-03-31 0.47.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132582/de9d68a9-fcd9-b874-0af8-0d824275f8f6.png)

うーん、ラベルそれぞれで画像データの数にばらつきもあったし、こんなものなのかな。
よくわかりませんが、２つの分類では面白くないので、次は分類を増やしていきます。


- アハハ（声が出てる）
- ニコニコ（顔全体の笑顔）
- にこり（口もしくは目が笑う）
- フフフ（微笑み）
- 真顔

追加での顔抽出も行い、それぞれ200枚程度準備して学習データとテストデータに分けてアップロード。

![スクリーンショット 2020-03-31 0.57.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132582/e49b88ac-babd-09eb-7ebc-b3f6b41bf193.png)


見事にグダグダな結果ですね。横に一つずれてるものも含めれば、なんとなく合ってるくらい。
原因は、私が分別する中でそもそも自分の中で定義できてないからかもしれません。抽出した笑顔をずっと見てると、何がなんだかわからなくなってしまい笑


分類を一つ減らし、４つでデータを作り直しました。

- アハハ（声が出てる）
- ニコニコ（口を開けてる）
- フフフ（口を閉じてる）
- 真顔

さっきより随分改善しました。が70％にも届かず。
![スクリーンショット 2020-03-31 1.02.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132582/412d56c2-5c72-020b-4044-e867a0d427e1.png)


その後は以下の動画塔を参考に、過学習を防ぐためにImage Augmentationやcutout、Dropoutなど試してみるものの、なかなか精度は向上せず。

期限ということもあり、結局中間層を減らし、以下の結果を出してタイムアップのようです。

![スクリーンショット 2020-03-31 1.08.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132582/ef46f080-0c41-f8aa-6e2e-7577f7a1ac0e.png)
![スクリーンショット 2020-03-31 1.09.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132582/b2672ae6-8795-fb62-3fe9-0c0c8d8e4e81.png)

![スクリーンショット 2020-03-31 21.08.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132582/f4969822-43b4-15f7-5f77-a18cd2aed2f1.png)
学習用データ提供：PIXTA

75％程度となかなか満足のいく結果とはなりませんでしたが、時間があれば、自分で準備した画像などで改めてチャレンジしてみたいと思いますー。知識ないながらも楽しく取り組めました！