ディープラーニングで笑顔を自動検知したい

ディープラーニングで笑顔を自動検知するまでの学習過程を綴っていきます。

数字識別(MNIST)を実践~プログラムその3~

数字識別(MNIST)を実践~プログラムその3~

今回はディープラーニングとは直接関係ありませんが、コマンドラインからファイルのパスを受け取れるようにしてみたいと思います。

プログラム

・eval_mnist_cnn.py

# -*- coding: utf-8 -*-
import argparse
import chainer
import chainer.functions as F
import chainer.links as L
import chainer.initializers as I
from chainer import training
from PIL import Image
import numpy as np

# ニューラル・ネットワークの構造
class CNN(chainer.Chain): # ニューラル・ネットワークの定義
    def __init__(self):
        super(CNN, self).__init__(
            conv1=L.Convolution2D(1, 16, 5, 1, 0),
            conv2=L.Convolution2D(16, 32, 5, 1, 0),
            l3=L.Linear(None, 10),
        )

    def __call__(self, x):
        h1 = F.max_pooling_2d(F.relu(self.conv1(x)), ksize=2, stride=2)
        h2 = F.max_pooling_2d(F.relu(self.conv2(h1)), ksize=2, stride=2) 
        y = self.l3(h2)
        return y

def convert_image(img):
    data = np.array(Image.open(img).convert('L').resize((28, 28)), dtype=np.float32)
    data = (255.0 - data) / 255.0
    data = data.reshape(1, 1, 28, 28)
    return data

def main():

    # オプションの設定
    parser = argparse.ArgumentParser(description='ChainerMNISTサンプル')
    parser.add_argument('--inputimage', '-i', default='', help='画像イメージファイル')    
    parser.add_argument('--model', '-m', default='', help='モデルファイル')
    args = parser.parse_args()

    model = L.Classifier(CNN())    
    chainer.serializers.load_npz(args.model, model)

    img = convert_image(args.inputimage)
    x = chainer.Variable(np.asarray(img))
    
    y = model.predictor(x)
    c = F.softmax(y).data.argmax()    
    print('判定結果:{}'.format(c))        

if __name__ == '__main__':
    main()

プログラムの解説

前回との違いは赤文字になっている個所です。
「数字画像ファイル」「学習済みモデルファイル」のパスをコマンドラインから受け取るようにしています。

python eval_mnist_cnn.py --inputimage image/num01.png --model result/CNN.model    

もしくは

python eval_mnist_cnn.py -i image/num01.png -m result/CNN.model    

でファイルを指定してプログラムを実行できるようになりました。

数字識別(MNIST)を実践~プログラムその2~

数字識別(MNIST)を実践~プログラムその2~

今回は前回作成した学習済みモデルを使って推論を行ってみるところまでをやってみたいと思います。

プログラム

プログラム全文が短いのでここに貼り付けてみたいと思います。

学習済みモデル、推論対象の画像ファイルのパスは一旦固定にしています。

・eval_mnist_cnn.py

# -*- coding: utf-8 -*-
import chainer
import chainer.functions as F
import chainer.links as L
import chainer.initializers as I
from chainer import training
from PIL import Image
import numpy as np

# (1)
class CNN(chainer.Chain):
    def __init__(self):
        super(CNN, self).__init__(
            conv1=L.Convolution2D(1, 16, 5, 1, 0),
            conv2=L.Convolution2D(16, 32, 5, 1, 0),
            l3=L.Linear(None, 10),
        )

    def __call__(self, x):
        h1 = F.max_pooling_2d(F.relu(self.conv1(x)), ksize=2, stride=2)
        h2 = F.max_pooling_2d(F.relu(self.conv2(h1)), ksize=2, stride=2) 
        y = self.l3(h2)
        return y

# (2)
def convert_image(img):
    data = np.array(Image.open(img).convert('L').resize((28, 28)), dtype=np.float32)
    data = (255.0 - data) / 255.0
    data = data.reshape(1, 1, 28, 28)
    return data

def main():

    # (3)
    model = L.Classifier(CNN())    
    chainer.serializers.load_npz('result/CNN.model', model)

    # (4)
    img = convert_image('image/num01.png')
    x = chainer.Variable(np.asarray(img))
    
    # (5)
    y = model.predictor(x)
    c = F.softmax(y).data.argmax()    
    print('判定結果:{}'.format(c))        

if __name__ == '__main__':
    main()

プログラムの解説

(1)ニューラルネットワークの定義
前回同様、ニューラルネットワークの定義を行います。前回と全く同じ内容です。


(2)画像変換処理
今回、推論対象となる画像ファイルを読み込むのですが、モデルに合うように変換を行います。
具体的には、ファイル読み込み、グレースケールに変換、28x28ピクセルにリサイズ、白黒反転して正規化等を行っています。


(3)モデルのインスタンス化と読み込み
(1)で定義したCNN()のインスタンス化を行い、前回作成した『CNN.model』ファイルを読み込みます。


(4)推論対象の画像ファイルの読み込み
推論させたい対象となる画像ファイルを読み込んで、convert_image()を使って変換します。
変換したら、それをchainerで扱うVariableという型に変換します。


(5)推論
(4)で読み込んだ画像が0~9のどれに該当するか推論を行って結果を出力します。

プログラムを動かす前の事前準備

(1)推論対象画像の準備
eval_mnist_cnn.pyと同階層に『image』というフォルダを作成して、フォルダ内に『num01.png』という名称で画像を配置します。
以下、筆者が使用した画像ファイルになります。(Windowsのペイントで作成しました)

f:id:amiami05:20190312205811p:plain

(2)学習済みモデル
前回の記事で作成した学習済みモデルファイルが必要になります。

(3)ライブラリのインストール
今回、画像ファイル処理をおこなっているため『pillow』というライブラリのインストールが必要になります。

数字識別(MNIST)を実践~プログラムその1~

数字識別(MNIST)を実践~プログラムその1~

今回の記事から実際にプログラムを書いていきたいと思います。
今回は学習済みモデルを作成するところまでをやっていきます。

プログラム

プログラム全文が短いのでここに貼り付けてみたいと思います。
このプログラムを実行するとresultフォルダ配下に『CNN.model』という学習済みモデルが作成されます。


・train_mnist_cnn.py

# -*- coding: utf-8 -*-
import chainer
import chainer.functions as F
import chainer.links as L
import chainer.initializers as I
from chainer import training
from chainer.training import extensions

class CNN(chainer.Chain):
    def __init__(self):
        super(CNN, self).__init__(
            # (1)
            conv1=L.Convolution2D(1, 16, 5, 1, 0),
            conv2=L.Convolution2D(16, 32, 5, 1, 0),
            l3=L.Linear(None, 10),
        )

    # (2)
    def __call__(self, x):
        h1 = F.max_pooling_2d(F.relu(self.conv1(x)), ksize=2, stride=2)
        h2 = F.max_pooling_2d(F.relu(self.conv2(h1)), ksize=2, stride=2) 
        y = self.l3(h2)
        return y

def main():

    # (3)
    train, test = chainer.datasets.get_mnist(ndim=3)

    model = L.Classifier(CNN(), lossfun=F.softmax_cross_entropy)
    optimizer = chainer.optimizers.Adam()
    optimizer.setup(model)

    # (4)
    train_iter = chainer.iterators.SerialIterator(train, 100)
    updater    = training.StandardUpdater(train_iter, optimizer)
    trainer    = training.Trainer(updater, (20,'epoch'))

    # (5)
    trainer.extend(extensions.ProgressBar())

    # (6)
    trainer.run()

    # (7)
    modelname = "result" + "/CNN.model"
    print('学習済みモデルを保存: {}'.format(modelname))
    chainer.serializers.save_npz(modelname, model)

if __name__ == '__main__':
    main()

プログラムの解説

(1)各層の処理
今回のネットワークは2層の畳み込みと1層のニューラルネットワークがあります。
conv1:1層目の畳み込み処理
引数の意味は、1=入力の数, 16=フィルタの数, 5=フィルタのサイズ(5x5), 1=ストライド, 0=パディング数

conv2:2層目の畳み込み処理
引数の意味は、16=入力の数, 32=フィルタの数, 5=フィルタのサイズ(5x5ピクセル数), 1=ストライド, 0=パディング数

l3:ニューラルネットの処理
引数の意味は、None=入力の数, 10=出力の数
第1引数は"None"を指定すると自動計算してくれます。
第2引数は今回だと0~9の10個の数字のため10としています。


(2)損失関数
今回"trainer"を使っているのですが、これを使用する場合はChainクラス内部に"__call__"メソッドで損失関数を返す必要があるらしく、
正直よく分かっていないのですが、こういうものなんだという事で今は理解しておきます。

h1:入力xに対してconv1の処理を行ってReLU処理をしてから、フィルタサイズ2x2, ストライド2としてプーリングを行う
h2:入力h1に対してconv2の処理を行ってReLU処理をしてから、フィルタサイズ2x2, ストライド2としてプーリングを行う
y:入力h2に対してl3の処理を行って最終的な値を出力する

前の層の出力が次の層の入力になる様子がハッキリと見てとれますね。


(3)MNISTデータセットの読み込みとニューラルネットワークの設定
get_mnist():データを読み込みます。trainに訓練用データ(データ数60,000)、testにテスト用データ(データ数10,000)が格納される
ndim=3:画像のチャネル数(今回はグレースケールなので1)、画像の横幅、画像の高さ、の3次元のデータにするという設定
optimizers.Adam():最適化アルゴリズムの1つ。よく分かっていないが、パフォーマンスが凄いらしい。。。


(4)Trainerの設定
Trainer:トレーナー。学習を実行するところ。学習とは以下の一連の流れを指す。
1.入力データが各層を伝播していき出力値を計算
2.出力値と教師データから損失(誤差)を計算
3.誤差を伝播させて誤差が小さくなるようにパラメータ(重み)を更新

iterators.SerialIterator(train, 100):100はミニバッチサイズ。1データごとにパラメータ更新を行うと効率が悪いため、ある程度のデータの塊(ミニバッチサイズ)で更新を行う。ミニバッチサイズ100だと、100個のデータの誤差の平均値を基にパラメータを更新する。


(5)進捗バーの表示
学習の進捗状況を表示するためのバーを表示


(6)実行
学習を開始する


(7)モデル出力
学習済みモデルを出力する。今回の場合だとresultフォルダ内(無ければ作成される)にCNN.modelという名称でファイルが作成される。

数字識別(MNIST)を実践~環境構築その3~

数字識別(MNIST)を実践~環境構築その3~

前回までの記事でディープラーニングについて僅かではありますが分かってきました。 そこで、今回から数字識別(MNIST)を題材としてディープラーニング実践編に入っていきたいと思います。

Chainerのアップデート

前回、Chainerをインストールしましたが、バージョンが古いようなので今回はアップデートをしてみます。

 

前回同様Anaconda Navigatorを開いて、現バージョンを確認します。

f:id:amiami05:20190127132309p:plain

 

"chainer"の横のチェックマークをクリックするとメニューが出てきます。

現時点(2019/01/27)での最新版をチェックして、画面右下の"Apply"ボタンを押します。

f:id:amiami05:20190127132322p:plain

 

インストールするパッケージ一覧が表示されるので、更に"Apply"ボタンを押します。

f:id:amiami05:20190127132338p:plain

 

 

しばらく待つと、アップデートが完了します。

f:id:amiami05:20190127223723p:plain



 

 

数字識別(MNIST)を実践~環境構築その2~

数字識別(MNIST)を実践~環境構築その2~

前回までの記事でディープラーニングについて僅かではありますが分かってきました。 そこで、今回から数字識別(MNIST)を題材としてディープラーニング実践編に入っていきたいと思います。

Chainerのインストール

今回はChainerを使って進めていきたいと思います。
参考にしてきた書籍がたまたまChainerを使っていたためですが、 後々、別フレームワークを利用して同じ事をやってみるのも面白いかなと考えています。

公式ページ
chainer.org/

・Anaconda Navigator を起動

作業用に仮想環境を作成してその中で開発を進めていきたいと思います。

f:id:amiami05:20190120231439p:plain

・Environments→Createを選択

f:id:amiami05:20190120231450p:plain

・Nameを入力→Createを選択

今回は"Chainer"と入力しました。

Pythonのバージョンは好きなもので構いません。今回は"3.5"にしました。

f:id:amiami05:20190120231500p:plain

しばらく待つと完成です。

f:id:amiami05:20190120231507p:plain

・Not installedを選択、検索窓に"Chainer"を入力

f:id:amiami05:20190120231522p:plain

・チェックを付けて、Applyを選択

f:id:amiami05:20190120231526p:plain

続けてApplyを選択します
f:id:amiami05:20190120231531p:plain

・Installedを選択、検索窓の入力を解除

f:id:amiami05:20190120231535p:plain

インストール後の確認

・"Chainer"の右の▶を選択、表示されるポップアップの"Open Terminal"を選択

f:id:amiami05:20190120231539p:plain

コマンドプロンプトが開かれるので、"python"と入力してインタラクティブモードにした後、

>>> import chainer

と入力し、下記のようにエラーメッセージが表示されなければインストール成功です

f:id:amiami05:20190120231543p:plain

 

数字識別(MNIST)を実践~環境構築その1~

数字識別(MNIST)を実践~環境構築その1~

前回までの記事でディープラーニングについて僅かではありますが分かってきました。 そこで、今回から数字識別(MNIST)を題材としてディープラーニング実践編に入っていきたいと思います。

開発環境

今回は学習モデル作成と数字認識の両方をWindows PCで実施したいと思います。
Pythonを動かす方法は色々ありますが今回はAnacondaを使うことにします。

Anacondaのインストールはここを参考に行いました。
Windows10環境でAnaconda、Pycharmインストール・設定トライ(1)

以下、筆者の環境となります。
Windows 10 Pro 64bit
・Anaconda3-5.1.0

インストール後の確認

Anacondaのインストールが終わったら、Pythonが使えるようになっているかを調べます。コマンドプロンプトを開いて以下のコマンドを実行します。

python -V

インストールされているPythonのバージョンが表示されたらインストール成功となります。
ちなみに、筆者の環境での実行結果です。

Python 3.6.7 :: Anaconda custom (64-bit)

畳み込みニューラルネットワーク

畳み込みニューラルネットワーク

ディープラーニングでは、画像認識や音声認識自然言語処理の分野で研究が進められているようですが、画像認識においてはCNN(Convolutional Neural Network)が高い画像認識率を達成しているようです。
今回のゴールを達成する上で必ず必要になると思われますので、こちらについても調べてみました。

CNNの構造

CNNの構造については以下のサイトが図入りで分かりやすいです。
CNN(畳み込みニューラルネットワーク)の仕組み

畳み込み

畳み込みとは元の画像から特徴点を抽出るする処理のこと


入力画像が5x5、畳み込みフィルタが3x3とした場合

f:id:amiami05:20190114210552p:plain

まずは左上(グレーになっている個所)に注目し、

f:id:amiami05:20190114210419p:plain

入力値とフィルタ値の掛け算を行って、すべて合算して、

f:id:amiami05:20190114210601p:plain

特徴マップを得ます。
活性化関数がReLU関数の場合、正ならばそのまま、負ならば0がセットされます。

f:id:amiami05:20190114210609p:plain

フィルタ適用範囲を1つ右にずらしてストライド1)、同様に計算を行います。

f:id:amiami05:20190114210621p:plain

同様に、右下まで進めていきます。

f:id:amiami05:20190115201129p:plain

f:id:amiami05:20190115201137p:plain

f:id:amiami05:20190115201144p:plain

パディング

畳み込みを行った場合、出力サイズ(特徴マップ)が小さくなってしまう。
これを防ぐための方法としてパディングがある。

f:id:amiami05:20190115201735p:plain

ストライド

フィルタのずらし方を変えることも可能

f:id:amiami05:20190115202017p:plain

プーリング

画像の情報を一気に捨ててサイズを小さくする

f:id:amiami05:20190115202445p:plain

f:id:amiami05:20190115202507p:plain

f:id:amiami05:20190115202519p:plain

f:id:amiami05:20190115202530p:plain

プーリング処理では、特徴マップの"ごく狭い領域"での最大値を取得して新たな特徴マップを作っており、これにより少々画像が変化しても同じ結果となるため画像の変化に強くなるらしい。

最後に

とりあえず、今後必要になりそうな言葉の意味について調べてみました。
従来ではこの特徴マップの作り方を人が設定していたようですが、CNNではコンピュータがこれをが自動抽出してくれます。これはとても画期的な事で、確かにこれを人がやるのはとてもじゃないけど無理と感じました。
今はライブラリを使えば簡単にCNNが使えるようなので、先人に感謝しつつ、学習を進めていきたいと思います。