Edited at

ネットで情報増えてきたけどPython3.4+Chainer1.9.0でディープラーニングを試してみる

More than 3 years have passed since last update.


はじめに

ここ数年,pythonが巷でにぎわっていますね。そん中でも気になるのが,機械学習分野です。ライブラリやソースが充実してきており,気軽に機械学習が試せるようになりました。機械学習でも,特に,ディープラーニングが活発ですね。

Pythonで動くディープラーニングには,次のようなものが出てきています。


  • Caffe

  • Teano

  • TensorFlow

  • Chainer

これらをインストールさえすれば簡単にディープラーニングが組めてしまします。

一つ一つ紹介するのはすでにたくさんあるので,省略して,既に記事を書いている人たちにお任せします。

今回は,Pythonで使えるChanierに注目しました。

なぜ?それは,先日パシフィコ横浜で開かれたSSII2016のチュートリアル講演で,「Pythonによる機械学習」があり,資料がとても充実していたため,試してみたくなりました。

この記事は,ニューラルネットワークや畳み込みニューラルネットワークがわかっている人向けです。

とりあえず動かしてみようという感じの記事です。


前準備

Pythonがインストールされていることが前提です。ネットで調べてみると,まだPython2.x系を使っている人が多いみたい。

私は,Ptyhon3.4がすでにインストールしてあったので,そのままChainerを入れて動かすことにしました。


必要なパッケージ

事前にnumpy,scikit-learn,Cyhton,setuptoolsが必要です。

pip install numpy, scikit-learn, cyhton, setuptools

で,インストールできるはずです。

できない→numpyは,公式サイトからダウンロードすればインストールできることが多いみたいです。

[追加]さらに,h5pyもいるもようです。


Chainerのインストール

Chainerのインストールは,コマンド一発です。

pip install Chainer

しかし,これで失敗する人もいるようです。

私がそうだった。。。

エラーコードを見ていると,どうやら,VCのバージョン違いみたい。。。

こちらの記事が役に立ちました。

レジストリの,(略)\Microsoft\VisualStudioの中に\10.0\Setup\VCを作成。

ProductDirのキー,値14.0を追加。

無事にインストール成功

※私の環境では,VS2015がインストールされているので,値は14.0です。

※各人の環境に合わせてください。

入っているパッケージのリストとバージョンです。

Chainerに関係ないものもあります。

pip_list.png


畳み込みニューラルネットワーク


必要なパッケージ

Chainerモジュール

from chainer import Chain, Variable, optimizers

import chainer.functions as F

scikit-learnモジュール

scikit-learnは,機械学習に関するものが準備されている。

機械学習用の様々なサンプルデータを取得するために次を記述

from sklearn.datasets import fetch_mldata

学習データと評価データを分割するために次を記述

from sklearn.cross_validation import train_test_split

認識率を計算するためと混合行列で評価するために次を記述

from sklearn.metrics import accuracy_score, confusion_matrix

numpyのモジュール

データを画像として扱いために次を記述

import numpy as np

matplotlibモジュール

データを可視化する場合は次を記述

import matplotlib.pyplot as plt


MNISTのデータを準備

MNISTのデータをダウンロードし,準備する。

Chainerでは,データがfloat32かfloat64。ラベルがint32だと問題ない。

#

# MNISTのダウンロード
#
print("fetch MNIST dataset")
mnist = fetch_mldata("MNIST original", data_home=".")
dataset = np.asarray(mnist.data, np.float32)
dataset /= dataset.max()

target = np.asarray(mnist.target, np.int32)


学習データとテストデータの分割

最後のテストサイズが,テストデータの割合を決めています。

# 学習データとテストデータに分割(8:2)

data_train, data_test, label_train, label_test = train_test_split(dataset, target, test_size=0.2)


学習データを画像形式に変換

(画像枚数,チャネル数,画像サイズ高さ,画像サイズ横)にする。

data_train = data_train.reshape((len(data_train), 1, 28, 28))

data_test = data_test.reshape((len(data_test), 1, 28, 28))


モデルの作成

model = Chain(

conv1=F.Convolution2D(1, 32, 3), #畳み込み(入力1枚,出力32枚,フィルタサイズ3)
conv2=F.Convolution2D(32, 64, 3), #畳み込み(入力32枚,出力64枚,フィルタサイズ3)
l1=F.Linear(576, 200), #全結合(入力576ユニット,出力200ユニット)
l2=F.Linear(200, 100), #全結合(入力200ユニット,出力100ユニット)
l3=F.Linear(100, 10) #全結合(入力100ユニット,出力10ユニット)
)


ネットワークの構造

データは配列からChainerのVariableという型のオブジェクトに変換する必要がある。

あとは,どのような構造にするか書く。

def forward(x_batch, t_batch, is_train=True):

x, t = Variable(x_batch), Variable(t_batch)
h1 = F.max_pooling_2d(F.relu(model.conv1(x)), 3)
h2 = F.max_pooling_2d(F.relu(model.conv2(h1)), 3)
h3 = F.dropout(F.relu(model.l1(h2)), train=is_train)
h4 = F.dropout(F.relu(model.l2(h3)), train=is_train)
p = model.l3(h4)
# 誤差を算出
return F.softmax_cross_entropy(p, t), F.accuracy(p, t)


最適化手法

ディープラーニングの最適化とは,モデルの予測値と実際の値との差から,重みを更新することです。

最適化手法はいろいろあります。Chainerで用意されている最適化手法は,


  • AdaDelta

  • AdaGrad

  • Adam

  • MomentumSGD(SGDにモーメントを加えたもの)

  • NesterovAG

  • RMSprop

  • RMSpropGraves

  • SGD(確率的勾配降下法)

があります。

書き方は,使い方手法を選択し,モデルを登録するだけ。

optimizer = optimizers.Adam()

optimizer.setup(model)


学習

今回は,ミニバッチ学習をします。

ミニバッチ学習とは,全学習データからいくつか選んで学習する方法です。

他には,バッチ学習(全学習データを使う)やオンライン(学習データを一つずつ)などあります。

train_loss = []

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

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

# ランダムに並び替える
perm = np.random.permutation(N)
sum_accuracy = 0
sum_loss = 0

# バッチサイズごとに学習
b_start = time.time()
for i in range(0, N, batchsize):
x_batch = data_train[perm[i:i + batchsize]]
t_batch = label_train[perm[i:i + batchsize]]

# 勾配を初期化
optimizer.zero_grads()

# 順伝搬
loss, accuracy = forward(x_batch, t_batch)

# 誤差逆伝搬
loss.backward()
optimizer.update() #パラメータ更新

train_loss.append(loss.data)
train_accuracy.append(accuracy.data)
sum_loss += float(loss.data) * batchsize
sum_accuracy += float(accuracy.data) * batchsize

batchtime = time.time() - b_start
# 誤差と精度を表示
print("[学習]loss: %f, accuracy: %f, time: %f秒" % (sum_loss / N, sum_accuracy / N, batchtime))

これで,ネットワークの学習ができます。


評価

では,実際に学習し終えたネットワークの評価をしてみます。

テストデータは,14000ありますが,とりあえず30程度で試してみます。

num = 30

results_score = []

p_start = time.time()
# ランダムに並び替える
perm = np.random.permutation(N_test)
x_test = data_test[perm[0:0 + num]] #num枚選ぶ
t_test = label_test[perm[0:0 + num]]
results_score = predict(x_test, is_train=False)
predict_time = time.time() - p_start
print("[認識] %f秒" % predict_time)


認識率や混合行列などみたいとき

axis=1で結果の中の最大値をとる

results = np.argmax(results_score, axis=1)

accuracy_scoreが認識率,confusion_matrixが混合行列

# 認識率と混合行列

score = accuracy_score(t_test, results)
print(score)
cmatrix = confusion_matrix(t_test, results)
print(cmatrix)

scikit-learnにあるclassification_reportを使うと,何がどれくらい正しかったかわかる。

print(classification_report(t_test, results))


実験

会議中にコードを書いて実験をしたので,環境は次のようになります。


  • Let's note CF-SZ5

種類
内容

OS
Windows 10 Pro 64bit

CPU
Intel Core i7-6500U 2.50GHz

Memory
8GB

学習

エポックは20回,バッチサイズは20

評価

テストデータからランダムに30枚選択


結果

学習時間は,1エポック約390秒

20回目の誤差は,0.024505。認識率は,0.993268。

認識時間は,30枚で,0.035254秒

混合行列による評価とclassification_reportは,次のようになった。

<混合行列>

python_cnn_confusionmatrix.png

<classification_report>

python_cnn_report.png


可視化

学習したconv1とconv2の重みは次のような感じでした。

conv1の重み

conv1.png

conv2の重み

conv2.png

テストデータをいれたときにh1やh2はどのような出力になっているのかも可視化してみました。

テストデータ

test_4.png

h1

h1_4.png

h2

h2_4.png

h1はなんとなく"4"に見えるものもあるけど,h2はまったくわからないですね。


おわりに

今回は,CPUでの実験をしてみた。NNなどの知識があれば,わりと早く組めると思った。

今後はGPU版も試してみたいな。

やってみる/動かしてみるスタイルの人ならPythonは結構いいのかも。


付録:コード

#

# 必要なモジュールの読み込み
#
import time
from chainer import Chain, Variable, optimizers
import chainer.functions as F

# 機械学習用の様々なサンプルデータを取得する関数
from sklearn.datasets import fetch_mldata
# 学習データと評価データを分割するための関数
from sklearn.cross_validation import train_test_split
# 認識率を計算するための関数,混合行列で評価するための関数
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report

import matplotlib.pyplot as plt
import numpy as np

#
# パラメータの定義
#
batchsize = 20 # バッチサイズ
n_epoch = 20 # 学習の繰り返し回数

#
# MNISTのダウンロード
#
print("fetch MNIST dataset")
mnist = fetch_mldata("MNIST original", data_home=".")
dataset = np.asarray(mnist.data, np.float32)
dataset /= dataset.max()

target = np.asarray(mnist.target, np.int32)

# 学習データとテストデータに分割(8:2)
data_train, data_test, label_train, label_test = train_test_split(dataset, target, test_size=0.2)

print("train data = %d" % label_train.size)
print("test data = %d" % label_test.size)

N = label_train.size
N_test = label_test.size

# 学習データを画像に変換
data_train = data_train.reshape((len(data_train), 1, 28, 28))
data_test = data_test.reshape((len(data_test), 1, 28, 28))

#
# モデルの設定
# 層のパラメータ
#
model = Chain(
conv1=F.Convolution2D(1, 32, 3),
conv2=F.Convolution2D(32, 64, 3),
l1=F.Linear(576, 200),
l2=F.Linear(200, 100),
l3=F.Linear(100, 10)
)

#
# ネットワークの構造
#
def forward(x_batch, t_batch, is_train=True):
x, t = Variable(x_batch), Variable(t_batch)
h1 = F.max_pooling_2d(F.relu(model.conv1(x)), 3)
h2 = F.max_pooling_2d(F.relu(model.conv2(h1)), 3)
h3 = F.dropout(F.relu(model.l1(h2)), train=is_train)
h4 = F.dropout(F.relu(model.l2(h3)), train=is_train)
p = model.l3(h4)
# 誤差を算出
return F.softmax_cross_entropy(p, t), F.accuracy(p, t)

def predict(x_batch, is_train=True):
x = Variable(x_batch)
h1 = F.max_pooling_2d(F.relu(model.conv1(x)), 3)
h2 = F.max_pooling_2d(F.relu(model.conv2(h1)), 3)
h3 = F.dropout(F.relu(model.l1(h2)), train=is_train)
h4 = F.dropout(F.relu(model.l2(h3)), train=is_train)
p = model.l3(h4)
return p.data

#
# 学習のさせかた
#
optimizer = optimizers.Adam()
optimizer.setup(model)

#
# 学習
#
train_loss = []
train_accuracy = []
test_loss = []
test_accuracy = []

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

# ランダムに並び替える
perm = np.random.permutation(N)
sum_accuracy = 0
sum_loss = 0

# バッチサイズごとに学習
b_start = time.time()
for i in range(0, N, batchsize):
x_batch = data_train[perm[i:i + batchsize]]
t_batch = label_train[perm[i:i + batchsize]]

# 勾配を初期化
optimizer.zero_grads()

# 順伝搬
loss, accuracy = forward(x_batch, t_batch)

# 誤差逆伝搬
loss.backward()
optimizer.update() #パラメータ更新

train_loss.append(loss.data)
train_accuracy.append(accuracy.data)
sum_loss += float(loss.data) * batchsize
sum_accuracy += float(accuracy.data) * batchsize

batchtime = time.time() - b_start
# 誤差と精度を表示
print("[学習]loss: %f, accuracy: %f, time: %f秒" % (sum_loss / N, sum_accuracy / N, batchtime))

# 学習したモデルを保存
import pickle
pickle.dump(model, open('model', 'wb'), -1)

#
# 評価
#
num = 30
results_score = []

p_start = time.time()
# ランダムに並び替える
perm = np.random.permutation(N_test)
x_test = data_test[perm[0:0 + num]] #num枚選ぶ
t_test = label_test[perm[0:0 + num]]
results_score = predict(x_test, is_train=False)
results = np.argmax(results_score, axis=1)

predict_time = time.time() - p_start
print("[認識] %f秒" % predict_time)
#print("結果: " + str(results))
#print("ラベル: " + str(t_test))
print(classification_report(t_test, results))

# 認識率と混合行列
score = accuracy_score(t_test, results)
print(score)
cmatrix = confusion_matrix(t_test, results)
print(cmatrix)