Kuroyagi飼育日誌

学んだことの備忘録

深層学習の学習 【その4】





導入

さて、pythonにも少しずつ慣れてきたところで続きをやっていきましょう。

 

前回はライブラリを使わずにディープラーニングについて学習しました。



kuroyagi.hatenablog.com



なるほどなと思いつつも、やはり適度に現実で役に立ったと実感できる実装をやらないと応用できる気がしません。



ということで、以下の【記事1】に良さそうなガイダンスがあったので、それに沿って進めていこうと思います。




【記事1】
www.mathgram.xyz


本編



【記事1】内にある【記事2】について先ずは学習します。



【記事2】
qiita.com




ライブラリ設定

今回使うのは以下のライブラリです。pipで仮想環境にインストールしておきます。
・matplotlib
・chainer
・numpy
・scipy
・sklearn


先ずはライブラリのimportを【ソースコード1】のように行います。



ソースコード1】

import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import fetch_mldata
from chainer import cuda, Variable, FunctionSet, optimizers
import chainer.functions  as F
import sys

plt.style.use('ggplot')




元記事にある一行目の%matplotlib inlineはmatplotlibのplotが表示されないような系の時に書くようです。私の環境では必要ない&停止するので、削除しています。




データの導入

あとは【ソースコード1】に記事中の内容を追加していきます。


ソースコード2】

import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import fetch_mldata
from chainer import cuda, Variable, optimizers
import chainer.functions  as F
import sys

plt.style.use('ggplot')


# MNISTの手書き数字データのダウンロード
# #HOME/scikit_learn_data/mldata/mnist-original.mat にキャッシュされる
print('fetch MNIST dataset')
mnist = fetch_mldata("MNIST original")
# mnist.data : 70,000件の784次元ベクトルデータ
mnist.data   = mnist.data.astype(np.float32)
mnist.data  /= 255     # 0-1のデータに変換

# mnist.target : 正解データ(教師データ)
mnist.target = mnist.target.astype(np.int32)


# 手書き数字データを描画する関数
def draw_digit(data):
    size = 28
    plt.figure(figsize=(2.5, 3))

    X, Y = np.meshgrid(range(size),range(size))
    Z = data.reshape(size,size)   # convert from vector to 28x28 matrix
    Z = Z[::-1,:]             # flip vertical
    plt.xlim(0,27)
    plt.ylim(0,27)
    plt.pcolor(X, Y, Z)
    plt.gray()
    plt.tick_params(labelbottom="off")
    plt.tick_params(labelleft="off")

    plt.show()

draw_digit(mnist.data[5])
draw_digit(mnist.data[12345])
draw_digit(mnist.data[33456])




と、【ソースコード2】でやろうとしたのですが、実行してみるとfetchのところで止まってしまいます。エラーは以下の通り。


OSError: could not read bytes




恐らく、mnistのダウンロードとかで失敗していると思うのですが、こういった細かいことから調べていくとたいてい良く分からなくなったりして効率が悪いので、そもそもchainerについてくるexamplesの中のサンプルコードが実行できるか確認して、環境起因なのかソースコード起因なのか確認しておきます。



chainer/examples/mnist/train_mnist.pyを実行します。問題なく実行できているようなので、やはり問題はソースコードのどこかにあるようです。



他のfecth_mldataの使用例を見てみると、mldataの保存先をオプションとしてdata_home=パスとして設定しています。また、キャッシュしたmldataが一度では上手くいかず何回か削除してはダウンロードすると上手くいくという記事も見かけたので、先ずはキャッシュを削除しようとしましたが、そもそもmldataが仮想環境のフォルダにありませんでした。いったいどこへ?ということで調べてみるとC:\Users\ユーザー名\scikit_learn_data\mldataに入っていました。オプションでパスを指定しないとここに保存されるようです。



出来れば仮想環境内に置きたいのでdata_home='.'としてソースコードの置いてある仮想環境のルートにmldataが生成されるようにします。そのように書き直したのが【ソースコード3】です。



ソースコード3】

import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import fetch_mldata
from chainer import cuda, Variable, optimizers
import chainer.functions  as F
import sys

plt.style.use('ggplot')


# MNISTの手書き数字データのダウンロード
# #HOME/scikit_learn_data/mldata/mnist-original.mat にキャッシュされる
print('fetch MNIST dataset')
mnist = fetch_mldata("MNIST original", data_home=".")
# mnist.data : 70,000件の784次元ベクトルデータ
mnist.data   = mnist.data.astype(np.float32)
mnist.data  /= 255     # 0-1のデータに変換

# mnist.target : 正解データ(教師データ)
mnist.target = mnist.target.astype(np.int32)


# 手書き数字データを描画する関数
def draw_digit(data):
    size = 28
    plt.figure(figsize=(2.5, 3))

    X, Y = np.meshgrid(range(size),range(size))
    Z = data.reshape(size,size)   # convert from vector to 28x28 matrix
    Z = Z[::-1,:]             # flip vertical
    plt.xlim(0,27)
    plt.ylim(0,27)
    plt.pcolor(X, Y, Z)
    plt.gray()
    plt.tick_params(labelbottom="off")
    plt.tick_params(labelleft="off")

    plt.show()

draw_digit(mnist.data[5])
draw_digit(mnist.data[12345])
draw_digit(mnist.data[33456])




さて、【ソースコード3】を実行してみると、初回はキャッシュにmnistデータがないのでダウンロードに数分ほど時間がかかりますが、晴れて以下の結果が得られます。



【画像1】
f:id:cocosuzu:20171015185633p:plain



【画像2】
f:id:cocosuzu:20171015185708p:plain



【画像3】
f:id:cocosuzu:20171015185807p:plain



ちなみに、二回目の実行では3秒くらいで終わります。




モデルの定義

ここからはchainerのクラスや関数を使っていくようです。先ずは【ソースコード3】にモデル定義などを追加して【ソースコード4】にします。



ソースコード4】

import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import fetch_mldata
from chainer import cuda, Variable, optimizers
import chainer.functions  as F
import sys

plt.style.use('ggplot')


# MNISTの手書き数字データのダウンロード
# #HOME/scikit_learn_data/mldata/mnist-original.mat にキャッシュされる
print('fetch MNIST dataset')
mnist = fetch_mldata("MNIST original", data_home=".")
# mnist.data : 70,000件の784次元ベクトルデータ
mnist.data   = mnist.data.astype(np.float32)
mnist.data  /= 255     # 0-1のデータに変換

# mnist.target : 正解データ(教師データ)
mnist.target = mnist.target.astype(np.int32)


# 手書き数字データを描画する関数
def draw_digit(data):
    size = 28
    plt.figure(figsize=(2.5, 3))

    X, Y = np.meshgrid(range(size),range(size))
    Z = data.reshape(size,size)   # convert from vector to 28x28 matrix
    Z = Z[::-1,:]             # flip vertical
    plt.xlim(0,27)
    plt.ylim(0,27)
    plt.pcolor(X, Y, Z)
    plt.gray()
    plt.tick_params(labelbottom="off")
    plt.tick_params(labelleft="off")

    plt.show()

draw_digit(mnist.data[5])
draw_digit(mnist.data[12345])
draw_digit(mnist.data[33456])


# Prepare multi-layer perceptron model
# 多層パーセプトロンモデルの設定
# 入力 784次元、出力 10次元
model = FunctionSet(l1=F.Linear(784, n_units),
                    l2=F.Linear(n_units, n_units),
                    l3=F.Linear(n_units, 10))

# Neural net architecture
# ニューラルネットの構造
def forward(x_data, y_data, train=True):
    x, t = Variable(x_data), Variable(y_data)
    h1 = F.dropout(F.relu(model.l1(x)),  train=train)
    h2 = F.dropout(F.relu(model.l2(h1)), train=train)
    y  = model.l3(h2)
    # 多クラス分類なので誤差関数としてソフトマックス関数の
    # 交差エントロピー関数を用いて、誤差を導出
    return F.softmax_cross_entropy(y, t), F.accuracy(y, t)

# F.reluテスト
x_data = np.linspace(-10, 10, 100, dtype=np.float32)
x = Variable(x_data)
y = F.relu(x)

plt.figure(figsize=(7,5))
plt.ylim(-2,10)
plt.plot(x.data, y.data)
plt.show()




活性化関数がシグモイドではなく、F.rule()関数であると…活性関数って何ですか?ということで参考として以下記事を読んで起きます。



【記事3】
qiita.com


【記事4】
hokuts.com



なるほど!なんとなく分かりました。今回は全体を進めることを優先して深くは掘り下げません。後で困ったらまた復習します。



ということで続けようと思ったのですが、【記事5】にあるようにchainerのFunctionSetがchiner v2から廃止されているようです。



【記事5】
Upgrade Guide from v1 to v2 — Chainer 3.2.0 documentation



代替として【記事6】を実装しようと思いましたが、まだ知識不足でかなり時間がかかりそうです。



【記事6】
chainer.Chain — Chainer 3.2.0 documentation




そこでFunctionSetの廃止について触れている記事を探したところ、【記事7】を発見しました。



【記事7】
yaju3d.hatenablog.jp



【記事7】で触れられているchainer v1のソースコードと見比べてみると恐らく【ソースコード5】と【ソースコード6】が対応していると思われます。



ソースコード5: chainer v1】

def main(n_bit, h1_size):
    if h1_size > 0:
        model = FunctionSet(
            l1=F.Linear(n_bit, h1_size),
            l2=F.Linear(h1_size, 2**n_bit)
        )
    else:
        model = FunctionSet(
            l1=F.Linear(n_bit, 2**n_bit)
        )

ソースコード6: chainer v2】

def main(n_bit, h1_size):
    if h1_size > 0:
        model = Chain(
            l1=F.Linear(n_bit, h1_size),
            l2=F.Linear(h1_size, 2**n_bit)
        )
    else:
        model = Chain(
            l1=F.Linear(n_bit, 2**n_bit)
        )

それから、【記事8】のWrite a model as a chainも参考にしておきます。




【記事8】
www.iandprogram.net



ちなみにFunctionSetで実装されている部分は【ソースコード7】の通り。



ソースコード7】

model = FunctionSet(l1=F.Linear(784, n_units),
                    l2=F.Linear(n_units, n_units),
                    l3=F.Linear(n_units, 10))


見よう見まねで無理やり【ソースコード8】のように改造してみます。



ソースコード8】

def main(n_units):
    model = Chain(l1=F.Linear(784, n_units),
                  l2=F.Linear(n_units, n_units),
                  l3=F.Linear(n_units, 10))

動きました!ということで、ちゃんと動く【ソースコード9】を以下に示します。



ソースコード9】

import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import fetch_mldata
from chainer import cuda, Variable, optimizers, Chain
import chainer.functions  as F
import sys

plt.style.use('ggplot')


# MNISTの手書き数字データのダウンロード
# #HOME/scikit_learn_data/mldata/mnist-original.mat にキャッシュされる
print('fetch MNIST dataset')
mnist = fetch_mldata("MNIST original", data_home=".")
# mnist.data : 70,000件の784次元ベクトルデータ
mnist.data   = mnist.data.astype(np.float32)
mnist.data  /= 255     # 0-1のデータに変換

# mnist.target : 正解データ(教師データ)
mnist.target = mnist.target.astype(np.int32)


# 手書き数字データを描画する関数
def draw_digit(data,nfig):
    size = 28
    plt.figure(nfig,figsize=(2.5, 3))

    X, Y = np.meshgrid(range(size),range(size))
    Z = data.reshape(size,size)   # convert from vector to 28x28 matrix
    Z = Z[::-1,:]             # flip vertical
    plt.xlim(0,27)
    plt.ylim(0,27)
    plt.pcolor(X, Y, Z)
    plt.gray()
    plt.tick_params(labelbottom="off")
    plt.tick_params(labelleft="off")

    plt.draw()

draw_digit(mnist.data[5],1)
draw_digit(mnist.data[12345],2)
draw_digit(mnist.data[33456],3)




# Prepare multi-layer perceptron model
# 多層パーセプトロンモデルの設定
# 入力 784次元、出力 10次元
def main(n_units):
    model = Chain(l1=F.Linear(784, n_units),
                  l2=F.Linear(n_units, n_units),
                  l3=F.Linear(n_units, 10))


# Neural net architecture
# ニューラルネットの構造
def forward(x_data, y_data, train=True):
    x, t = Variable(x_data), Variable(y_data)
    h1 = F.dropout(F.relu(model.l1(x)),  train=train)
    h2 = F.dropout(F.relu(model.l2(h1)), train=train)
    y  = model.l3(h2)
    # 多クラス分類なので誤差関数としてソフトマックス関数の
    # 交差エントロピー関数を用いて、誤差を導出
    return F.softmax_cross_entropy(y, t), F.accuracy(y, t)

# F.reluテスト
x_data = np.linspace(-10, 10, 100, dtype=np.float32)
x = Variable(x_data)
y = F.relu(x)

plt.figure(4,figsize=(7,5))
plt.ylim(-2,10)
plt.plot(x.data, y.data)
plt.show()




実行結果は以下の通りです。



【画像4】
f:id:cocosuzu:20171017202535p:plain



ソースコード9】ではグラフ描画の度に都度figureウィンドウをと閉じないと次のプロセスに進めまなかった点を改良してあります。今までplt.show()としていたところをplt.draw()にすると複数のグラフを描画して最後のfigureが描画されると全てのfigureが描画された状態で停止します。plt.ion()という方法もあるみたいでしたが、これを使うと全てのfigureが表示されきると、とたんに全てのfigureが勝手に閉じるので使いませんでした。




Adamのzero_grads()がエラーになるときはLink.zerograds()もしくはさらに良いのはLink.cleargrads()らしいです。



【記事9】
github.com




【記事10】
Upgrade Guide from v1 to v2 — Chainer 3.2.0 documentation



【記事11】
chainer.Link — Chainer 3.2.0 documentation



selfの意味
【記事12】
qiita.com



どうしてもchainerのバージョン互換性や基本関数の理解が足りない為に本筋へと戻れず効率が悪いのでひとまず本記事の進行は保留とします。



直近はchainer v2の簡単な例で慣れて基本構造を理解してから本記事に戻ってくることにします。



ということで、保留!