機械学習で芸能人の顔を分類させるコードを読み解いてみた

[機械学習test04](https://qiita.com/yuni/items/7edfe68de918e12af0ee)で使った、引数に画像を与えると沢尻エリカ(0)と武井咲(1)の結果が0/1で返ってくるやつを自分なりに読み解いてみた。

```FaceType.py
#!/usr/bin/env python
#! -*- coding: utf-8 -*-

import sys
import numpy as np
import tensorflow as tf
import cv2


NUM_CLASSES = 2 #今回は0/1の2つに分類
IMAGE_SIZE = 28
IMAGE_PIXELS = IMAGE_SIZE*IMAGE_SIZE*3

def inference(images_placeholder, keep_prob):
    """ モデルを作成する関数

    引数: 
      images_placeholder: inputs()で作成した画像のplaceholder
      keep_prob: dropout率のplace_holder

    返り値:
      cross_entropy: モデルの計算結果
    """
    def weight_variable(shape):
      initial = tf.truncated_normal(shape, stddev=0.1) 
                #tf.truncated_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None)
                #切断正規分布から乱数を出力
                #つまり、標準偏差の2倍の間に収まるような乱数を生成するということ
                #shape: 戻り値のTensorの次元,mean: 正規分布の平均(デフォルト 0.0), stddev: 正規分布の標準偏差(デフォルト 1.0), dtype: 値の型
      return tf.Variable(initial) #変数の定義

    def bias_variable(shape):
      initial = tf.constant(0.1, shape=shape) #定数の定義
      return tf.Variable(initial) #変数の定義

    def conv2d(x, W):
      return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
             #畳込みを行う関数
             #tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, name=None)
             #input:4次元([batch, in_height, in_width, in_channels])のテンソル
             #filter:畳込みでinputテンソルとの積和に使用するweight
             #strides:１画素ずつではなく、数画素ずつフィルタの適用範囲を計算するための値
             #padding:ゼロパディング(周りを0で埋める)はSAMEを指定
                     #SAMEでは、畳み込みで小さくなる値の分のパディングをあらかじめ付加して処理する
             #ex)weight = tf.random_normal([3, 3, 1, 64])の場合は3x3x1のフィルタで畳込み、アウトプットを64チャネルにする

    def max_pool_2x2(x):
      return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
                            strides=[1, 2, 2, 1], padding='SAME')
             #最大プーリング用の関数
             #tf.nn.max_pool(value, ksize, strides, padding, name=None)
             #values:畳込み層からの出力データを与える
             #ksize:プーリングサイズ

    x_image = tf.reshape(images_placeholder, [-1, 28, 28, 3])
              #tf.reshape(tensor, shape, name=None)
              #tensor:インプットのテンソル
              #shape:[バッチ数Nx画像縦x横xチャネル数(3:RGB)]

    with tf.name_scope('conv1') as scope:
        W_conv1 = weight_variable([5, 5, 3, 32]) #5(縦)×5(横)×3(色)×32(種類)
        b_conv1 = bias_variable([32])
        h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
                  #ReLU関数
                  #入力した値が0以下のとき0になり、1より大きいとき入力をそのまま出力

    with tf.name_scope('pool1') as scope:
        h_pool1 = max_pool_2x2(h_conv1)
                  #最大値プーリングの実行
                  #畳み込み層で抽出した特徴量を圧縮

    with tf.name_scope('conv2') as scope:
        W_conv2 = weight_variable([5, 5, 32, 64])
        b_conv2 = bias_variable([64])
        h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)

    with tf.name_scope('pool2') as scope:
        h_pool2 = max_pool_2x2(h_conv2)

    with tf.name_scope('fc1') as scope:
        W_fc1 = weight_variable([7*7*64, 1024])
        b_fc1 = bias_variable([1024])
        h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
        h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
        h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
                    #ドロップアウト：過学習を防ぐために、あまり関係がないところは無視するような処理
                    #tf.nn.dropout(x, keep_prob, noise_shape=None, seed=None, name=None)
                    #x:プーリング層からの出力
                    #keep_prob:ニューロンが(ドロップアウトされる確率ではなく)生き残る確率

    with tf.name_scope('fc2') as scope:
        W_fc2 = weight_variable([1024, NUM_CLASSES])
        b_fc2 = bias_variable([NUM_CLASSES])

    with tf.name_scope('softmax') as scope:
        y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
               #y=softmax(h_fc_drop*W_fc2+b_fc2)
    return y_conv

if __name__ == '__main__':
    test_image = []
    for i in range(1, len(sys.argv)):
        img = cv2.imread(sys.argv[i]) #openCVで画像読み込み
        img = cv2.resize(img, (28, 28)) #28*28,にリサイズ
        test_image.append(img.flatten().astype(np.float32)/255.0) #画像データを1列にし、0～1のfloat値に正規化する
    test_image = np.asarray(test_image) # numpy形式に変換

    images_placeholder = tf.placeholder("float", shape=(None, IMAGE_PIXELS)) #画像を入れる変数
    labels_placeholder = tf.placeholder("float", shape=(None, NUM_CLASSES)) #正解ラベルを入れる変数
    keep_prob = tf.placeholder("float") #過学習を防ぐためdropoutの適用

    logits = inference(images_placeholder, keep_prob)
    sess = tf.InteractiveSession() #計算グラフを構築して実行

    saver = tf.train.Saver() #chackpointファイル（model作成履歴のようなもの)を保存
    sess.run(tf.global_variables_initializer()) #変数を初期化
    saver.restore(sess, "./model.ckpt") #学習済みモデルmodel.ckptを復元

    #実行
    for i in range(len(test_image)):
        pred = np.argmax(logits.eval(feed_dict={ 
            images_placeholder: [test_image[i]],
            keep_prob: 1.0 })[0]) #テストの際はドロップアウトは行わない（残す率100%:keep_prob=1）
            #np.argmax:指定された配列の中で最大値となっている要素のうち先頭のインデックスを返す
        print (pred)
```

ながながとなりまりした。
まちがってたら教えてくださいm(_ _)m