はじめに
ここ数年,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に関係ないものもあります。
畳み込みニューラルネットワーク
必要なパッケージ
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は,次のようになった。
可視化
学習したconv1とconv2の重みは次のような感じでした。
テストデータをいれたときにh1やh2はどのような出力になっているのかも可視化してみました。
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)