2018年度 MI/CS実験第二
山
匡 (MIコース)
「作ってわかる深層学習」
1回目スライド資料
ありがとうございます
はじめに
「深層学習でやってみた」はもう古い!
「深層学習を作ってみた」が熱い!
教科書レベルの数理を理解して、かつ
プログラムに落とし込む能力を鍛える
内容
深層学習のプログラムをC言語で一から書く
・パーセプトロン
・誤差逆伝播法
・自己符号化器
・手書き文字認識
スケジュール
第1回 (10/03): ガイダンス、ニューラルネット概論と肩慣らし
第2回 (10/08): 誤差逆伝播のミニ講義と実装
第3回 (10/10): 手書き文字認識のテスト
第4回 (10/15): 自己符号化器のミニ講義と実装
第5回 (10/17): 実装
第6回 (10/22): 実装
第7回 (10/24): 実装
参考文献
TODO: 本を持参する
実験の進め方
一人でやらない
ただしレポートは自分の言葉で書く
相談しながらやる
成績のつけ方
出席 + レポート
実験なので必ず出席する
誤差逆伝播まで → 可
手書き文字認識まで → 良
自己符号化器まで → 優
ボーナス課題もやる → 秀 (ただし出来映えによる)
提出場所と締切は後日メーリングリストで
メーリングリスト
http://numericalbrain.org/lectures/deep-learning/
研究室ホームページ 「講義関連」
「ニューラルネット概論」
これがニューラルネットだ!
z
i
(
l)
= f
⎛
⎝
#
j
w
ij
(
l−1)
z
(
j
l−1)
⎞
⎠
これがニューラルネットだ!
層
l-1のニューロンjの活動
結合重み (実数)
活性化関数
(正の実数)
f (x) =
1
1 +
e
−x
f (x) =
!
x x > 0
0
x ≤ 0
シグモイド関数
Rectified Linear Unit (ReLU)
活性化関数
z
i
(
l)
= f
⎛
⎝
#
j
w
ij
(
l−1)
z
(
j
l−1)
⎞
⎠
これがニューラルネットだ!
層
l-1のニューロンjの活動
結合重み (実数)
活性化関数
(正の実数)
順伝播 (Forward Propagation)
入力層のニューロンを活性化し、
出力層に向かって順番に活動を
伝播させる
逆伝播 (Backward Propagation)
出力層のニューロンにある値を
持たせ、入力層に向かって順番
にその値を伝播させる
(もしくはバックプロパゲーション)
「ニューラルネット概論」
以上。これくらい単純で無いとブームは来ない。
実装の仕方
色んな実装の仕方がある
「doubleと配列を生で使う」から「抽象化の鬼」まで
雛形 (skel.c) を用意しました (資料の付録A)
typedef struct { Layer *layer; Connection *connection; sfmt_t rng; int n; } Network;typedef struct { Neuron *z; int n; } Layer;
typedef struct { Weight *w; int n_pre; int n_post; } Connection;
データ構造
typedef double Neuron, Weight;
ネットワークの作り方
void createNetwork ( Network *network, const int number_of_layers, const sfmt_t rng ) {
network -> layer = ( Layer * ) malloc ( number_of_layers * sizeof ( Layer ) );
network -> connection = ( Connection * ) malloc ( number_of_layers * sizeof ( Connection ) ); network -> n = number_of_layers;
network -> rng = rng; }
void deleteNetwork ( Network *network ) {
free ( network -> layer ); free ( network -> connection ); }
層の作り方
void createLayer ( Network *network, const int layer_id, const int number_of_neurons ) {
Layer *layer = &network -> layer [ layer_id ]; layer -> n = number_of_neurons;
layer -> z = ( Neuron * ) malloc ( number_of_neurons * sizeof ( Neuron ) ); for ( int i = 0; i < layer -> n; i++) { layer -> z [ i ] = 0.; } }
void deleteLayer ( Network *network, const int layer_id ) {
Layer *layer = &network -> layer [ layer_id ]; free ( layer -> z );
}
層の作り方
void createLayer ( Network *network, const int layer_id, const int number_of_neurons ) {
Layer *layer = &network -> layer [ layer_id ]; layer -> n = number_of_neurons;
layer -> z = ( Neuron * ) malloc ( number_of_neurons * sizeof ( Neuron ) ); for ( int i = 0; i < layer -> n; i++) { layer -> z [ i ] = 0.; } }
void deleteLayer ( Network *network, const int layer_id ) {
Layer *layer = &network -> layer [ layer_id ]; free ( layer -> z );
}
void createLayer ( Network *network, const int layer_id, const int number_of_neurons ) {
Layer *layer = &network -> layer [ layer_id ]; layer -> n = number_of_neurons;
int bias = ( layer_id < network -> n - 1 ) ? 1 : 0; // 出力層以外はバイアスを用意 layer -> z = ( Neuron * ) malloc ( ( number_of_neurons + bias ) * sizeof ( Neuron ) ); for ( int i = 0; i < layer -> n; i++) { layer -> z [ i ] = 0.; }
if ( bias ) { layer -> z [ layer -> n ] = +1.; } // バイアス初期化 }
結合の作り方
void createConnection ( Network *network, const int layer_id, double ( *func ) ( Network *, const int, const int ) )
{
Connection *connection = &network -> connection [ layer_id ]; const int n_pre = network -> layer [ layer_id ] . n + 1; // +1はバイアスの分
const int n_post = ( layer_id == network -> n - 1 ) ? 1 : network -> layer [ layer_id + 1 ] . n; connection -> w = ( Weight * ) malloc ( n_pre * n_post * sizeof ( Weight ) );
for ( int i = 0; i < n_post; i++ ) { for ( int j = 0; j < n_pre; j++ ) {
connection -> w [ j + n_pre * i ] = func ( network, i, j ); }
}
connection -> n_pre = n_pre; connection -> n_post = n_post; }
void deleteConnection ( Network *network, const int layer_id ) {
Connection *connection = &network -> connection [ layer_id ]; free ( connection -> w );
}
後はmain()を作る
double all_to_all ( Network *n, const int i, const int j ) { return 1.; } int main ( void )
{ sfmt_t rng;
sfmt_init_gen_rand ( &rng, getpid ( ) ); // 作る Network network; createNetwork ( &network, 3, rng ); createLayer ( &network, 0, 2 ); createLayer ( &network, 1, 2 ); createLayer ( &network, 2, 1 );
createConnection ( &network, 0, all_to_all ); createConnection ( &network, 1, all_to_all ); // 消す deleteConnection ( &network, 1 ); deleteConnection ( &network, 0 ); deleteLayer ( &network, 2 ); deleteLayer ( &network, 1 ); deleteLayer ( &network, 0 ); deleteNetwork ( &network ); return 0; }
これで一応動く
順伝播のさせ方
void forwardPropagation ( Network *network, double ( *activation ) ( double ) ) {
for ( int i = 0; i < network -> n - 1; i++ ) { Layer *l_pre = &network -> layer [ i ]; Layer *l_post = &network -> layer [ i + 1 ]; Connection *c = &network -> connection [ i ]; for ( int j = 0; j < c -> n_post; j++ ) { Neuron u = 0.;
for ( int k = 0; k < c -> n_pre; k++ ) {
u += ( c -> w [ k + c -> n_pre * j ] ) * ( l_pre -> z [ k ] ); } l_post -> z [ j ] = activation ( u ); } } }
double sigmoid ( double x ) { return 1. / ( 1. + exp ( - x ) ); } :
:
Neuron x [] = { 0., 1. }; setInput ( &network, x );
forwardPropagation ( &network, sigmoid ); :
void setInput ( Network *network, Neuron x [ ] ) {
Layer *input_layer = &network -> layer [ 0 ]; for ( int i = 0; i < input_layer -> n; i++ ) { input_layer -> z [ i ] = x [ i ]; } }
z
(l) i= f
⎛
⎝
#
jw
(l−1) ijz
(l−1) j⎞
⎠
動かしてみる
[ya000836@purple99 ~/dl]$ gcc -O3 -std=c99 -Wall -I SFMT-src-1.5.1 -D SFMT_MEXP=19937 -o skel skel.c SFMT-src-1.5.1/SFMT.c
[ya000836@purple99 ~/dl]$ ./skel [ya000836@purple99 ~/dl]$
レポート課題 1
ネットワークの状態を出力する関数dumpを作成せよ。
void dump ( Network * );フォーマットは自由だが、少なくとも
・層の総数
・各層のニューロン数
・各層間の結合重み
・各層のニューロンの活動
は出力すること。
注: コードとMakefileは ya000836/dl/codeにある
以上
質問がなければCEDへGO!
2018年度 MI/CS実験第二
山
匡 (MIコース)
「作ってわかる深層学習」
2回目スライド資料
スケジュール
第1回 (10/03): ガイダンス、ニューラルネット概論と肩慣らし
第2回 (10/08): 誤差逆伝播のミニ講義と実装
第3回 (10/10): 手書き文字認識のテスト
第4回 (10/15): 自己符号化器のミニ講義と実装
第5回 (10/17): 実装
第6回 (10/22): 実装
第7回 (10/24): 実装
パーセプトロン
パーセプトロン
Rosenblat (1958)パーセプトロン
Rosenblat (1958)世界で最初のニューラルネット
第一次ブームの火付け役
3層構造 (入力・中間・出力)
教師付学習機械
教師信号
パーセプトロンの特徴
中間層に多数のニューロン
入力層∼中間層の結合は
スパースかつランダムで
固定
→ (教師付)学習
教師信号
中間層∼出力層の結合重みを
教師信号を使って変化させる
教師信号
教師付学習
文脈信号 (
x) と教師信号 (z)のペア
n種類
x
(i)を入力層に入れると
z
(i)
を出力
するように重みを変える
→ (教師付)学習
誤差
を定義して、これを減らす
E =
!
n
1
2
"
"z
(teacher)
n
− z
(output)
n
"
"
2
D = {(x
(1)
, z
(1)
)
, · · · , (x
(
n)
, z
(
n)
)
}
ベクトル
学習ルール (パーセプトロン則)
E =
!
n
1
2
"
"z
(teacher)
n
− z
(output)
n
"
"
2
誤差
dw
(hidden)
dt
= −
∂E
∂w
(hidden)
計算する式
w
(hidden)
(
t + 1) = w
(hidden)
(
t) − η
∂E
∂w
(hidden)
= w
(hidden)
(
t) − η∇E
w
(hidden)エポック数に
関して離散化
ベクトル
学習係数
w
(hidden)
(
t + 1) = w
(hidden)
(
t) − η
!
n
∂E
n
∂w
(hidden)
= w
(hidden)
(
t) − η
!
n
∇E
n,w
(hidden)個々のサンプル
に関して書くと
E =
!
n
E
n
E
n=
1
2
!
i"
z
(teacher)n i− z
(output)n i#
2学習ルール (パーセプトロン則)
w
(hidden)
(
t + 1) = w
(hidden)
(
t) − η
!
n
∂E
n
∂w
(hidden)
= w
(hidden)
(
t) − η
!
n
∇E
n,w
(hidden)個々のサンプル
に関して書くと
E =
!
n
E
n
def⎛
⎜
⎜
⎝
∂En ∂w0..
.
∂En ∂wl−1⎞
⎟
⎟
⎠
中間層∼出力層の結合一つ一つ
よって、任意の
w
ij
(hidden)
に対して、
を計算
∂E
n
∂w
ij
(hidden)
すれば良い。
∂E
n
∂w
ij
(hidden)
の計算
∂E
n
∂w
(hidden)
ij
=
∂E
n
∂z
(output)
n
i
∂z
(output)
n
i
∂w
(hidden)
ij
E
n=
1
2
!
i"
z
(teacher)n i− z
(output)n i#
2∂E
n∂z
(output)n i= −
!
z
(teacher)n i− z
(output)n i"
z
(output)n i= f
⎛
⎝
#
jw
(hidden) ijz
(hidden)n j⎞
⎠
∂z (output)n i ∂w(hidden) ij = f′ ⎛ ⎝# j w(hidden) ij z (hidden)n j ⎞ ⎠ z(hidden)n jf (x) =
1
1 +
e
−x
f
′
(
x) = f (x) (1 − f (x))
まとめると
∂E
n∂w
hidden ij= −
!
z
(teacher)n i− z
(output)n i"
z
(output)n i!
1
− z
i(output)n"
z
(hidden)n jw
(hidden) ij(
t + 1) = w
(hidden) ij(
t) − η
!
n∂E
n∂w
(hidden) ij= w
(hidden) ij(
t) + η
!
n"
z
(teacher)n i− z
(output)i n#
z
(output)n i"
1
− z
(output)n i#
z
(hidden)n jとなるので、更新式は
となる。
勾配降下法
という名前がついている
コード
Layer *output_layer = &network -> layer [ network -> n - 1 ]; Layer *hidden_layer = &network -> layer [ network -> n - 2 ]; Connection *c = &network -> connection [ network -> n - 2 ]; for ( int i = 0; i < c -> n_post; i++ ) {
for ( int j = 0; j < c -> n_pre; j++ ) { double o = output_layer -> z [ i ];
double d = ( z [ i ] - o ) * diff_activation ( o );
c -> dw [ j + c -> n_pre * i ] += Eta * d * ( hidden_layer -> z [ j ] ); } }
w
(hidden) ij(
t + 1) = w
ij(hidden)(
t) − η
!
n∂E
n∂w
(hidden) ij= w
(hidden) ij(
t) + η
!
n"
z
(teacher)n i− z
(output) n i#
z
(output)n i"
1
− z
(output)n i#
z
(hidden)n jdouble updateByPerceptronRule ( Network *network, Neuron z [ ],
( double ) ( *diff_activation ) ( double ) );
質問は?
例: XORの学習
x
1x
0z
0
0
0
0
1
1
1
0
1
1
1
0
1個
128個
2個
Network network; createNetwork ( &network, 3, rng ); createLayer ( &network, 0, 2 ); createLayer ( &network, 1, 128 ); createLayer ( &network, 2, 1 );createConnection ( &network, 0, sparse_random ); createConnection ( &network, 1, uniform_random );
double uniform_random ( Network *n, const int i, const int j ) {
return 1. - 2. * sfmt_genrand_real2 ( &n -> rng ); }
double sparse_random ( Network *n, const int i, const int j ) {
return ( sfmt_genrand_real2 ( &n -> rng ) < 0.5 ) ? uniform_random ( n, i, j ) : 0.;
}
コード
void createConnection ( Network *network, const int layer_id, double ( *func ) ( Network *, const int, const int ) )
{
Connection *connection = &network -> connection [ layer_id ];
const int n_pre = network -> layer [ layer_id ] . n + 1; // +1はバイアスの分 const int n_post = ( layer_id == network -> n - 1 ) ? 1 : network -> layer [ layer_id + 1 ] . n;
connection -> w = ( Weight * ) malloc ( n_pre * n_post * sizeof ( Weight ) ); for ( int i = 0; i < n_post; i++ ) {
for ( int j = 0; j < n_pre; j++ ) {
connection -> w [ j + n_pre * i ] = func ( network, i, j ); }
}
connection -> dw = ( Weight * ) malloc ( n_pre * n_post * sizeof ( Weight ) ); for ( int i = 0; i < n_post; i++ ) {
for ( int j = 0; j < n_pre; j++ ) { connection -> dw [ j + n_pre * i ] = 0.; }
}
connection -> n_pre = n_pre; connection -> n_post = n_post; }
void deleteConnection ( Network *network, const int layer_id ) {
Connection *connection = &network -> connection [ layer_id ]; free ( connection -> w );
free ( connection -> dw ); }
Neuron x [ 4 ][ 2 ] = { { 0., 0. }, { 0., 1. }, { 1., 0. }, { 1., 1. } }; Neuron z [ 4 ][ 1 ] = { { 0. } , { 1. } , { 1. } , { 0.} };
const int number_of_training_data = 4; // Training
double error = 1.0; // arbitrary large number const double Epsilon = 0.001; // tolerant error rate int i = 0;
while ( error > Epsilon ) { error = 0.;
initializeDW ( &network );
for ( int j = 0; j < number_of_training_data; j++ ) {
//int k = ( int ) ( number_of_training_data * sfmt_genrand_real2 ( &rng ) );
int k = j;
setInput ( &network, x [ k ] ); forwardPropagation ( &network, sigmoid );
error += updateByPerceptronRule ( &network, z [ k ], diff_sigmoid ); }
updateW ( &network ); printf ( "%d %f\n", i, error ); i++;
}
fprintf ( stderr, "# of epochs = %d\n", i );
double diff_sigmoid ( double z ) { return z * ( 1. - z ); }
// Test
Layer *output_layer = &network . layer [ network. n - 1 ]; const int n = output_layer -> n;
for ( int i = 0; i < number_of_training_data; i++ ) { setInput ( &network, x [ i ] );
forwardPropagation ( &network, sigmoid ); for ( int j = 0; j < n; j++ ) {
fprintf ( stderr, "%f%s", output_layer -> z [ j ], ( j == n - 1 ) ? "\n" : " " );
} }
void initializeDW ( Network *network ) {
Connection *c = &network -> connection [ network -> n - 2 ]; for ( int i = 0; i < c -> n_post; i++ ) {
for ( int j = 0; j < c -> n_pre; j++ ) { c -> dw [ j + c -> n_pre * i ] = 0.; }
} }
void updateW ( Network *network ) {
Connection *c = &network -> connection [ network -> n - 2 ]; for ( int i = 0; i < c -> n_post; i++ ) {
for ( int j = 0; j < c -> n_pre; j++ ) {
c -> w [ j + c -> n_pre * i ] += c -> dw [ j + c -> n_pre * i ]; }
} }
コンパイルして実行
[ya000836@purple99 ~/dl]$ gcc -O3 -std=c99 -Wall -I SFMT-src-1.5.1 -D SFMT_MEXP=19937 -o perceptron perceptron.c SFMT-src-1.5.1/SFMT.c [ya000836@purple99 ~/dl]$ ./perceptron 0 0.879170 1 0.836272 2 0.766234 3 0.658244 4 0.543239 : 4302001 0.001000 4302002 0.001000 4302003 0.001000 4302004 0.001000 4302005 0.001000 # of epochs = 4302006 0.022023 0.977739 0.977538 0.022691
}
学習結果
}
エポック番号と誤差
誤差の減り方を見る
[ya000836@purple99 ~/dl]$ ./perceptron > perceptron.dat 0.022023 0.977739 0.977538 0.022691 [ya000836@purple99 ~/dl]$ gnuplot G N U P L O T
Version 4.6 patchlevel 6 last modified September 2014 Build System: Darwin x86_64
Copyright (C) 1986-1993, 1998, 2004, 2007-2014 Thomas Williams, Colin Kelley and many others gnuplot home: http://www.gnuplot.info faq, bugs, etc: type "help FAQ"
immediate help: type "help" (plot window: hit 'h') Terminal type set to 'x11'
gnuplot> plot 'perceptron.dat'
ボーナス課題 1
2層のパーセプトロンでXORは学習できるか?
単純パーセプトロン
学習できる→具体的にネットワークを呈示せよ
学習できない→なぜなのか理由を考察せよ
ヒント: 「線形分離」についてググれ
色々試すこと
・中間層のニューロン数を変える
・収束までのエポック数を調べる
多層パーセプトロン
多層パーセプトロン
XORのためには中間層のニューロンは何個必要か?
2個で必要十分
パーセプトロンの問題
中間層にたくさんのニューロンが必要
→ 入力信号を区別可能にするため
解決策:
入力層∼中間層の結合も変化させる
→誤差逆伝播
誤差逆伝播
w
i j
(l −1)
(t + 1) = w
i j
(l −1)
(t) − η
!
n
∂E
n
∂w
i j
(l −1)
∂E
n
∂w
i j
(l −1)
=
∂E
n
∂z
i
(l )
∂z
(l )
i
∂w
i j
(l −1)
層
l-1から層lへの結合の重みの更新式は同様に
上の層で決まる値
∂z
i(l )∂w
i j(l −1)= z
(l )i!
1 − z
(l )i"
z
(l −1) j誤差逆伝播
∂E
n∂z
(l ) i= −
!
z
(teacher)n i−
z
(l ) i"
層
lが出力層の場合はパーセプトロン則と同じで
∂E
n
∂z
(l )
i
=
!
k
∂E
n
∂z
(l +1)
k
∂z
(l +1)
k
∂z
(l )
i
それ以外の場合は
さらに上の層で決まる値
誤差逆伝播
上の層から下の層に向かって順番に計算すれば、
∂E
n
∂z
(l +1)
k
は計算済み (出力層の場合は
∂En ∂z(l ) i = − ! z(teacher)n i −z (l ) i ")
∂E
n
∂z
(l )
i
=
!
k
∂E
n
∂z
(l +1)
k
∂z
(l +1)
k
∂z
(l )
i
(再掲)
z(l+1) k = f ! " i w(l) kiz (l) i #∂z
(l +1)
k
∂z
(l )
i
= f
′
!
"
i
w
(l )
ki
z
(l )
i
#
w
(l )
ki
= z
k
(l +1)
$
1 − z
(l +1)
k
%
w
(l )
ki
ここで
かつ
fはシグモイド
誤差逆伝播
w
i j(l −1)(t + 1) = w
i j(l −1)(t) + η
!
n⎛
⎝
∂E
n∂z
(l )i⎞
⎠
&
z
i(l )&
1 − z
(l )i'
z
j(l −1)'
∂E
n∂z
(l ) i=
⎧
⎨
⎩
$
z
(teacher)n i−
z
(l ) i%
l
is output layer
&
k ∂En ∂z(l +1) kz
(l +1) k$
1 − z
(l +1)k%
w
(l ) kiotherwise
まとめると、
とってもきれいな式が導出できる
「デルタ」という名前がついている
質問は?
何層あってもこの方法は使える→多層パーセプトロン
コード
関数updateByBackPropagationを実装しよう
typedef double Neuron, Delta, Weight;
typedef struct { Neuron *z; Delta *delta; int n; } Layer;
void createLayer ( Network *network, const int layer_id, const int number_of_neurons )
{ :
layer -> delta = ( Delta * ) malloc ( ( number_of_neurons + bias ) * sizeof ( Delta ) );
for ( int i = 0; i < layer -> n; i++) { layer -> delta [ i ] = 0.; } if ( bias ) { layer -> delta [ layer -> n ] = 0.; } // バイアス初期化 }
1. デルタの定義を追加
void deleteLayer ( Network *network, const int layer_id ) {
:
free ( layer -> delta ); }
2. デルタの確保と初期化
コード
あとはperceptron.cの
updateByPerceptronRuleを
updateByBackPropagationに
変更する
レポート課題 2
色々試すこと
・中間層のニューロン数を変える (中間層2個でいいか?)
・収束までのエポック数を調べる
updateByBackPropagationを実装して上記を試せ
コードの雛形はdl/code/bp.cにある
手書き文字認識
XORはあまりにもtoy problemなので、もう少しましな何か
手書き文字認識
MNISTデータベース
・手書き文字画像のデータベース+ラベル付き
・画像は28x28ピクセル256階調グレースケール
・ラベルは 0 から 9 の数字 (そりゃそうだ)
詳しくは付録B
・60000個のトレーニングデータ
・10000個のテストデータ
必ずデータを分ける
データの読み書き
code/mnist.{c, h}を用意した。
code/mnist_test.cをまず試す。
code/mnist/以下にデータがあるのでフォルダ毎コピー。
make mnist_test
mkdir png
./mnist_test
./pngの下にpngファイルが作成される。
手書き文字のテスト
レポート課題 3
誤差逆伝播法を実装した3層のパーセプトロンで、
手書き文字の学習と認識を行え。
やり方はテキストを参照。
以下では多少補足を。
バッチ学習の問題点
E =
!
n
1
2
"
"z
(teacher)
n
− z
(output)
n
"
"
2
誤差 (再掲)
全トレーニングデータに関する和
XORのときは
n=4だった。
MNISTでは
n=60000。←計算が終わらない
学習が局所解に落ちる可能性が高い。
勾配降下法の性質
「最も良い解」はすごく少ない。
「そこそこ良い解」はたくさんある。
勾配降下法は初期値に依存するので、「最も良い解」
の近くから始めない限り「そこそこ良い解」に捕まる。
確率的勾配法とミニバッチ
確率的に勾配を降りることで、「そこそこ良い解」から
脱出できるようにする。
× 全トレーニングデータを使う
◎
トレーニングデータからいくつかをランダムに選んで使う
(バッチ)
(ミニバッチ)
エポック毎にミニバッチを作り直す
ミニバッチのサイズをどうするかに依存
極端な例: サイズ 1 (1データ毎に重みを更新)
ミニバッチへの布石がここに…
while ( error > Epsilon ) { error = 0.;
initializeDW ( &network );
for ( int j = 0; j < number_of_training_data; j++ ) {
//int k = ( int ) ( number_of_training_data * sfmt_genrand_real2 ( &rng ) );
int k = j;
setInput ( &network, x [ k ] ); forwardPropagation ( &network, sigmoid );
error += updateByBackPropagation ( &network, z [ k ], diff_sigmoid ); }
:
while ( error > Epsilon ) { error = 0.;
initializeDW ( &network );
for ( int j = 0; j < MINI_BATCH_SIZE; j++ ) {
int k = ( int ) ( number_of_training_data * sfmt_genrand_real2 ( &rng ) );
setInput ( &network, x [ k ] ); forwardPropagation ( &network, sigmoid );
error += updateByBackPropagation ( &network, z [ k ], diff_sigmoid ); }
手書き文字のテスト
レポート課題 3
誤差逆伝播法を実装した3層のパーセプトロンで、
手書き文字の学習と認識を行え。
やり方はテキストを参照。
サイズ1のミニバッチでとりあえずやる。その後で
ミニバッチのサイズを色々変えてみる。
手書き文字のテスト
ボーナス課題 2
中間層のニューロンは何を表現しているか?
・入力層∼中間層の結合を可視化する。
・mnist_generate_png関数を参考にすると良い。
質問は?
(参考) バイアスとは何か
教科書的には
閾値
積和と和の計算が必要
2種類の更新式が必要
d θ
(l )d t
= −
∂E
∂θ
(l )d w
(l )d t
= −
∂E
∂w
(l )バイアスからの重みと考える
z
i
(l )
= f
⎛
⎝
#
j
w
i j
(l −1)
z
(l −1)
j
− θ
(l )
i
⎞
⎠
2018年度 MI/CS実験第二
山
匡 (MIコース)
「作ってわかる深層学習」
3回目スライド資料
スケジュール
第1回 (10/03): ガイダンス、ニューラルネット概論と肩慣らし
第2回 (10/08): 誤差逆伝播のミニ講義と実装
第3回 (10/10): 手書き文字認識のテスト
第4回 (10/15): 自己符号化器のミニ講義と実装
第5回 (10/17): 実装
第6回 (10/22): 実装
第7回 (10/24): 実装
自己符号化器
自己符号化器 (オートエンコーダ)
符号化
復号化
https://www.slideshare.net/ginrou799/ss-46355460 「フーリエ変換と画像圧縮」武田祐一例: 画像圧縮
画像をうまく表現する基底を構成する
深層化と勾配消失問題
符号化
復号化
f
(x) =
1
1 + e
−
x
f
′
(x) = f (x) (1 − f (x))
< 1/4
逆伝播する度にデルタの値が指数的に小さくなる
深層化と勾配消失問題
ReLUはその点は性質がいい → 一気に普及
深層化と勾配消失問題
f
(x) =
!
x
x >
0
0
x ≤
0
f
′
(x) =
!
1
x >
0
0
x ≤
0
汎化と過学習
積層自己符号化器
あとはこれを繰り返せばいくらでも深くできる
コーディング
void copyConnection ( Network *network_src, int layer_id_src, Network *network_dst, int layer_id_dst )
void copyConnectionWithTranspose ( Network *network_src, int layer_id_src, Network *network_dst, int layer_id_dst );
double updateByBackPropagationPartial ( Network *network, Neuron z [ ], ( double ) ( *diff_activation ) ( double ) );
3つの関数を作成する
Network network; createNetwork ( &network1, 3, rng );createLayer ( &network1, 0, MNIST_IMAGE_SIZE ); createLayer ( &network1, 1, 128 );
createLayer ( &network1, 2, MNIST_IMAGE_SIZE ); createConnection ( &network1, 0, uniform_random ); createConnection ( &network1, 1, uniform_random ); copyConnectionWithTranspose ( &network, 0, &network, 1 );
// Training
for ( int i = 0; i < MNIST_TRAINING_DATA_SIZE; i++ ) { initializeDW ( &network1 );
double error = 0.;
copyConnectionWithTranspose ( &network, 0, &network, 1 );
for ( int j = 0; j < 1; j++ ) {
//int k = ( int ) ( MNIST_TRAINING_DATA_SIZE * sfmt_genrand_real2 ( &rng ) );
int k = i; // Use all training data one by one sequentially setInput ( &network1, training_image [ k ] );
forwardPropagation ( &network1, sigmoid );
error += updateByBackPropagationPartial ( &network1, network1 . layer [ 0 ] . z, diff_sigmoid ); } updateW ( &network1 ); if (i % 100 == 0 ) { printf ( "%d %f\n", i, error ); } }
コーディング
Network network2; createNetwork ( &network2, 4, rng );createLayer ( &network2, 0, MNIST_IMAGE_SIZE ); createLayer ( &network2, 1, 128 );
createLayer ( &network2, 2, 64 ); createLayer ( &network2, 3, 128 );
createConnection ( &network2, 0, uniform_random ); createConnection ( &network2, 1, uniform_random ); createConnection ( &network2, 2, uniform_random ); copyConnection ( &network1, 0, &network2, 0 );
copyConnectionWithTranspose ( &network2, 1, &network2, 2 ); deleteConnection ( &network1, 1 ); deleteConnection ( &network1, 0 ); deleteLayer ( &network1, 2 ); deleteLayer ( &network1, 1 ); deleteLayer ( &network1, 0 ); deleteNetwork ( &network1 );
コーディング
// Trainingfor ( int i = 0; i < MNIST_TRAINING_DATA_SIZE; i++ ) { initializeDW ( &network2 );
double error = 0.;
copyConnectionWithTranspose ( &network2, 1, &network2, 2 ); for ( int j = 0; j < 1; j++ ) {
//int k = ( int ) ( MNIST_TRAINING_DATA_SIZE * sfmt_genrand_real2 ( &rng ) );
int k = i; // Use all training data one by one sequentially setInput ( &network2, training_image [ k ] );
forwardPropagation ( &network2, sigmoid );
error += updateByBackPropagationPartial ( &network2, network2 . layer [ 1 ] . z, diff_sigmoid ); } updateW ( &network2 ); if (i % 100 == 0 ) { printf ( "%d %f\n", i, error ); } }