あっぱれ日和、 - What a "Appare" day -

あっぱれ日和、 - What a "Appare" day -

通りすがりのしがない映画好きが綴る、モノづくりの記録や備忘録。そして、影響を受けた映画の記録。

機械学習初心者がDeep Learningを試してみる season CNN ~リベンジ編:第6話 改良、さらなる高みへ。Alexnetモデルの実装~

はじめに、

こんにちは、そしてこんばんわ。
前回の記事(以下リンク)ではData Augmentationを試してみました。この時は学習データを増やすことで精度が向上するかを試しました。で結果は60%ほどまでしか行きませんでした。
apao-m-appare99.hatenablog.com


最近、仕事が忙しいのとオンライン英会話に没頭していたので更新できていませんでした。
嗚呼、早くDeepLearningでワンちゃん猫ちゃん品種認識作りたい!
今回も前回も記事の続きとなります。今回はAlexnetを実装してみるということを実践してみたいと思います。

【目次】

Alexnetとは

今までのモデルは1998年に考案されたLeNetと呼ばれるCNNモデルです。
今回使用するのは2012年に開催された画像認識のコンテストであるILSVRCにおいてDeep Leerningのモデルとして初めて優勝したAlexnetです。

元の論文に記載されているモデルとしては以下のようなものになります。

f:id:appare99:20200725185152p:plain
AlexNet architecture

これオフィシャルな図なのですが画像が切れてしまってますよね。。。
以下の画像の方が分かりやすいかもしれません。以下は
Understanding AlexNet | Learn OpenCV
から引用した画像になります。

f:id:appare99:20200725185427p:plain
AlexNet architecture

実装環境

そういえば、ずっと実装環境について書いていなかった。
実装環境はずっと変わっていないです。すべての記事で同じ環境で行っています。
環境は次の通りになります。

  • OSはwindows10
  • メモリは8G
  • プログラムの言語はpythonでエディタはjupyter notebook

プログラム

コードを書くにあたり参考にしたサイトは次のサイトになります。
qiita.com
qiita.com

そして、今回のモデル構築部分のプログラムは以下になります。

from keras.initializers import TruncatedNormal, Constant

def conv2d(filters, kernel_size, strides=(1, 1), bias_init=1, **kwargs):
    trunc = TruncatedNormal(mean=0.0, stddev=0.01) # 正規分布で重みを初期化
    cnst = Constant(value=bias_init) # 全ての重みを定数(ここではbias_init=1で初期化)
    # 活性化関数relu
    # ゼロパディング
    return Conv2D(
        filters,
        kernel_size,
        strides=strides,
        padding='same',
        activation='relu',
        kernel_initializer=trunc,
        bias_initializer=cnst,
        **kwargs
    )
def dense(units, activation='tanh'):
    trunc = TruncatedNormal(mean=0.0, stddev=0.01)
    cnst = Constant(value=1)
    return Dense(
        units,
        activation=activation,
        kernel_initializer=trunc,
        bias_initializer=cnst,
    )
from keras.models import Sequential
from keras.layers.convolutional import MaxPooling2D
from keras.layers import Activation, Conv2D, Flatten, Dense,Dropout
from keras.optimizers import SGD, Adadelta, Adagrad, Adam, Adamax, RMSprop, Nadam
import time
from keras.layers.normalization import BatchNormalization


model = Sequential()

# 第1畳み込み層
model.add(conv2d(96, 11, strides=(4,4), bias_init=0, input_shape=(image_size, image_size, 3)))
model.add(MaxPooling2D(pool_size=(3, 3), strides=(2, 2)))
model.add(BatchNormalization())

# 第2畳み込み層
model.add(conv2d(256, 5, bias_init=1))
model.add(MaxPooling2D(pool_size=(3, 3), strides=(2, 2)))
model.add(BatchNormalization())

# 第3~5畳み込み層
model.add(conv2d(384, 3, bias_init=0))

model.add(conv2d(384, 3))

model.add(conv2d(256, 3))

model.add(MaxPooling2D(pool_size=(3, 3), strides=(2, 2)))
model.add(BatchNormalization())

# 密結合層
model.add(Flatten())
model.add(dense(4096))
model.add(Dropout(0.5))
model.add(dense(4096))
model.add(Dropout(0.5))

# 読み出し層
model.add(dense(dense_size, activation='softmax'))

# コンパイル
model.compile(optimizer=SGD(lr=0.01), loss='categorical_crossentropy', metrics=['accuracy'])

model.summary()

実際に学習させてみる①

ここで前回の記事では以下のData Augmentationで精度が60%程に達したということでした。なので今回は以下のデータを元データに加えたデータで学習させていきましょう。

  • 上下反転
  • 左右反転
  • Cutout(画面の一部を塗りつぶす)
  • 画像回転(60°~120°)×3

...と思ったのですがこれだと以下のようなエラーが出てきました。Xのところは数字が入ってました。

MemoryError: Unable to allocate XXXX GiB for an array with shape (XXXXXXX, 227, 227, 3) and data type float32

●ここでポイント
今回の実装環境では以下のようなエラーが出てきます。

MemoryError: Unable to allocate 29.8 GiB for an array with shape (51688, 227, 227, 3) and data type float32

これはおそらくtensorflowで使うメモリを使いすぎてしまっていることによって発生しているものと思われます。(間違っていたらご指摘ください)
その証拠に例えば複数のそれぞれ違うプログラムを立ち上げて学習させていたとしましょう。この時、今エラーが起きているプログラム以外に回しているプログラムを止めて(shutdownして)、改めて学習をさせるとエラーが解消されることがあります。

しかし

  • 上下反転
  • 左右反転
  • Cutout(画面の一部を塗りつぶす)
  • 画像回転(60°~120°)×3

と元画像を含んだデータでは他のプログラムをshutdownしてもエラーが出たままになりました。

少しずつ減らしましたが、結局、元画像 +上下反転で学習させてみることになりました。これ以上増やすと上記エラーが出てきてしまい学習できないです。せっかくのData Augmntationの検証がパアになってしまいました。HAHAHA....

まあいいでしょう。この元画像 +上下反転で学習させてみます。
・・・
あまり時間は定かではないですが、2日~3日プログラムを回し続けた状態になりました。
そして3日目になって以下のメッセージが出て学習のプログラムが止まりました。

f:id:appare99:20200613190818p:plain

見てください。赤枠で囲まれているところ。エポック数100で回して91回目のエポック数の時に止まっている。後、9回だったのに。。。。
f:id:appare99:20200613192026p:plain

OMG!!!!!!!
頭の中の小梅太夫が叫んでます。「チクショー!!!」って。そして頭の中のザブングルが叫んでます「悔しいです!!!!」って。
もう混乱です。。。
というかこんな時間かけて結果1つ出せないとめんどくさくなってきます。。。

最近、私の大切なノートPCに負荷をかけすぎていると思うので当分の間はできるだけ学習とかは自粛したいなぁなんて思ってます。。。

そもそもDeep LearningってGPUとかが出てきたから現実性の出てきた技術なのにCPUしか積んでないノートPCで挑もうなんて最初から無謀だったんすかね。。。

愚痴多めで申し訳ないです。
学習方法①(実際に回してみる①)だと効率が悪いというかPCに負荷をかけすぎている気がしてならないので次の学習方法②(実際に回してみる②)を試してみたいと思います。

実際に学習させてみる②

「実際に学習させてみる①」とは別の方法として学習方法2ではモデルを保存しながら学習を繰り返していくという方法をとりたいと思います。
具体的には、

元画像を学習→学習させたモデルを保存(この時のモデルをモデル phase 1とする)→モデル phase 1を用いて今度は左右反転させたモデルを学習させる→学習させたモデルを保存(この時のモデルをモデル phase 2とする)→モデル phase 1を用いて今度は上下反転させたモデルを学習させる→...

というように学習させて学習させたモデルを保存、そして保存したモデルを今度は別の学習データ(上下反転、左右反転、Cutout...etc)を使って学習するということを繰り返していくことにしました。
この方法がいい方法なのかはわかりませんが、、、ただ、特徴量を最適化するための学習を繰り返して更新していくので特に問題ないじゃないかと思ってます。

まずは1回目の学習における検証の損失と精度になります。(phase 1)
1回目は普通の画像データを学習させます。
...。
検証損失: ????
検証精度: ????
...。
すいません。1回目の結果を出力させるのを忘れてしまっていました。

まずは2回目の学習における検証の損失と精度になります。(phase 2)
2回目は左右反転させた画像データを学習させたものになります。
検証損失: 1.72
検証精度: 0.559

続いて3回目の学習における検証の損失と精度になります。(phase 3)
3回目は上下反転させた画像データを学習させたものになります。
検証損失: 1.86
検証精度: 0.552
いや、悪化しとる...

続けます。4回目の学習における検証の損失と精度になります。(phase 4)
4回目はCutoutさせた画像(画像の一部を塗りつぶした画像)データを学習させたものになります。
検証損失: 1.17
検証精度: 0.689
お、精度が上がった!

そして5回目。5回目の学習における検証の損失と精度になります。(phase 5)
5回目はランダムな角度に回転させた画像データを学習させたものになります。
検証損失: 2.34
検証精度: 0.468
あらら、下がった。

なんか学習させていくのにつかれたのでphase 4の段階のモデルでいったん完成といたします。約70%の精度になります。時には引くことも大事ですよね。
精度が上がったり下がったりしてましたが、結局の原因はわからずですね。これが機械学習ブラックボックスと言われる所以ですね。

まとめ

とりあえず、猫ちゃんとワンちゃんの認識器が完成いたしました(完成したということにしましょう(笑))。精度は約70%ですが、まあ許容範囲とします。(甘えです申し訳ないです。)
次回以降はこの認識モデルを用いてLINEのチャットボットとして実装することに挑戦したいと思っております。

ちょっと考察

以下のグラフ画像は画像認識コンテストであるILSVRCにおいて優勝したチームのモデルの誤認識率を表しています。

引用ページは以下になります。
devopedia.org

f:id:appare99:20200613203529p:plain
Image Net画像認識コンテスト(ILSVRC)の年度ごとの優勝チームの誤認識率

こちらのグラフでAlexnetは16%の誤認識率になっています。つまり良くても84%ほどの精度にまでしかならないということでしょう。
それ以降のモデルだとさらに高い精度が期待できるかもしれません。いつかやってみる機会があればやってみたいです。