KerasでFashion-MNISTを分類してみた #IBMCloud 編

※この記事は、[IBM Cloud Advent Calendar 2017](https://qiita.com/advent-calendar/2017/bluemix) の22日目の記事です。

#機械学習／Deep Learningインフラの選択肢
最近は、機械学習／Deep Learningのインフラとしてオンプレからクラウドまで様々な製品・サービスが提供されています。例えば、IBMの場合は以下のような選択肢があります。

<img width="931" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/63451/9dae55cf-9a39-51b4-f33b-a9113ef2ba5c.png">

各製品・サービスの特徴をまとめると以下の表となりますが、ざっくり言うと、とにかくパフォーマンス重視でサーバー・ルームやデータセンターを持っていればオンプレミスのPOWER。固定資産は持ちたくないけれど最新のGPUが利用したい場合はベアメタルGPUが利用可能なIBM Cloud IaaS (旧SoftLayer)。手軽さ重視でパフォーマンスはそこそこでOKであれば、IBM Cloud PaaS（旧Bluemix）で提供されるData Science Experienceがおすすめです。また、インフラではありませんが、機械学習／Deep Learningを内部的に使った画像認識、音声認識、自然言語認識ソリューションをSaaSのように利用したい場合は、Watson Developer Cloudといった感じでしょうか。

|提供形態|サービス名|特徴|URL|
|:--|:--|:--|:--|
|オンプレミス|PowerAI| ハードウェアは、IBM Power System S822LC for HPC(Minsky) IBM Power Systems AC922(IBM POWER9) 。POWERプロセッサおよびGPUに最適化されたコンパイル済みの主要なDeep Learningフレームワーク (Caffe、TensorFlow、Chainer)をパッケージ化して提供。|https://www.ibm.com/jp-ja/marketplace/deep-learning-platform|
|IaaS|IBM Cloud Infrastructure（旧SoftLayer)|国内含む世界39データセンターで利用可能。NVIDIA Tesla P100、NVIDIA Tesla K80といった主要なGPUを月額・時間課金のベアメタル・サーバーに搭載して提供。2018年には、最大Tesla P100ベースの仮想GPUサービスを提供予定（最大32vGPU）。|https://www.ibm.com/cloud-computing/bluemix/ja/gpu-computing|
|PaaS|Data Science Experience|Jupyter Notebook上のScala/Python、R Studio上のR、Spark Cluster、Brunel(Visualization)/Apache Toree (SparkとJupyterの統合)といったデータ・サイエンス系分析の開発・実行環境をPaaS (SaaS)として提供。CPU駆動のみでGPUは利用できない。|https://datascience.ibm.com/|
|SaaS|Watson Developer Cloud|コグニティブ・アプリケーションを開発するために、機械学習／Deep Learningを活用して画像認識、自然言語認識、音声認識系のサービスをAPIとして提供。学習モデルはブラックボックス化されている。|https://www.ibm.com/jp-ja/marketplace/cognitive-application-development|

本記事では、クレジットカードなしで無料利用できるIBM Cloud PaaSの[ライトアカウント](https://www.ibm.com/cloud-computing/jp/ja/bluemix/lite-account/)を使って、Data Science Experience (DSX)によるDeep Learningの手順をご紹介します。いま自分のPCで機械学習／Deep Learningをやられている方はぜひこの機会にDSXをお試しください。

#Data Science Experience (DSX)概要
DSXは、複数ユーザが共同でデータ分析を行うための統合分析プラットフォームです。ブラウザさえあれば、いつでもどこでも分析を進めることができます。機械学習ライブラリを標準サポートしており、分析ツールとしてJupyter Notebookを利用できます。Python/Scalaを利用して、オープンソース・ツールを利用したデータ分析が可能となります。また、Spark環境への接続が可能なため、PySparkなどを利用してサーバリソースを有効活用することができます。DSXで開発したコードはGitHubを利用して管理することができる上、共同作業環境としても大変利便性が高い作りになっております。DSXについてさらに詳しく知りたい方は[こちらの記事](https://qiita.com/ishida330/items/db496801290279aa015d)を参考にしてください。

Jupyter Notebookがクラウドで利用できる環境は各社各様ですが、DSXの特長は以下のとおりです。

- [IBM Watson Data Platform](https://www-01.ibm.com/software/jp/cmp/wdp/)の中核サービスの１つ
- IBM Cloud [ライトアカウント](https://www.ibm.com/cloud-computing/jp/ja/bluemix/lite-account/)が利用可能
- [SPSS](https://www.ibm.com/analytics/jp/ja/technology/spss/)のスコアリング機能も提供
- プロジェクト単位でのノートブックの管理
- Python2.7/Python3.5/R/Scalaが使用可能
- Apache Sparkのバージョンも指定可能
- リンク共有やGitHub/Gistでの共有可能
- numpy/pandas/matplotlib/scikit-learn/tensorflowインストール済み

参考：[Google・Microsoft・AWS・IBMが提供するJupyter Notebook サービスのまとめ](https://qiita.com/akimach/items/38d253c4d5b8daddcee5)

#DSXを使った画像認識の流れ

ライトアカウントを作成後、カタログから[Data Science Experience](https://console.bluemix.net/catalog/services/data-science-experience)を選択して作成ボタンをクリックすればすぐに利用できます。

<img width="948" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/63451/03ad5235-0652-3b98-5e47-d1ac2be1dc98.png">

DSXの場合、ライトアカウントで利用できる範囲は以下のとおりです。2つのSpark実行環境、5GBまでのオブジェクト・ストレージまで無料です。30日間放置すると削除されますので、1ヶ月に1回は動かすようにしてください。

<img width="809" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/63451/02100b5e-e30b-737a-7c71-765eaa115b8f.png">

起動後の画面イメージは以下のような感じです。

<img width="1268" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/63451/ed253eb1-687a-9c7e-3440-600cc0038a9f.png">

[New project]より新規プロジェクトを作成します。適当にプロジェクト名をつけてください。[Choose project options]より[Restrict who can be a collaborator]を選択すれば、コラボレーターを制限することができます。データを保存するストレージとしては、旧SoftLayerのObject Storage (Swift API)と、S3ベースのIBM Cloud Object Storageが選択可能です。Select Spark ServiceよりSpark実行環境を作成することができます。

<img width="1264" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/63451/4df890d4-a186-732d-3553-f6a08ef4e070.png">

[Create]をクリックすれば、Jupyoter Notebookの作成が可能です。

<img width="942" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/63451/85534e89-065a-3775-cf71-3adcc1a830bb.png">

#Keras (TensorFlow)による画像分類

##Kerasとは
Kerasは、Pythonで書かれたオープンソースニューラルネットワークライブラリです。MXNet（英語版）、Deeplearning4j、TensorFlow、CNTK、Theano（英語版）の上部で動作することができます。ディープニューラルネットワークを用いた迅速な実験を可能にするよう設計され、最小限、モジュール式、拡張可能であることに重点が置かれています。プロジェクトONEIROS (Open-ended Neuro-Electronic Intelligent Robot Operating System) の研究の一部として開発されました。中心的な開発者、メンテナはGoogleのエンジニアの[François Chollet](https://twitter.com/fchollet)です。最近、Kerasの作成者本人であるFrançois Cholletが、Deep Learning with Pythonという本を出しています。PythonとKerasライブラリを使用したDeep Learningについて分かりやすく解説しているので、興味のある方は御覧ください。O'Reillyの[Safari Books Online](https://www.safaribooksonline.com/library/view/deep-learning-with/9781617294433/)でも配信されています。
[![代替テキスト](![image.png](https://qiita-image-store.s3.amazonaws.com/0/63451/2c64651e-7592-3fd2-57e9-3aaa792Zc609f.png)
画像のURL "画像タイトル")](https://www.amazon.co.jp/Deep-Learning-Python-Francois-Chollet/dp/1617294438)

[![Keras](https://qiita-image-store.s3.amazonaws.com/0/63451/64b7f6b1-83bd-0264-4f21-bd17deb25513.png "Keras")](https://www.amazon.co.jp/Deep-Learning-Python-Francois-Chollet/dp/1617294438)

##データセット
ここでは、データセットとして、Fashion-MNISTを用います。画像ソースは、[Zalando](http://www.zalando.com/)というファッション記事のデータベースから取得されています。60,000枚の28x28、10個のファッションカテゴリの白黒画像と10,000枚のテスト用画像データセットからなります。このデータセットは、[MNIST](http://yann.lecun.com/exdb/mnist/)の完全な互換品として使えます。[Fashion-MNIST](https://github.com/zalandoresearch/fashion-mnist/blob/master/README.md)はgithub上に公開されており、日本語解説は[こちら](http://tensorflow.classcat.com/category/fashion-mnist/)にあります。

<img width="765" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/63451/0a74e3e4-b594-e3f5-8d2b-592d5daf8da8.png">


クラスラベルは次の通りです。

| ラベル | 説明 | 
|:-----------------|:------------------|
|0|Tシャツ/トップス|
|1|ズボン|
|2|プルオーバー|
|3|ドレス|
|4|コート|
|5|サンダル|
|6|シャツ|
|7|スニーカー|
|8|バッグ|
|9|アンクルブーツ|

使い方

```py3
from keras.datasets import fashion_mnist
(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()
```


戻り値:

- 2つのタプル:
    - x_train, x_test: shape (num_samples, 28, 28) の白黒画像データのuint8配列
    - y_train, y_test: shape (num_samples,) のラベル(0-9のinteger)のuint8配列

#Jupyter Notebookでプログラミング
早速、DSXのJupyter Notebookを起動しプログラミングしていきます。Jupyter Notebookを[こちら](https://dataplatform.ibm.com/analytics/notebooks/586972f9-a677-4259-b37f-38c1bb12bd6d/view?access_token=d84b76ecaedbb46cc4ea8730a48533dee2386ee69c4dc81f0759f058336a8ba3)に公開していますので、本記事では重要な箇所のみ解説します。

##DSX環境確認

```py3

#OSディストリビューションの確認
!cat /etc/redhat-release 

#CPUスペックの確認
!lscpu

#メモリサイズの確認
!free -g

#ライブラリ・バージョンの確認
!python --version
import keras; print('Keras ' + keras.__version__)
import tensorflow as tf; print('TensorFlow ' + tf.__version__)
```

DSXでは、CPUとしては、Intel(R) Xeon(R) CPU E5-2690 v3 @ 2.60GHz（48コア）が使われていました。OSは、CentOS Linux release 7.4、そしてPython 3.5.2、Keras 2.1.2、TensorFlow 1.2.1でした。

##ライブラリのインポート
```py3
from __future__ import print_function
import numpy as np

#Kerasが提供するSequentialモデルをインポート
from keras.models import Sequential

from keras.layers.core import Dense, Dropout, Activation
from keras.optimizers import SGD, Adam, RMSprop
from keras.utils import np_utils
```
##データセットのインポート
```py3
from keras.datasets import fashion_mnist
```
##データのロード
```py3
#学習データとテストデータをKerasが用意しているFashion-MNISTデータ・セットから読み込む
(X_train, y_train), (X_test, y_test) = fashion_mnist.load_data()
```
##データセットの一部を視覚化する
```py3


#matplotlibのグラフをJupyter Notebookのインラインで表示 
%matplotlib inline

#NumPyのためのグラフ描画ライブラリmatplotlibをインポートする
import matplotlib.pyplot as plt

# 400枚の画像を20×20の行列としてプロットする
n_cols = 20
n_rows = 20

#サブプロットで複数の画像を16ピクセル×16ピクセルとして表示させる。
fig, axs = plt.subplots(n_rows, n_cols, figsize=(16,16))

for ax, pixels in zip(axs.flat, X_train):
    ax.imshow(pixels, cmap="gray")
    ax.set_xticks([])
    ax.set_yticks([])
    
plt.show()

```
<img width="493" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/63451/d05dd81e-7e3b-4499-5538-86a4d5aa880c.png">

##データのリシェープ
```py3
#学習データを784列の2次元配列としてリシェープする
X_train = X_train.reshape(60000, 784)  # Training set
X_test = X_test.reshape(10000, 784)    # Same for the test set
```

##データの正規化
```py3
#各画像は、グレースケールとしてエンコーディング（255: 白、0: 黒）に対応する8ビットの符号なし整数を使って符号化されているため、データを正規化し、値を[0,1]間隔にリスケールし、浮動小数点数に変換する。
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train = X_train / 255
X_test = X_test / 255
```

##ラベルをOne-Hotでエンコード

```py3
#One-Hotエンコーディングを適用する
Y_train = np_utils.to_categorical(y_train, 10)
Y_test = np_utils.to_categorical(y_test, 10)
```

##ニューラルネットの学習
```py3
# np.random.seedの引数を指定して毎回同じ乱数を生成させる（再現性のため）
np.random.seed(1234)

#batch_size - ANNトレーニングで使用されたミニバッチのサイズ
batch_size = 128

#nr_classes - ターゲットクラスの数。可能な10桁の数字（0〜9）
nr_classes = 101

#nr_iterations - ANNトレーニングの期間（エポック）
nr_iterations = 1000

#Kerasが用意するSequentialモデルを使用する
model = Sequential()

#入力層に784個のニューロン（28×28ピクセル）を追加する
model.add(Dense(200, input_shape=(784,)))

#入力層の活性化関数を設定し、そのニューロンを整流された線形単位に変換する
model.add(Activation('relu'))

#過学習を防ぐため学習時間中に各更新時にランダムに入力単位の割合を0に設定するドロップアウトレイヤーを追加する
model.add(Dropout(0.5))

#10個のニューロン（10個の可能なクラス）で構成される出力層を追加し、値はsoftmax関数に渡す。
model.add(Dense(10))
model.add(Activation('softmax'))

# モデルの概要を表示させる
model.summary()

Layer (type)                 Output Shape              Param #   
=================================================================
dense_1 (Dense)              (None, 200)               157000    
_________________________________________________________________
activation_1 (Activation)    (None, 200)               0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 200)               0         
_________________________________________________________________
dense_2 (Dense)              (None, 10)                2010      
_________________________________________________________________
activation_2 (Activation)    (None, 10)                0         
=================================================================
Total params: 159,010
Trainable params: 159,010
Non-trainable params: 0
_________________________________________________________________

```

Sequentialモデルを使用して下図のような順伝播型ニューラルネットワークを定義しています。

<img width="520" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/63451/5409f113-e077-d59a-6793-1d342943efab.png">

```py3
#コンパイルしてニューラルネットワークを学習する
model.compile(loss='categorical_crossentropy', optimizer=Adam(), metrics=['accuracy'])
```

```py3
#経過を表示させながら学習を実行させる
history = model.fit(X_train, Y_train,
                    batch_size = batch_size, epochs = nr_iterations,
                    verbose = 1, validation_data = (X_test, Y_test))

score = model.evaluate(X_test, Y_test, verbose = 0)

Train on 60000 samples, validate on 10000 samples
Epoch 1/1000
60000/60000 [==============================] - 2s 40us/step - loss: 0.6379 - acc: 0.7763 - val_loss: 0.4682 - val_acc: 0.8319
Epoch 2/1000
60000/60000 [==============================] - 2s 36us/step - loss: 0.4590 - acc: 0.8368 - val_loss: 0.4136 - val_acc: 0.8511
Epoch 3/1000
60000/60000 [==============================] - 2s 36us/step - loss: 0.4205 - acc: 0.8490 - val_loss: 0.3964 - val_acc: 0.8589
Epoch 4/1000
60000/60000 [==============================] - 2s 35us/step - loss: 0.3983 - acc: 0.8558 - val_loss: 0.3867 - val_acc: 0.8622
Epoch 5/1000
60000/60000 [==============================] - 2s 36us/step - loss: 0.3834 - acc: 0.8611 - val_loss: 0.3693 - val_acc: 0.8680
（省略）
Epoch 996/1000
60000/60000 [==============================] - 2s 36us/step - loss: 0.0849 - acc: 0.9651 - val_loss: 0.6909 - val_acc: 0.8909
Epoch 997/1000
60000/60000 [==============================] - 2s 36us/step - loss: 0.0864 - acc: 0.9642 - val_loss: 0.6994 - val_acc: 0.8926
Epoch 998/1000
60000/60000 [==============================] - 2s 36us/step - loss: 0.0847 - acc: 0.9650 - val_loss: 0.7164 - val_acc: 0.8919
Epoch 999/1000
60000/60000 [==============================] - 2s 37us/step - loss: 0.0882 - acc: 0.9638 - val_loss: 0.6794 - val_acc: 0.8911
Epoch 1000/1000
60000/60000 [==============================] - 2s 36us/step - loss: 0.0842 - acc: 0.9643 - val_loss: 0.7088 - val_acc: 0.8954
```

##損失関数のグラフ化
```py3
#結果を表示させる
print('Test loss:', score[0])
print('Test accuracy:', score[1])
```
<img width="527" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/63451/25dec66e-cdba-a599-a864-11048e3b4eeb.png">

#まとめ
今回はIBM Cloud PaaSのDSX上で、Kerasが提供するSequentialモデルを用いて、Fashion-MNISTを分類してみました。結果として、5 epochsで損失は0.3834、精度は86.11%を超えました。35 epochsで損失0.2607、精度は90.26%と90%を超えました。365 epochsで損失0.1230となり、精度は95.08 %となりました。上図は1,000 epochs までの訓練損失のグラフです。1,000 epochsで損失0.0842、精度 96.43%という結果なのでまず良好な結果と言えます。1,000 epochsとなるとDSXでも数十分は学習時間にかかるためGPUが欲しくなるところですが、自分のPCで実行するよりかなり高速でした。ぜひ一度お試しください。

IBMが提供する[CognitiveClass.ai](https://cognitiveclass.ai/)では、12月31日が〆切となりますが、6ヶ月間でIBM Cloudを$1,200使い放題の[プロモーション](https://qiita.com/ishida330/items/62d0f3ddaf89c62ed1c0)もやっているようなので、この機会にぜひ登録ください！

<img width="1109" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/63451/71a390d8-2b35-2874-5b27-903e904d0bbb.png">