過去記事
Lua版 ゼロから作るDeep Learning その1[パーセプトロンの実装]
Lua版 ゼロから作るDeep Learning その2[活性化関数]
Lua版 ゼロから作るDeep Learning その3[3層ニューラルネットワークの実装]
[Lua版 ゼロから作るDeep Learning その4[ソフトマックス関数の実装]]
(http://qiita.com/Kazuki-Nakamae/items/20e53a02a8b759583d31)
Lua版 ゼロから作るDeep Learning その5[MNIST画像の表示]
Lua版 ゼロから作るDeep Learning その5.5[pklファイルをLua Torchで使えるようにする]
Lua版 ゼロから作るDeep Learning その6[ニューラルネットワークの推論処理]
Lua版 ゼロから作るDeep Learning その7[バッチ処理]
Lua版 ゼロから作るDeep Learning その8[損失関数]
はじめに
今回は原書4章の損失関数に手を加えてミニバッチ学習が行えるようにします。
[バッチ対応版]交差エントロピー誤差の実装
前回実装した交差エントロピー誤差算出関数をバッチ対応版に変更します。
スクリプトは以下の通りです。
---バッチ対応版交差エントロピー誤差算出関数
-- テンソル同士の交差エントロピー誤差(-∑tilogyi)を求める
-- @param y 入力1、今回はNNが出力する確率リスト {Type:Tensor}
-- @param t 入力2、今回は正解ラベルリスト {Type:ByteTensor}
-- @return 交差エントロピー誤差 {Type:number}
function cross_entropy_error_batch(y, t)
if y:dim() == 1 then
y = y:resize(1,y:nElement())
end
local batch_size = y:size()[1]
return -( getElement( y, torch.range( 1, batch_size ), t + 1 ):log() ):sum() / batch_size
end
python の実装例となるべく同じ形で実装しました。
ただnumpy の配列のようにnparray[[x(i1),x(i2),x(i3)][t(i1),t(i2),t(i3)]] (in = インデックス順)のような対応付けた配列を出力する機能(下記参照)はありません。
>>> x = np.array([[1, 2], [3, 4], [5, 6]])
>>> x[[0, 1, 2], [0, 1, 0]]
array([1, 4, 5])
そのためTorchで似たような動作をするgetElement()関数を定義します。
---任意要素アクセス関数
-- 入力されたテンソルに対して、次元分の指定テンソルの順序でアクセスした要素のリストを返す。
-- @param readTensor データサイズ (Type:tensor)
-- @param ... 指定テンソルを内包するテーブル (Type:table)
-- @return 取得した要素のリスト (Type:Tensor)
function getElement(readTensor,...)
local args = {...}
local elelist = {}
for order = 1, args[1]:size()[1] do
local indexlist = {}
for dim = 1, readTensor:dim() do
table.insert(indexlist,(args[dim])[order])
end
table.insert(elelist,readTensor[indexlist])
end
return torch.Tensor(elelist)
end
>>>x = torch.Tensor({{1, 2}, {3, 4}, {5, 6}})
>>>print( getElement( x, torch.Tensor({0, 1, 2}) + 1, torch.Tensor({0, 1, 0}) + 1 ) )
1
4
5
[torch.DoubleTensor of size 3]
よさそうなのでこの関数を用いて計算してみたいと思います。
まずは前回の1次元のテンソルでの出力を見てみましょう。
--バッチサイズ1の場合
require './lossfunc_batch.lua'
require './exTorch.lua'
print("correct judgement : ")
local t = torch.Tensor({2})
local y = torch.Tensor({0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0})
print( cross_entropy_error_batch(y, t) .."\n")
print("incorrect judgement : ")
t = torch.Tensor({2})
y = torch.Tensor({0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.5, 0.0})
print( cross_entropy_error_batch(y, t) .."\n")
correct judgement :
0.51082562376599
incorrect judgement :
2.302585092994
以前と同じような計算結果となっているのが確認できました。
それでは次にバッチを1よりも多く設定した場合を試します。せっかくなので今回はMNISTの訓練データを利用して行いたいと思います。
MNISTデータの適用
まず訓練データを準備します。原書の付録データの中にはいっているdatasetディレクトリをカレントディレクトリに置いてsaveMMISTtrain.py
を実行します。
# coding: utf-8
import sys, os
import numpy as np
import pickle
from dataset.mnist import load_mnist
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, flatten=True, one_hot_label=False)
np.save("x_train",x_train)
np.save("t_train",t_train)
$python3 saveMMISTtrain.py
これでカレントディレクトリ上に訓練データをnumpy 形式でまとめたx_train.npy とt_train.npy ができると思います。
それではこの訓練データをLua上で読み込み、以前に使った重みとバイアスで交差エントロピー誤差を計算してみたいと思います。
require './activationFunc.lua'
require './softmax.lua'
require './lossfunc_batch.lua'
require './exTorch.lua'
npy4th = require 'npy4th' --https://github.com/htwaijry/npy4th (Author:Hani Altwaijry)
--バッチサイズ1の場合
(先ほど示したので省略)
--バッチサイズ10の場合(MNISTデータを適用)
---データ取得関数
-- MNISTの訓練データ取得する。
-- @return 訓練データのイメージ ,訓練データのラベル {Type:ByteTensor}
function get_traindata()
--画像データ
local x_train = npy4th.loadnpy('x_train.npy')
--ラベルデータ
local t_train = npy4th.loadnpy('t_train.npy')
return x_train, t_train
end
---ネットワーク生成関数.
-- 重みとバイアスが決定した3層NNを返す。今回はpklで記述された重みを用いる
-- @return 3層NN (Type:table)
function init_network()
local network = npy4th.loadnpz('sample_weight.npz')
return network
end
---分類関数.
-- ネットワークにしたがって分類計算を行う。
-- @param network 入力 (Type:torch.table)
-- @param x 入力
-- @return 出力 (Type:torch.DoubleTensor)
function predict(network, x)
local W1, W2, W3 = network['W1'], network['W2'], network['W3']
local b1, b2, b3 = network['b1'], network['b2'], network['b3']
local a1 = mulTensor(x, W1) + makeIterTensor(b1,mulTensor(x, W1):size()[1])
local z1 = sigmoid(a1)
local a2 = mulTensor(z1,W2) + makeIterTensor(b2,mulTensor(z1,W2):size()[1])
local z2 = sigmoid(a2)
local a3 = mulTensor(z2,W3) + makeIterTensor(b3,mulTensor(z2,W3):size()[1])
local y = softmax(a3)
return y
end
local x_train, t_train = get_traindata()
--ネットワークの生成(仮)
local network = init_network()
--ミニバッチ学習用訓練データ取得
local train_size = x_train:size()[1] --訓練データ数
local batch_size = 10 --バッチ数
local seed = 1
local batch_mask = getRandIndex( train_size, batch_size, seed )
print("Selected index : ")
print(batch_mask)
local x_train_batch = x_train:index( 1, batch_mask )
local t_train_batch = t_train:index( 1, batch_mask )
local y_pred_batch = predict( network, x_train_batch )
print("cross entropy error : "..cross_entropy_error_batch(y_pred_batch, t_train_batch))
Torch にはnp.random.choice( )にあたるメソッドがないため自前で実装します。
torch.randperm( n )は1からnまでの整数のランダムな順列を出力するため、これをバッチサイズ分切り取るgetRandIndex( )を定義しました。
---ランダムインデックス取得関数
-- 入力されたサイズ内で指定した数の整数を取得する
-- @param datasize データサイズ (Type:number)
-- @param getsize 取得サイズ (Type:number)
-- @param seed 乱数のシード (Type:number)
-- @return インデックスリスト (Type:long Tensor)
function getRandIndex(datasize,getsize,seed)
torch.manualSeed(seed)
--データサイズ分の整数のランダムな順列からgetsize分切り取る
return torch.randperm(datasize):sub(1,getsize):long()
end
それではmnist_minibatch.lua の出力結果を見てみましょう。
$ th mnist_minibatch.lua
correct judgement :
0.51082562376599
incorrect judgement :
2.302585092994
Selected index :
35846
27523
33255
23646
11300
16179
58356
27003
35816
26560
[torch.LongTensor of size 10]
cross entropy error : 3.8591951772777
先ほどのバッチサイズ1の場合の検証から、一つ間違えるごとに2程度増大すると考えますとバッチサイズ10で3.85という数字はかなり低く抑えられているように思います。
ただこの部分に関しては原書付録のサンプルコードや計算例が見当たらないため、numpyの場合での確認をしていません。どなたか検証された方がいらっしゃっればご教授お願いしたく思います。
おわりに
今回は以上です。
次回は勾配計算をみていきたいと思います。
ありがとうございました。