LoginSignup
36
43

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-06-12

はじめに

ここ数年,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)
36
43
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
36
43