[[FrontPage]]

#contents

2016/05/07からのアクセス回数 &counter;

ここで紹介したSageワークシートは、以下のURLからダウンロードできます。

http://www15191ue.sakura.ne.jp:8000/home/pub/72/

また、私の公開しているSageのサーバ(http://www15191ue.sakura.ne.jp:8000/)にユーザIDを作成することで、ダウンロードしたワークシートを
アップロードし、実行したり、変更していろいろ動きを試すことができます。

** 参考サイト [#bb48825a]
** 参考サイト [#qf21a7b9]

ここでは、
[[人工知能に関する断創録>http://aidiary.hatenablog.com/]]
のTheanoに関連する記事をSageのノートブックで実装し、Thenoの修得を試みます。	
のTheanoに関連する記事をSageのノートブックで実装し、Thenoの修得を試みます。    

今回は、TheanoのTutorialから畳み込みニューラルネット(CNN)を使った手書き数字認識を以下のページを参考にSageのノートブックで試してみます。
前半は、人工知能に関する断創録から参照されている「StatsFragments」さんのページを参考に畳み込みとMaxPoolingをSageで動かしてみました(ほぼ引用ですみません)。

- [[Theano で Deep Learning <3> : 畳み込みニューラルネットワーク>http://sinhrks.hatenablog.com/entry/2014/12/07/203048]]

- [[Theanoによる畳み込みニューラルネットワークの実装 (1)>http://aidiary.hatenablog.com/entry/20150626/1435329581]]


** 前準備 [#k6390ca0]
** 前準備 [#xa68d8b8]

*** 処理系をSageからPythonに変更 [#zb3ed71c]
*** 処理系をSageからPythonに変更 [#i3958b17]

SageでTheanoのtutorialのCNNを実行すると、DimShuffleでエラーになるため、今回もPythonを使用します。	
SageでTheanoのtutorialのCNNを実行すると、DimShuffleでエラーになるため、今回もPythonを使用します。    

そこで、ノートブックの処理系をSageからPythonに切り替えます。上部の左から4つめのプルダウンメニューから
「python」を選択してください。

&ref(theano_setup.png);


*** 必要なライブラリのimport [#id16ee7c]
*** 必要なライブラリのimport [#pfa8bb07]

最初に、theanoを使うのに必要なライブラリをインポートします。


sageへの入力:
#pre{{
# 必要なライブラリのインポート
import six.moves.cPickle as pickle
import gzip
import os
import sys
import timeit
import time
import urllib

import numpy as np
import pylab as pl


import theano
import theano.tensor as T
from theano.tensor.nnet import conv
from theano.tensor.signal import downsample

# これまで確認したlogistic_sgd.pyのLogisticRegression, mlpのHiddenLayerをインポートする
from logistic_sgd import LogisticRegression, load_data
from mlp import HiddenLayer
}}


** 畳み込みニューラルネットワークの構成 [#t573fb70]
** 畳み込みニューラルネットワークの構成 [#ydd2a4fe]

「Theanoによる畳み込みニューラルネットワークの実装 (1)」から
ニューラルネットワーク(CNN)の構成図を引用します。

&ref(http://cdn-ak.f.st-hatena.com/images/fotolife/a/aidiary/20150626/20150626203849.png,60%x60%);
&ref(http://cdn-ak.f.st-hatena.com/images/fotolife/a/aidiary/20150626/20150626203849.png,614x312);


最初に畳み込み(convolutionの矢印部)とプーリング(maxpoolingの矢印部)を持つLeNetConvPoolLayerが2層あり、
その後に多層パーセプトロン(HiddenLayer)と最後のロジスティック(LogisticRegression)から構成されています。

前半の畳み込みでは画像の特徴を際立たせるためのフィルタリングを行い、Max Poolingでは画像のずれを吸収し、
疎な結合を構成しています。


*** 畳み込み演算 [#x7c330b2]
[[Theano で Deep Learning <3> : 畳み込みニューラルネットワーク>http://sinhrks.hatenablog.com/entry/2014/12/07/203048]]
の例題をSageで動かしながら、畳み込み演算の効果をみてみましょう。

Theanoでは、畳み込みの処理がパッケージtheano.tensor.nnet.convのconv2dで提供されています。

conv2dへの入力テンソルは、以下の様な次元を持ちます。

- Minibatchのサイズ: 以下の例では入力が1個なので1としています
- 入力の特徴マップ数: 例ではRGBの3つを特徴マップとしているので、3としています
- W: 重みテンソル、次元は(出力特徴マップ数、入力特徴マップ数、フィルタの縦幅、フィルタの横幅)
- b: バイアスベクトル、長さは出力の特徴マップ数

入力テンソルinputを4次元のテンソルとして定義します。

sageへの入力:
#pre{{
rng = np.random.RandomState(23455)
# 入力画像を受けとるシンボルを作成
input = T.tensor4(name='input')
}}


重みテンソルWの次元は、(2, 3, 9, 9)で、フィルタは9x9で、2x3(出力の特徴マップ数x入力の特徴マップ数)=6種類のフィルタが使われます。

以下の例では、重みの値は一様乱数を使って生成した意味のないものです。実際にはこのWが学習によって画像の特徴をより抽出できる形になります。

sageへの入力:
#pre{{
# 重みテンソル
# W の次元を指定 ( 出力の特徴マップ数, 入力の特徴マップ数, 畳み込み(フィルタ)の縦幅, 畳み込み(フィルタ)の横幅)
w_shp = (2, 3, 9, 9)

# 発生させる一様乱数の範囲を指定
w_bound = np.sqrt(3 * 9 * 9)
-1.0 / w_bound, 1.0 / w_bound
}}
#pre{{
(-0.064150029909958411, 0.064150029909958411)
}}

バイアスベクトルは、出力の特徴マップ数の長さを持ち、フィルタリングの後に各画像に加えられます。

そのため、4次元のテンソルと足し合わせられるようにdimshuffleで次元を調整します。
ここでは、conv_outの2つ目の次元と合うようにします。

sageへの入力:
#pre{{
# 重みテンソルを初期化
W = theano.shared( np.asarray(
            rng.uniform(
                low=-1.0 / w_bound,
                high=1.0 / w_bound,
                size=w_shp),
            dtype=input.dtype), name ='W')

# バイアスベクトルを初期化
b_shp = (2,)
b = theano.shared(np.asarray(
            rng.uniform(low=-.5, high=.5, size=b_shp),
            dtype=input.dtype), name ='b')
b.get_value()
}}
#pre{{
array([-0.3943425 ,  0.16818965])
}}

sageへの入力:
#pre{{
b.dimshuffle('x', 0, 'x', 'x').eval()
}}
#pre{{
array([[[[-0.3943425 ]],

        [[ 0.16818965]]]])
}}

sageへの入力:
#pre{{
# 畳み込み とバイアスの足し合わせの定義
conv_out = conv.conv2d(input, W)
output = T.nnet.sigmoid(conv_out + b.dimshuffle('x', 0, 'x', 'x'))
f = theano.function([input], output)
}}


*** 畳み込みの画像 [#l34baf6d]
通常の画像をどのようにしてTheanoで解析可能な形式にするのか、
[[Theano で Deep Learning <3> : 畳み込みニューラルネットワーク>http://sinhrks.hatenablog.com/entry/2014/12/07/203048]]
の例がとても分かりやすく参考になりました。

入力画像は、縦130px * 横120pxのRGBのJPEGファイルです。

これをswapaxesを使ってTheanoの入力にあった形式に変換していきます。

sageへの入力:
#pre{{
# 画像の読み込み
from scipy.misc import imread

# 読み込むファイル名を指定
img = imread(DATA+"01.jpg")
img = img / 256.
# 元画像の次元 (縦幅, 横幅, RGB)
img.shape
}}
#pre{{
(130, 120, 3)
}}

sageへの入力:
#pre{{
# 1軸目と3軸目を入替 (RGB, 横幅, 縦幅)
img.swapaxes(0, 2).shape
}}
#pre{{
(3, 120, 130)
}}

sageへの入力:
#pre{{
# 2軸目と3軸目を入替 (RGB, 縦幅, 横幅)
img.swapaxes(0, 2).swapaxes(1, 2).shape
}}
#pre{{
(3, 130, 120)
}}

sageへの入力:
#pre{{
# 1軸目にダミーの軸を追加 (dummy, RGB, 縦幅, 横幅)
img.swapaxes(0, 2).swapaxes(1, 2).reshape(1, 3, img.shape[0], img.shape[1]).shape
}}
#pre{{
(1, 3, 130, 120)
}}

sageへの入力:
#pre{{
img_ = img.swapaxes(0, 2).swapaxes(1, 2).reshape(1, 3, img.shape[0], img.shape[1])
}}


*** サンプル画像へのたたき込み [#k7cf2450]
サンプル画像のRGBのそれぞれの画像にフィルタを使って畳み込みを施すと以下の様になります。

フィルタによる畳み込みで2つの特徴マップ(画像)は、異なる表情を示します。
これがフィルタによる特徴抽出の効果です。

sageへの入力:
#pre{{
# 係数テンソル + バイアスベクトルを適用
filtered_img = f(img_)
filtered_img.shape
}}
#pre{{
(1, 2, 122, 112)
}}


sageへの入力:
#pre{{
# オリジナルの画像
html("<img src='01.jpg'/>")
}}

&ref(01.jpg);


sageへの入力:
#pre{{
# RGBの画像
pl.figure()
for index in range(3):
    pl.subplot(1, 3, index + 1)
    pl.axis('off')
    pl.imshow(img_[0][index], cmap=pl.cm.gray, interpolation='nearest')
pl.savefig(DATA+'RGB.png', dpi=75)
html("<img src='RGB.png'/>")
}}

&ref(RGB.png);

sageへの入力:
#pre{{
# フィルタ後の画像
pl.figure()
for index in range(2):
    pl.subplot(1, 2, index + 1)
    pl.axis('off')
    pl.imshow(filtered_img[0][index], cmap=pl.cm.gray, interpolation='nearest')
pl.savefig(DATA+'FILTERED.png', dpi=50)
html("<img src='FILTERED.png'/>")
}}

&ref(FILTERED.png);


*** フィルタの形 [#k5ab3a10]
畳み込みに使用されたフィルタの形を表示してみると訳の分からないランダムな模様に見えます。
畳み込みニューラルネットの学習によってこのランダムなフィルタに少しずつ特徴的な形がでてきます。

sageへの入力:
#pre{{
#フィルタの形
w = W.get_value()
pl.figure()
pos = 1
for i in range(2):
    for j in range(3):
        pl.subplot(2, 3, pos)
        pl.axis('off')
        pl.imshow(w[i][j], cmap=pl.cm.gray, interpolation='nearest')
        pos += 1
    pl.savefig(DATA+'FILTER.png', dpi=50)
html("<img src='FILTER.png'/>")
}}

&ref(FILTER.png);


*** Max Pooling [#y8078759]
Max Pooling 法とは、あるウィンドウサイズの中で 最大の値を代表値としてサンプリングする方法です。

Theanoのtheano.tensor.signal.downsampleパッケージのmax_pool_2dでMax Poolingを提供しています。

sageへの入力:
#pre{{
# Max Pooling のウィンドウサイズ
maxpool_shape = (2, 2)
pool_out = downsample.max_pool_2d(input, maxpool_shape, ignore_border=True)
f = theano.function([input],pool_out)
}}


sageへの入力:
#pre{{
# Max Poolingの動作確認
invals = np.random.RandomState(1).rand(3, 2, 5, 5)
print 'invals[0, 0, :, :] =\n', invals[0, 0, :, :]
print 'output[0, 0, :, :] =\n', f(invals)[0, 0, :, :]
}}
#pre{{
invals[0, 0, :, :] =
[[  4.17022005e-01   7.20324493e-01   1.14374817e-04   3.02332573e-01
    1.46755891e-01]
 [  9.23385948e-02   1.86260211e-01   3.45560727e-01   3.96767474e-01
    5.38816734e-01]
 [  4.19194514e-01   6.85219500e-01   2.04452250e-01   8.78117436e-01
    2.73875932e-02]
 [  6.70467510e-01   4.17304802e-01   5.58689828e-01   1.40386939e-01
    1.98101489e-01]
 [  8.00744569e-01   9.68261576e-01   3.13424178e-01   6.92322616e-01
    8.76389152e-01]]
output[0, 0, :, :] =
[[ 0.72032449  0.39676747]
 [ 0.6852195   0.87811744]]
}}


以下の例では、2x2のマトリックスからその最大の要素(0.7203)が抽出されていることが確認できます。

sageへの入力:
#pre{{
print invals[0, 0, 0:2, 0:2]
print invals[0, 0, 0:2, 0:2].max()
}}
#pre{{
[[ 0.417022    0.72032449]
 [ 0.09233859  0.18626021]]
0.720324493442
}}


*** Max Poolingの結果 [#y45431ed]
サンプル画像のMax Pooling後の画像を表示してみると、左の画像は瞳の白い部分が強調され、右の部分はまぶたの部分が強調されているように見えます。

sageへの入力:
#pre{{
# フィルタ後の画像にMax Poolingを施す
pool_img = f(filtered_img)
pool_img.shape
}}
#pre{{
(1, 2, 61, 56)
}}


sageへの入力:
#pre{{
# Max Pooling後の画像
for index in range(2):
    pl.subplot(1, 2, index + 1)
    pl.axis('off')
    pl.imshow(pool_img[0][index], cmap=pl.cm.gray, interpolation='nearest')
pl.savefig(DATA+'POOLED.png', dpi=50)
html("<img src='POOLED.png'/>")
}}

&ref(POOLED.png);

*** LeNetモデル [#e6fa6a1b]
LeNetに関する
[[Theanoによる畳み込みニューラルネットワークの実装 (1)>http://aidiary.hatenablog.com/entry/20150626/1435329581]]
の説明(図を引用)も分かりやすいです。

&ref(http://cdn-ak.f.st-hatena.com/images/fotolife/s/sinhrks/20141214/20141214220532.png,480x242);


特徴マップに畳み込みを実行し、Max Poolingで疎な結合とし、これを複数層連結して最後に、多層パーセプトロンと目的の活性化関数を施す、
一連の流れが、とても分かりやすいです。

LeNetというのは、畳み込みニューラルネットを発明したLeCunの最初のニューラルネットの名前に由来するのだそうです。


*** LeNetConvPoolLayerクラス [#re245782]
Deep Learning TutorialのLeNetConvPoolLayerクラスは、畳み込み層とプーリング層のペアを実装しています。

重みWの初期値に、
$$
\left [ - \sqrt{\frac{6}{in + out}},\sqrt{\frac{6}{in + out}} \right ]
$$
を与えるのは、
[[Theanoによる多層パーセプトロンの実装>http://aidiary.hatenablog.com/entry/20150618/1434628272]]
で紹介されている、活性化関数にtanhを使う時の収束の良いWの初期値です。

sageへの入力:
#pre{{
class LeNetConvPoolLayer(object):
    """畳み込みニューラルネットの畳み込み層+プーリング層"""
    def __init__(self, rng, input, image_shape, filter_shape, poolsize=(2, 2)):
        # 入力の特徴マップ数は一致する必要がある
        assert image_shape[1] == filter_shape[1]

        fan_in = np.prod(filter_shape[1:])
        fan_out = filter_shape[0] * np.prod(filter_shape[2:]) / np.prod(poolsize)

        W_bound = np.sqrt(6.0 / (fan_in + fan_out))
        self.W = theano.shared(
            np.asarray(rng.uniform(low=-W_bound, high=W_bound, size=filter_shape),
                       dtype=theano.config.floatX),  # @UndefinedVariable
            borrow=True)

        b_values = np.zeros((filter_shape[0],), dtype=theano.config.floatX)  # @UndefinedVariable
        self.b = theano.shared(value=b_values, borrow=T)

        # 入力の特徴マップとフィルタの畳み込み
        conv_out = conv.conv2d(
            input=input,
            filters=self.W,
            filter_shape=filter_shape,
            image_shape=image_shape)

        # Max-poolingを用いて各特徴マップをダウンサンプリング
        pooled_out = downsample.max_pool_2d(
            input=conv_out,
            ds=poolsize,
            ignore_border=True)

        # バイアスを加える
        self.output = T.tanh(pooled_out + self.b.dimshuffle('x', 0, 'x', 'x'))

        self.params = [self.W, self.b]
}}


** 畳み込みニューラルネットワークを使った数字認識 [#d02c29c0]
2つのLeNetConvPoolLayerと全結合したHiddenLayerとLogisticRegressionを組み合わせて、
MNISTの数字文字認識のMini版を試してみます。

[[Theanoによる畳み込みニューラルネットワークの実装 (1)>http://aidiary.hatenablog.com/entry/20150626/1435329581]]
との違いは、最後に収束したモデルを以下の様に保存しているところです。

#pre{{
    # dump layers
    with gzip.open(DATA+'model.pkl.gz', 'wb') as f:
        pickle.dump([layer0_input, layer0, layer1, layer2_input, layer2, layer3], f)              
}}

sageへの入力:
#pre{{
def evaluate_lenet5(learning_rate=0.1, n_epochs=200,
                    dataset='mnist.pkl.gz', batch_size=500):
    rng = np.random.RandomState(23455)

    # 学習データのロード
    datasets = load_data(dataset)
    train_set_x, train_set_y = datasets[0]
    valid_set_x, valid_set_y = datasets[1]
    test_set_x, test_set_y = datasets[2]

    # ミニバッチの数
    n_train_batches = train_set_x.get_value(borrow=True).shape[0] / batch_size
    n_valid_batches = valid_set_x.get_value(borrow=True).shape[0] / batch_size
    n_test_batches = test_set_x.get_value(borrow=True).shape[0] / batch_size

    print "building the model ..."

    # ミニバッチのインデックスを表すシンボル
    index = T.lscalar()

    # ミニバッチの学習データとラベルを表すシンボル
    x = T.matrix('x')
    y = T.ivector('y')

    # 入力
    # 入力のサイズを4Dテンソルに変換
    # batch_sizeは訓練画像の枚数
    # チャンネル数は1
    # (28, 28)はMNISTの画像サイズ
    layer0_input = x.reshape((batch_size, 1, 28, 28))

    # 最初の畳み込み層+プーリング層
    # 畳み込みに使用するフィルタサイズは5x5ピクセル
    # 畳み込みによって画像サイズは28x28ピクセルから24x24ピクセルに落ちる
    # プーリングによって画像サイズはさらに12x12ピクセルに落ちる
    # 特徴マップ数は20枚でそれぞれの特徴マップのサイズは12x12ピクセル
    # 最終的にこの層の出力のサイズは (batch_size, 20, 12, 12) になる
    layer0 = LeNetConvPoolLayer(rng,
                input=layer0_input,
                image_shape=(batch_size, 1, 28, 28),  # 入力画像のサイズを4Dテンソルで指定
                filter_shape=(20, 1, 5, 5),           # フィルタのサイズを4Dテンソルで指定
                poolsize=(2, 2))

    # layer0の出力がlayer1への入力となる
    # layer0の出力画像のサイズは (batch_size, 20, 12, 12)
    # 12x12ピクセルの画像が特徴マップ数分(20枚)ある
    # 畳み込みによって画像サイズは12x12ピクセルから8x8ピクセルに落ちる
    # プーリングによって画像サイズはさらに4x4ピクセルに落ちる
    # 特徴マップ数は50枚でそれぞれの特徴マップのサイズは4x4ピクセル
    # 最終的にこの層の出力のサイズは (batch_size, 50, 4, 4) になる
    layer1 = LeNetConvPoolLayer(rng,
                input=layer0.output,
                image_shape=(batch_size, 20, 12, 12), # 入力画像のサイズを4Dテンソルで指定
                filter_shape=(50, 20, 5, 5),          # フィルタのサイズを4Dテンソルで指定
                poolsize=(2, 2))

    # 隠れ層への入力
    # 画像のピクセルをフラット化する
    # layer1の出力のサイズは (batch_size, 50, 4, 4) なのでflatten()によって
    # (batch_size, 50*4*4) = (batch_size, 800) になる
    layer2_input = layer1.output.flatten(2)

    # 全結合された隠れ層
    # 入力が800ユニット、出力が500ユニット
    layer2 = HiddenLayer(rng,
        input=layer2_input,
        n_in=50 * 4 * 4,
        n_out=500,
        activation=T.tanh)

    # 最終的な数字分類を行うsoftmax層
    layer3 = LogisticRegression(input=layer2.output, n_in=500, n_out=10)

    # コスト関数を計算するシンボル
    cost = layer3.negative_log_likelihood(y)

    # index番目のテスト用ミニバッチを入力してエラー率を返す関数を定義
    test_model = theano.function(
        [index],
        layer3.errors(y),
        givens={
            x: test_set_x[index * batch_size: (index + 1) * batch_size],
            y: test_set_y[index * batch_size: (index + 1) * batch_size]
        })

    # index番目のバリデーション用ミニバッチを入力してエラー率を返す関数を定義
    validate_model = theano.function(
        [index],
        layer3.errors(y),
        givens={
            x: valid_set_x[index * batch_size: (index + 1) * batch_size],
            y: valid_set_y[index * batch_size: (index + 1) * batch_size]
        })

    # パラメータ
    params = layer3.params + layer2.params + layer1.params + layer0.params

    # コスト関数の微分
    grads = T.grad(cost, params)

    # パラメータ更新式
    updates = [(param_i, param_i - learning_rate * grad_i) for param_i, grad_i in zip(params, grads)]

    # index番目の訓練バッチを入力し、パラメータを更新する関数を定義
    train_model = theano.function(
        [index],
        cost,
        updates=updates,
        givens={
            x: train_set_x[index * batch_size: (index + 1) * batch_size],
            y: train_set_y[index * batch_size: (index + 1) * batch_size]
        })

    print "train model ..."

    # eary-stoppingのパラメータ
    patience = 10000
    patience_increase = 2
    improvement_threshold = 0.995
    validation_frequency = min(n_train_batches, patience / 2)

    best_validation_loss = np.inf
    best_iter = 0
    test_score = 0
    start_time = time.clock()

    epoch = 0
    done_looping = False

    fp1 = open("validation_error.txt", "w")
    fp2 = open("test_error.txt", "w")

    while (epoch < n_epochs) and (not done_looping):
        epoch = epoch + 1
        for minibatch_index in xrange(n_train_batches):
            iter = (epoch - 1) * n_train_batches + minibatch_index
            cost_ij = train_model(minibatch_index)

            if (iter + 1) % validation_frequency == 0:
                validation_losses = [validate_model(i) for i in xrange(n_valid_batches)]
                this_validation_loss = np.mean(validation_losses)
                print "epoch %i, minibatch %i/%i, validation error %f %%" % (epoch, minibatch_index + 1, n_train_batches, this_validation_loss * 100)
                fp1.write("%d\t%f\n" % (epoch, this_validation_loss * 100))

                if this_validation_loss < best_validation_loss:
                    if this_validation_loss < best_validation_loss * improvement_threshold:
                        # 十分改善したならまだ改善の余地があるためpatienceを上げてより多くループを回せるようにする
                        patience = max(patience, iter * patience_increase)
                        print "*** iter %d / patience %d" % (iter, patience)
                    best_validation_loss = this_validation_loss
                    best_iter = iter

                    # テストデータのエラー率も計算
                    test_losses = [test_model(i) for i in xrange(n_test_batches)]
                    test_score = np.mean(test_losses)
                    print "    epoch %i, minibatch %i/%i, test error of best model %f %%" % (epoch, minibatch_index + 1, n_train_batches, test_score * 100)
                    fp2.write("%d\t%f\n" % (epoch, test_score * 100))

            # patienceを超えたらループを終了
            if patience <= iter:
                done_looping = True
                break

    fp1.close()
    fp2.close()

    end_time = time.clock()
    print "Optimization complete."
    print "Best validation score of %f %% obtained at iteration %i, with test performance %f %%" % (best_validation_loss * 100.0, best_iter + 1, test_score * 100.0)
    print "Ran for %.2fm" % ((end_time - start_time) / 60.0)

    # dump layers
    with gzip.open(DATA+'model.pkl.gz', 'wb') as f:
        pickle.dump([layer0_input, layer0, layer1, layer2_input, layer2, layer3], f)
}}



*** CNNの計算 [#g9e6999c]
さくらのVPSでこの計算をすると約2時間CPUを占有してしまいますので、以下の処理は実行しないでください。

または、このノートブックをダウンロードして、ローカルのSageで試してみてください。

sageへの入力:
#pre{{
# 以下を実行するとCNNを計算します。1/10のmini_mnist.pkl.gzデータで約2時間かかりました。
# さくらのVPSではこの計算を解放できないので、計算結果で読みすすめてください。
# evaluate_lenet5(dataset=DATA+"mini_mnist.pkl.gz")
}}
#pre{{
WARNING: Output truncated! 
... loading data
building the model ...
train model ...
epoch 1, minibatch 10/10, validation error 38.000000 %
*** iter 9 / patience 10000
    epoch 1, minibatch 10/10, test error of best model 38.400000 %
epoch 2, minibatch 10/10, validation error 34.200000 %
*** iter 19 / patience 10000
    epoch 2, minibatch 10/10, test error of best model 34.600000 %
epoch 3, minibatch 10/10, validation error 22.700000 %
*** iter 29 / patience 10000
    epoch 3, minibatch 10/10, test error of best model 23.300000 %
epoch 4, minibatch 10/10, validation error 17.700000 %
途中省略
poch 189, minibatch 10/10, validation error 3.100000 %
epoch 190, minibatch 10/10, validation error 3.000000 %
*** iter 1899 / patience 10000
    epoch 190, minibatch 10/10, test error of best model 2.900000 %
epoch 191, minibatch 10/10, validation error 3.000000 %
epoch 192, minibatch 10/10, validation error 3.000000 %
epoch 193, minibatch 10/10, validation error 3.000000 %
epoch 194, minibatch 10/10, validation error 3.000000 %
epoch 195, minibatch 10/10, validation error 3.000000 %
epoch 196, minibatch 10/10, validation error 3.000000 %
epoch 197, minibatch 10/10, validation error 3.000000 %
epoch 198, minibatch 10/10, validation error 3.000000 %
epoch 199, minibatch 10/10, validation error 3.000000 %
epoch 200, minibatch 10/10, validation error 3.000000 %
Optimization complete.
Best validation score of 3.000000 % obtained at iteration 1900, with test performance 2.900000 %
Ran for 119.10m
}}


*** フィルタの可視化 [#oed434de]
[[Theanoによる畳み込みニューラルネットワークの実装 (1)>http://aidiary.hatenablog.com/entry/20150626/1435329581]]
のフィルタの可視化を使って最初のフィルタを可視化してみました。         

当たり前かもしれませんが、
[[Theanoによる畳み込みニューラルネットワークの実装 (1)>http://aidiary.hatenablog.com/entry/20150626/1435329581]]
と同じ結果が得られました。

各フィルタの形を見ると数字を特徴付ける形の断片のようにも思われますが、ややシャープさに欠けるように思います。

sageへの入力:
#pre{{
def visualize_filter(layer, gName):
    """引数に指定されたLeNetConvPoolLayerのフィルタを描画する"""
    W = layer.W.get_value()
    n_filters, n_channels, h, w = W.shape
    pl.figure()
    pos = 1
    for f in range(n_filters):
        for c in range(n_channels):
            pl.subplot(n_channels, n_filters, pos)
            pl.subplots_adjust(wspace=0.1, hspace=0.1)
            pl.imshow(W[f, c], cmap=pl.cm.gray_r)
            pl.axis('off')
            pos += 1
    pl.savefig(DATA+gName);
    html("<img src='%s' />" % gName)
}}

sageへの入力:
#pre{{
# 保存したモデルのロード
classifiers = pickle.load(gzip.open(DATA+"model.pkl.gz"))
(layer0_input, layer0, layer1, layer2_input, layer2, layer3) = classifiers
}}

sageへの入力:
#pre{{
# 最初の畳み込み層のフィルタの可視化
# layer0のフィルタの可視化
visualize_filter(layer0, "layer0.png")
}}

&ref(layer0.png);


** テストデータの予測 [#c10b7b9b]
畳み込みニューラルネットの練習用データから求まったモデルを使って、
テスト用データを予測してみましょう。

mini_mnist.pkl.gzからテスト用の画像データを読み込み、batch_sizeはCNNの計算と同じ500として、
モデルの入力layer0_inputとLogisticRegressionの0〜9の数字に対する確率p_y_given_xを返す
関数predict_modelを定義し、テストデータの最初の500サンプルに対して結果を求めます。

#pre{{
predict_model = theano.function(
    [layer0_input],
    layer3.p_y_given_x,
)              
}}

sageへの入力:
#pre{{
# テストデータの読み込み
f = gzip.open(DATA+"mini_mnist.pkl.gz", 'rb')
(_, _, test_set) = pickle.load(f)
f.close()
test_set_x = test_set[0]
}}


sageへの入力:
#pre{{
# テストデータを使って求まったモデルの認識率を試す
# 分析に使ったのと同じbatch_sizeで計算する必要がある
batch_size = 500
# compile a predictor function
index = T.lscalar()

predict_model = theano.function(
    [layer0_input],
    layer3.p_y_given_x,
)

predicted_values = predict_model(
  test_set_x[:batch_size].reshape((batch_size, 1, 28, 28))
)
}}


ロジスティック回帰の結果は、

#pre{{
['(7, 0.988)', '(2, 0.733)', '(1, 0.951)', '(0, 0.995)', '(4, 0.936)',
'(1, 0.979)', '(4, 0.957)', '(9, 0.912)', '(6, 0.912)', '(9, 0.844)',
'(0, 0.904)', '(6, 0.668)', '(9, 0.917)', '(0, 0.983)', '(1, 0.991)',
'(5, 0.832)', '(9, 0.790)', '(7, 0.986)', '(3, 0.659)', '(4, 0.979)',
'(9, 0.775)', '(6, 0.952)', '(6, 0.801)', '(5, 0.955)', '(4, 0.862)']              
}}

多層回帰の結果は、

#pre{{
['(7, 0.999)', '(2, 0.780)', '(1, 0.991)', '(0, 0.999)', '(4, 0.993)',
'(1, 0.998)', '(4, 0.995)', '(9, 0.999)', '(6, 0.995)', '(9, 0.982)',
'(0, 0.996)', '(6, 0.994)', '(9, 0.996)', '(0, 0.993)', '(1, 1.000)',
'(5, 0.974)', '(9, 0.995)', '(7, 0.998)', '(3, 0.980)', '(4, 1.000)',
'(9, 0.911)', '(6, 0.905)', '(6, 0.933)', '(5, 1.000)', '(4, 0.994)']              
}}
であることから、CNNではかなり認識率が良くなっているように思えます。

sageへの入力:
#pre{{
print('予測計算終了')
print(["(%d, %.3f)" % (np.argmax(vals), max(vals)) for vals in predicted_values[0:25]])
}}
#pre{{
予測計算終了
['(7, 1.000)', '(2, 0.999)', '(1, 1.000)', '(0, 0.998)', '(4, 1.000)',
'(1, 1.000)', '(4, 1.000)', '(9, 0.999)', '(5, 0.985)', '(9, 0.995)',
'(0, 1.000)', '(6, 1.000)', '(9, 1.000)', '(0, 1.000)', '(1, 1.000)',
'(5, 0.997)', '(9, 0.999)', '(7, 1.000)', '(3, 0.976)', '(4, 1.000)',
'(9, 0.995)', '(6, 0.992)', '(6, 0.999)', '(5, 1.000)', '(4, 1.000)']
}}


同様にlayer0の出力画像も求めることができます。

sageへの入力:
#pre{{
# テストデータのlayer0の出力画像を計算する
filter0_model =  theano.function(
    [layer0_input],
    layer0.output,
)

filter0_images = filter0_model(
  test_set_x[:batch_size].reshape((batch_size, 1, 28, 28))
)
}}


テストデータの最初の7をフィルタリングした結果は、以下の様になりました。

sageへの入力:
#pre{{
# LeNetConvPoolLayerの出力画像を可視化
# layer0
print filter0_images.shape
pl.figure()
for i in range(20):
    pl.subplot(4, 5, i + 1)
    pl.axis('off')
    pl.imshow(filter0_images[0, i], cmap=pl.cm.gray_r)
pl.savefig(DATA+"layer0_out_image.png", dpi=70);
html("<img src='layer0_out_image.png' />")
}}
#pre{{
(500, 20, 12, 12)
}}

&ref(layer0_out_image.png);

** コメント [#o7008bf1]
#vote(おもしろかった,そうでもない,わかりずらい)


皆様のご意見、ご希望をお待ちしております。
#comment_kcaptcha


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
SmartDoc