イメージと正解ラベルだけで高精度の画像認識DLモデル作成 - Watsonの最新機能NeuNetSのご紹介 -

# はじめに
IBM Cloud上のサービスOpenScaleでは、イメージデータと正解ラベルをクラウドストレージ上にアップするだけで高精度のDLモデルが作れるNetNetS機能の提供が始まりました。
どれぐらい高精度かというと、Deep Learningのベンチマークデータとして有名なCIFAR-10で大体93%程度の精度が出ています。

![スクリーンショット 2018-12-25 19.51.28.png](https://qiita-image-store.s3.amazonaws.com/0/206130/4c3e0bd8-c2e7-2ff3-f953-a74286510efe.png)

[2019-01-30] 派手なデモページができていたので、最後にリンクを紹介しておきます。

# どんなことをやっているのか
NeuNetSは「AIによるAI」と呼ばれています。学習用データを見て、どんなニューラルネットを組むのが最適か、調べていくということになります。
要素技術としては、TAPASと呼ばれる、入力データの複雑さから目標とする精度の見積もりを行う仕組みや、NCEvolve(Neuro-Cell-based Evolution)と呼ばれる、より精度の高いネットワークに進化させる仕組みを組み合わせて実装しています。これ以上の内容は、この記事の範囲を超えてしまいますので、関心のある方は以下のリンクの論文などを参考にされて下さい。

・[TAPAS: Train-less Accuracy Predictor for Architecture Search](https://arxiv.org/abs/1806.00250)
・[Deep Learning Architecture Search by Neuro-Cell-based Evolution with Function-Preserving Mutations](http://www.ecmlpkdd2018.org/wp-content/uploads/2018/09/108.pdf)

以下は、論文のポイントを抜粋したブログ記事などです。

[NeuNetS: Automating neural network model synthesis for broader adoption of AI](https://phys.org/news/2018-12-neunets-automating-neural-network-synthesis.html)
[NeuNetS: Automating Neural Network Model Synthesis for Broader Adoption of AI](https://www.ibm.com/blogs/research/2018/12/neunets/)
[Using AI to Design Deep Learning Architectures](https://www.ibm.com/blogs/research/2018/09/ai-design-deep-learning/)

参考までに論文中の図については、いくつか下に添付しておきます。

![スクリーンショット 2018-12-27 9.26.46.png](https://qiita-image-store.s3.amazonaws.com/0/206130/071dc79c-c60c-f8be-fbf3-5861c776b143.png)

![スクリーンショット 2018-12-27 9.26.12.png](https://qiita-image-store.s3.amazonaws.com/0/206130/21da7fd2-b658-5639-a2dd-198295f3843c.png)

![スクリーンショット 2018-12-27 9.25.37.png](https://qiita-image-store.s3.amazonaws.com/0/206130/439fa17c-071e-6dc1-633f-4cfc858c7b14.png)


難しい話はさておき、論より証拠なので、まずは実際の動作を試してみましょう。
NetNetSは、IBM Cloudの**ライトアカウント**というクレジットカード登録不要のアカウントで簡単に試してみることができます。以下にその手順を記載しましたので、Deep Learningに関心のある方は是非お試し下さい!

# ライトアカウントのサインアップ
最初のステップは、IBM Cloudのライトアカウントのサインアップです。
手順については、こちら[最新版IBM Cloud ライトアカウント登録手順](https://qiita.com/makaishi2/items/ae83828a711d0d946011)を参照して下さい。

# Watson Studioのプロジェクト作成
次のステップはWatson Studioでプロジェクトを作成することです。
こちらも手順を記事に書いておきましたので、こちら[Watson Studioセットアップガイド(ライトアカウント可)](https://qiita.com/makaishi2/items/b666f847a98c2e42dfbb)を参照して下さい。

# OpenScaleインスタンスの追加
NeuNetsのフロントのUIはWatson Studioなのですが、裏ではOpenScaleが動いています。そのため、OpenScaleのインスタンスを作成します。そのため、[IBM Cloudダッシュボード]
(https://console.bluemix.net/dashboard/apps)から、「リソースの作成」->カテゴリ「AI」->「AI OpenSCale」を選択します。
![スクリーンショット 2018-12-26 16.07.11.png](https://qiita-image-store.s3.amazonaws.com/0/206130/2be71150-f0dc-d3e5-a0d3-c557ec147a96.png)

しばらく待って下の図のように「作成」ボタンが有効になったら、プランが「Lite」であることを確認の上、「作成」をクリックします。

![スクリーンショット 2018-12-26 16.11.54.png](https://qiita-image-store.s3.amazonaws.com/0/206130/84aeec85-0238-47c6-d651-86032d0acf35.png)

# 学習用データのダウンロード
まずは最短コースでお試し下さいということで、私が作成した学習用データを連係しておきます。このデータがCIFAR-10の訓練用データ50,000件をNeuNetsの学習用に使えるようにpng形式に変換し、必要なラベルデータもあわせて作成したものです。カスタムの学習用データの作り方については後ほど解説します。
次の[Boxリンク](https://ibm.box.com/s/7dv87td6bwzjsa4swqp102wag7firh51)から2つのファイル、``train.zip`` と ``labels.csv`` をダウンロードします。train.zipは約130MBと大きめのサイズなのでご注意下さい。

# 学習用入力・出力バケットの作成
モデル作成はクラウド上でバッチ処理として行われるため、作業のストレージが必要です。NueNetsでは、IBM CloudのストレージサービスColoud Object Storage(Amazon S3互換)を利用します。以下の手順でその設定を行います。

まず、先ほど同様に[IBM Cloudダッシュボード]
(https://console.bluemix.net/dashboard/apps)を表示し、リストにあるインスタンスの中からcloud-object-storage-xxとなっているものを選択します。(このインスタンスは、Watson Studioのプロジェクトを作った際に裏で自動的に作られているはずです。)

![スクリーンショット 2018-12-26 16.26.37.png](https://qiita-image-store.s3.amazonaws.com/0/206130/d8e8322f-9ce2-3778-4050-df6d26103bb8.png)

下の画面になったら、「バケットの作成」をクリックします。

![スクリーンショット 2018-12-26 16.26.54.png](https://qiita-image-store.s3.amazonaws.com/0/206130/282f8e6d-6a6e-9fea-af37-47bc2b9563ce.png)

下にバケット作成時の入力画面を示します。デフォルト値から変更が必要なのは、「固有のバケット名」と「回復力」です。
固有のバケット名はなんでもいいのですが、グローバルでユニークな名前を指定します。最初に作るのは出力用バケットなので、図のように出力用とわかるものが望ましいです。「回復力」はデフォルトの「Regional」を図のように「Cross Region」に変更します。ロケーションはデフォルトで「us-geo」のはずですが、違っていたら「us-geo」に変更して下さい。「ストレージクラス」は図のようにデフォルト値のままでいいです。

![スクリーンショット 2018-12-26 16.53.58.png](https://qiita-image-store.s3.amazonaws.com/0/206130/9c301ffe-2266-3354-6bc1-472a6f3ed789.png)

次に同じ要領で入力用バケットを作成します。オプションなどは上とまったく同じ方式です。

![スクリーンショット 2018-12-26 16.57.17.png](https://qiita-image-store.s3.amazonaws.com/0/206130/5d65ea7c-7c17-7b83-18b8-0dd7292ce671.png)

# 学習用ファイルのアップロード

入力用バケットには、先ほど事前準備でダウンロードした2つのファイルをアップロードして下さい。drag & dropでアップロード可能です。最終的には下の図のようになっているはずです。

![スクリーンショット 2018-12-26 17.08.57.png](https://qiita-image-store.s3.amazonaws.com/0/206130/2e759c0e-0a57-31ba-7b9f-09e2dba80dcb.png)

# モデル作成
お待たせしました。これで必要な準備作業はすべて完了しました。次にいよいよNeuNetSのモデル作成機能を呼び出します。
[Watson Studio初期画面](https://www.ibm.com/cloud/watson-studio)から右上のリンクをクリックしてWatson Studioにログインします。ログイン後に下の画面になったら、右上の「Sign in ..」のリンクをクリックします。

![スクリーンショット 2018-12-26 17.12.40.png](https://qiita-image-store.s3.amazonaws.com/0/206130/7a94eada-2d40-ce26-05b2-0284165cabff.png)

下の画面になったら、右上の「Get started」のリンクをクリック。

![スクリーンショット 2018-12-26 17.16.08.png](https://qiita-image-store.s3.amazonaws.com/0/206130/3948720f-2f7e-1538-83c5-665440f1054f.png)

次の画面で先ほど作ったばかりのプロジェクトのリンクをクリックします。

![スクリーンショット 2018-12-26 17.16.17.png](https://qiita-image-store.s3.amazonaws.com/0/206130/2fb11bc6-b97b-3ce7-7576-7b614d993491.png)

プロジェクトのメイン画面になったら、画面右上の「Add to project」をクリック、引き続きメニューから「SYNTHESIZED NE..」を選択します。

![スクリーンショット 2018-12-26 17.16.56.png](https://qiita-image-store.s3.amazonaws.com/0/206130/756a4d25-d905-acf6-b88a-dd206479034a.png)

すると下のように「Synthesize text or image classifier」というパネルが表示されます。「CIFAR10 Test」のように名前を適当に設定した後、「Training data connection」の欄の「Select」ボタンをクリックします。

![スクリーンショット 2018-12-26 16.46.53.png](https://qiita-image-store.s3.amazonaws.com/0/206130/c9cc5c30-6b7c-ad39-262a-5a1d3cbf09b8.png)

下のような画面になりますので、「New connection」タブをクリック

![スクリーンショット 2018-12-26 16.47.26.png](https://qiita-image-store.s3.amazonaws.com/0/206130/8c31127e-5c29-3740-3595-fb3dafe8b6a4.png)

ドロップダウンから先ほど作成したCloud Object Storageのインスタンスと、入力用のバケットを順に選択し、画面右下の「Create」ボタンをクリックします。

![スクリーンショット 2018-12-26 16.48.30.png](https://qiita-image-store.s3.amazonaws.com/0/206130/4f7391a2-1590-ec04-afdf-f5ceec690300.png)

元の画面に戻りますので、次の「Trained model connection」の下のボタンをクリックします。今度は「Existing Connections」のタブから設定可能です。
「Cloud Object Storage connection」のドロップダウンは先ほど自動作成したconnection名、「Bucket containing results data」の欄は、出力用バケットを指定して、画面右下の「Select」をクリックします。

![スクリーンショット 2018-12-26 17.42.12.png](https://qiita-image-store.s3.amazonaws.com/0/206130/b1bf0d38-42d7-2d6b-0f59-28f04b7b3a5f.png)

下記が、必要な項目の設定がすべて完了した状態での設定画面です。赤枠で囲んだ設定項目がすべて適切に設定されたことを確認した上で、画面右下の「Begin Synthesis」をクリックします。

![スクリーンショット 2018-12-26 16.59.10.png](https://qiita-image-store.s3.amazonaws.com/0/206130/68ff5278-045c-cd8b-96f6-4eca48120647.png)

下記がボタンをクリックした直後の画面です。ステータスが「Preprocessing」となっており、学習データの整合性チェックを行っている状態であることを示しています。

![スクリーンショット 2018-12-26 17.04.01.png](https://qiita-image-store.s3.amazonaws.com/0/206130/7402cb9a-961f-bd31-f135-183baede84ba.png)

数分間待っていると、下のような画面に遷移します。整合性チェックが完了して最適なモデルを自動作成するフェーズに移行したことを示しています。ここまでくればもう画面を見ている必要はないです。ブラウザをいったん閉じて、後のことはWatsonに任せて下さい。

![スクリーンショット 2018-12-26 17.31.05.png](https://qiita-image-store.s3.amazonaws.com/0/206130/4aba07c1-8183-42c6-eb48-f4e040aa3495.png)

2、3時間後にWatson Studioの同じ画面を開くと、下の図のように完成したモデルの画面に遷移していることがわかります。

下の図が、完成後の画面例です。(機械学習の通例でいつもこの精度が出るわけではありません)
Accuracyが93.6%となっていることがわかります。クラウドには50000件アップしたのですが、学習に使われたのは40000件で、10000件は検証データとして使われていることもわかります。

![スクリーンショット 2018-12-26 18.06.52.png](https://qiita-image-store.s3.amazonaws.com/0/206130/fc160475-dd9b-989e-ff96-42ffee193ff0.png)

ツールにはConfusion Matrixの表示機能も持っていて、その機能を使って表示した結果です。イヌとネコの区別が難しいことが、この表から読み取れます。

![スクリーンショット 2018-12-26 18.07.50.png](https://qiita-image-store.s3.amazonaws.com/0/206130/022723eb-96d1-837d-0314-32c62b598df2.png)

# モデルの検証
ツールの画面上はこんな形で高い精度がでているのですが、本当にそうなのか確認してみます。
NeuNetSで作成したモデルの使い方としては次の2つの方法があります。

* モデルファイルをローカルにダウンロードして利用する
* モデルファイルをWatson Studio上のMachine Learningモデルとしてデプロイする

このうち、精度の検証は前者の方法のほうが簡単にできるので、前者の方法で確かめました。
まず、下の画面から「Download model」を選択して、作ったモデルをダウンロードします。

![スクリーンショット 2018-12-26 18.17.04.png](https://qiita-image-store.s3.amazonaws.com/0/206130/c29da1ab-ffef-4363-2be6-7f623bde4abf.png)
ダウンロードしたファイルは ``neunets_output.tar.gz`` という名前になっています。
解凍すると、``keras_model.hdf5``と``metadata.json``という二つのファイルになっています。この``keras_model.hdf5``というファイルはKerasのライブラリですぐにKerasのModelクラスにすることができます。
以下に、このファイルをModelに戻して、CIFAR-10の検証データ(学習には一切使われていない)を使って精度を検証するサンプルプログラムを記載します。

```py3

# Modelのロード
from keras.models import load_model
model = load_model('keras_model.hdf5')

# CIFAR-10データ読み込み
from keras.datasets import cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()

# 入力データの前処理
# 入力データに関しては前処理として標準化を行う必要がある
x = x_test
import numpy as np
mean = np.mean(x)
std = np.std(x)
x = (x - mean)/std

# 正解データを配列に
yt = y_test.ravel()

# テストデータから予測値の取得
predict = model.predict(x)

# 予測値から、予測ラベルを求める
yp = np.argmax(predict, axis=1).ravel()

# 精度(Accuracy)の計算
from sklearn.metrics import accuracy_score
print('accuracy:', accuracy_score(yt, yp))

# 
from sklearn.metrics import classification_report
print(classification_report(yt, yp))

# Confusion Matrixの表示
from sklearn.metrics import confusion_matrix
print(confusion_matrix(yt, yp))
```

結果は次のとおりで、確かに93%強の精度がでていそうなことがわかります。

```
accuracy: 0.9323
```

```
             precision    recall  f1-score   support

          0       0.95      0.93      0.94      1000
          1       0.95      0.97      0.96      1000
          2       0.91      0.91      0.91      1000
          3       0.88      0.85      0.86      1000
          4       0.94      0.94      0.94      1000
          5       0.89      0.89      0.89      1000
          6       0.91      0.97      0.94      1000
          7       0.97      0.95      0.96      1000
          8       0.97      0.96      0.96      1000
          9       0.96      0.94      0.95      1000
avg / total       0.93      0.93      0.93     10000
```

```
[[930   4  23   4   4   1   6   1  23   4]
 [  2 975   1   0   0   2   2   1   1  16]
 [ 13   0 909  17  17  13  24   4   1   2]
 [  5   2  19 852  14  62  33   3   3   7]
 [  0   0  13  13 941  11  14   7   0   1]
 [  4   0  12  56  18 891   8  11   0   0]
 [  4   0  11  11   0   3 971   0   0   0]
 [  3   0   6  10  11  15   4 949   0   2]
 [ 16   8   2   2   0   2   1   0 960   9]
 [  5  36   3   5   1   0   0   0   5 945]]
```

次に検証データの先頭50個で、どんな画像を間違えていたのか調べてみました。

```
# データ内容の確認
import matplotlib.pyplot as plt
N = 50
indexes = range(N)

# x_orgの選択結果表示 (白黒反転)
x_selected = x_test[indexes]
y_selected = yt[indexes]

# 予測値の計算
yp_test = yp[indexes]

# グラフ表示
plt.figure(figsize=(15, 20))
for i in range(N):
    ax = plt.subplot(10, 10, i + 1)
    plt.imshow(x_selected[i])
    ax.set_title('%d:%d' % (y_selected[i], yp_test[i]),fontsize=14 )
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
plt.show()
```

結果がこちら。
写真のタイトルの文字の左が正解値、右が予測値です。それぞれの数字は、この表で読み替えます。

```
[['0' 'airplane']
 ['1' 'automobile']
 ['2' 'bird']
 ['3' 'cat']
 ['4' 'deer']
 ['5' 'dog']
 ['6' 'frog']
 ['7' 'horse']
 ['8' 'ship']
 ['9' 'truck']]
```

![スクリーンショット 2018-12-26 19.15.51.png](https://qiita-image-store.s3.amazonaws.com/0/206130/41cbe94e-5c61-e43d-38a5-58a894f9ff04.png)

50件中3件エラーですが、人間でもわけわからないものばかりです。
相当いい精度のモデルができたことは確かなようです。

最後に、以下のコードでモデルのネットワーク図を作ってみました。

```
from keras.utils import plot_model
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
plot_model(model, to_file='neunets-model.png', show_shapes=True, show_layer_names=True)
```

その結果については、Githubにアップしておきました。
[ネットワーク図](https://github.com/makaishi2/NeuNetS-sample/blob/master/model/neunets-model.png)

全部は載せきれないので、冒頭の部分だけアップしておくと、こんな感じです。

![スクリーンショット 2018-12-26 19.09.48.png](https://qiita-image-store.s3.amazonaws.com/0/206130/1cf4ebac-8a28-ac07-95e1-a95c8fec6678.png)

結構すごい仕組みができているかなという感想です。皆様も是非お試し下さい!

# (参考)学習データの作り方
[Training data format required for training models using NeuNetS](https://dataplatform.cloud.ibm.com/docs/content/analyze-data/nns-training-data-format.html?linkInPage=true)に解説がありますが、データの作り方は大変簡単です。

``train.zip``と``labels.csv``という2つの名前決め打ちのファイルを作って、COS(Cloud Object Storage)にアップロードするだけです。

イメージデータ以外に正解ラベルを示すファイル(labels.csv)が必要ですが、そのファイルの書式は下記のとおりです。

```
<filename1>,<labelname1>
<filename2>,<labelname2>
:
```

例として、サンプルで作ったCIFAR10学習用データの冒頭の部分は次のようになっています。

```
frog/train-frog-00000.png,frog
truck/train-truck-00001.png,truck
truck/train-truck-00002.png,truck
deer/train-deer-00003.png,deer
automobile/train-automobile-00004.png,automobile
automobile/train-automobile-00005.png,automobile
bird/train-bird-00006.png,bird
horse/train-horse-00007.png,horse
ship/train-ship-00008.png,ship
cat/train-cat-00009.png,cat
deer/train-deer-00010.png,deer
```

イメージファイルの種類としては``jpeg``と``png``が対応しています。
以下に、kerasライブラリで提供されるCIFAR-10の学習用データから、検証用のtrain.zipとlabels.csvを生成するサンプルコードを添付します。


```py3
# 作業用ディレクトリの準備
base_dir = 'Image'
zip_dir = 'ZIP'

import os
import shutil
shutil.rmtree(base_dir)
shutil.rmtree(zip_dir)
os.mkdir(base_dir)
os.mkdir(zip_dir)

# イメージ読み込み
from keras.datasets import cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()

# ラベル名配列
import numpy as np
cifar10_labels = np.array([
    'airplane',
    'automobile',
    'bird',
    'cat',
    'deer',
    'dog',
    'frog',
    'horse',
    'ship',
    'truck'])

# サブディレクトリ作成
for i in range(10):
    subdir = '%s/%s' % (base_dir, cifar10_labels[i])
    print(subdir)
    os.mkdir(subdir)

    from PIL import Image

label_file = '%s/labels.csv' % zip_dir
f = open(label_file, "w")

# 生成イメージ数(最大50000)
image_num = 100
#image_num = 50000

# イメージファイル生成
for i in range(image_num):
    img = Image.fromarray(x_train[i])
    label_i = y_train[i][0]
    label = cifar10_labels[label_i]
    #filename = '%s/%s/train-%s-%05d.png' % (base_dir, label, label, i)
    filename1 = '%s/train-%s-%05d.png' % (label, label, i)
    filename2 = '%s/%s' % (base_dir, filename1)
    #print(filename1)
    f.write('%s,%s\n' % (filename1, label))
    with open(filename2, mode='wb') as out:
        #img.save(out, format='jpeg', quality=100, optimize=True)
        img.save(out, format='png')
f.close()

# 圧縮ファイル作成
import tarfile
os.chdir(base_dir)

# 圧縮ファイル名
tar_name = '../%s/train.zip' % zip_dir

# 圧縮処理
archive = tarfile.open(tar_name, mode='w:')
archive.add('.')
archive.close()
os.chdir('..')
```

# (参考)サンプルアプリケーション
上記の方法で作ったModelをWatson Machine Learningにデプロイ、Webサービス化しました。そしてそのWebサービスを呼び出すサンプルアプリを作ってみました。
Watson StudioでCIFAR-10用に作った[サンプルアプリ](https://github.com/makaishi2/wml-cifar10-sample/blob/master/README.md)をほんの少し手直ししただけです。

![スクリーンショット 2018-12-27 13.07.46.png](https://qiita-image-store.s3.amazonaws.com/0/206130/228e1c4e-4772-c5b7-7290-ea9af4b021b4.png)

具体的には、イメージを受け取って加工し(イメージを(32,32,3)のNumpy配列に変換、更にListにする)APIを呼び出すところは一切変更していません。
APIの戻り値はデータの表現が若干変わりました。下記の様な形式のJsonデータがやってくるようになっています。

```json
{
  "automobile": 1.6959260384918906e-11,
  "bird": 1.0,
  "dog": 4.241513806846342e-10,
  "deer": 3.1998675931177445e-10,
  "truck": 8.276909539695065e-12,
  "frog": 1.0537125616494336e-09,
  "cat": 4.5520576891222575e-11,
  "horse": 2.7509487743326844e-11,
  "airplane": 2.0071992956616214e-09,
  "ship": 1.0772259473323942e-10
}
```

これに伴い、データを受けて表示するまでのロジックを若干変更しています。今までと比べるとシンプルな形式の戻り値なので、新たにアプリをつくる分にはかえってやりやすくなったと思います。

Githubのリンクは[こちら](https://github.com/makaishi2/wml-neunets-sample/blob/master/README.md)

README.MDに簡単な導入手順もついていますので、こちらもあわせてお試しいただければと思います。このアプリケーションもLiteアカウントの範囲内で利用可能です。

# おまけ
デモページ
最後に最近見つけたデモページをご紹介しておきます。なかなか派手なアニメーションで見ていて面白いです。

[Automatically build an optimized neural network](https://neunets.mybluemix.net)

![スクリーンショット 2019-01-30 13.25.11.png](https://qiita-image-store.s3.amazonaws.com/0/206130/82b11df6-9bca-45c4-ad43-7a6057b2728e.png)

![スクリーンショット 2019-01-30 13.25.55.png](https://qiita-image-store.s3.amazonaws.com/0/206130/769fbcdd-f655-9172-ce9c-2d0d58ed56fc.png)