Chainerのチュートリアルを試してみた
Chainerのチュートリアルを試してみました。初めてのChainerです。
目次
Chainer
Chainer とは、言わずと知れたPython用のディープラーニングフレームワークです。pipでインストールできます。
本家でチュートリアルを公開されてますが、今回試してみたのは こちらのサイトのもの です。
試した環境は、Windows10 64bit、Python 3.7、Chainer 6.1です。GPUは使用しません。 参考にしたサイトはJupyter notebookでの実行を前提に書かれていますが、この投稿ではVisual Studio Codeで実行しました。
内容としては、簡単なネットワークを使ってMNISTの画像セットの分類をするというものです。
基本的にはチュートリアルにあるコードをそのままトレースしてます。
データの準備
まずデータを準備します。
Chainerの便利機能を使ってMNISTのデータセットをダウンロードするのですが、トライアンドエラーを繰り返すことが想定されましたので、ダウンロードしたデータをpickleで保存して2回目以降はローカルに保存したデータを使うことにしました。
import pickle
import matplotlib.pyplot as plt
from chainer.datasets import mnist
# データセットのダウンロード
train_val, test = mnist.get_mnist(withlabel=True, ndim=1)
# データセットをローカルファイルに保存
with open('train_val.pickle', mode='wb') as fo1:
pickle.dump(train_val, fo1)
with open('test.pickle', mode='wb') as fo2:
pickle.dump(test, fo2)
# データを表示
x, t = train_val[0] # 0番目の (data, label) を取り出す
plt.imshow(x.reshape(28, 28), cmap='gray')
plt.axis('off')
plt.show()
print('label:', t)
学習
学習して、得られた学習データを保存します。
どこのコードで何をしているかはコメントを見ていただきたいのですが、基本的にはこういう流れのようです。
ネットワークを定義するクラスを作成する。
データセットを読み込む。
イテレーター(データからバッチを取り出す機能)を設定する。
ネットワークのインスタンスを作る。
オプティマイザー(パラメータを最適化する機能)を設定する。
学習(順方向の計算→損失の計算→勾配の計算→パラメータ更新)
ミニバッチ単位で学習をして、指定したエポック数だけ繰り返すわけですね。
import pickle
import matplotlib.pyplot as plt
import random
import numpy
import chainer
from chainer.datasets import split_dataset_random
from chainer import iterators
import chainer.links as L
import chainer.functions as F
from chainer import optimizers
from chainer.dataset import concat_examples
from chainer.cuda import to_cpu
from chainer import serializers
# 乱数を初期化する関数
def reset_seed(seed=0):
random.seed(seed)
numpy.random.seed(seed)
# ネットワークを定義するクラス
class MLP(chainer.Chain):
def __init__(self, n_mid_units=100, n_out=10):
super(MLP, self).__init__()
# 各層の定義
with self.init_scope():
self.l1 = L.Linear(None, n_mid_units)
self.l2 = L.Linear(n_mid_units, n_mid_units)
self.l3 = L.Linear(n_mid_units, n_out)
def __call__(self, x):
# ネットワークの定義
h1 = F.relu(self.l1(x))
h2 = F.relu(self.l2(h1))
return self.l3(h2)
# ローカルファイルからデータセットを読み込む
with open('test.pickle', mode='rb') as fi1:
test = pickle.load(fi1)
with open('train_val.pickle', mode='rb') as fi2:
train_val = pickle.load(fi2)
# データセットを学習用と検証用に分割する
train, valid = split_dataset_random(train_val, 50000, seed=0)
# イテレーターの設定
batchsize = 128
train_iter = iterators.SerialIterator(train, batchsize)
valid_iter = iterators.SerialIterator(valid, batchsize, repeat=False, shuffle=False)
test_iter = iterators.SerialIterator(test, batchsize, repeat=False, shuffle=False)
# 乱数の初期設定
reset_seed(0)
# ネットワークのインスタンスを作る
net = MLP()
# オプティマイザーの設定
optimizer = optimizers.SGD(lr=0.01).setup(net)
# 学習の実行
max_epoch = 10
gpu_id = -1 # GPU不使用
while train_iter.epoch < max_epoch:
train_batch = train_iter.next()
x, t = concat_examples(train_batch, gpu_id)
# 予測値の計算
y = net(x)
# ロスの計算
loss = F.softmax_cross_entropy(y, t)
# 勾配の計算
net.cleargrads()
loss.backward()
# パラメータの更新
optimizer.update()
# 1エポック終わったらロスと精度を表示する
if train_iter.is_new_epoch:
# ロスの表示
print('epoch:{:02d} train_loss:{:.04f} '.format(train_iter.epoch, float(to_cpu(loss.data))), end='')
valid_losses = []
valid_accuracies = []
while True:
valid_batch = valid_iter.next()
x_valid, t_valid = concat_examples(valid_batch, gpu_id)
# Validationデータをforward
with chainer.using_config('train', False), chainer.using_config('enable_backprop', False):
y_valid = net(x_valid)
# ロスを計算
loss_valid = F.softmax_cross_entropy(y_valid, t_valid)
valid_losses.append(to_cpu(loss_valid.array))
# 精度を計算
accuracy = F.accuracy(y_valid, t_valid)
accuracy.to_cpu()
valid_accuracies.append(accuracy.array)
if valid_iter.is_new_epoch:
valid_iter.reset()
break
print('val_loss:{:.04f} val_accuracy:{:.04f}'.format(numpy.mean(valid_losses), numpy.mean(valid_accuracies)))
# テストデータでの評価
test_accuracies = []
while True:
test_batch = test_iter.next()
x_test, t_test = concat_examples(test_batch, gpu_id)
# テストデータをforward
with chainer.using_config('train', False), chainer.using_config('enable_backprop', False):
y_test = net(x_test)
# 精度を計算
accuracy = F.accuracy(y_test, t_test)
accuracy.to_cpu()
test_accuracies.append(accuracy.array)
if test_iter.is_new_epoch:
test_iter.reset()
break
print('test_accuracy:{:.04f}'.format(numpy.mean(test_accuracies)))
# 学習結果の保存
serializers.save_npz('my_mnist.model', net)
出力はこうなります。
epoch:01 train_loss:0.9035 val_loss:0.9046 val_accuracy:0.8071
epoch:02 train_loss:0.4777 val_loss:0.5205 val_accuracy:0.8667
epoch:03 train_loss:0.4600 val_loss:0.4219 val_accuracy:0.8851
epoch:04 train_loss:0.3510 val_loss:0.3747 val_accuracy:0.8955
epoch:05 train_loss:0.2335 val_loss:0.3468 val_accuracy:0.9021
epoch:06 train_loss:0.2353 val_loss:0.3288 val_accuracy:0.9049
epoch:07 train_loss:0.3196 val_loss:0.3137 val_accuracy:0.9100
epoch:08 train_loss:0.2127 val_loss:0.2988 val_accuracy:0.9156
epoch:09 train_loss:0.4406 val_loss:0.2892 val_accuracy:0.9155
epoch:10 train_loss:0.2950 val_loss:0.2790 val_accuracy:0.9194
test_accuracy:0.9233
学習を繰り返すうちに正解率が上がっていきます。最終的には92.3%になりました。
なお、この程度の学習でしたらSurface Pro6(Core i5-8250U)でも数分で計算が終わります。
推論してみた
学習データを使って推論してみました。
import pickle
import matplotlib.pyplot as plt
import numpy
import chainer
import chainer.links as L
import chainer.functions as F
from chainer import serializers
# ネットワークを定義するクラス
class MLP(chainer.Chain):
def __init__(self, n_mid_units=100, n_out=10):
super(MLP, self).__init__()
# パラメータを持つ層の登録
with self.init_scope():
self.l1 = L.Linear(None, n_mid_units)
self.l2 = L.Linear(n_mid_units, n_mid_units)
self.l3 = L.Linear(n_mid_units, n_out)
def __call__(self, x):
# データを受け取った際のforward計算を書く
h1 = F.relu(self.l1(x))
h2 = F.relu(self.l2(h1))
return self.l3(h2)
# テスト用データを読み込む
with open('test.pickle', mode='rb') as fi1:
test = pickle.load(fi1)
# ネットワークのインスタンスを作る
infer_net = MLP()
# ネットワークに学習済みのパラメータを読み込む
serializers.load_npz('my_mnist.model', infer_net)
# テスト用のバッチデータを作る
test_batch_size = 100
t = [] # 画像のラベル
x, tt = test[0] # xが画像のデータ
t.append(tt)
for i in range(1,test_batch_size):
xt, tt = test[i]
t.append(tt)
x = numpy.vstack((x,xt))
# ネットワークと同じデバイス上にデータを送る
x = infer_net.xp.asarray(x)
# モデルのforward関数に渡す
with chainer.using_config('train', False), chainer.using_config('enable_backprop', False):
y = infer_net(x)
# Variable形式で出てくるので中身を取り出す
y = y.array
# 予測確率の最大値のインデックスを見る
pred_label = y.argmax(axis=1)
# 予測に失敗したデータの抽出
failed_index = []
for i in range(len(pred_label)):
if t[i] != pred_label[i]:
failed_index.append(i)
print('Failed: n = ', len(failed_index), ' / ', len(pred_label))
# 予測に失敗した画像の表示
for i in range(len(failed_index)):
label_text = 'Label: ' + str(t[failed_index[i]])
pred_text = 'Pred: ' + str(pred_label[failed_index[i]])
plt.imshow(x[failed_index[i]].reshape(28, 28), cmap='gray')
plt.text(1,1, label_text, color='white')
plt.text(1,2, pred_text, color='white')
plt.show()
チュートリアルでは画像1つだけでテストしていましたが、ここではバッチにしてみました。 ついでに、判定に失敗した画像を全て表示するようにしてあります。
100枚の画像データでテストして、判定に失敗したのは5枚でした。
失敗した5枚はこれなのですが、私も正解する自信のない画像があります・・・
公開日
広告
Chainerカテゴリの投稿
- ChainerCVで使える画像のデータ拡張
- ChainerCVで画像を出力する方法
- ChainerCVのResNetを使う
- ChainerCVのSSDに学習させてみた
- ChainerCVのデモンストレーションプログラムを読んでみた
- ChainerCVのデモンストレーションプログラムを読んでみた(推論編)
- Chainerが出力するネットワーク構造図をGraphvizで見る
- Chainerで数字を分類してみた
- ChainerのSSDのデモで物体検出をしてみる
- Chainerのチュートリアルを試してみた
- Chainerのチュートリアルを試してみた(ChainerCVでデータ拡張編)
- Chainerのチュートリアルを試してみた(データ拡張編)
- Chainerのチュートリアルを試してみた(トレーナー編)
- Chainerのチュートリアルを試してみた(畳み込みを深くする編)
- Chainerのチュートリアルを試してみた(畳み込み編)
- Chainerのデータセットの作り方(ラベル付き画像編)
- VoTTのPascal VOC出力をChainerCVのデータセットとして読み込んでみた