ディープラーニングはイカとタコを見分けられるか?

イカとタコは似ている

いや、実際にはそんなに似ていないのかもしれないが。

スプラトゥーンにおけるイカとタコはまあまあ見た目は似ているものの、人間が見ればどちらがどちらであるかはすぐにわかる。

そこそこプレイしている人であれば髪型だけでも判断がつくだろうし、目の周りの黒いアイラインが繋がっているかそうでないかでも区別ができる。

しかし、コンピュータがそれを正しく認識できるとは限らない。今では画像認識の精度はかなり高くなっており、一部では人間の認識を超えるまでに至っているが、そこまで辿り着くにはかなりの時間を要した。

最近はコンピュータの処理能力が発達したことや、アルゴリズムが改善されたこと、画像認識ライブラリ(OpenCVなど)が充実してきたこともあって初心者でも簡単に画像認識を試せるようになった。

じゃあ、ぼくも試してやろうじゃないか!!

先人の知恵を借りる

前々から知っていたのだが、TensorFlowを使ってアニメ制作会社を判定する記事がここで紹介されている。

アニメ制作会社を判定するのは人間からするとイカとタコを判定するよりもよっぽど難しいように思える。実際、イカとタコがすぐに区別できる人でも動画工房とTYOアニメーションズを区別するのは難しいだろう。

自慢ではないが、自分は99%見分けられる自信がある。これでも作画にはこだわりがあるのだ。

この記事によるとたった100枚程度でかなり正確に判断できているようだ。二択問題であれば適当に答えても正解率50%が期待できるので「判定するプログラムができた!」と主張するためには正解率90%以上は少なくともほしいところだ。

ちなみに数字判定は適当に答えたら正解率10%程度なのだが、CNNというアルゴリズムはたった1分程度の学習で正解率99.25%近くを叩き出している。

むむ?なんだか予備知識だけでいけそうな気がしてきたぞ??

方針

ゆるゆり判定機では顔を抜き出していたのですが、イカでも同じことができるんでしょうか?

まあ人間っぽい顔立ちをしているので無理ではないような気も??

しかし、イカとタコの区別は顔ももちろんのことながら、髪型も影響してくるのでこの辺はあまり意味がないかもしれません。

顔認識を行う

そのうちやるかも。でもいまはやりませーん!!

学習用画像を用意する

これが結構めんどくさい。しかもラベリングしなきゃいけない。キャプチャソフト持っていたので、それでひたすらスクショを繰り返して180×180のキャラクターの顔周辺の画像を合計12000枚程度作成した。

インクリングのボーイとガール。オクトリングのボーイとガールがいるので、4パターンの分類が必要である。

必要なのだが、まずは簡単にインクリングのボーイとガールを識別するためのコードを書くことにする。

ディープラーニングはボーイとガールを見抜けるか

ボーイとガールの違い。

人間が見れば全く違うということがわかる。これを間違える人はいないだろう。ただ、ギアの種類によっては結構難しい。

特にこのように頭部が完全に覆われてしまうパターン。人間であれば腕組のポーズからボーイであることがわかるかもしれないが、AIにこれをいきなり認識しろというのは酷だろう。

認識させるためにはこの腕を組んだボーイの画像を大量に用意する必要があるが、今回はランダムに画像をとっただけなので腕組のポーズの画像がそこまで集まっていない。

なので、ある程度の誤判断は許容しなければいけないだろう。

コードの解説

こうやってコードがどんな動きをしているのかを知ることは重要です。

インポート部
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras import backend as K

今回は自分でデータセットを作るのでImageDataGeneratorを使用します。

Sequentialはニューラルネットワークを構築するときにどんなネットワーク層にするかを決定します。Sequentialは単純に積み重ねるようなイメージです。

Conv2D, MaxPooling2DはCNNを使うときに使用します。画像認識はCNNが手軽かつ最強(認識率を簡単に上げられるの意)なので、とりあえずこれを使っておけば問題ないです。線形パーセプトロンは非線形な関数を作れないので、複雑な画像に対してはかなり弱いです。

Activation, Dropout, Flatten, DenseはCNNを使うときの画像処理に使用します。平滑化したり、過学習を防ぐために余計な情報を覚えさせないようにしたりとかそういうのです。

backendはKerasに与えるバックエンドライブラリを指定します。デフォルトはTensorFlowなので問題ないです。

前設定というかなんというか
if K.image_data_format() == 'channels_first':
    input_shape = (3, img_width, img_height)
else:
    input_shape = (img_width, img_height, 3)

画像を読み込むときのレイヤー構造を指定します。色のチャネル数と画像の解像度の情報があるのですが、TensorFlowの設定によってチャネル数が一番下のレイヤーにくるか、一番上のレイヤーにくるかが変わってきます。

デフォルトはchannels_firstになっていますが、念の為に設定しておいても問題ないでしょう。

設定部
nb_train_samples = 4000
nb_validation_samples = 900
epochs = 100
batch_size = 200

この辺は適当にいじってください(わはは

エポック数を増やしすぎるとめっちゃ時間かかるので注意。

ネットワーク部のコード
CNN
model = Sequential()
model.add(Conv2D(32, (3, 3), activation='relu', input_shape=input_shape))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.1))

model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.1))
model.add(Dense(1, activation='sigmoid'))

model.compile(loss='binary_crossentropy', optimizer='rmsprop', metrics=['accuracy'])

MNISTのコードを二値分類用に改造しただけのコード。本来はMNISTは0~9の10種類の出力が必要なのだが、今回はボーイかガールしかいないためバイナリで良い。

binary_crossentropyというのがまさに二値で分類するための損失関数。

ニューラルネットワークとしては比較的小規模で単純な構造をしているにもかかわらず、こんなコードでも正解率は99%に達します(2回実行したら99.25%と99.45%でした)。やっぱりCNNは手軽で便利ですなあ。

ちなみにCUDAを使ってGTX1060 6GBに実行させると、学習に大体2分位かかります。

これ以上の正解率を出すのは単純なCNNでは難しいでしょう。

CNNは行列計算を多用するので、CPUで計算させると死ぬほど時間かかります。時間を無駄にしたくなければかならずGPUを使用しましょう。

イカとタコの判定

先程行ったのは簡単に言えば、ガールかボーイの画像を見せられて「これはボーイですか?」という問いにYESかNOで答える問題でした。

次はそうではなく「インクリングボーイ、インクリングガール、オクトリングボーイ、オクトリングガールの4つのうちどれですか?」と答えるカテゴリ分類の問題になります。

これはさっきとは問題の本質が異なるため、ちょっとコードを変える必要があります。

形式としてはこちらの方がMNISTに近いので、やはりMNISTのコードが流用できるはずです。

# model.add(Dense(num_classes, activation='sigmoid'))
model.add(Dense(num_classes, activation='softmax'))

sigmoidでも多分問題はないのですが、softmaxにする方が無難な気がします。

train_generator = train_datagen.flow_from_directory(
train_dir,
target_size=(img_width, img_height),
batch_size=batch_size,
class_mode='categorical')

validation_generator = test_datagen.flow_from_directory(
validation_dir,
target_size=(img_width, img_height),
batch_size=batch_size,
class_mode='categorical')

さっきまでは二値分類だったのでbinaryでしたが、今回はカテゴリ分類なのでcategoricalを指定します。

実行結果

[table id=4 /]

パラメータをいろいろ変えてみたが、ほぼ実行時間に比例して精度が上がっていき97~98%付近で頭打ちになることがわかった。

おそらくエポック数を増やしても過学習してしまうだけで、これ以上精度が上がることはないように思う。

精度を上げるのであればネットワーク構造を変えるしかない。

Transfer Conventional Neural Networkという(転移畳み込みニューラルネットワークとでも呼ぶのだろうか?)はCNNよりも僅かに良い性能を与えるらしいので、それを使ってみようかと思ったのだが、ちょっとコードを見たけれど全然わからなかったので諦めることにした(あかん

次はstat.inkのデータを使って誰が戦犯か突き止めてやるAIを作ろうと思います。

99%の精度を誇るAI作りたいなーって思っているので、できた人は教えてください。

訓練用データ

全部ビットマップデータで700MBくらいあるのでネットワークの調子がいいときにぜひ。

おいておくので自由にどうぞ。

任天堂さんに怒られたらすぐに削除します。

無駄に解像度大きいからいろんなことに使えるかもしれません。