Python
Kaggle
Chainer

Leaf Classificationで良精度を出すまで_3日目

ついに実装していきます。
今回はChainerで実装しております。
chainerは2.0.0を使用しました。
なお、chainerのバージョンは

import chainer
chainer.__version__

とすることで確認することができます。

構築

前処理おさらい

まずは簡単におさらいしておきます。
画像データなどはKaggleのLeafClassificationのものをそのままダウンロードしてimagesというフォルダをimages2に名前だけ変更しています。

リサイズ

下記コードの前処理ではimages2というフォルダに入っている1584枚の画像データを32x32の画像サイズに変更したのち、imagesというフォルダに対して保存しています。

images=[]
for i in range(1, 1585):
    image = cv2.imread("images2/%d.jpg"%i) #images2から取り出す
    grayed = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    images.append(grayed) #imagesに格納する

def resize_image(img):
    end_size = 32
    max_ = np.maximum(img.shape[0], img.shape[1])
    scale = end_size / max_
    height, width = img.shape
    size = (int(width*scale), int(height*scale))

    rescale_image = cv2.resize(img, size, interpolation=cv2.INTER_CUBIC)

    height, width = rescale_image.shape

    if width > height:
        z_pad = np.zeros([int((width-height)/2), width], dtype=np.uint8)
        end_image = np.vstack((z_pad, rescale_image, z_pad))
    else:
        z_pad = np.zeros([height, int((height - width)/2)], dtype=np.uint8)
        end_image = np.hstack((z_pad, rescale_image, z_pad))

    end_image = cv2.resize(end_image, (end_size, end_size))

    return end_image

for i,img in enumerate(images):
    cv2.imwrite("images/%d.jpg"%(i+1), resize_image(img)) #imagesというフォルダに画像を格納する

データセットの作成

では次にトレーニングデータからトレーニングに使用可能なデータセットを作成します。

dataset.py
import numpy as np
import pandas as pd
import cv2

#Kaggleにあるtrain.csvをダウンロードし、それを読み込ませる
#(train.csvにはラベルのついているものしかないから)
train = pd.read_csv("train.csv")
labels=train["species"].values

#labelsは今文字列が入っているので、それをscikit-learnの関数を使って数字のラベルに変える
from sklearn import preprocessing
le = preprocessing.LabelEncoder()
le.fit(labels)
labels = le.transform(labels)
labels = labels.astype(np.int32)

#imagesフォルダから、train.csvのidカラムから、リサイズされた画像を持ってくる。
resized_images = []
for i in train.id:
    image = cv2.imread("images/%d.jpg"%i)
    grayed = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    resized_images.append(grayed)

#chainerで読み込ませられる型に変換する。
resized_images = np.array([resized_images])
resized_images = resized_images.reshape(-1, 1024).astype(np.float32)
resized_images /=255 #0~255のデータを0~1のデータに変える

#train_test_splitはscikit-learnのバージョンが新しければ、cross_validationではなくmodel_selectionの中に入っている。
#画像の行列データとラベルデータから、トレーニング用のデータセットを作成する。
from sklearn.cross_validation import train_test_split 
X_train, X_test, y_train, y_test = train_test_split(resized_images, labels, test_size=0.2, random_state=0)

#後で使用する数字を指定しておく。
N_train = y_train.size
N_test = y_test.size

こうすることで、データセットを作成することができました。

NNで分類する

まずはCNNではなくNNから作ってみます。

NN_class.py
class MLP(chainer.Chain):

    def __init__(self):
        super().__init__(
            l1 = L.Linear(1024, 1000),
            l2 = L.Linear(1000, 900),
            l3 = L.Linear(900, 500),
            l4 = L.Linear(500, 99),
        )

    def __call__(self, x):
        h1 = F.relu(self.l1(x))
        h2 = F.relu(self.l2(h1))
        h3 = F.relu(self.l3(h2))
        return self.l4(h3)

ここで、

__call__

部分の説明はPythonのクラスにおけるcallメソッドの使い方に載っています。

とりあえず、inputが1024(32x32),outputが99(99クラス分類)
で、NN(ニューラルネット)をつなぎ、活性化関数にrelu関数を用いている、という感じですね。

モデルの構築

model.py
model = L.Classifier(MLP())

from chainer import optimizers
optimizer = optimizers.Adam()
optimizer.setup(model)

これによりモデルを作成できました。
僕のPCだとなぜかoptimizer=chainer.optimizers.Adam()とするとエラーが出たのでその場合はこのように書けば大丈夫だと思います。

実行

NN_実行.py
batchsize = 99 #一回のバッチサイズ
n_epoch = 8 #学習の繰り返し回数

train_loss = []
train_accuracy = []
test_loss = []
test_accuracy = []

for epoch in range(n_epoch):
    print("epoch", epoch+1)

    perm = np.random.permutation(N_train)

    sum_accuracy, sum_loss = 0,0

    for i in range(0, N_train, batchsize):
        X_batch = X_train[perm[i:i+batchsize]]
        y_batch = y_train[perm[i:i+batchsize]]


        optimizer.update(model, X_batch, y_batch)

        sum_loss += float(model.loss.data)
        sum_accuracy += float(model.accuracy.data)


    mean_loss = sum_loss/(N_train/batchsize)
    mean_accuracy = sum_accuracy/(N_train/batchsize)
    print("train_mean_loss={:.3f}, train_mean_accuracy={:.3f}".format(mean_loss, mean_accuracy))

    train_loss.append(model.loss.data)
    train_accuracy.append(model.accuracy.data)

    #少ないから、全部でテストしよう
    sum_loss, sum_accuracy= 0,0

    for i in range(0, N_test, batchsize):
        X_batch = X_test[i:i+batchsize]
        y_batch = y_test[i:i+batchsize]

        loss = model(X_batch, y_batch)

        sum_loss += float(model.loss.data)
        sum_accuracy += float(model.accuracy.data)

    mean_loss = sum_loss/(N_test/batchsize)
    mean_accuracy = sum_accuracy/(N_test/batchsize)
    print("test_mean_loss={:.3f}, test_mean_accuracy={:.3f}".format(mean_loss, mean_accuracy))

    test_loss.append(model.loss.data)
    test_accuracy.append(model.accuracy.data)

ここの部分のコードの部分の詳しい説明は参考にしたサイト様が非常にわかりやすく解説してくださっているので、割愛します。

書き換えた部分は、N_train.size=792だったのでbatchsize=99として、n_epoch=8にしたことくらいです。

というわけで実行結果はこうなりました。

スクリーンショット 2017-07-19 1.59.28.png

まぁ、わかったいたことですが99クラス分類を各10毎のデータで行うのは無理がありました。
一応CNNでも実装をしてみます。

CNN

モデルの構築

model_CNN.py
class CNN(chainer.Chain):

    def __init__(self):
        super(CNN, self).__init__(
            conv1 = L.Convolution2D(1, 32, 5),
            conv2 = L.Convolution2D(32, 64, 5),
            conv3 = L.Convolution2D(64, 64, 5),
            l1 = L.Linear(None, 500),
            l2 = L.Linear(500, 99)
        )

    def __call__(self, x):
        x = x.reshape((len(x.data), 1, 32, 32))

        h = F.relu(self.conv1(x))
        h = F.max_pooling_2d(h, 2)
        h = F.relu(self.conv2(h))
        h = F.max_pooling_2d(h, 2)
        h = F.relu(self.conv3(h))
        h = F.max_pooling_2d(h, 2)
        h = F.dropout(F.relu(self.l1(h)))
        y = self.l2(h)
        return y

model = L.Classifier(CNN())

from chainer import optimizers
optimizer = optimizers.Adam()
optimizer.setup(model)

これも、参考にしたサイト様を見た方がわかりやすいと思うので、説明は割愛します。
そして実行のコードはNNの方と全く同じなので、結果だけ載せます。

スクリーンショット 2017-07-19 2.04.41.png

、、、あれっ?
こんなにも正答率が低くなってしまうのかと、感服でした。
やはり、データセットの数を増やさなければならないようです。
というわけで4日目に続きます。

参考にしたサイト

【機械学習】ディープラーニングフレームワークChainerを試しながら解説してみる。
とてもわかりやすいです。FunctionSetなどが2.0.0にはないので、そこは他のサイトをみながらやればいいと思うのですが、relu関数などの説明がとても丁寧です。
訓練のコードはここから拝借しました。

ChainerのMNISTサンプルを試す
この方はモデルの構築を引数の初期値として与える書き方をしているので、何をしているのかがなんとなく掴むことができました。
CNNのモデル構築の書き方は一部ここから拝借しました。

雑記chainerエンジョイ勢のためのトラブルシューティング
データのエラーコードについて調べた時に見つけたサイトです。