【ConfusionMatrix】mnist、cifar10、そしてcifar100の正誤表を作ってみる♬

[機械学習のとき使ったfrom sklearn.metrics import confusion_matrix](https://qiita.com/MuAuan/items/355e0357437d1ce6184d)を利用して、精度を具体的な正誤表で表現できたので、記事にしておこうと思う。mnistに対する参考②を参考にさせていただきました。

【参考】
・①[【機械学習入門】「SVMでクラス分類」というのをやってみる♬](https://qiita.com/MuAuan/items/355e0357437d1ce6184d)
・②[[Keras/TensorFlow] KerasでMINSTの学習と予測](https://qiita.com/agumon/items/4c6ff24bfd77a5c8101d)
・③[Keras (Cifar10)](https://qiita.com/takeshikondo/items/81ff6b1e7d5190c27e03)
・④[Docs » Callbacks@KerasDocumentation ](https://keras.io/callbacks/)
###やったこと
・mnistの正誤表
・正誤表に基づいて拡大して画像分類格納
・cifar10の正誤表と正誤表に基づいた画像分類格納（割愛します）
・cifar100の正誤表と正誤表に基づいた画像分類格納
###・mnistの正誤表
####コードは以下に置きました
・[SPP/utils/mnist_confusion.py](https://github.com/MuAuan/SPP/blob/master/utils/mnist_confusion.py)
####library
```py
from __future__ import print_function
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout
from keras.optimizers import RMSprop, adam
from keras.callbacks import ReduceLROnPlateau, EarlyStopping, CSVLogger
import cv2
import matplotlib.pyplot as plt
```
####Data-load & Model
```py
batch_size = 128
num_classes = 10
epochs = 200

# the data, shuffled and split between train and test sets
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_test_original=x_test
y_test_original=y_test
x_train = x_train.reshape(60000, 784)
x_test = x_test.reshape(10000, 784)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')

# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)

model = Sequential()
model.add(Dense(512, activation='relu', input_shape=(784,)))
model.add(Dropout(0.2))
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(10, activation='softmax'))

model.summary()
```
####Training
以下の部分は参考③を参考にさせていただきました。
csv_loggerは、参考④のKerasDocumentationを参考にしています。

```py
early_stopping = EarlyStopping(monitor='val_acc', patience=20, mode='max', verbose=1)
lr_reduction = ReduceLROnPlateau(monitor='val_acc', patience=5, 
                          factor=0.5, min_lr=0.000001, verbose=1)
csv_logger = CSVLogger('history_mnist.log', separator=',', append=True)
cbks = [early_stopping, lr_reduction, csv_logger]  #参考②には存在しないので上記記載を追加
#cbks = [lr_reduction, csv_logger]

model.compile(loss='categorical_crossentropy',
              optimizer= 'adam',  #RMSprop(),
              metrics=['accuracy'])
history = model.fit(x_train, y_train,
                    batch_size=batch_size,
                    epochs=epochs,
                    verbose=1,
                    callbacks=cbks, 
                    validation_data=(x_test, y_test))
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
```

```py
Test loss: 0.0826290478306
Test accuracy: 0.9869
```
####Prediction & Confusion_matrix
```py
# Prediction
import numpy as np
from sklearn.metrics import confusion_matrix

predict_classes = model.predict_classes(x_test[1:10000,], batch_size=32)
true_classes = np.argmax(y_test[1:10000],1)
print(confusion_matrix(true_classes, predict_classes))
```

```
[[ 976    0    1    0    0    0    1    1    1    0]
 [   0 1128    1    1    0    1    2    1    1    0]
 [   2    1 1017    2    1    0    0    5    3    1]
 [   0    0    4  996    0    3    0    3    2    2]
 [   0    0    1    1  968    0    4    1    1    6]
 [   2    0    0    5    1  877    3    1    2    1]
 [   2    2    0    1    2    3  947    0    1    0]
 [   2    4    6    0    0    0    0 1009    2    4]
 [   2    0    2    2    2    1    1    2  959    3]
 [   0    2    0    2    9    1    1    3    0  991]]
```
####サイズを(112,112)に拡大して正誤に基づいて格納
拡大は前回と同じく実施。
プリント文はどこまで進んだかわかるように入れています。
ときどき分類を間違えると、画像を表示して0.1sec止まります。
全ての画像を元の画像Dirに格納しますが、正しい場合は、正しいDirのみに格納し、間違った画像は、正解画像の配下の間違った番号にも入れました。
こうすることにより、何を何に間違えたかの検証ができます。

```py
path = './mnist'
img_rows, img_cols=112,112
print(predict_classes)

for i in range(1,10000,1):
    dst = cv2.resize(x_test_original[i], (img_rows, img_cols), interpolation=cv2.INTER_CUBIC)
    print(i, y_test_original[i],predict_classes[i-1],true_classes[i-1])
    cv2.imwrite(path+'/X_test/'+str(int(y_test_original[i]))+'/'+str(i)+'.jpg', dst)
    if predict_classes[i-1] != true_classes[i-1]:
        plt.imshow(dst)
        plt.pause(0.1)
        plt.close()
        cv2.imwrite(path+'/X_test/'+str(int(y_test_original[i]))+'/'+str(int(predict_classes[i-1]))+'/'+str(i)+'.jpg', dst)
```
![categorize_mnist.jpg](https://qiita-image-store.s3.amazonaws.com/0/233744/2a4b6f2a-811f-95b9-14f5-692d40eb7165.jpeg)

###・cifar100の正誤表
これも同じようにできると思いましたが、できませんでした。
決定的な要素は以下の二点です。
・mnistで使えたmodel.predict_classesが使えない
・100x100のconfusion_matrixが大きくて、namupy配列が省略される
ということで、この部分はどちらも解決する必要がありました。
###コードは以下に置きました
・[SPP/utils/Prediction_vgg16.py](https://github.com/MuAuan/SPP/blob/master/utils/Prediction_vgg16.py)

###Data-load & Resize
```py
num_classes = 100
img_rows=64 #32 #64
img_cols=64 #32 #64
result_dir="./history"

# The data, shuffled and split between train and test sets:
(x_train, y_train), (x_test, y_test) = cifar100.load_data()
y_test_original = y_test
X_train =[]
X_test = []
for i in range(50000):
    dst = cv2.resize(x_train[i], (img_rows, img_cols), interpolation=cv2.INTER_CUBIC) #cv2.INTER_LINEAR #cv2.INTER_CUBIC
    dst = dst[:,:,::-1]
    X_train.append(dst)
for i in range(10000):
    dst = cv2.resize(x_test[i], (img_rows, img_cols), interpolation=cv2.INTER_CUBIC)
    dst = dst[:,:,::-1]
    X_test.append(dst)
X_train = np.array(X_train)
X_test = np.array(X_test)

y_train=y_train[:50000]
y_test=y_test[:10000]
print(X_train.shape, y_train.shape)
print(X_test.shape, y_test.shape)

x_train = X_train.astype('float32')
x_test = X_test.astype('float32')
x_train /= 255
x_test /= 255
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')

# Convert class vectors to binary class matrices.
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
```
####Model Define & Pretrained-Weight
```py
# VGG16モデルと学習済み重みをロード
# Fully-connected層（FC）はいらないのでinclude_top=False）
input_tensor = Input(shape=x_test.shape[1:]) 
vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)

# FC層を構築
top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16.output_shape[1:])) 
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.75))
top_model.add(Dense(num_classes, activation='softmax'))

# VGG16とFCを接続
model = Model(input=vgg16.input, output=top_model(vgg16.output))

# Fine-tuningのときはSGDの方がよい⇒adamがよかった
lr = 0.00001
opt = keras.optimizers.Adam(lr, beta_1=0.5, beta_2=0.999, epsilon=1e-08, decay=1e-6)
#opt = keras.optimizers.SGD(lr=1e-4, momentum=0.9)
model.compile(loss='categorical_crossentropy',
              optimizer=opt,
              metrics=['accuracy'])

# モデルのサマリを表示
model.summary()
model.load_weights('params_model_VGG16_64-64-3_001.hdf5')
```
####Show a loaded Data & Predict
#####・mnistで使えたmodel.predict_classesが使えないので
以下のようにpredictして最大要素の引数をクラスとしました。

```py
plt.imshow(x_test[0])
plt.show()
# Prediction
predict_classes=[]
for i in range(10000):
    answer=model.predict(x_test[i:i+1], batch_size=1, verbose=0, steps=None)
    #print(answer)
    predict_cl = np.argmax(answer)
    predict_classes.append(predict_cl)
```
####・100x100のconfusion_matrixが大きくて、namupy配列が省略される
省略させない記載方法は参考⑤、そして改行幅の制御が参考⑥にありました。
さらにcsvファイルに保存（取得）する方法が、参考⑤に記載されています。
【参考】
・⑤[pythonでprintが省略されるが、全部みたい](https://ja.stackoverflow.com/questions/23332/python%E3%81%A7print%E3%81%8C%E7%9C%81%E7%95%A5%E3%81%95%E3%82%8C%E3%82%8B%E3%81%8C-%E5%85%A8%E9%83%A8%E3%81%BF%E3%81%9F%E3%81%84)
・⑥[printの自動改行@APUNTE忘れやすいのでメモ。](https://pinguino.sakura.ne.jp/wp/archives/542)

```py
import numpy as np
from sklearn.metrics import confusion_matrix
np.set_printoptions(threshold=np.inf)
np.set_printoptions(linewidth=400)

true_classes = y_test_original[0:10000] #np.argmax(y_test[1:10],1)
data = confusion_matrix(true_classes[0:10000], predict_classes[0:10000])
print(data)
import csv
with open('file.csv', 'wt') as f:
    writer = csv.writer(f)
    writer.writerows(data)
```

####結果
雰囲気だけですが、以下のとおり
![confusion_cifar100.jpg](https://qiita-image-store.s3.amazonaws.com/0/233744/d80c0ee8-5a2f-3d32-d3b4-e5181ebd178f.jpeg)
この図からやはり間違いは集中している傾向があるのが分かります。
実際、類似したカテゴリは以下のような感じです。
※ちょっと見、人でも区別がつきません
![category_cifar100_13.jpg](https://qiita-image-store.s3.amazonaws.com/0/233744/c1164261-57f6-70d7-39e0-3a2e9fbd5cb6.jpeg)
![category_cifar100_81.jpg](https://qiita-image-store.s3.amazonaws.com/0/233744/00f7c845-4c0b-023f-d708-a9b68351a731.jpeg)
![category_cifar100_90.jpg](https://qiita-image-store.s3.amazonaws.com/0/233744/1ce814f1-f5c1-8795-e103-bcb85b954950.jpeg)

###まとめ
・mnist, cifar10, そしてcifar100の正誤表を作ってみた
・正誤に基づいて拡大画像をカテゴライズした
・cifar100の間違いは人でも間違うほど分類が難しい
・いくつかコードの書き方を学んだ
  (from sklearn.metrics import confusion_matrix, np.set_printoptions(threshold=np.inf),
np.set_printoptions(linewidth=400)など)

・正誤表に基づいて再学習して精度を上げようと思う
・このデータを使って、アプリを作る