[Windows編] Deep Learningをすぐに試せるライブラリKeras講座～その１

# はじめに
　近年流行りのディープラーニングを理論を知らなくてもとりあえず試してみたい人やただ使いたい人向けの投稿になります。自分のメモのために書いているので、分かりにくい箇所や間違いがあればご指摘ください。
　[Keras](http://keras.io/ja/)とは、TensorflowとTheanoのためのPythonによるラッパーであり、深層学習ライブラリです。このライブラリを使用することで、非常に簡単に深層学習を体感することができると共に、多くのネットワーク構造を難しい理論を意識することなくコーディングすることができます。
　この記事では、TensorflowとKerasの導入とまず手始めに多層パーセプトロンで四角形の識別を行います。

（追記）多くの方に見ていただけているようなので、学習済みパラメータを読み込んでの識別も追加しました。皆様の参考になれば幸いです。

（2017/03/09追記）2017年2月15日にGoogleからTensorFlow 1.0が公開されました。これに伴い、TensorFlowに新たにtf.kerasモジュールが導入される予定です。（今のところ、TensorFlow 1.2で追加予定みたいです。）そのため、これまで通りKerasを使ってバックエンドでTensorFlowを動かさなくても、直接KerasのAPIをTensorFlowから使うことができるようになります。
　ちょっと続きの記事をどちらで書くべきか迷っていますが、おそらくこの連載は純粋なKerasで書き切ろうと思っています。TensorFlowがKerasにも対応したらTensorFlowのコードも参考として、Gistにはアップすると思います。

# 実行環境
- Windows 10（64bit）with only CPU
- Python 3.5.2
- Anaconda 4.2.0（64bit）
- Tensorflow 0.12.1
- Keras 1.2.1

# 実行環境の導入手順
## Anacondaのインストール
　私は、Windowsでのpythonの実行環境として、Anacondaを使用しています。既に、使用している環境がある方は読み飛ばしていただいて問題ありません。Anacondaのインストールは、公式の[ダウンロードページ](http://www.continuum.io/downloads#windows)に移動して、環境にあったインストーラをダウンロードします。後は、インストーラを実行して、次へをクリックしていけば完了です。
　Anacondaのインストールと共に、ダウンロード時に選択したPython（私の場合はPython3）とjupyter notebookというエディタも入るので、別でpythonをインストールする必要はありません。
![anaconda2.png](https://qiita-image-store.s3.amazonaws.com/0/146858/b410b16f-ca3d-2528-ecb8-fcf69963b607.png)

## Tensorflowのインストール
　Kerasでは、バックエンドで動かすライブラリをTensorflowかTheanoかで選択できるのですが、ここではTensorflowを使用します。インストールには、pipを用いるため、まず、スタート画面からAnacondaを検索し起動します。
![anaconda_pronpt2.png](https://qiita-image-store.s3.amazonaws.com/0/146858/4042bdea-19f9-45d7-7ebc-65eb3ecc8aaf.png)
　インストール直後は、念のためpipを最新版に更新しておきましょう。最新版にするには、次のコマンドをAnaconda Promptで実行します。

```shell-session:pip_upgrade
$ pip install --upgrade pip
```
　最新版にできたら、Tensorflowをインストールします。Anacondaプロンプトを開いた状態のまま、次のコマンドを実行してください。GPU搭載PCとCPUのみのPCではインストール時のコマンドが異なるので注意してください。

```shell-session:tensorflow_install
# CPUのみの場合
$ pip install tensorflow
# GPU搭載している場合
$ pip install tensorflow-gpu

# インストールされているかの確認方法
# 次のコマンドを実行し、リストの中にtensorflowがあればOKです
$ pip list |grep tensorflow
```

（追記）（2017/3/8現在）TensorflowはPythonのバージョンが3.5.xでないと上記のコマンドでインストールすることができません。これを回避する方法をまとめた記事を書いたので、インストールできない方はそれを参考にTensorflowをインストールしてください。下記がその記事になります。
[WindowsのAnaconda環境に深層学習フレームワークTensorflow 1.0をインストールする方法](http://qiita.com/tomochiii/items/fe4c791b313ddcef4cd7)

## Kerasのインストール
　続けて、次のコマンドを実行するとKerasをインストール完了です。

```shell-session:Keras_install
# Kerasインストール
$ pip install keras

# インストールされているかの確認
# 同じくKerasが確認できればOKです。
$ pip list |grep Keras
```

## 導入できているかの確認
　ここで、本題に入る前に導入が正しくできているかを確認しておきます。Anacondaプロンプトを開いている状態だと思うので、そこでpythonを起動し、きちんとimportできるか確認してください。次のようになっていれば大丈夫です。

```shell-session:introduction_confirmation
# pythonの起動
$ python
# tensorflowのインポート
>>> import tensorflow 
# Kerasのインポート
>>> import keras
using TensorFlow backend
```

# 多層パーセプトロン（MLP）を用いた多値分類
## 四角形の横長か縦長かの分類
### データセットのダウンロード
　次のような画像を入力として、簡単な分類をしてみます。まず、Deeplearningをするにはデータが必要なので、[サイト](http://www.iro.umontreal.ca/~lisa/twiki/bin/view.cgi/Public/RectanglesData)からデータをダウンロードしてください。
![rectangle2.png](https://qiita-image-store.s3.amazonaws.com/0/146858/a819c590-f644-5c31-1edb-acf237c90101.png)
　このデータセットは次のような四角形の画像のデータセットとなっています。なので、手始めにKerasを使って縦長か横長かの２クラス分類をしてみます。

![rectangleImage.png](https://qiita-image-store.s3.amazonaws.com/0/146858/78e6a4ba-6c42-6605-aa01-55e0d18c2cb5.png)

### プログラミング
　Jupyterの使い方は、[こちら](http://qiita.com/taka4sato/items/2c3397ff34c440044978)が分かりやすくまとめられているので参考にしてください。さて、プログラムの手順についてですが、ざっくりいうと次のようになります。

1. 学習用のデータの読み込み
2. 学習できる形にデータの整形（前処理）
3. ネットワーク層の構築
4. 学習
5. テストデータで性能の評価
6. 画面に結果の一部を表示して確認
7. アーキテクチャと重みを書き出し

　一度学習してしまえば、２回目からは書き出したアーキテクチャと重みを読み込んで使えるので３、４が必要なくなります。実装するとコードは次のようになります。実行結果などはJupyter Notebookを[Gist](http://gist.github.com/keit0222/24922f11c7c3e3c36e42a4e4f2722ee4)にアップしているので、必要でしたらそちらを確認してください。

```py3:rectangle_mlp.py
import numpy as np
from scipy.misc import toimage
from keras.utils import np_utils
import matplotlib.pyplot as plt

from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation

# 四角形のデータ読み込み関数
def load_rectangles_data(filename):
    data = np.loadtxt(filename)
    # 画像データの抽出
    x = data[:,0:784]
    # 正解ラベルの抽出
    y = data[:,784]
    return [x,y]

# パラメータ設定
nb_classes = 2
epoch = 20
batch = 10

# ラベル："Horizontal": 横長，"Vertical": 縦長
label = ["Horizontal","Vertical"]

# 四角形の訓練データの読み込み
[X_train,y_train] = load_rectangles_data('rectangles_train.amat')

# 四角形のテストデータの読み込み
[X_test,y_test] = load_rectangles_data('rectangles_test.amat')

# ラベルをクラス数に対応する配列に変更
# 例：y_train:[0 1 0 0] -> Y_train:[[1 0],[0 1],[1 0],[1 0]]
Y_train = np_utils.to_categorical(y_train,nb_classes)
Y_test = np_utils.to_categorical(y_test,nb_classes)

# 多層パーセプトロンのネットワーク作成
# 入力を784次元(28x28)で、最終的な出力をクラス数に設定
model = Sequential()
model.add(Dense(512, input_dim=784, init='uniform'))
model.add(Activation('sigmoid'))
model.add(Dropout(0.5))
model.add(Dense(512, init='uniform'))
model.add(Activation('sigmoid'))
model.add(Dropout(0.5))
model.add(Dense(nb_classes, input_dim=512, init='uniform'))
model.add(Activation('softmax'))

# 2値分類なのでバイナリを選択，最適化アルゴリズムはRMSpropを選択
model.compile(loss='binary_crossentropy',
              optimizer='rmsprop',
              metrics=['accuracy'])

model.fit(X_train, Y_train,
          nb_epoch=epoch,
          batch_size=batch)

# テストデータを使って，作成したモデルと重みの評価
score = model.evaluate(X_test, Y_test, batch_size=batch)
# 今回は正解率92%
print(score)
# テストデータの一部のラベルを予測
classified_labels = model.predict_classes(X_test[0:10,:].reshape(-1,784))

# 表示する大きさを指定
plt.figure(figsize=(20,10))
for i in range(10):
    plt.subplot(2,5,i+1)
    # 画像データに変換
    img = toimage(X_test[i].reshape(28,28))
    plt.imshow(img, cmap='gray')
    plt.title("Class {}".format(label[classified_labels[i]]),fontsize=20)
    plt.axis('off')
plt.show()

# モデルと重みのパラメータを書き出し
model_json = model.to_json()
open('rectangles_architecture.json', 'w').write(model_json)
model.save_weights('rectangles_weights.h5', overwrite=True)
```
### 結果
　エポック数、バッチ数、最適化アルゴリズムなどのパラメータを適当に調整してやると、この簡単な分類だと正答率９２%程度は出るみたいです。一部のテストデータの結果を表示しています。正しく分類されているのが確認てきますね。
![result.png](https://qiita-image-store.s3.amazonaws.com/0/146858/0f9df2d9-962a-6faf-72b4-fb55abd0a2af.png)

### 学習済みデータを使っての分類
　学習させるには、やはり時間がかかります。そこで、実用の際には、あらかじめ学習済みの重みを保存しておいてそれを使用するという方法をとります。具体的にどうするのかと言うと、先ほどのスクリプトの最後でJSONとh5という拡張子で保存していると思うので、それを読み込むだけです。簡単です。
　上では、テストデータを分類していましたが、どんなデータでも分類できるのかこれだけだと信用できません。そこで、ペイントで適当に次のような５６ピクセル×５６ピクセルの四角形のデータを４枚用意しました。これが分類できるかどうか試してみます。結果は、見ていただくと正しく分類できているのが分かりますね。
![result3.png](https://qiita-image-store.s3.amazonaws.com/0/146858/27088bea-a04a-f362-898b-81a63bde397e.png)
　使用した読み込んで分類するPythonスクリプトは次になります。[Gist](http://gist.github.com/keit0222/77dcf73e9930dcae1d5708ab154f3034)にJupyter Notebookの結果も載せておりますので、必要な方はそちらを確認ください。

```py3:rectangle_load_learned_parameter.py
import numpy as np
import matplotlib.pyplot as plt

from keras.utils import np_utils

from keras.models import Sequential,model_from_json
from keras.layers import Dense, Dropout, Activation
from keras.preprocessing import image
from keras.applications.imagenet_utils import preprocess_input, decode_predictions

# アレイの値を閾値を指定して0と1に2値化する関数
# array:2次元のアレイ、threshold:閾値
def array2d_binarization(array,threshold):
    (row,column) = array.shape
    for i in range(row):
        for j in range(column):
            if(array[i,j] > threshold):
                array[i,j] = 1
            else:
                array[i,j] = 0
    return array

# テストに用意した画像の枚数
test_num = 4

# 識別したラベルの意味
# "Horizontal": 横長，"Vertical": 縦長
label = ["Horizontal","Vertical"]

# モデルの読み込み
model = model_from_json(open('rectangles_architecture.json').read())
# モデルの重みの読み込み
model.load_weights('rectangles_weights.h5')

for s in range(test_num):
    # ファイル名の指定
    img_path = "rectangle" + str(s)+".jpg"
    # 28ピクセルx28ピクセル、グレースケールで画像を読み込み
    img = image.load_img(img_path, grayscale=True, target_size=(28, 28))
    # 読み込んだイメージをアレイに変換
    x = image.img_to_array(img)
    # アレイサイズの確認
    print(x.shape)
    
    # 画像データを閾値50で2値化、引数として28x28を与えるためにx[:,:,0]としている
    x = array2d_binarization(x[:,:,0],50)
    # 入力するために、28x28の2次元配列を大きさ784のベクトルに整形
    x = x.reshape(-1,784)

    # クラスの予測
    classified_label = model.predict_classes(x)
    
    # 予測結果のプロット
    plt.subplot(1,test_num,s+1)
    plt.imshow(img, cmap='gray')
    plt.title("Class {0}".format(label[classified_label[0]]),fontsize=10)
    plt.axis('off')
plt.show()
```

# おわりに
　続けて、何回かに分けてKerasの使い方を記事にしていきたいと思っています。次回はもう少し複雑な分類についての予定です。誤りがあれば、訂正しますのでご指摘くださると助かります。

# 参考
- [Tensorflow公式ドキュメント-インストール](http://www.tensorflow.org/get_started/os_setup#pip_installation)
- [Keras公式ドキュメント-インストール](http://keras.io/ja/#_2)
- [Keras公式ドキュメント-Sequentialモデルのガイド](http://keras.io/ja/getting-started/sequential-model-guide/#_5)
- [四角形のデータセット](http://www.iro.umontreal.ca/~lisa/twiki/bin/view.cgi/Public/RectanglesData)
- [Jupyter事始め](http://qiita.com/taka4sato/items/2c3397ff34c440044978)
- [TensorFlow 1.0を発表](http://developers-jp.googleblog.com/2017/02/announcing-tensorflow-10..html)