Kerasでとりあえず動かしてみるディープラーニング#3

#はじめに
GPUが載ってないPCでディープラーニングをやってみたい、という人向けです。
「Kerasでとりあえず動かしてみるディープラーニング#1」及び「Kerasでとりあえず動かしてみるディープラーニング#2」で作ったプログラムを改変します。
https://qiita.com/norikawamura/items/f748b9bbf597a5953235

#CSVデータで分類を行う
1.サンプルデータの用意
「UC Irvine Machine Learning Repository」の乳癌の細胞の画像診断からの良性・悪性に関するデータを取得します。
下記のURLから「wdbc.data」「wdbc.names」の２ファイルをダウンロードします。
https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/

2.サンプルデータの確認
wdbc.namesがデータの内容の説明、wdbc.dataが実際のデータのCSVファイルになっています。

```Python
import pandas as pd
#CSVからデータを読み込む
filename = 'wdbc.data'
bc_data = pd.read_csv(filename, header=None)
```

3.学習用のデータを用意する
２列目が「B」：良性（benign）または「M」：悪性（malignant）の２種類なので、これを整数にするための関数を定義します。

```Python
#クラス文字列の数値化
def classtonum(c):
    if c=='B':
        return 0
    elif c=='M':
        return 1
    else:
        return -1
```
１列目がIDで２列目が教師データにしたいデータなので、３列目以降をXにセットします。
また、２列目をターゲットに設定します。
Kerasは分類の場合、one hotの形式になっている必要があるようなので、keras.utils.to_categorical()でy_trainを変換します。
詳細はy_train、y_keras等の中身を確認して下さい。

```Python
# 特徴量のセットを変数Xに、ターゲットを変数yに格納
X = bc_data.values[:,2:]
y = bc_data.values[:,1]

for index, item in enumerate(y):
    y[index] = classtonum(item)

# データの順番を入れ替えるためのランダムなNumPy配列
np.random.seed(20)
indices = np.random.permutation(len(X))
val_len = int(len(X) * -0.1)

# 学習用のデータ。全体から100データを省いたもの
X_train = X[indices[:val_len]]
y_train = y[indices[:val_len]]

# テスト用のデータ。全体から100データ取り出したもの
X_test  = X[indices[val_len:]]
y_test  = y[indices[val_len:]]

# サンプル数、特徴量の次元、クラス数の取り出し
(n_samples, n_features) = X_train.shape
n_classes = len(np.unique(y))

# ターゲットyをkeras用の形式に変換
y_keras = keras.utils.to_categorical(y_train, n_classes)
```

4.Kerasのネットワークを定義し、コンパイルする
前回までと同様に100個のパーセプトロンを３層定義しています。
分類の問題のため、クラス分の出力パーセプトロンが必要なため「units=n_classes」と指定しています。
出力層の活性化関数に「activation='softmax'」とsoftmaxを指定しています。これにより、
良性と悪性のケースの合計が１になり、確率として判定できるようになります。
分類の問題のため、「loss='categorical_crossentropy'」及び「metrics=['accuracy']」に
モデルのコンパイル時のパラメータを変更しています。
学習が完了すると学習済みのモデルデータが「keras_clf_bc.h5」の名前で保存されます。
「useSavedModel = True」に変更することで保存したモデルで予測を行えるようにしています。

```Python
savefile = './keras_clf_bc.h5'
useSavedModel = False
if useSavedModel == False:
    # ニューラルネットワークを定義
    model = Sequential()

    # 中間層と入力層を定義
    model.add(Dense(units=100, activation='relu', input_shape=(n_features,)))
    model.add(Dropout(0.1))

    model.add(Dense(units=100, activation='relu'))
    model.add(Dropout(0.1))

    model.add(Dense(units=100, activation='relu'))
    model.add(Dropout(0.1))

    # 出力層を定義
    model.add(Dense(units=n_classes, activation='softmax'))

    # モデルのコンパイル
    model.compile(loss='categorical_crossentropy', optimizer=Adam(lr=0.0001), metrics=['accuracy'])

    # モデルの学習
    early_stopping = EarlyStopping(monitor='val_loss', mode='min', patience=1500)
    #history = model.fit(X, y_keras, epochs=4000, validation_split=0.1, batch_size=n_samples, verbose=2)
    plotHistory(
        model.fit(
             X_train
            ,y_keras
            ,epochs=4000
            ,validation_split=0.1
            ,batch_size=n_samples
            ,verbose=2
            ,callbacks=[early_stopping]
        )
    )

    # 学習結果を保存
    model.save(savefile)
else:
    # 学習済ファイルを読み込んでmodelを作成
    model = keras.models.load_model(savefile)
```

5.作成したモデルで分類（予測）を行い、結果を表示する。

```Python
# 結果の表示
result = model.predict_classes(X_test, verbose=0)

print('ターゲット')
print(y_test)
print('ディープラーニングによる予測')
print(result)

# データ数をtotalに格納
total = len(X_test)
# ターゲット（正解）と予測が一致した数をsuccessに格納
success = sum(result==y_test)

# 正解率をパーセント表示
print('正解率')
print(100.0*success/total)

y_result = model.predict(X_test, verbose=0)

for index, item in enumerate(y_result):
    #y[index] = getnum(item)
    print('{0}:正解={1}、予想={2}、{5}、良性の確率={3:.3f}、悪性の確率={4:.3f}'.format(index, y_test[index], result[index], y_result[index][0], y_result[index][1], y_test[index] == result[index]))
```

6.実行する
まず以下のようなグラフが表示されます。前回までと同様に適当に学習が進んだところで自動で停止します。
![a3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93459/113dc4d3-5ef7-842c-90f1-ea05026079b3.png)


次に以下の様にターゲット、予測、正解率及び個別の結果の確率が表示されます。
ターゲット
[0 0 0 0 1 0 0 0 1 0 1 0 1 0 1 1 1 1 0 1 0 0 0 0 0 0 0 0 1 0 1 1 1 0 0 1 1
 0 1 1 1 0 0 1 1 0 0 1 1 0 1 1 1 0 0 0]
ディープラーニングによる予測
[0 0 0 0 1 0 0 0 1 0 1 0 1 0 1 1 1 1 0 1 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0 1 1
 0 1 1 1 0 0 0 1 0 0 1 1 0 1 1 1 0 0 0]
正解率
96.42857142857143
0:正解=0、予想=0、True、良性の確率=0.725、悪性の確率=0.275
…
29:正解=0、予想=0、True、良性の確率=0.768、悪性の確率=0.232
30:正解=1、予想=0、False、良性の確率=0.692、悪性の確率=0.308
31:正解=1、予想=1、True、良性の確率=0.286、悪性の確率=0.714
…
42:正解=0、予想=0、True、良性の確率=0.950、悪性の確率=0.050
43:正解=1、予想=0、False、良性の確率=0.822、悪性の確率=0.178
44:正解=1、予想=1、True、良性の確率=0.000、悪性の確率=1.000
…
55:正解=0、予想=0、True、良性の確率=0.970、悪性の確率=0.030

7.結果について
前回までのプログラムと基本的に同じニューラルネットワークで異なるタイプのデータの分類もできることがわかります。
損失関数のグラフを見ると、３００エポック、８００エポック位で壁を超えるように損失が下がっていることがわかります。

8.次回
　Google Colaboratoryを使って、通常GPUが必要な画像のディープラーニングを試します。
　https://qiita.com/norikawamura/items/637c92545e222f16c535