阿見町の特産品を見分けるAIを作る

7月6日に悲しい統計が発表されました。総務省がまとめた2017年度締めのふるさと納税に関する現況調査です。
阿見町は茨城県内で下から2番目、年間15件、53万円の納税額。全自治体1789中、1751位でした。茨城県の合計額は47都道府県で8位と、なかなかの健闘です。

スイカ、メロン、ヤーコン、たけのこ、特産品はいろいろあるのにもったいないです。
阿見町の特産品 | 茨城県阿見町ホームページ
まい・あみ・マルシェ | あみ観光協会

そこで、町のアピールにつながるか(?)ですが、阿見町の特産品(スイカ、メロン、ヤーコン、たけのこ)の画像を見分けるAI(人工知能)を作ってみました。

  • AIのバックエンドにはtheanoを、フロントエンドはkerasを使いました。
  • 転移学習させたので、1クラス当たり100枚くらいの画像で学習できています。それでもMacBookProで20時間位かかりました。
  • 最終的な識別精度は99.8%となかなか良好でした。

AIで画像を分類する

画像の分類はAIがよく活用される分野の1つです。MNISTという手書き数字の画像集はAIの性能測定のためによく用いられますし、GoogleLensなども様々な画像を学習させた結果、今撮った写真の被写体を認識して推定することが出来るようになっています。

AIの学習には教師あり学習と教師なし学習がありますが、今回は教師あり学習です。スイカの画像はこんなのだよ、メロンの画像はこんなのだよと大量の教師データを用意する必要があります。また学習した結果の分類精度を測るためにテストデータも用意します。

以下の内容はこちらのサイトを参考にさせていただきました。

転移学習をさせて学習時間を省く

AIに画像を学習させて分類させるには、1つの分類対象(クラス)毎に相当な数の(数百枚じゃ足りないくらい)の教師データが必要になります。個人がそんな大量の画像を収集してくるのは難しいですし、集めたデータを学習させるには通常のコンピュータでは時間がかかりすぎてしまいます。

そこで、転移学習という方法を使います。転移学習はあらかじめ、別な目的で大量の画像を学習した学習済み結果を元にAIのモデルを作成し、一部分だけ今回の目的の教師データで学習し直すという方法です。今回使った学習済みモデルはVGG16というもので、16層からなる畳み込みニューラルネットワークに1000クラス、120万枚の画像を学習させたものです。1000クラスのリストはこちらにあります。

このVGG16を転移学習してスイカ、メロン、ヤーコン、たけのこの分類器を作ります。モデルの構築、学習にはtheanoとkerasを使いました。

画像を集めてくる

画層の収集はBing Search APIを使いました。無償利用のアカウントを作成して、APIキーを取得します。pythonで画像を集めてくるコードはこんな感じです。

# -*- coding: utf-8 -*-
import requests, json

#api_key1 = "XXXXXXXXXXXXXXXXXXXXXXXXXXX"

def get_bing_image(query, offset=0, count=150):
    url = 'https://api.cognitive.microsoft.com/bing/v7.0/images/search'
    headers = {'Ocp-Apim-Subscription-Key': api_key1,'Content-Type':'multipart/form-data'}
    res = requests.get(url,{'q':query,'count':count,'offset':offset,'filetype':'png'},headers=headers)
    res = json.loads(res.text)
    print(res)
    return res['value']

def save_image(filename, image):
    with open(filename, "wb") as fout:
        fout.write(image)

def get_item_image(query, fname_h, num):
    res = get_bing_image(query,0,num)
    num = 1
    urls = [i['contentUrl'] for i in res]
    for url in urls:
        try:
            image = requests.get(url).content
            save_image("./images/" + fname_h + str(num).zfill(4) + ".png", image)
            num += 1
        except:
            print("error")

get_item_image("ヤーコン","yacon")

集めてきた画像ファイルは以下のような階層のディレクトリにしまっておきます。
・images
 ・train
  ・class1
  ・class2
  ・class3
  ・class4
 ・test
  ・class1
  ・class2
  ・class3
  ・class4

AIに学習させる

さて、いよいよAIを学習させます。学習させるためのコードはこちらに書かれているものほとんどそのままで実行できます。

  • 今回は4分類なので、N_CATEGORIES=4。
  • NUM_TRAINING=1600、NUM_VALIDATION=400はマシンパワーによって調節した方が良いです。
  • 先程取得した画像ファイルは”png”、”jpg”などの拡張子がついていないと学習してくれないようです。

最後の16層目だけ学習させます。

Total params: 15,244,100
Trainable params: 7,608,836
Non-trainable params: 7,635,264
_________________________________________________________________
train_generator
Found 496 images belonging to 4 classes.
validation_generator
Found 120 images belonging to 4 classes.
Epoch 1/50
1/100 […………………………] – ETA: 11:17 – loss: 1.5320 – acc: 0.0625/usr/local/lib/python3.6/site-packages/PIL/Image.py:918: UserWarning: Palette im
ages with Transparency expressed in bytes should be converted to RGBA images
to RGBA images’)
100/100 [==============================] – 779s 8s/step – loss: 1.3235 – acc: 0.3831 – val_loss: 1.2442 – val_acc: 0.5133

16層目の学習だけでも760万個のパラメータをフィッティングしているのですね。Epochが進む毎に、acc(教師データの分類精度)、val_acc(テストデータの分類精度)が高くなっていきます。

そして20時間後・・・

Epoch 50/50
100/100 [==============================] – 737s 7s/step – loss: 0.0118 – acc: 0.9981 – val_loss: 0.0359 – val_acc: 0.9840
99.8%の精度が得られました。

学習結果を使ってAIに分類させる

学習が完了するとhdf5形式のファイルにモデルが保存されます。このモデルを読み込んで、新しい画像を分類するには以下のようなコードです。

import os.path,sys
os.environ['KERAS_BACKEND'] = 'theano'
from keras.models import Model
from keras.models import load_model
from PIL import Image
import numpy as np
import pandas as pd

IMAGE_SIZE = 224

model = load_model('amimage.hdf5')
model.summary()
categories = ["melon","suica","takenoko","yacon"]
fnames = [name for name in os.listdir("./predict") if name != ".DS_Store"]

print("---start predictions---")
print(categories)
X = []
for fname in fnames:
    img = Image.open("./predict/" + fname)
    img = img.convert("RGB")
    img = img.resize((IMAGE_SIZE, IMAGE_SIZE))
    in_data = np.asarray(img) / 255
    X.append(in_data)
X = np.array(X)

predictions = model.predict(X)
i = 0
for p in predictions:
    print(fnames[i] + " must be " + categories[p.argmax()])
    i = i + 1

見事に分類できました。

—start predictions—
[‘melon’, ‘suica’, ‘takenoko’, ‘yacon’]

photo_watermelon.jpg must be suica
melon3.jpg must be melon
photo_lotusroot.jpg must be yacon
takenoko.jpg must be takenoko

kikushun: