【まとめ】機械学習初心者がTensorflowでネコの画像認識をつくってみたらどうなったか


Tensorflowでネコを画像認識する仕組みをつくってみました!

機械学習の超初心者がTensorflowを元にいろいろつくってみたので紹介します。

最終的につくったものはこちら↓↓↓

ねこチェッカー


Introduction


前提

どんな人がやってみたのか、私のプロフィールを少し詳しく説明します。


  • インフラ・運用系エンジニア(約9年)

  • 機械学習ははじめてチャレンジ

  • 数学や統計学も初心者(文系の人)

  • 言語はPHP/JavaScript/Perlなどの軽いものだけやったことがある

まとめると超初心者ですね。

統計とかさっぱりです。

今回試してみたきっかけは、トレンドである機械学習は本当に簡単なのか、もしくは大変なことが多いのか、レベルの高いものは作れるのか、いろいろと自分自身で体感してみたかったからです。


Themes

この記事でまとめるのはこんなことです。


  • 機械学習初心者がTensorflowを試して

  • 良かったこと

  • 大変だったこと

機械学習の統計とか数学的な話はでてきません。

というか勉強中なので書けません。

主に、何が楽しくて何が大変かを整理してお伝えしていきます。

大変なことのほうが圧倒的に多いのですが、どんな壁が待っているかをみなさんに伝えられたら幸いです。


Tensolflowとは?

まずTensorflowについて。

ここは割愛します。ググってください。たくさん記事が出てきますよ!

簡単にいうと、機械学習を簡単に利用できるライブラリです。

仕様を意識せずに機械学習を行える反面、細かいカスタマイズはできないようです。

今回は機械学習の効果を体感するのが目的なので、細かいところは一旦無視します。


やったこと

今回実施したことは以下の3点です。


  • ネコの画像をスクレイピングして集める

  • 画像を学習させる

  • お題となる画像を与えて認識できるかチェックしてみる


イメージ

これが完成イメージ。

まずはネコの画像データを与えます。

russian.jpg

このネコの画像(ロシアンブルー)を元に結果を問い合わせるコマンドを実行。

python3 /usr/local/tf/label_image.py --image cat1.jpg --graph /usr/local/tf/retrained_graph.pb

すると結果が返ってくる!

russianblue 0.7167683

americanshorthair 0.15780476
munchkin 0.0629924
mikanecat 0.047680147
persia 0.014754322

この場合はrussianblue 0.71...となっています。

ロシアンブルーの確信度が71%ということですね!


環境

実施した環境はGCPを使っています。

一番弱いインスタンスです。


  • GCP VMインスタンス:n1-standard-1(vCPU x 1、メモリ 3.75 GB)

  • OS:Debian 4.9

  • Python:3.5.3


チュートリアルにそって実装

基本的にチュートリアルにそってコーディングしていきます。

Image recognitionのページです。

TensorFlow - Image Recognition

tensor.png

基本的にすべて英語ですが、コマンドさえわかれば問題ありません。

臆せずチャレンジしましょう。


まずはスクレイピング

TensorFlowを始める前に、まずは利用する画像データをとってきます。

手でダウンロードしていくのは大変なので、スクレイピングという方法をとります。

今回はgoogleimagesdownloadというコマンドを使います。

pip3 install google_images_download

googleimagesdownload -k "Scottish Fold"

ダウンロードして実行。

-k のあとのダブルクオート内にサーチするキーワードを入れればOKです。

この場合はスコティッシュフォールドの画像を探しています。


画像をとってきた!

うまくいくと以下のような形で画像データがゲットできます。

-rw-r--r-- 1 xxx xxx   19369 Sep 16 09:04 18. scottish_fold2.jpg

-rw-r--r-- 1 xxx xxx 5604 Sep 16 09:04 16. 220px-white_scottishfold.jpg
-rw-r--r-- 1 xxx xxx 688222 Sep 16 09:04 99. 6.jpg
-rw-r--r-- 1 xxx xxx 75596 Sep 16 09:04 95. scottish_fold_white_550x367.jpg
-rw-r--r-- 1 xxx xxx 140625 Sep 16 09:04 82. angus.jpg
-rw-r--r-- 1 xxx xxx 10598 Sep 16 09:04 68. scottish334.jpg
-rw-r--r-- 1 xxx xxx 201425 Sep 16 09:04 44. animal023_0702_4k.jpg
-rw-r--r-- 1 xxx xxx 24863 Sep 16 09:04 38. 250px-scottish_fold_cat.jpg
-rw-r--r-- 1 xxx xxx 42854 Sep 16 09:04 35. fabio-petroni-scottish-fold-cat_a-g-13454525-14258384.jpg
-rw-r--r-- 1 xxx xxx 54599 Sep 16 09:04 15. 1.jpg
-rw-r--r-- 1 xxx xxx 117165 Sep 16 09:04 11. 16979752-curious-striped-scottish-fold-kitten.jpg
-rw-r--r-- 1 xxx xxx 715242 Sep 16 09:04 10. scottish-fold-cats-michael-d3f35258_cfa6cf2b.jpg
-rw-r--r-- 1 xxx xxx 17777 Sep 16 09:04 55. scottish-fold-1.jpg
-rw-r--r-- 1 xxx xxx 233039 Sep 16 09:04 50. adult-male-blue-scottish-fold-cat-with-golden-eyes-standing-looking-picture-id505322557.jpg

一気にダウンロードできるので相当楽です。便利ですねー。

最初は5種類×60枚で行なっていたのですが、このやり方が楽だったため、30種類(人気順)×100枚とってみました。

が・・・これが大変なことになるきっかけとなっていたのです・・・


3つの代表的なエラーケース


ネコじゃない。。。

まずはひとつめ。

Minuetというネコを取得したくて、キーワードに"Minuet"と指定したケース。

曲がヒットしてしまいました。。。

本当はネコが欲しいのに。

このようにネコの名前であっても、一般名詞的な使われ方をする場合があるので注意が必要です。

このような場合、"Minuet cat"というように、ネコであることを強調してスクレイピングしましょう。


ネコ以外が入っている!

次はネコ以外が入っているケース。

かわいい。

かわいいことはかわいいのです。

ただ違う。

イヌは今回は呼んでないのです。


顔が見えない!

次はこんな場面。

ネコであることは良いのですが、こっちを向いてくれていません。。。

お願いだから顔を上げて。。。


ここまでやってみて。。。

_人人人人人人人人人人人_

> 意外と手作業が大変 <

 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄

でした。スクレイピングしたデータをそのまま使える、と思っていたのですが、ある程度は手でなんとかしなきゃいけないんですね。


Tensorflowインストールと実行


Tensorflowのインストール

やっとTensorFlowです。

以下のコマンドでインストール。

sudo apt-get install -y git build-essential libssl-dev language-pack-id

pip3 install --upgrade pip
pip3 install tensorflow


Tensorflowの実行テスト

実際にTensorflowの動作確認をしてみます。

python3

import tensorflow as tf
hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print(sess.run(hello))

’Hello, TensorFlow!' と表示されればOK。


学習させる

続いて学習させます。

このコマンドもチュートリアルにあります。

python3 retrain.py \

--bottleneck_dir=bottlenecks \
--how_many_training_steps=100 \
--model_dir=inception \
--summaries_dir=training_summaries/basic \
--output_graph=retrained_graph.pb \
--output_labels=retrained_labels.txt \
--image_dir=gakusyu_data

2つポイントがあります。

--how_many_training_steps=100

ここには学習回数を指定します。この場合100回です。

--image_dir=gakusyu_data

ここにはネコの画像があるディレクトリを指定します。この場合gakusyu_dataです。

さあ、これでコマンドを実行します!

学習が完了したかな?と思ったところ、また問題が・・・


エラー①:ファイル名 too long

OSError: [Errno 36] File name too long: "bottlenecks/British Shorthair/85. pet-cat-mammal-whiskers-vertebrate-british-shorthair-european-shorthair-chartreux-russian-blue-korat-cat-mia-small-to-medium-sized-cats-cat-like-mammal-domestic-short-haired-cat-american-shorthair-blue-cat's-743876.jpg_https~tfhub.dev~google~imagenet~inception_v3~feature_vector~1.txt"

ファイル名が長いらしいです。。

よくみると確かに恐ろしい長さです。

mvで短いファイル名に置換してあげましょう。


エラー②:ファイルサイズ too large

RuntimeError: Error during processing file gakusyu_data_max/Singapura cat/4. moonwalker_the_singapura.jpg (Invalid JPEG data or crop window, data size 1671168

[[Node: DecodeJpeg = DecodeJpeg[acceptable_fraction=1, channels=3, dct_method="", fancy_upscaling=true, ratio=1, try_recover_truncated=false, _device="/job:localhost/replica:0/task:0/device:CPU:0"](_arg_DecodeJPGInput_0_0)]]

今度はファイルサイズが大きすぎるようです。

よく見てみると解像度が「5120×3200」とかです。

サイズの大きいものは削除しましょう。


エラー③:メモリエラー

2018-09-04 22:38:06.231041: W tensorflow/core/framework/allocator.cc:108] Allocation of 49152000 exceeds 10% of system memory.

Killed

こんなエラーも出ました。Killedされてる。。

GCPのインスタンスをケチったのが原因かな?

vmstatでリソースを見てみると・・・

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----

r b swpd free buff cache si so bi bo in cs us sy id wa st
0 1 0 450924 20304 161204 0 0 6956 0 419 723 17 5 1 77 0
1 0 0 400084 20952 177928 0 0 7512 0 636 1252 27 6 0 67 0
1 0 0 286564 23868 192108 0 0 5916 0 333 853 80 9 0 11 0
6 0 0 275664 23872 192704 0 0 8 0 843 1527 93 6 0 1 0
1 0 0 218664 23872 192772 0 0 0 0 282 108 92 8 0 0 0
2 0 0 177000 23884 194284 0 0 1508 0 289 142 92 4 0 4 0
0 1 0 80156 23884 243408 0 0 38980 0 455 1121 9 8 0 83 0
0 2 0 81912 23884 207460 0 0 32028 0 491 4125 17 15 0 69 0
1 0 0 75488 8708 208428 0 0 19568 0 638 1390 57 8 0 35 0
1 21 0 609060 236 50240 0 0 14988 84 670 1367 28 56 0 15 0

si/soこそ発生していないものの、純粋なfree memoryは75KBですね。

一時的にスケールアップさせてあげます。

クラウドなのですぐできたのは良かった点。


やっと画像認識を確認してみる

さあ、これでやっと準備OKです。

ここまで来るのに大変でした。

画像認識は試しにこの画像で実行してみます。

実行コマンドは以下のものです。

xxx.jpgに画像ファイルを指定します。

python3 label_image.py --xxx.jpg --graph retrained_graph.pb --labels retrained_labels.txt


判定結果が出力された!!

ついに!判定結果が出力されました!

persia: 0.28

bengal: 0.07
american shorthair: 0.05
singapura cat: 0.04
abyssinian: 0.03

この場合、ペルシャ猫の確信度が28%ということですね。

あれ?でもちょっとまってください。

30種類インプットしたはずなのに、5種類しか表示されていませんね。


label_image.pyを改修する

いろいろ試行錯誤や質問してみた結果、label_image.pyのソースにキーはありました。

top_k = results.argsort()[-5:][::-1]

既存のソースコードだと、上位5件のみをリターンするようになっています。

そのため、以下のように変更する必要があります。

  top_k = results.argsort()[::-1]

labels = load_labels(label_file)
for i in top_k:
print('{}: {:.2f}'.format(labels[i], results[i]))

こうすると、すべての結果をリターンしてくれるようになります。

実行してみると・・・

persia: 0.28

bengal: 0.07
american shorthair: 0.05
singapura cat: 0.04
abyssinian: 0.03
tonkinese: 0.028
birman: 0.025
ragdoll: 0.023
munchkin cat: 0.021
laperm: 0.02
scottish fold: 0.02
ragamuffin: 0.02
...
..
.

すべて表示されました!これで思っていた結果になりました。


学習回数を増やしてみる

学習回数を変更して実施してみました。

学習回数100回

persia 0.124754322

学習回数500回

persia 0.291819281

結構変わりますね。学習回数を上げるほど良い結果がでるようですが、どこまでチューニングすればいいかはまだわかりません。


まとめ


Good!


動く!楽しい!

初心者でもここまで動かすことができると本当に楽しいです。


大変さが実感できる。

今回いろいろなエラーケースを書きました。できる人からするとなんでもないことかもしれませんが、初めてやってみるとどれだけ大変かが身をもってわかりました。


Try


手作業(工夫の余地)が結構多い。

スクレイピングしてそのままのデータを利用できないということがわかりました。

使える形にするスクリプトを作って効率化するかkaggleのようなデータセットをもってくるのが良さそうです。(kaggleはあとで知りました)


似ている種類は判別難しい。

30種類で試してみましたが、似ている種類は間違った結果になることも多かったです。

もっと特徴が全然違うものにするとわかりやすいのかもしれません。


Webにしてみました

コマンドラインだけで実行していると味気なかったのでWebにしてみました。

web.png

実際のサイトはこちら。

ねこチェッカー

画像ファイルをアップロードすると、結果を返してくれます。

ネコじゃなくても必ず結果をかえすのでおもしろいです。


さいごに

大変なことも多かった、はじめての機械学習。

チュートリアルメインでしたが、ほぼ自力でここまでできました。

色々な種類や方法があふれていますが、Tensorflowは比較的やりやすいと思います。

あまり多くのデータや種類を組み込まなければ、エラーになるケースも少ないかと思います。

興味がある方は、一度試してみても良いかもしれませんね。