Kinpatu Diary

プログラミング×マラソン

kerasでmnistデータの学習を試してみる【Kaggle:Digit Recognizer】

最初のKaggleチャレンジはAidemyでも教材になっていたmnistです。

『Digit Recognizer』
Learn computer vision fundamentals with the famous MNIST data

上位は1.00ばかりというもう数字は全部認識できてるよ!!状態なのですが僕もここにチャレンジしてみます!!

Kaggleを覗いてみると上位の人のコードを勉強することができるし、上位の人同士がお互いのコードを指摘しあいやり取りを覗くことの出来るとても素敵な場所でした。

それに加え書かれているのが全て英語なので機械学習の英語の勉強にもなるという一石二鳥の効果もあります。

ちなみに僕は英語がほとんど出来ないのですが毎日単語を調べたりしながら読むことで少しづつですが読めるようになってきました。

Kaggleって最高ですね♪


事前準備としてAidemyの『教師あり分類』、『ディープラーニング基礎』、『CNNを用いた画像認識』を一通り学習しました。

ではチャレンジ行ってみましょう!!

#必要なライブラリをimport
import numpy as np
import pandas as pd

#可視化ライブラリ
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

#訓練データとテストデータに切り分ける為
from sklearn.model_selection import train_test_split

from keras.models import Sequential,load_model
from keras.layers import Dense,Activation,Dropout,Conv2D,MaxPool2D,Flatten,BatchNormalization
from keras.utils.np_utils import to_categorical
#データの読み込み
train=pd.read_csv('train.csv')
test=pd.read_csv('test.csv')

train.columns

f:id:kinpatucom821:20180829182852p:plain

train_Xにlabelを削除したdata、train_yにlabelという形に分ける。

train_ytrain_y=train['label']
train_X=train.drop(['label'],axis=1)


#train_yに0~9までの数字が何個ずつ入っているか調べる
g=sns.countplot(train_y)
train_y.value_counts()


f:id:kinpatucom821:20180829183500p:plain

手書き文字のため1が一番描きやすかったのか一番多いですね。

#正規化
train_X=train_X/255.0
test=test/255.0

画像サイズが28×28でmnistはグレースケールの1チャンネルなのでデータを再編成する。

train_X=train_X.values.reshape(-1,28,28,1)
test=test.values.reshape(-1,28,28,1)

ラベルが0から9までの10桁の数字なのでone-hotベクトルにエンコード
例 0→[00000001]

One-hot - Wikipedia

train_y=to_categorical(train_y,num_classes=10)

訓練データ(9割)とテストデータ(1割)に分割しvalidationセットも作る

train_X,val_X,train_y,val_y=train_test_split(train_X,train_y,test_size=0.1,random_state=42)

モデル作り
Aidemyで学んだ過学習防止の為のDropoutとバッチ正規化のBachNormalizationも組み込む。

model=Sequential()
model.add(Conv2D(input_shape=(28,28,1),filters=32,activation='relu',kernel_size=(3,3),strides=(1,1),padding='same'))
model.add(Conv2D(filters=32,kernel_size=(3,3),activation='relu',strides=(1,1),padding='same'))
model.add(MaxPool2D(pool_size=(2,2)))
model.add(Dropout(rate=0.25))
model.add(Conv2D(filters=64,kernel_size=(3,3),padding='same',activation='relu'))
model.add(Conv2D(filters=64,kernel_size=(3,3),padding='same',activation='relu'))
model.add(MaxPool2D(pool_size=(2,2),strides=(2,2)))
model.add(Dropout(rate=0.25))
model.add(Flatten())
model.add(BatchNormalization())
model.add(Dense(128,activation='relu'))
model.add(BatchNormalization())
model.add(Dense(10,activation='softmax'))

model.compile(loss='categorical_crossentropy',optimizer='adadelta',metrics=['accuracy'])

これだけで実行すると正解率が全然上がらなかったのでImageDataGeneratorを使いデータの水増しを行う。
その際、vertical_flipやhorizonal_flipは6と9とか2と7とか反転させると間違えて分類する可能性があるため指定しない。

from keras.preprocessing.image import ImageDataGenerator
datagen=ImageDataGenerator(featurewise_center=False, 
                          samplewise_center=False, 
                          featurewise_std_normalization=False, 
                          samplewise_std_normalization=False, 
                          rotation_range=0.1, 
                          width_shift_range=0.1, 
                          height_shift_range=0.1, 
                          zoom_range=0.1, 
                          horizontal_flip=False, 
                          vertical_flip=False, )

datagen.fit(train_X)

準備が出来たので学習する。

history=model.fit_generator(imggen.flow(train_X,train_y,batch_size=32),epochs=15,
                           validation_data=(val_X,val_y),
                           verbose=1,steps_per_epoch=len(train_X)/32)

f:id:kinpatucom821:20180830203903p:plain

学習が終わったので可視化する。

fig, ax = plt.subplots(2,1)
ax[0].plot(history.history['loss'], color='b', label="Training loss")
ax[0].plot(history.history['val_loss'], color='r', label="validation loss",axes =ax[0])
legend = ax[0].legend(loc='best', shadow=True)

ax[1].plot(history.history['acc'], color='b', label="Training accuracy")
ax[1].plot(history.history['val_acc'], color='r',label="Validation accuracy")
legend = ax[1].legend(loc='best', shadow=True)

f:id:kinpatucom821:20180830203923p:plain


いい感じに学習できている気がする🤔

学習結果をみて見る。

score=model.evaluate(val_X,val_y,verbose=1)
print('loss:{0[0]}\n acc :{0[1]}'.format(score))

#可視化
for i in range(10):
    plt.subplot(2,5,i+1)
    plt.imshow(val_X[i].reshape((28,28)),'gray')
    
plt.suptitle('First ten numbers',fontsize=20)
plt.show()

pred=np.argmax(model.predict(val_X[:10]),axis=1)
print(pred)

model.summary()

f:id:kinpatucom821:20180830204034p:plain

f:id:kinpatucom821:20180830204048p:plain


学習結果は表示出来たけど結局何を間違えていたのか???

とても気になったので間違えていた数字を確認してみる。

何が正解で何が不正解だったのかという分類精度を知るには混同行列 (Confusion matrix)を使えばいいみたいなので、Confision matrixを使ってみようと思います。

sklearnのドキュメントを読む。
http://scikit-learn.org/stable/auto_examples/model_selection/plot_confusion_matrix.html#sphx-glr-auto-examples-model-selection-plot-confusion-matrix-py

ドキュメントから必要な物を抜き出して使う(最後の5行以外は全てコピペ)

from sklearn.metrics import confusion_matrix
import itertools

def plot_confusion_matrix(cm, classes,
                          normalize=False,
                          title='Confusion matrix',
                          cmap=plt.cm.Blues):
    """
    This function prints and plots the confusion matrix.
    Normalization can be applied by setting `normalize=True`.
    """
    if normalize:
        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
        print("Normalized confusion matrix")
    else:
        print('Confusion matrix, without normalization')

    print(cm)

    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=45)
    plt.yticks(tick_marks, classes)

    fmt = '.2f' if normalize else 'd'
    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, format(cm[i, j], fmt),
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")

    plt.tight_layout()
    plt.ylabel('True label')
    plt.xlabel('Predicted label')


pred_y = model.predict(val_X)
pred_y_classes = np.argmax(pred_y,axis = 1) 
tue_y= np.argmax(val_y,axis = 1) 
confusion_mtx = confusion_matrix(tue_y, pred_y_classes) 
plot_confusion_matrix(confusion_mtx, classes = range(10)) 

f:id:kinpatucom821:20180830204129p:plain

4と9の判別が難しかったみたいですね。

他も5,6,8の数字の誤分類が多かったです。

もっといろんな方法を調べて正解率を上げるチャレンジをしよう!!