TensorFlowで嵐の顔を判別

# はじめに
今回 TensorFlow を初めて使いました。TensorFlow で画像認識をやりたいのですが、公式のサンプルはあまりピンと来ず、よさそうな記事を見つけたので参考にしました。

参考にした記事
[keras(tensorflow)で花の画像から名前を特定 - Qiita](https://qiita.com/tsunaki/items/608ff3cd941d82cd656b)

過去のiPhoneを使った機械学習の投稿 
[iPhoneで顔認識 - Qiita](https://qiita.com/koji4104/items/7afc8c8fba6a942f4145)
[機械学習でストリートファイター - Qiita](https://qiita.com/koji4104/items/bbcd5ba257f0b2b98961)
[iPhoneと機械学習でフライドチキンの部位を分類する - Qiita](https://qiita.com/koji4104/items/91d6353254c01f61f8f8)

# Tensorflowの準備

**TensorFlowって何**
（ウィキペディアより）
TensorFlow（テンソルフロー)とは、Googleが開発しオープンソースで公開している、機械学習に用いるためのソフトウェアライブラリである。

具体的には、Python環境で下記を実行するとインストールできます。
pip install tensorflow

**画像判別に必要なもの**
1. 訓練画像を集めます。
2. 訓練画像から学習モデルを作ります。
3. 入力画像を学習モデルに与えると判別されます。

# 訓練画像の準備
<img width="1120" alt="2019-0330-01.png" src="https://qiita-image-store.s3.amazonaws.com/0/253995/5bbf7bd1-672b-00ce-8f81-cf5c5e2e373b.png">

imagesフォルダ以下に画像を集めます。フォルダー名がカテゴリー名（分類名）となります。150枚以上が望ましいのですが、私は50枚程で心が折れました。
今回は、相葉、松本、二宮、大野、櫻井の５人です。入力画像から「強いて言えば誰か」を判別します。

# 使い方
1. images フォルダ内に学習モデル用の訓練画像を用意します。
2. inputs フォルダ内に予測したい入力画像を用意します。
3. python model.py　→　mymodel.hdf5 が作成されます。
4. python main.py　→ print で結果が出力されます。

# ソースコード

以下を実行します。足りないものは pip install 行ってください。

```python:model.py
# -*- coding: utf-8 -*-
import sys
import os
import gc
import glob
import numpy as np
import pandas as pd
import random, math
import traceback
from PIL import Image
from keras.models import Sequential
from keras.layers import Convolution2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.utils import np_utils

class MyModel:
    def __init__(self):
        self.images_dir = './images'
        self.hdf5_file = './mymodel.hdf5'
        self.sub_dir = [name for name in os.listdir(self.images_dir) if name != ".DS_Store"]
    
    #------------------------------------
    # 学習モデルの作成と保存
    #------------------------------------
    def save_model(self):
        try:
            # 画像読み込み
            train_data = [] 
            for i, sdir in enumerate(self.sub_dir): 
                print("->", sdir)
                files = [name for name in os.listdir(self.images_dir + "/" + sdir) if name != ".DS_Store"]
                for f in files:
                    data = self.create_data_from_image(self.images_dir + '/' + sdir + '/' + f)
                    train_data.append([data,i])
        
            # シャッフル
            random.shuffle(train_data)
            X, Y = [],[]
            for data in train_data: 
                X.append(data[0])
                Y.append(data[1])
        
            test_idx = math.floor(len(X) * 0.8)
            xy = (np.array(X[0:test_idx]), np.array(X[test_idx:]), 
                np.array(Y[0:test_idx]), np.array(Y[test_idx:]))
            x_train, x_test, y_train, y_test = xy

            # 正規化
            self.x_train = x_train.astype("float") / 256
            self.x_test = x_test.astype("float") / 256
            self.y_train = np_utils.to_categorical(y_train, len(self.sub_dir))
            self.y_test = np_utils.to_categorical(y_test, len(self.sub_dir))
            
            # 学習モデルの保存
            model = self.create_model_from_shape(self.x_train.shape[1:])
            model.fit(self.x_train, self.y_train, batch_size=32, nb_epoch=10)
            model.save_weights(self.hdf5_file)
        
            # テスト
            score = model.evaluate(self.x_test, self.y_test)
            print('loss=', score[0])
            print('accuracy=', score[1])

        except Exception as e:
            print('Exception:', traceback.format_exc(), e.args)

    #------------------------------------
    # 入力画像の予測
    #------------------------------------
    def predict_from_dir(self, dir):
        X = []
        files = [name for name in os.listdir(dir) if name != ".DS_Store"]
        for f in files: 
            data = self.create_data_from_image(os.path.join(dir,f))
            X.append(data)
        X = np.array(X)
        model = self.create_model_from_shape(X.shape[1:])
        model.load_weights(self.hdf5_file)
        predictions = model.predict(X)
        return predictions

    # 画像ファイルからデータを作成
    def create_data_from_image(self, file):
        img = Image.open(file)
        img = img.convert("RGB")
        img = img.resize((50,50))
        data = np.asarray(img)
        return data

    # Shape から Model の作成
    def create_model_from_shape(self, shape):
        # K=32, M=3, H=3
        model = Sequential()
        model.add(Convolution2D(32, 3, 3, border_mode='same', input_shape=shape))
        
        # K=64, M=3, H=3 調整
        model.add(Activation('relu'))
        model.add(MaxPooling2D(pool_size=(2, 2)))
        model.add(Dropout(0.25))
        model.add(Convolution2D(64, 3, 3, border_mode='same'))
        model.add(Activation('relu'))
        # K=64, M=3, H=3 調整
        model.add(Convolution2D(64, 3, 3))
        model.add(MaxPooling2D(pool_size=(2, 2)))
        model.add(Dropout(0.25))
        model.add(Flatten()) 
        model.add(Dense(512))
        # biases
        model.add(Activation('relu'))
        model.add(Dropout(0.5))
        model.add(Dense(len(self.sub_dir)))
        model.add(Activation('softmax'))
    
        model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy'])
        return model

if __name__ == "__main__":
    m = MyModel()
    m.save_model()
    gc.collect()
```

```python:main.py
# -*- coding: utf-8 -*-
import sys, os
import numpy as np
import pandas as pd
from PIL import Image
from model import MyModel

images_dir = './images'
inputs_dir = './inputs'
categories = [name for name in os.listdir(images_dir) if name != ".DS_Store"]
inputs = [name for name in os.listdir(inputs_dir) if name != ".DS_Store"]

# 入力画像の予測値
predictions = MyModel().predict_from_dir(inputs_dir)

# 結果出力
strcat = ''
for cat in categories:
    strcat += cat.ljust(10,' ')
print("             ", strcat)

for i,input in enumerate(inputs):    
    strpre = input.ljust(14, ' ')
    for pre in predictions[i]:
        strpre += str(round(pre,3)).ljust(10,' ')
    print(strpre)
```

# 結果
```出力
              Aiba      Matsumoto Ninomiya  Ohno      Sakurai
aiba.png      1.0       0.0       0.0       0.0       0.0
ninomiya.png  0.0       0.0       1.0       0.0       0.0
sakurai.png   0.0       0.0       0.0       0.0       1.0
koji1.png     0.0       0.0       0.0       1.0       0.0
koji2.png     0.0       0.0       0.0       1.0       0.0
koji3.png     0.0       0.0       0.0       1.0       0.0
```

|入力画像|予測結果|
|:--|:--|
|<img src='https://qiita-image-store.s3.amazonaws.com/0/253995/d97bfc51-273c-ba17-03cb-e589d7a3e53b.png' width=50>|Aiba|
|<img src='https://qiita-image-store.s3.amazonaws.com/0/253995/79c89940-0167-dca5-4539-5806dab67e31.png' width=50>|Ninomiya|
|<img src='https://qiita-image-store.s3.amazonaws.com/0/253995/156f0294-3ada-f4a9-9831-eb5376a815a1.png' width=50>|Sakurai|
|koji1.png<br>koji2.png<br>koji3.png   |Ohno|

う～ん、０と１しか出ないのですね。要勉強です。

# まとめ
私（Koji4104）は大野くんに似ているという結果が出ました。私も違う景色を見てみたいです。