あさの畑

プログラミングが好きな大学生のブログ。統計学や機械学習の勉強記録と、SIGNATE/Kaggle/AtCoderのお話。

【ディープラーニングで物体検出】シカを見つける人工知能をつくってみた

今年の4月頃から人工知能、ディープラーニング(Deep Learning、深層学習)に興味を持ち始めました。そして、樹皮の写真から木の名前を当てる人工知能を作ってみました。

 

こちらの記事で詳しく書いています。

www.asanohatake.com

 

次のステップとして物体検出にチャレンジしたいと以前から思っていたものの、難しそうでなんとなく挑戦できないでいました。しかし、大学の授業が終わって夏休みになり、時間ができたので今こそ物体検出に取り組むタイミングだと思って勉強してみました。

 

シカを検出する人工知能を作るぞ!

さて、何を題材にして物体検出をしてみるかですが、「シカとイノシシを検出する」というテーマにしました。

 

近年、シカによる樹木の食害被害はかなり問題になっているので、シカを自動で検出できればシカの調査が効率的になるかなと思いました。(イノシシはそのついでです。)

 

地道な画像データの収集

「シカが写った写真110枚」「イノシシが写った写真110枚」をGoogle画像検索の結果から地道に集めました。110という数字には特に深い意味はありません。(じゃあ100にしろよ!って感じですね。)

 

地道なアノテーション作業

さて、無事に集めた写真ですが、このまま学習させるわけではありません。今回の目標である「物体検出」では、写真のどこにシカ(もしくはイノシシ)がいるのかまで特定したいのです。つまり、まずは手動で合計220枚の写真1枚ずつ、シカ(もしくはイノシシ)が写っているところに印をつけていく必要があります。

 

この印をつける作業をアノテーションと言います。これには便利なソフトがいくつかあって、効率よく地道な作業を行うことができます。ちなみに印をつけた結果はXML形式のファイルにしないといけないのですが、勝手にソフトがしてくれるのでありがたいです。

 

マイクロソフトの「VoTT」というものや、「labelImg」というものがあります。個人的には「labelImg」の方が使いやすいと感じたのでこちらで作業をしました。ファイルの保存がわかりやすかったのです。(←後々大変なことになりましたが…)

 

参考:

ディープラーニングで動画に自動でモザイクをかける「ディープモザイク」作ってみました - karaage. [からあげ]

 

Deepに理解する深層学習による物体検出 by Keras

 

学習

上でも紹介させていただいた「からあげさんの記事」を参考にして(まるパクリして)物体検出してみることにしました。

 

しかし、「よし、学習させるぞ!」と思っても同じエラーばかりが出ました。pyenvはインストールしていなかったのですが、他のバージョンは以下のものにしっかり合わせました。(PCはWindowsです。)

tensorflow==1.0.0
keras==1.2.2
opencv-python==3.3.0

 

やはり、pyenvとやらもインストールしないといけないのか、、、と思いつつ嫌な予感が…。

 

エラーの文章を見ると、なんか「ファイルの形式(?)が違うんじゃ!そんなファイルはないんじゃ!」って感じだったんです。もしかして、これはバージョンの問題ではない。アノテーションツールの問題なのか…。

 

予感は的中。まあ結局、「labelImg」じゃなくて「VoTT」を使っておけば良かったんですよね。鋭い直感をほめましょう。

 

というわけで再びアノテーション作業。220枚。最初は楽しかったのにだんだんと腕が疲れてきます…。

 

記事に書かれている通りに、コードの修正やディレクトリの確認を行って、さあ実行。

f:id:gadada:20180812232521p:plain

 

ん?精度が低すぎるぞ。学習を回すことはできたのですが、精度が3割にも届いていません。

 

もちろんepochが10ってこともあるとは思うのですが、教師データの質にも問題がありそうです。実は今回は、シカ(イノシシ)の顔を検出していて、大人でも子どもでも横顔でも正面でも角があっても無くても全部「シカ」(イノシシ)だとして教師データを作成したのです。どうやらこれが良くないのかもしれません。

 

【追記】パソコンに頑張らせてepoch50でやってみました。5割程度の精度まで上がりましたが、それが限界みたいです。やはり教師データをしっかりさせないといけないようです。

f:id:gadada:20180908012740p:plain

 

まとめと今後の課題

・教師データの質を高めるために、「シカ」の基準をできるだけ明確にする。そしてそもそもデータの量も多くする。

・コードに何が書いてあるかまで深く理解したい。(今回はとりあえず実行してみた感じなので…。)

 

以上です。

 

最後に、小学生の頃に奈良公園に行った時の写真で物体検出してみたサンプルです。なかなかいい写真が撮れてる!(笑)

 

(「rykov8/ssd_keras」を参考にして自分のサンプルで実行しました。クラス、重み、画像パスは変える必要があります。)

import cv2
import keras
from keras.applications.imagenet_utils import preprocess_input
from keras.backend.tensorflow_backend import set_session
from keras.models import Model
from keras.preprocessing import image
import matplotlib.pyplot as plt
import numpy as np
from scipy.misc import imread
import tensorflow as tf

from ssd import SSD300
from ssd_utils import BBoxUtility

plt.rcParams['figure.figsize'] = (8, 8)
plt.rcParams['image.interpolation'] = 'nearest'

np.set_printoptions(suppress=True)

config = tf.ConfigProto()
config.gpu_options.per_process_gpu_memory_fraction = 0.45
set_session(tf.Session(config=config))

voc_classes = ['class1', 'class2']
NUM_CLASSES = len(voc_classes) + 1

input_shape=(300, 300, 3)
model = SSD300(input_shape, num_classes=NUM_CLASSES)
model.load_weights('./checkpoints/weights〇〇.hdf5', by_name=True)
bbox_util = BBoxUtility(NUM_CLASSES)

inputs = []
images = []
img_path = './sample.jpg'
img = image.load_img(img_path, target_size=(300, 300))
img = image.img_to_array(img)
images.append(imread(img_path))
inputs.append(img.copy())

inputs = preprocess_input(np.array(inputs))
preds = model.predict(inputs, batch_size=1, verbose=1)
results = bbox_util.detection_out(preds)

a = model.predict(inputs, batch_size=1)
b = bbox_util.detection_out(preds)

for i, img in enumerate(images):
    # Parse the outputs.
    det_label = results[i][:, 0]
    det_conf = results[i][:, 1]
    det_xmin = results[i][:, 2]
    det_ymin = results[i][:, 3]
    det_xmax = results[i][:, 4]
    det_ymax = results[i][:, 5]

    # Get detections with confidence higher than 0.6.
    top_indices = [i for i, conf in enumerate(det_conf) if conf >= 0.6]

    top_conf = det_conf[top_indices]
    top_label_indices = det_label[top_indices].tolist()
    top_xmin = det_xmin[top_indices]
    top_ymin = det_ymin[top_indices]
    top_xmax = det_xmax[top_indices]
    top_ymax = det_ymax[top_indices]

    colors = plt.cm.hsv(np.linspace(0, 1, 21)).tolist()

    plt.imshow(img / 255.)
    currentAxis = plt.gca()

    for i in range(top_conf.shape[0]):
        xmin = int(round(top_xmin[i] * img.shape[1]))
        ymin = int(round(top_ymin[i] * img.shape[0]))
        xmax = int(round(top_xmax[i] * img.shape[1]))
        ymax = int(round(top_ymax[i] * img.shape[0]))
        score = top_conf[i]
        label = int(top_label_indices[i])
        label_name = voc_classes[label - 1]
        display_txt = '{:0.2f}, {}'.format(score, label_name)
        coords = (xmin, ymin), xmax-xmin+1, ymax-ymin+1
        color = colors[label]
        currentAxis.add_patch(plt.Rectangle(*coords, fill=False, edgecolor=color, linewidth=2))
        currentAxis.text(xmin, ymin, display_txt, bbox={'facecolor':color, 'alpha':0.5})

    plt.show()

f:id:gadada:20180907002811p:plain

おいおい、もうちょっと頑張ってくれ!右に写ってるのもシカですよ!!!

 

と、まあ、こんな感じの精度なわけです(笑)

 

ちなみに、つい最近こんな論文が出ていることを知って感激しています。すごい。

Automatically identifying, counting, and describing wild animals in camera-trap images with deep learning | PNAS

 

 

ディープラーニングの勉強記録についてはこちらの記事でまとめています。

www.asanohatake.com

www.asanohatake.com