ミスがあったので更新しました。 2016/10/25
すでにやりつくされている感がありますが、意外と学習から任意画像での評価までやっているサイトがなかったのでまとめておきます。学習させて用意されているテストデータを食べさせて何%の精度だったかより、ソリューション開発側としては、アプリに組み込んで動くかどうかの方が重要なので。
CIFAR-10データの準備と学習
というわけで、まずは、ChainerでCIFAR-10を学習してみます。基本は以下のサイトを参考にしました。
ai-programming.hatenablog.jp
ほぼそのままですが、少し修正して試してみます。
データの準備
まず、CIFAR-10を読み込みます。こちらは上のサイトと同じコードになりますが、読み込んだデータを画像として取り出してみました。画像として取り出すための処理は以下を参考にしました。
qiita.com
コードは以下の通りです。
import sklearn.datasets import matplotlib import matplotlib.pyplot as plt import sys import pickle import os.path def unpickle(file): fp = open(file, 'rb') data = pickle.load(fp, encoding='latin-1') fp.close() return data X_train = None y_train = [] for i in range(1,6): data_dic = unpickle(os.getcwd()+"\\data\\cifar-10\\data_batch_{0}".format(i)) if i == 1: X_train = data_dic['data'] else: X_train = np.vstack((X_train, data_dic['data'])) y_train += data_dic['labels'] test_data_dic = unpickle(os.getcwd()+"\\data\\cifar-10\\test_batch") # 評価用データ X_test = test_data_dic['data'] X_test = X_test.reshape(len(X_test), 3, 32, 32) y_test = np.array(test_data_dic['labels']) X_train = X_train.reshape(len(X_train), 3, 32, 32) y_train = np.array(y_train) # ラベル名 batch_meta = unpickle(os.getcwd()+"\\data\\cifar-10\\batches.meta") label_names = batch_meta['label_names'] label_names # indexに入っている画像を描画(3×32×32のデータを32×32×3に変更) index = 220 img = np.rollaxis(test_data_dic['data'][index].reshape((3,32,32)), 0, 3) plt.title(label_names[y_test[index]]) plt.imshow(img, interpolation='none')
画像の出力結果はこんな感じ。
学習
モデルと学習のコードは以下の通りです。ちょっとだけ修正しています。
話が逸れますがtqdmってライブラリがあるんですね。進捗が分かるようになっていたので感動しました。
import chainer from chainer import cuda, Function, FunctionSet,gradient_check, Variable, optimizers, serializers, utils from chainer import Link, Chain, ChainList import chainer.functions as F import chainer.links as L import time plt.style.use('ggplot') # ニューラルネットワークの順伝播を記述 class Model(Chain): def __init__(self): super(Model, self).__init__( conv1=F.Convolution2D(3, 32, 3, pad=1), conv2=F.Convolution2D(32, 32, 3, pad=1), conv3=F.Convolution2D(32, 32, 3, pad=1), conv4=F.Convolution2D(32, 32, 3, pad=1), conv5=F.Convolution2D(32, 32, 3, pad=1), conv6=F.Convolution2D(32, 32, 3, pad=1), l1=F.Linear(512, 512), l2=F.Linear(512, 10) ) def __call__(self, x): #x = chainer.Variable(x_data) h1 = F.relu(self.conv1(x)) h2 = F.max_pooling_2d(F.relu(self.conv2(h1)), 2) h3 = F.relu(self.conv3(h2)) h4 = F.max_pooling_2d(F.relu(self.conv4(h3)), 2) h5 = F.relu(self.conv5(h4)) h6 = F.max_pooling_2d(F.relu(self.conv6(h5)), 2) h7 = F.dropout(F.relu(self.l1(h6))) y = self.l2(h7) return y # Classifier Chain model = L.Classifier(Model()) # Optimizer optimizer = optimizers.Adam() optimizer.setup(model) ## 学習 from tqdm import tqdm train_loss = [] train_acc = [] test_loss = [] test_acc = [] batchsize = 100 N = len(X_train) N_test = len(X_test) # 学習ループ n_epoch = 30 for epoch in range(1, n_epoch+1): print("eopch", epoch) # training perm = np.random.permutation(N) sum_accuracy = 0 sum_loss = 0 # 0~Nまでのデータをバッチサイズごとに使って学習 for i in tqdm(range(0, N, batchsize)): x = Variable(X_train[perm[i:i + batchsize]]) t = Variable(y_train[perm[i:i + batchsize]]) optimizer.update(model, x, t) sum_loss += float(model.loss.data) * len(t.data) sum_accuracy += float(model.accuracy.data) * len(t.data) train_loss.append(float(model.loss.data) * len(t.data)) train_acc.append(float(model.accuracy.data) * len(t.data)) # 訓練データの誤差と正解精度を表示 print("train mean loss={0}, accuracy={1}".format(sum_loss / N, sum_accuracy / N)) # 評価 # テストデータの誤差と正解精度を算出し、汎化性能を確認 sum_accuracy = 0 sum_loss = 0 for i in tqdm(range(0, N_test, batchsize)): x_batch = Variable(X_test[i:i+batchsize]) y_batch = Variable(y_test[i:i+batchsize]) loss = model(x, t) sum_loss += float(loss.data) * len(t.data) sum_accuracy += float(model.accuracy.data) * len(t.data) train_loss.append(float(loss.data) * len(t.data)) train_acc.append(float(model.accuracy.data) * len(t.data)) # テストデータでの誤差と正解精度を表示 print("test mean loss={0}, accuracy={1}".format(sum_loss / N_test, sum_accuracy / N_test))
ちなみに、Trainerという機能を使った場合は、以下のコードになります。開発元のPFNの方が、Trainerを使ってください、とカンファレンスで言っていたので、できるだけこちらを利用した方がいいはず。ただ、残念ながら、私の環境では進捗がうまく表示されませんでしたが・・・コードのせいなのかな・・・。
from chainer import report, training, datasets, iterators from chainer.training import extensions from chainer.datasets import tuple_dataset # データセットの準備 train = tuple_dataset.TupleDataset(X_train, y_train) test = tuple_dataset.TupleDataset(X_test, y_test) train_iter = iterators.SerialIterator(train, batch_size=100) test_iter = iterators.SerialIterator(test, batch_size=100, repeat=False, shuffle=False) updater = training.StandardUpdater(train_iter, optimizer) trainer = training.Trainer(updater, (30, 'epoch'), out='result') # trainerを設定 trainer.extend(extensions.Evaluator(test_iter, model)) trainer.extend(extensions.LogReport()) trainer.extend(extensions.PrintReport(['epoch', 'main/accuracy', 'validation/main/accuracy'])) trainer.extend(extensions.ProgressBar()) trainer.run()
学習時のエポック数は30にしました。学習結果は精度が90%程度でした。元サイトの精度グラフの30あたりを確認すると90%となっているので、妥当な値になっているようです。
ちなみに学習時間はCPU利用で6時間程度。このレベルならまだCPUでがんばれます。。
学習ファイルの保存(2016/10/25更新)
以下のコードで学習したモデルを保存します。次からは、この学習データを使って評価することができます。
# 学習データの保存 # 古いやり方らしく、うまく動作しない # model.to_cpu() # with open('model.pkl', 'wb') as o: # pickle.dump(model, o) serializers.save_npz('model_cifar10.model', model) serializers.save_npz('model_cifar10.state', optimizer)
その他
ちなみに学習の最中にMemoryErrorが発生することが何度かありました。そのため、Jupyterを利用して検証しながらやっていましたが、最終的にはコマンドプロンプトで実行しました。それでも発生することがありましたが、エラーの頻度は減ったようです。原因はよくわかりませんが、PCを再起動するとでなくなったりします。
次は、学習ファイルを読み込んで任意の画像ファイルが認識できるか確認してみます。
↓更新しました。
hellkite.hatenablog.com