numpyを使った特殊な行列演算
はじめに
chainerで新しいconnectionのファイルを作成する際、Numpyの演算で行き詰まった。そこで、Numpyの特殊な行列演算を実行する際にどうするか検討してみた。
問題設定
Figure 1のように3次元(0次元目〜2次元目)行列2つの行列演算を考える。 図左上が(a,b,c)行列、図右上が(a,c,d)行列。0次元目が一致する(b,c)と(c,d)のドット積を求め、図下(a,b,d)の3次元行列にしたい。
通常のdot()で計算すると4次元行列が生成される。
>>> a = np.arange(24).reshape(2,3,4) >>> a array([[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]], [[12, 13, 14, 15], [16, 17, 18, 19], [20, 21, 22, 23]]]) >>> b = np.arange(40).reshape(2,4,5) >>> b array([[[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19]], [[20, 21, 22, 23, 24], [25, 26, 27, 28, 29], [30, 31, 32, 33, 34], [35, 36, 37, 38, 39]]]) >>> a.dot(b) array([[[[ 70, 76, 82, 88, 94], [ 190, 196, 202, 208, 214]], [[ 190, 212, 234, 256, 278], [ 630, 652, 674, 696, 718]], [[ 310, 348, 386, 424, 462], [1070, 1108, 1146, 1184, 1222]]], [[[ 430, 484, 538, 592, 646], [1510, 1564, 1618, 1672, 1726]], [[ 550, 620, 690, 760, 830], [1950, 2020, 2090, 2160, 2230]], [[ 670, 756, 842, 928, 1014], [2390, 2476, 2562, 2648, 2734]]]])
求めたいのは(a,b,d)の3次元行列。そこで例えばfor文を使ってみる。
>>> c = np.zeros(30).reshape(2,3,5) >>> c array([[[ 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0.]], [[ 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0.]]]) >>> for i in range(2): ... c[i] = a[i].dot(b[i]) ... >>> c array([[[ 70., 76., 82., 88., 94.], [ 190., 212., 234., 256., 278.], [ 310., 348., 386., 424., 462.]], [[ 1510., 1564., 1618., 1672., 1726.], [ 1950., 2020., 2090., 2160., 2230.], [ 2390., 2476., 2562., 2648., 2734.]]])
確かにうまくいってる。
Numpyの行列演算を復習する(1)
Deep Learningで新しいモデルを作ってる時に、Numpyの行列演算をよく理解してないがために苦労した。
そこで改めてNumpyの演算をおさらいしてみる。参照する本はO'REILLYのWes. McKinney著、小林他訳「Pyhtonによるデータ分析入門」。 www.amazon.co.jp この本の4章、12章を参考にしました。あまりに基本的な部分は読み飛ばします。
Numpy ndarrayの型について
>>>a = np.arange(5) >>>a.dtype dtype('int64')
このようにndarrayを生成した場合、通常はint64になる。そこでchainerなどでよく使うfloat32に変えてみる。
>>> a = np.arange(5) >>> a.dtype dtype('int64') >>> a.dtype = 'float32' >>> a.dtype dtype('float32')
しかしここで
>>> a array([ 0.00000000e+00, 0.00000000e+00, 1.40129846e-45, 0.00000000e+00, 2.80259693e-45, 0.00000000e+00, 4.20389539e-45, 0.00000000e+00, 5.60519386e-45, 0.00000000e+00], dtype=float32)
とすると変なことになった。確かにdtypeはfloat32に変換されてるが、要素数が増えて、しかも値が変なことになってる。こんな時はastypeを使うといいみたい。
>>> a = np.arange(5) >>> a.dtype dtype('int64') >>> b.astype(np.float32) array([ 0., 1., 2., 3., 4.], dtype=float32)
ちゃんとdtypeが変わって、しかも元の値が維持されている。
スカラー計算
*記号は要素同士の掛け算だが、**だと要素同士のexponential。
>>> c = np.arange(6).reshape(2,-1) >>> c array([[0, 1, 2], [3, 4, 5]]) >>> d = np.arange(6).reshape(2,-1) >>> d array([[0, 1, 2], [3, 4, 5]]) >>> e = c*d >>> e array([[ 0, 1, 4], [ 9, 16, 25]]) >>> f = c**d >>> f array([[ 1, 1, 4], [ 27, 256, 3125]])
また各要素を3で割る場合
>>> g = np.arange(4).reshape(2,-1) >>> g array([[0, 1], [2, 3]]) >>> g / 3 array([[0, 0], [0, 1]])
などとする。
インデックスの参照とスライス
「:」使って切り出すと、インデックスの参照になるみたい。これは便利。知らんかった。
>>> c = np.arange(8) >>> c array([0, 1, 2, 3, 4, 5, 6, 7]) >>> d = c[3:6] >>> d array([3, 4, 5]) >>> d[0] = 0 >>> c array([0, 1, 2, 0, 4, 5, 6, 7]) >>>
逆にコピーする時はc[3:6].copy()などとする。
>>> c = np.arange(8) >>> c array([0, 1, 2, 3, 4, 5, 6, 7]) >>> d = c[3:6].copy() >>> d[0] = 0 >>> c array([0, 1, 2, 3, 4, 5, 6, 7])
2次元以上の場合も同様。
>>> d = np.arange(6).reshape(2,-1) >>> d array([[0, 1, 2], [3, 4, 5]]) >>> e = d[0] >>> e array([0, 1, 2]) >>> e[1] = 0 >>> d array([[0, 0, 2], [3, 4, 5]])
3次元配列の一部2x2を切り取ってみる。
>>> d = np.arange(12).reshape(2,3,-1) >>> d array([[[ 0, 1], [ 2, 3], [ 4, 5]], [[ 6, 7], [ 8, 9], [10, 11]]]) >>> e = d[1:,:2] >>> e array([[[6, 7], [8, 9]]]) >>> f = d[:,:2,1:] >>> f array([[[1], [3]], [[7], [9]]])
配列の結合
2次元配列の場合、軸0が行、軸1が列方向となる。この軸番号を指定することで、その軸に沿った結合ができる。
>>> e = np.arange(9).reshape(3,-1) >>> e array([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) >>> g = np.arange(9).reshape(e.shape) >>> g array([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) >>> h = np.concatenate([e,g], axis = 0) >>> h array([[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 1, 2], [3, 4, 5], [6, 7, 8]]) >>> i = np.concatenate([e,g], axis = 1) >>> i array([[0, 1, 2, 0, 1, 2], [3, 4, 5, 3, 4, 5], [6, 7, 8, 6, 7, 8]])
もしくはvstackやhstackも使える。
>>> h = np.vstack((e,g)) >>> h array([[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 1, 2], [3, 4, 5], [6, 7, 8]]) >>> i = np.hstack((e,g)) >>> i array([[0, 1, 2, 0, 1, 2], [3, 4, 5, 3, 4, 5], [6, 7, 8, 6, 7, 8]])
配列の分割
次がchainerいじる際に何かと問題になった分割(split)関数。まず2次元配列を分割してみる。
>>> from numpy.random import randn >>> a = randn(4,2) >>> a array([[ 0.79958787, 0.98382543], [ 0.5307794 , 2.15389327], [ 2.1209991 , -2.5880856 ], [ 0.74934568, -1.18431146]]) >>> b,c = np.split(a,2) >>> b array([[ 0.79958787, 0.98382543], [ 0.5307794 , 2.15389327]]) >>> c array([[ 2.1209991 , -2.5880856 ], [ 0.74934568, -1.18431146]])
要素の繰り返し
要素を繰り返す便利な関数もあるらしい。
>>> d = np.arange(4) >>> d array([0, 1, 2, 3]) >>> d.repeat([2,1,2,3]) array([0, 0, 1, 2, 2, 3, 3, 3])
ここでは0番目の要素を2回、1番目の要素を1回、2番目の要素を2回、3番目の要素を3回繰り返している。次に3次元配列に対して2次元方向に増やしてみる。
>>> c array([[ 2.1209991 , -2.5880856 ], [ 0.74934568, -1.18431146]]) >>> f = c.repeat(2,axis = 1) >>> f array([[ 2.1209991 , 2.1209991 , -2.5880856 , -2.5880856 ], [ 0.74934568, 0.74934568, -1.18431146, -1.18431146]]) >>> g = c.repeat(2,axis = 0) >>> g array([[ 2.1209991 , -2.5880856 ], [ 2.1209991 , -2.5880856 ], [ 0.74934568, -1.18431146], [ 0.74934568, -1.18431146]])
repeat関数は繰り返す次元において、その要素ごとに繰り返していくが、tile関数はその次元の要素を1つの塊(tile)と考え、その塊を繰り返す。
>>> c array([[ 2.1209991 , -2.5880856 ], [ 0.74934568, -1.18431146]]) >>> h = np.tile(c,(2,2)) >>> h array([[ 2.1209991 , -2.5880856 , 2.1209991 , -2.5880856 ], [ 0.74934568, -1.18431146, 0.74934568, -1.18431146], [ 2.1209991 , -2.5880856 , 2.1209991 , -2.5880856 ], [ 0.74934568, -1.18431146, 0.74934568, -1.18431146]])
takeやputを使った参照
まず通常のファンシーインデックス参照。
>>> a = np.arange(10) * 10 >>> a array([ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90]) >>> b = [4,2,1,7] >>> a[b] array([40, 20, 10, 70])
これをtakeを使うと
>>> a.take(b) array([40, 20, 10, 70])
次にputを使って値を入れる。
>>> a.put(b,0) >>> a array([ 0, 0, 0, 30, 0, 50, 60, 0, 80, 90])
CNNでシマリスとモルモットとフクロモモンガを区別する
CNN(convolutional neural network)を使って、うちで飼ってるシマリスとモルモットとフクロモモンガを区別する。
imageNetとかから勝手に画像を引っ張ってきて学習させて下さい。画像は227X227のJPEGにして下さい。
開発環境はxcode6でC++。jpeglibというjpegを配列に変換するライブラリを使ってます。パラメータを勝手に変えて下さい。
main.cpp
// // main.cpp // CNN06_01 // // Created by 大政 孝充 on 2015/10/04. // Copyright (c) 2015年 大政 孝充. All rights reserved. // CNN06 conv1,pool1,norm1,conv2,pool2,fc3,fc4 で学習、入力画像(訓練データ)227x227x3x300 // dropout無し、自己符号化無し、conv1,2はReL、pool1,2はmax pooling #include <iostream> #include <fstream> #include <string> #include <sstream> #include <math.h> #include <jpeglib.h> #include "jpeg_to_array.h" #include "conv1.h" #include "pool1.h" #include "norm1.h" #include "conv2.h" #include "pool2.h" #include "fc3.h" #include "fc4.h" #include "backpro_fc4.h" #include "backpro_fc3.h" #include "backpro_pool2.h" #include "backpro_conv2.h" #include "backpro_pool1.h" #include "backpro_conv1.h" #include "filter.h" //----------------------------------------------------------------- //定数の定義等 //----------------------------------------------------------------- const int SEED = 23504;//乱数のシード const double LIMIT_E = 0.001;//許容する誤差の上限 const int COUNT_LEARN = 1000;//学習回数の上限 const double EPSILON = 0.001;//学習係数 //const double EPSILON_FC2 = 0.002; const double EPSILON_CONV = 0.001; //パラメーターの定義 //const int n_data = 45; //----------------------------------------------------------------- //グローバル変数の定義 //----------------------------------------------------------------- //img int img_In[300][227][227][3];//初期データ double img_C1[16][55][55];//conv1の出力画像 double img_P1[16][27][27];//pool1の出力画像 double img_N1[16][27][27];//norm1の出力画像 double img_C2[48][13][13];//conv2の出力画像 double img_P2[48][6][6];//pool2の出力画像 double img_F3[300];//fc3の出力画像 double img_F4[3];//fc4の出力画像 //filter double fil_C1[16][11][11];//フィルタC1画像 double fil_C2[48][5][5];//フィルタC2 //重み double wei_F3[300][48][6][6];//fc3の重み double wei_F4[3][300];//fc4の重み //bias double bias_F3[300];//fc3のバイアス double bias_F4[3];//fc4のバイアス //delta double delta_C1[16][55][55];//conv1のデルタ double delta_P1[16][27][27];//pool1のデルタ double delta_C2[48][13][13];//conv2のデルタ double delta_P2[48][6][6];//pool2のデルタ double delta_F3[300];//fc3のデルタ double delta_F4[3];//fc4のデルタ //u(=Σwy+b) double u_F3[300];//fc3のu double u_F4[3];//fc4のu //共有重み double cowei_P1[16][27][27][3][3];//最大poolingとして採用されたユニットはwij、それ以外は0、順伝播中は採用されたユニットだけ1、他は0 double cowei_P2[48][6][6][3][3]; //----------------------------------------------------------------- //関数の定義 //----------------------------------------------------------------- //jpegを読み込みJSAMP配列へ格納する関数(訓練データ画像、テストデータ画像用) //void jpeg_to_array(int img_In[45][227][227], const char r_filename[][70], int number_A); //乱数の生成 double drnd(void); //全結合層fc3の重み、バイアスの初期化 void initW_F3(double wei_F3[300][48][6][6], double bias_F3[300]); //全結合層fc4の重み、バイアスの初期化 void initW_F4(double wei_F4[3][300], double bias_F4[3]); //確認用画像の出力 //void array_to_jpeg(double img_out[2][27][27]); //----------------------------------------------------------------- //メイン関数 //----------------------------------------------------------------- int main(int argc, const char * argv[]) { //以下初期化等---------------------------------------------- //jpeg_to_arrayクラスのインスタンス化 jpeg_to_array jpeg_to_array; //初期画像配列img_Inへ値を格納 jpeg_to_array.change_j_to_a(img_In); //フィルタクラスのインスタンス化 filter filter; //フィルタ画像配列の生成(臨時的措置) filter.make_filter_array(fil_C1, fil_C2); //conv1のインスタンス化 conv1 conv1; //pool1のインスタンス化 pool1 pool1; //norm1のインスタンス化 norm1 norm1; //conv2のインスタンス化 conv2 conv2; //ppool2のインスタンス化 pool2 pool2; //乱数を初期化する srand(SEED); //fc3の重み、バイアスの初期化 initW_F3(wei_F3, bias_F3); //fc2のインスタンス化 fc3 fc3; //fc4の重み、バイアスの初期化 initW_F4(wei_F4, bias_F4); //fc4のインスタンス化 fc4 fc4; //backpro_fc4のインスタンス化 backpro_fc4 backpro_fc4; //backpro_fc3のインスタンス化 backpro_fc3 backpro_fc3; //backpro_pool2のインスタンス化 backpro_pool2 backpro_pool2; //backpro_conv2のインスタンス化 backpro_conv2 backpro_conv2; //backpro_pool1のインスタンス化 backpro_pool1 backpro_pool1; //backpro_conv1のインスタンス化 backpro_conv1 backpro_conv1; //以下順伝播、および学習のルーティーン---------------------------------------- //臨時的措置 int count_l = 0;//ルーティーンの回数を制御する double target_l[300][3];//目標出力 //目標出力の値を代入 for (int i = 0; i < 100; i++) {//0〜15は丸で1,0,0 target_l[i * 3][0] = 1.0;//phalanger target_l[i * 3][1] = 0; target_l[i * 3][2] = 0; target_l[i * 3 + 1][0] = 0; target_l[i * 3 + 1][1] = 1.0;//squirrel target_l[i * 3 + 1][2] = 0; target_l[i * 3 + 2][0] = 0; target_l[i * 3 + 2][1] = 0; target_l[i * 3 + 2][2] = 1.0;//cavia } while (count_l < COUNT_LEARN) { //訓練データ300回分ルーティーン for (int k = 0; k < 300; k++) { //以下順伝播--------------------------------------------------------- //conv1の順伝播 conv1.num_trai = k;//訓練データの番号 for (int i = 0; i < 16; i++) { conv1.num_nue = i; conv1.convolution1(img_In, fil_C1, img_C1); } //pool1の順伝播 for (int i = 0; i < 16; i++) { pool1.num_nue = i; pool1.pooling1(img_C1, img_P1, cowei_P1); } //norm1の順伝播 norm1.normalization(img_P1, img_N1); //conv2の順伝播 for (int i = 0; i < 16; i++) { conv2.num_nue = i; conv2.convolution2(img_N1, fil_C2, img_C2); } //pool2の順伝播 for (int i = 0; i < 48; i++) { pool2.num_nue = i; pool2.pooling2(img_C2, img_P2, cowei_P2); } //初期化、または修正したバイアスをfc3へ送る for (int i = 0; i < 300; i++) { fc3.bias_F3[i] = bias_F3[i]; } //fc3の順伝播 fc3.full_connection3(img_P2, img_F3, u_F3, wei_F3); //初期化、または修正したバイアスをfc4へ送る for (int i = 0; i < 3; i++) { fc4.bias_F4[i] = bias_F4[i]; } //fc4の順伝播 fc4.full_conection4(img_F3, img_F4, wei_F4); //確認用出力 for (int i = 0; i < 3; i++) { std::cout << "カテゴリー " << i << "の尤度は" << img_F4[i] << std::endl; } //二乗誤差 double en = 0; for (int i = 0; i < 3; i++) { en += 0.5 * (img_F4[i] - target_l[k][i]) * (img_F4[i] - target_l[k][i]); } if (en < LIMIT_E) { //while文からも抜ける処理 count_l = COUNT_LEARN; break;//二乗誤差が上限を下回れば終了 } //以下学習処理--------------------------------------------------------- //fc3の学習 引数(fc4の重み, fc4のバイアス, 目標出力, fc4への入力, fc4からの出力, デルタ値, 学習係数) backpro_fc4.num_trai = k;//訓練番号の代入 backpro_fc4.learn_fc4(wei_F4, bias_F4, target_l, img_F3, img_F4, delta_F4, EPSILON); //fc2の学習 引数(fc3の重み、fc4の重み、fc3のバイアス、fc4のバイアス、fc3への入力、fc3におけるu、fc3におけるδ、fc4におけるδ、学習係数) backpro_fc3.learn_fc3(wei_F3, wei_F4, bias_F3, bias_F4, img_P2, u_F3, delta_F3, delta_F4, EPSILON); //pool2の学習 引数(fc3の重み、pool2のデルタ、fc3のデルタ) backpro_pool2.learn_pool2(wei_F3, delta_P2, delta_F3); //learn_pool2(double wei_F3[300][48][6][6], double delta_P2[48][6][6], double delta_F3[300]) //conv2の学習 //backpro_conv1.num_trai = k;//訓練データの番号を代入 backpro_conv2.learn_conv2(img_N1, fil_C2, cowei_P2, delta_C2, delta_P2, EPSILON_CONV); //learn_conv2(double img_N1[16][27][27], double fil_C2[48][5][5], double cowei_P2[48][6][6][3][3], double delta_C2[48][13][13], double delta_P2[48][6][6], double epsilon) //pool1の学習 backpro_pool1.learn_pool1(fil_C2, delta_P1, delta_C2); //double fil_C2[48][5][5], double delta_P1[16][27][27], double delta_C2[48][13][13] //conv1の学習 backpro_conv1.num_trai = k;//訓練データの番号を代入 backpro_conv1.learn_conv1(img_In, fil_C1, cowei_P1, delta_C1, delta_P1, EPSILON_CONV); //(int img_In[300][227][227][3], double fil_C1[16][11][11], double cowei_P1[16][27][27][3][3], double delta_C1[16][55][55], double delta_P1[16][27][27], double epsilon) //確認用出力 std::cout << "1つ前の二乗誤差は " << en << std::endl; } count_l += 1; } return 0; } //----------------------------------------------------------------- //init_F3() 全結合層fc3の重みの初期化 //----------------------------------------------------------------- void initW_F3(double wei_F3[300][48][6][6], double bias_F3[300]){ //乱数による結合荷重の決定 for (int i = 0; i < 300; i++) { for (int j = 0; j < 48; j++) { for (int k = 0; k < 6; k++) { for (int l = 0; l < 6; l++) { wei_F3[i][j][k][l] = drnd(); } } } } //乱数によるバイアスの決定 for (int i = 0; i < 300; i++) { bias_F3[i] = drnd(); } } //----------------------------------------------------------------- //init_F4() 全結合層fc4の重みの初期化 //----------------------------------------------------------------- void initW_F4(double wei_F4[3][300], double bias_F4[3]){ //乱数による結合荷重の決定 for (int i = 0; i < 3; i++) { for (int j = 0; j < 300; j++) { wei_F4[i][j] = drnd(); } } //乱数によるバイアスの決定 for (int i = 0; i < 3; i++) { bias_F4[i] = drnd(); } } //----------------------------------------------------------------- //drnd() 乱数の生成 //----------------------------------------------------------------- double drnd(void){ double rndno;//生成した乱数を格納 while ((rndno = (double)rand() / RAND_MAX) == 1.0) ; rndno = rndno * 2 - 1;//乱数を-1から1へ変換 return rndno; }
jpeg_to_array.h
// // jpeg_to_array.h // CNN06_01 // // Created by 大政 孝充 on 2015/10/04. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #ifndef __CNN06_01__jpeg_to_array__ #define __CNN06_01__jpeg_to_array__ #include <stdio.h> #include <iostream> class jpeg_to_array{ public: void change_j_to_a(int img_In[300][227][227][3]); private: void use_libjpeg(int img_In[300][227][227][3], int array_num, std::string filename); }; #endif /* defined(__CNN06_01__jpeg_to_array__) */
jpeg_to_array.cpp
// // jpeg_to_array.cpp // CNN06_01 // // Created by 大政 孝充 on 2015/10/04. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #include "jpeg_to_array.h" #include <string> #include <iostream> #include <jpeglib.h> void jpeg_to_array::change_j_to_a(int img_In[300][227][227][3]){ std::string r_filename_phal = "phalanger/phalanger227/phalanger_"; std::string r_filename_squi = "squirrel/squirrel227/squirrel_"; std::string r_filename_cavia = "cavia/cavia227/cavia_"; std::string filename_JPEG = ".JPEG"; //phalangerを配列1限目の3の倍数に格納(0,3,6,・・・) for (int l = 0; l < 100; l++) { //読み込みファイル名の作成 std::string file_num = std::to_string(l + 1000);//ファイル番号 std::string filename_tmp = ""; filename_tmp += r_filename_phal; filename_tmp += file_num; filename_tmp += filename_JPEG; //確認用処理 //std::cout << "filename_tmpの値は" << filename_tmp << std::endl; //配列に画像情報を格納 jpeg_to_array::use_libjpeg(img_In, l * 3, filename_tmp); } //squirrelを配列1限目の3の倍数+1に格納(1,4,7,・・・) for (int l = 0; l < 100; l++) { //読み込みファイル名の作成 std::string file_num = std::to_string(l + 1000);//ファイル番号 std::string filename_tmp = ""; filename_tmp += r_filename_squi; filename_tmp += file_num; filename_tmp += filename_JPEG; //配列に画像情報を格納 jpeg_to_array::use_libjpeg(img_In, l * 3 + 1, filename_tmp); } //caviaを配列1限目の3の倍数+2に格納(2,5,8,・・・) for (int l = 0; l < 100; l++) { //読み込みファイル名の作成 std::string file_num = std::to_string(l + 1000);//ファイル番号 std::string filename_tmp = ""; filename_tmp += r_filename_cavia; filename_tmp += file_num; filename_tmp += filename_JPEG; //配列に画像情報を格納 jpeg_to_array::use_libjpeg(img_In, l * 3 + 2, filename_tmp); } } //----------------------------------------------------------------- //jpeg画像からJSAMP配列を生成 1つの画像に対する処理 //----------------------------------------------------------------- void jpeg_to_array::use_libjpeg(int img_In[300][227][227][3], int array_num, std::string filename){ //読み込み用変数の宣言 JSAMPARRAY img_oriData;//初期RGB配列 struct jpeg_decompress_struct cinfo1;//処理する画像 struct jpeg_error_mgr jerr1; FILE *infile1; //読み込みの処理 //JPEGオブジェクトの初期化 cinfo1.err = jpeg_std_error(&jerr1); jpeg_create_decompress(&cinfo1); //ファイルを開く infile1 = fopen(filename.c_str(), "rb"); jpeg_stdio_src(&cinfo1, infile1); //ヘッダの読み込み jpeg_read_header(&cinfo1, TRUE); //展開の開始 jpeg_start_decompress(&cinfo1); //幅と高さの取得 int width_src = cinfo1.output_width; int height_src = cinfo1.output_height; //確認用 //std::cout << "array_num " << array_num << " の縦幅は " << height_src << std::endl; //std::cout << "横幅" << width_src << std::endl; //イメージを保持するメモリ領域の確保と初期化 img_oriData = (JSAMPARRAY)malloc(sizeof(JSAMPROW) * height_src); for (int i = 0; i < height_src; i++) { img_oriData[i] = (JSAMPROW)calloc(sizeof(JSAMPLE), 3 * width_src); } //全イメージデータを取得 while (cinfo1.output_scanline < cinfo1.output_height) { jpeg_read_scanlines(&cinfo1, img_oriData + cinfo1.output_scanline, cinfo1.output_height - cinfo1.output_scanline); } //確認用処理 /*for (int i = 0; i < 227; i++) { for (int j = 0; j < 227; j++) { for (int k = 0; k < 3; k++) { std::cout << "img_oriDataの値 " << i << j << k << "は " << img_oriData[i][j * 3 + k] << std::endl; } } }*/ //確認用処理 //std::cout << "現在 " << array_num << " 番" << std::endl; //img_Inに格納 for (int i = 0; i < height_src; i++) { for (int j = 0; j < width_src; j++) { img_In[array_num][i][j][0] = img_oriData[i][j * 3]; img_In[array_num][i][j][1] = img_oriData[i][j * 3 + 1]; img_In[array_num][i][j][2] = img_oriData[i][j * 3 + 2]; //確認用処理 /*std::cout << "img_Inの値(" << i << "," << j << "," << 0 << ")は" << img_In[array_num][i][j][0] << std::endl; std::cout << "img_Inの値(" << i << "," << j << "," << 1 << ")は" << img_In[array_num][i][j][1] << std::endl; std::cout << "img_Inの値(" << i << "," << j << "," << 2 << ")は" << img_In[array_num][i][j][2] << std::endl; */ } } //展開の終了 jpeg_finish_decompress(&cinfo1); //JPEGオブジェクトの破棄 jpeg_destroy_decompress(&cinfo1); //ファイルを閉じる fclose(infile1); }
conv1.h
// // conv1.h // CNN06_01 // // Created by 大政 孝充 on 2015/10/04. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #ifndef __CNN06_1conv1__conv1__ #define __CNN06_1conv1__conv1__ #include <stdio.h> class conv1{ public: //畳み込み層1番目の関数 //入力画像サイズ227x227x3、出力画像サイズ55x55x96、slide4、フィルタ画像サイズ11x11、関数ReL void convolution1(int img_In[300][227][227][3], double fil_C1[16][11][11], double img_C1[16][55][55]); //neuron層を管理する変数 int num_nue; //訓練データの番号を管理する変数 int num_trai; }; #endif /* defined(__CNN06conv1__conv1__) */
conv1.cpp
// // conv1.cpp // CNN06_01 // // Created by 大政 孝充 on 2015/10/04. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #include "conv1.h" void conv1::convolution1(int img_In[300][227][227][3], double fil_C1[16][11][11], double img_C1[16][55][55]){//1つの訓練データ、1つのフィルタにおける処理 //値の定義 const int STRIDE1 = 4;//STRIDE値 const double BIAS = 0;//バイアス //原画像とフィルタの積を計算 double tmp_fil;//積を一時的に保持する for (int i = 0; i < 55; i++) { for (int j = 0; j < 55; j++) { //原画像上のフィルタ内中心位置 int pix_h = i * STRIDE1 + 3; int pix_w = j * STRIDE1 + 3; tmp_fil = 0;//初期化 //フィルタの要素ごとに積を計算 for (int k = -5; k <= 5; k++) { for (int l = -5; l <= 5; l++) { //原画像からはみ出している場合はゼロパッディング if (pix_h + k < 0 || pix_h + k >= 227 || pix_w + l < 0 || pix_w + l >= 227) { continue; } for (int m = 0; m < 3; m++) { //加算 tmp_fil += img_In[num_trai][pix_h + k][pix_w + l][m] * fil_C1[num_nue][k + 5][l + 5]; } } } //バイアスの加算 tmp_fil = tmp_fil + BIAS; //正規化線形関数を適用 if (tmp_fil < 0) { tmp_fil = 0; } //出力画像へ出力 img_C1[num_nue][i][j] = tmp_fil; } } }
pool1.h
// // pool1.h // CNN06_01 // // Created by 大政 孝充 on 2015/10/04. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #ifndef __CNN06_01pool1__pool1__ #define __CNN06_01pool1__pool1__ #include <stdio.h> class pool1{ public: void pooling1(double img_C1[16][55][55], double img_P1[16][27][27], double cowei_P1[16][27][27][3][3]); //nueronを管理する番号 int num_nue; }; #endif /* defined(__CNN06_01pool1__pool1__) */
pool1.cpp
// // pool1.cpp // CNN06_01 // // Created by 大政 孝充 on 2015/10/04. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #include "pool1.h" #include <algorithm> #include <iostream> void pool1::pooling1(double img_C1[16][55][55], double img_P1[16][27][27], double cowei_P1[16][27][27][3][3]){//入力画像、出力画像、重み(順伝播時は伝播した重みは1、他は0、逆伝播時は伝播した重みのみδ) //STRIDE値 const int STRIDE1 = 2; //原画像とフィルタの積を計算 double tmp_max;//積を一時的に保持する int num_adopted[2];//採用した3x3のマス目位置を保持 for (int i = 0; i < 27; i++) { for (int j = 0; j < 27; j++) { //画像上の位置 int pix_h = i * STRIDE1 + 1; int pix_w = j * STRIDE1 + 1; tmp_max = 0;//初期化 num_adopted[0] = 0;//初期化 マス目縦 num_adopted[1] = 0;//マス目横 //対応関係を格納する配列の初期化 for (int k = 0; k < 3; k++) { for (int l = 0; l < 3; l++) { cowei_P1[num_nue][i][j][k][l] = 0; } } //フィルタの要素ごとに積を計算 for (int k = -1; k <= 1; k++) { for (int l = -1; l <= 1; l++) { //原画像からはみ出している場合はゼロパッディング・・・計算上無いはず if (pix_h + k < 0 || pix_h + k >= 55 || pix_w + l < 0 || pix_w + l >= 55) { continue; } //max値を採用 if (img_C1[num_nue][pix_h + k][pix_w + l] > tmp_max) { tmp_max = img_C1[num_nue][pix_h + k][pix_w + l]; //フィルタ上の対応座標を格納 num_adopted[0] = k + 1; num_adopted[1] = l + 1; } } } //出力画像へ出力 img_P1[num_nue][i][j] = tmp_max; //重み配列へ値を代入 1 or 0 cowei_P1[num_nue][i][j][num_adopted[0]][num_adopted[1]] = 1; } } /* //確認用処理 for (int i = 0; i < 27; i++) { for (int j = 0; j < 27; j++) { for (int k = 0; k < 3; k++) { for (int l = 0; l < 3; l++) { std::cout << "pool層重み" << num_nue << "," << i << "," << j << "," << k << "," << l << "の値は" << corres_p_fc2[num_nue][i][j][k][l] << std::endl; } } } } */ }
norm1.h
// // norm1.h // CNN06_01 // // Created by 大政 孝充 on 2015/10/04. // Copyright (c) 2015年 大政 孝充. All rights reserved. // norm1 #ifndef __CNN06_01norm1__norm1__ #define __CNN06_01norm1__norm1__ #include <stdio.h> class norm1{ public: void normalization(double img_P1[16][27][27], double img_N1[16][27][27]); //neuron番号を管理する変数 //int num_nue; }; #endif /* defined(__CNN06_01norm1__norm1__) */
norm1.cpp
// // norm1.cpp // CNN06_01 // // Created by 大政 孝充 on 2015/10/04. // Copyright (c) 2015年 大政 孝充. All rights reserved. // norm1 他チャンネル正規化 #include "norm1.h" #include <math.h> #include <iostream> void norm1::normalization(double img_P1[16][27][27], double img_N1[16][27][27]){ const int STRIDE1 = 1;//ストライド const double THR_SIG = 1.0;//σの閾値 //平均の重み w ガウシアンを採用(571倍したもの) const double wei[5][5] = {{2,7,12,7,2},{7,31,52,31,7},{12,52,127,52,12}, {7,31,52,31,7},{2,7,12,7,2}}; double mean_SQ;//5x5正方形領域の平均 double sigma2_SQ;//5x5正方形領域の分散 int number_wei;//5x5正方形領域の該当重みの総和 //正規化 for (int i = 0; i < 27; i++) { for (int j = 0; j < 27; j++) { //初期化 mean_SQ = 0; sigma2_SQ = 0; number_wei = 0; //画像上の位置 int pix_h = i * STRIDE1; int pix_w = j * STRIDE1; //平均値を求める前段階の加算処理 for (int k = -2; k <= 2; k++) { for (int l = -2; l <= 2; l++) { //原画像からはみ出している場合はスキップ if (pix_h + k < 0 || pix_h + k >= 27 || pix_w + l < 0 || pix_w + l >= 27) { continue; } //加算(多チャンネル) for (int m = 0; m < 16; m++) {//ここ、各チャンネル毎にやるのは効率悪い mean_SQ += img_P1[m][pix_h + k][pix_w + l] * wei[k + 2][l + 2]; number_wei += wei[k + 2][l + 2]; } } } //平均を算出 mean_SQ = mean_SQ / number_wei; //確認用出力 //std::cout << "mean_SQの(" << i << "," << j << ")番目値は " << mean_SQ << std::endl; //分散を求める前段階の加算処理 for (int k =-2; k <= 2; k++) { for (int l = -2; l <= 2; l++) { //原画像からはみ出している場合はスキップ if (pix_h + k < 0 || pix_h + k >= 27 || pix_w + l < 0 || pix_w + l >= 27) { continue; } //多チャンネルに対応 for (int m = 0; m < 16; m++) {//ここ、各チャンネル毎にやるのは効率悪い sigma2_SQ += pow(img_P1[m][pix_h + k][pix_w + l] - mean_SQ, 2) * wei[k + 2][l + 2]; } } } //分散を算出 sigma2_SQ = sigma2_SQ / number_wei; sigma2_SQ = std::max(sigma2_SQ, THR_SIG); //確認用出力 //std::cout << pow(sigma2_SQ,0.5) << std::endl; //std::cout << "正規化処理途中、標準偏差の("<< num_nue << "," << i << "," << j << ")番目値は " << pow(sigma2_SQ,0.5) << std::endl; //出力画像へ値を出力 for (int k = 0; k < 16; k++) {//ピクセルごとに、全チャンネルを処理 img_N1[k][i][j] = (img_P1[k][i][j] - mean_SQ) / pow(sigma2_SQ, 0.5); } } } //確認用出力 /* for (int k = 0; k < 10; k++) { for (int i = 0; i < 27; i++) { for (int j = 0; j < 27; j++) { std::cout << "norm1の出力("<< k << "," << i << "," << j << ")の値は" << out_img[k][i][j] << std::endl; } } } */ }
conv2.h
// // conv2.h // CNN06_01 // // Created by 大政 孝充 on 2015/10/05. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #ifndef __CNN06_01__conv2__ #define __CNN06_01__conv2__ #include <stdio.h> class conv2{ public: //畳み込み層2番目の関数 //入力画像サイズ227x227x3、出力画像サイズ55x55x96、slide4、フィルタ画像サイズ11x11、関数ReL void convolution2(double img_N1[16][27][27], double fil_C2[48][5][5], double img_C2[48][13][13]); //neuron層を管理する変数 int num_nue; }; #endif /* defined(__CNN06_01__conv2__) */
conv2.cpp
// // conv2.cpp // CNN06_01 // // Created by 大政 孝充 on 2015/10/05. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #include "conv2.h" void conv2::convolution2(double img_N1[16][27][27], double fil_C2[48][5][5], double img_C2[48][13][13]){//1つの訓練データ、1つのフィルタにおける処理 //値の定義 const int STRIDE1 = 2;//STRIDE値 const double BIAS = 0;//バイアス //原画像とフィルタの積を計算 double tmp_fil;//積を一時的に保持する for (int i = 0; i < 13; i++) { for (int j = 0; j < 13; j++) { //原画像上のフィルタ内中心位置 int pix_h = i * STRIDE1 + 1; int pix_w = j * STRIDE1 + 1; tmp_fil = 0;//初期化 //フィルタの要素ごとに積を計算 for (int k = -2; k <= 2; k++) { for (int l = -2; l <= 2; l++) { //原画像からはみ出している場合はゼロパッディング if (pix_h + k < 0 || pix_h + k >= 27 || pix_w + l < 0 || pix_w + l >= 27) { continue; } for (int m = 0; m < 16; m++) { //加算処理 tmp_fil += img_N1[m][pix_h + k][pix_w + l] * fil_C2[num_nue][k + 3][l + 3]; } } } //バイアスの加算 tmp_fil = tmp_fil + BIAS; //正規化線形関数を適用 if (tmp_fil < 0) { tmp_fil = 0; } //出力画像へ出力 img_C2[num_nue][i][j] = tmp_fil; } } }
pool2.h
// // pool2.h // CNN06_01 // // Created by 大政 孝充 on 2015/10/05. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #ifndef __CNN06_01__pool2__ #define __CNN06_01__pool2__ #include <stdio.h> class pool2{ public: void pooling2(double img_C2[48][13][13], double img_P2[48][6][6], double cowei_P2[48][6][6][3][3]); //nueronを管理する番号 int num_nue; }; #endif /* defined(__CNN06_01__pool2__) */
pool2.cpp
// // pool2.cpp // CNN06_01 // // Created by 大政 孝充 on 2015/10/05. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #include "pool2.h" void pool2::pooling2(double img_C2[48][13][13], double img_P2[48][6][6], double cowei_P2[48][6][6][3][3]){//入力画像、出力画像、共有重み(順伝播時は伝播した重みは1、他は0、逆伝播時は伝播した重みのみδ) //STRIDE値 const int STRIDE1 = 2; //原画像とフィルタの積を計算 double tmp_max;//積を一時的に保持する int num_adopted[2];//採用した3x3のマス目位置を保持 for (int i = 0; i < 6; i++) { for (int j = 0; j < 6; j++) { //画像上の位置 int pix_h = i * STRIDE1 + 1; int pix_w = j * STRIDE1 + 1; tmp_max = 0;//初期化 num_adopted[0] = 0;//初期化 マス目縦 num_adopted[1] = 0;//マス目横 //対応関係を格納する配列の初期化 for (int k = 0; k < 3; k++) { for (int l = 0; l < 3; l++) { cowei_P2[num_nue][i][j][k][l] = 0; } } //フィルタの要素ごとに積を計算 for (int k = -1; k <= 1; k++) { for (int l = -1; l <= 1; l++) { //原画像からはみ出している場合はゼロパッディング・・・計算上無いはず if (pix_h + k < 0 || pix_h + k >= 13 || pix_w + l < 0 || pix_w + l >= 13) { continue; } //max値を採用 if (img_C2[num_nue][pix_h + k][pix_w + l] > tmp_max) { tmp_max = img_C2[num_nue][pix_h + k][pix_w + l]; //フィルタ上の対応座標を格納 num_adopted[0] = k + 1; num_adopted[1] = l + 1; } } } //出力画像へ出力 img_P2[num_nue][i][j] = tmp_max; //重み配列へ値を代入 1 or 0 cowei_P2[num_nue][i][j][num_adopted[0]][num_adopted[1]] = 1; } } }
fc3.h
// // fc3.h // CNN06_01 // // Created by 大政 孝充 on 2015/10/05. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #ifndef __CNN06_01__fc3__ #define __CNN06_01__fc3__ #include <stdio.h> class fc3{ public: void full_connection3(double img_P2[48][6][6], double img_F3[300], double u_F3[300], double wei_F3[300][48][6][6]); //バイアス double bias_F3[10]; }; #endif /* defined(__CNN06_01__fc3__) */
fc3.cpp
// // fc3.cpp // CNN06_01 // // Created by 大政 孝充 on 2015/10/05. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #include "fc3.h" #include "math.h" #include <iostream> void fc3::full_connection3(double img_P2[48][6][6], double img_F3[300], double u_F3[300], double wei_F3[300][48][6][6]){ //uの計算 for (int i = 0; i < 300; i++) { //初期化 u_F3[i] = 0; for (int j = 0; j < 48; j++) { for (int k = 0; k < 6; k++) { for (int l = 0; l < 6; l++) { u_F3[i] += wei_F3[i][j][k][l] * img_P2[j][k][l]; } } } u_F3[i] = u_F3[i] + bias_F3[i]; //活性化関数はReL if (u_F3[i] < 0) { img_F3[i] = 0; }else{ img_F3[i] = u_F3[i]; } } //確認用出力 /* for (int i = 0; i < 10; i++) { std::cout << "fc2のuの値 " << i << "番目は" << u[i] << std::endl; } */ for (int i = 0; i < 300; i++) { std::cout << "fc3の出力値 " << i << "番目は" << img_F3[i] << std::endl; } }
fc4.h
// // fc4.h // CNN06_01 // // Created by 大政 孝充 on 2015/10/05. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #ifndef __CNN06_01__fc4__ #define __CNN06_01__fc4__ #include <stdio.h> class fc4{ public: void full_conection4(double img_F3[300], double img_F4[3], double wei_F4[3][300]); //バイアス double bias_F4[3]; }; #endif /* defined(__CNN06_01__fc4__) */
fc4.cpp
// // fc4.cpp // CNN06_01 // // Created by 大政 孝充 on 2015/10/05. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #include "fc4.h" #include "math.h" #include <iostream> void fc4::full_conection4(double img_F3[300], double img_F4[3], double wei_F4[3][300]){ //確認用処理 /*for (int i = 0; i < 3; i++) { for (int j = 0; j < 10; j++) { std::cout << "fc3の出力計算前の重み(" << i << "," << j << ")番目は " << w_fc3[i][j] << std::endl; } }*/ //uの計算 double u[3]; double exp_sum = 0;//uのexpを集計 for (int i = 0; i < 3; i++) { for (int j = 0; j < 300; j++) { u[i] += wei_F4[i][j] * img_F3[j]; } u[i] = u[i] + bias_F4[i]; exp_sum += exp(u[i]); } //double z[2]; for (int i = 0; i < 3; i++) { //softmax関数 if (exp_sum == 0) { img_F4[i] = 0; }else{ img_F4[i] = (exp(u[i])) / exp_sum; } } //確認用処理 for (int i = 0; i < 3; i++) { std::cout << "fc4の出力 " << i << "番目は " << img_F4[i] << std::endl; } }
backpro_fc4.h
// // backpro_fc4.h // CNN06_01 // // Created by 大政 孝充 on 2015/10/05. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #ifndef __CNN06_01__backpro_fc4__ #define __CNN06_01__backpro_fc4__ #include <stdio.h> class backpro_fc4{ public: void learn_fc4(double wei_F4[3][300], double bias_F4[3], double target_l[300][3], double img_F3[300], double img_F4[3], double delta_F4[3], double epsilon); //訓練データの番号 int num_trai; }; #endif /* defined(__CNN06_01__backpro_fc4__) */
backpro_fc4.cpp
// // backpro_fc4.cpp // CNN06_01 // // Created by 大政 孝充 on 2015/10/05. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #include "backpro_fc4.h" #include <iostream> void backpro_fc4::learn_fc4(double wei_F4[3][300], double bias_F4[3], double target_l[300][3], double img_F3[300], double img_F4[3], double delta_F4[3], double epsilon){//引数(fc4の重み, fc4のバイアス, 目標出力, fc4への入力, fc4からの出力, デルタ値, 学習係数) //重み、バイアスの学習 for (int i = 0; i < 3; i++) { //デルタ値(=∂E/∂u)の算出 δj = yj-dj delta_F4[i] = img_F4[i] - target_l[num_trai][i]; //重みの更新(w' = w - εxδxz) for (int j = 0; j < 300; j++) { wei_F4[i][j] = wei_F4[i][j] - epsilon * delta_F4[i] * img_F3[j]; } //バイアスの更新 bias_F4[i] = bias_F4[i] - epsilon * delta_F4[i]; } //確認用出力 /*for (int i = 0; i < 3; i++) { std::cout << "fc4のδ" << i << "は " << delta_F4[i] << std::endl; }*/ }
backpro_fc3.h
// // backpro_fc3.h // CNN06_01 // // Created by 大政 孝充 on 2015/10/05. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #ifndef __CNN06_01__backpro_fc3__ #define __CNN06_01__backpro_fc3__ #include <stdio.h> class backpro_fc3{ public: void learn_fc3(double wei_F3[300][48][6][6], double wei_F4[3][300], double bias_F3[300], double bias_F4[3], double img_P2[48][6][6], double u_F3[300], double delta_F3[300], double delta_F4[3], double epsilon); }; #endif /* defined(__CNN06_01__backpro_fc3__) */
backpro_fc3.cpp
// // backpro_fc3.cpp // CNN06_01 // // Created by 大政 孝充 on 2015/10/05. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #include "backpro_fc3.h" #include <iostream> void backpro_fc3::learn_fc3(double wei_F3[300][48][6][6], double wei_F4[3][300], double bias_F3[300], double bias_F4[3], double img_P2[48][6][6], double u_F3[300], double delta_F3[300], double delta_F4[3], double epsilon){//引数:fc3の重み、fc4の重み、fc3のバイアス、fc4のバイアス、入力画像、fc3のu、fc3のデルタ、fc4のデルタ、エプシロン //重み、バイアスの学習 for (int i = 0; i < 300; i++) { delta_F3[i] = 0;//初期化 for (int j = 0; j < 3; j++) { //デルタ値(=∂E/∂u)の算出 δj = Σδ * w * f'(u) //f(u)はReLなので、f'(u)は1(u>=0),0(u<0) if (u_F3[i] >= 0) { delta_F3[i] += delta_F4[j] * wei_F4[j][i] * 1; }else{ delta_F3[i] += 0; } } //重みの更新(w' = w - ε * δ * z) for (int j = 0; j < 48; j++) { for (int k = 0; k < 6; k++) { for (int l = 0; l < 6; l++) { wei_F3[i][j][k][l] = wei_F3[i][j][k][l] - epsilon * delta_F3[i] * img_P2[j][k][l]; } } } //バイアスの更新 bias_F3[i] = bias_F3[i] - epsilon * delta_F3[i]; } //確認用出力 /* int num_array1 = 4; int num_array4 = 16; for (int i = 0; i < 10; i++) { for (int j = 0; j < 27; j++) { std::cout << "fc2層のw " << num_array1 << "," << i << "," << j << "," << num_array4 << "は " << w_fc2[num_array1][i][j][num_array4] << std::endl; } } */ /*for (int i = 0; i < 300; i++) { std::cout << "fc3層のδ " << i << "は " << delta_F3[i] << std::endl; }*/ }
backpro_pool2.h
// // backpro_pool2.h // CNN06_01 // // Created by 大政 孝充 on 2015/10/05. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #ifndef __CNN06_01__backpro_pool2__ #define __CNN06_01__backpro_pool2__ #include <stdio.h> class backpro_pool2{ public: void learn_pool2(double wei_F3[300][48][6][6], double delta_P2[48][6][6], double delta_F3[300]); }; #endif /* defined(__CNN06_01__backpro_pool2__) */
backpro_pool2.cpp
// // backpro_pool2.cpp // CNN06_01 // // Created by 大政 孝充 on 2015/10/05. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #include "backpro_pool2.h" #include <iostream> void backpro_pool2::learn_pool2(double wei_F3[300][48][6][6], double delta_P2[48][6][6], double delta_F3[300]){ //引数cowei_P2は4次元目、5次元目に関して採用された点のみ1、他は0で送られてくる //デルタの計算 for (int i = 0; i < 48; i++) { for (int j = 0; j < 6; j++) { for (int k = 0; k < 6; k++) { delta_P2[i][j][k] = 0;//初期化 //デルタ値(=∂E/∂u)の算出 δj = Σδ * w * f'(u) for (int l = 0; l < 300; l++) { delta_P2[i][j][k] += delta_F3[l] * wei_F3[l][i][j][k]; } } } } //確認用出力 pool層のδ /* for (int i = 0; i < 10; i++) { for (int j = 0; j < 27; j++) { for (int k = 0; k < 27; k++) { std::cout << "pool層のδ " << i << "," << j << "," << k << "は " << delta_p[i][j][k] << std::endl; } } } */ }
backpro_conv2.h
// // backpro_conv2.h // CNN06_01 // // Created by 大政 孝充 on 2015/10/05. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #ifndef __CNN06_01__backpro_conv2__ #define __CNN06_01__backpro_conv2__ #include <stdio.h> class backpro_conv2{ public: void learn_conv2(double img_N1[16][27][27], double fil_C2[48][5][5], double cowei_P2[48][6][6][3][3], double delta_C2[48][13][13], double delta_P2[48][6][6], double epsilon); //訓練データの番号 //sint num_trai; }; #endif /* defined(__CNN06_01__backpro_conv2__) */
backpro_conv2.cpp
// // backpro_conv2.cpp // CNN06_01 // // Created by 大政 孝充 on 2015/10/05. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #include "backpro_conv2.h" #include <iostream> void backpro_conv2::learn_conv2(double img_N1[16][27][27], double fil_C2[48][5][5], double cowei_P2[48][6][6][3][3], double delta_C2[48][13][13], double delta_P2[48][6][6], double epsilon){ //conv2のストライド const int STRIDE = 2; //convのδを初期化 for (int i = 0; i < 48; i++) { for (int j = 0; j < 13; j++) { for (int k = 0; k < 13; k++) { delta_C2[i][j][k] = 0; } } } //conv1画像上における座標 int point_h; int point_w; //conv1のδをさん出する処理 for (int i = 0; i < 48; i++) { for (int j = 0; j < 6; j++) { for (int k = 0; k < 6; k++) { for (int l = 0; l < 3; l++) { for (int m = 0; m < 3; m++) { if (cowei_P2[i][j][k][l][m] == 0) { continue;//0ならスキップ } //conv2画像上における座標の特定 point_h = 2 * j + 1 + (l - 1); point_w = 2 * k + 1 + (m - 1); //conv2のδへ加算 delta_C2[i][point_h][point_w] += delta_P2[i][j][k]; } } } } } //入力画像におけるフィルタ中心に相当する座標 int pC2_h; int pC2_w; //フィルタの重み更新 δを格納した配列に沿って加算していく for (int i = 0; i < 48; i++) {//フィルタ48枚 for (int j = 0; j < 13; j++) { for (int k = 0; k < 13; k++) { if (delta_C2[i][j][k] == 0) {//δが0の場合(伝播が来てない)スキップ continue; } //入力画像におけるフィルタ中心に相当する座標の特定 pC2_h = j * STRIDE + 1; pC2_w = k * STRIDE + 1; for (int l = 0; l < 5; l++) { for (int m = 0; m < 5; m++) { //対応点が画像の枠外ならばスキップ if (pC2_h + l - 2 < 0 || pC2_h + l - 2 >= 27 || pC2_w + m - 2 < 0 || pC2_w + m - 2 >= 27) { continue; } for (int n = 0; n < 16; n++) { //フィルタの値を更新 fil_C2[i][l][m] += (-1) * epsilon * img_N1[n][pC2_h][pC2_w] * delta_C2[i][j][k]; } } } } } } //確認用出力 /* for (int i = 0; i < 10; i++) { for (int j = 0; j < 11; j++) { for (int k = 0; k < 11; k++) { std::cout << "更新されたフィルタの値" << i << "," << j << "は " << fil_img[i][j][k] << std::endl; } } } */ }
backpro_pool1.h
// // backpro_pool1.h // CNN06_01 // // Created by 大政 孝充 on 2015/10/04. // Copyright (c) 2015年 大政 孝充. All rights reserved. // pool1に対する誤差逆伝播法(poolingとして採用された点はδ、それ以外は0) #ifndef __CNN06_01__backpro_pool1__ #define __CNN06_01__backpro_pool1__ #include <stdio.h> class backpro_pool1{ public: void learn_pool1(double fil_C2[48][5][5], double delta_P1[16][27][27], double delta_C2[48][13][13]); }; #endif /* defined(__CNN06_01__backpro_pool1__) */
backpro_pool1.cpp
// // backpro_pool1.cpp // CNN06_01 // // Created by 大政 孝充 on 2015/10/04. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #include "backpro_pool1.h" #include <iostream> void backpro_pool1::learn_pool1(double fil_C2[48][5][5], double delta_P1[16][27][27], double delta_C2[48][13][13]){ //デルタの計算 /*for (int i = 0; i < 16; i++) { for (int j = 0; j < 27; j++) { for (int k = 0; k < 27; k++) { delta_P1[i][j][k] = 0;//初期化 //デルタ値(=∂E/∂u)の算出 δj = Σδ * w * f'(u) for (int l = 0; l < 48; l++) { for (int m = 0; m < 13; m++) { for (int n = 0; n < 13; n++) { delta_P1[i][j][k] += delta_C2[l][m][n] * w_fc2[l][i][j][k]; } } } } } }*/ //conv2のスライド const int SLIDE = 2; //δの初期化 for (int i = 0; i < 16; i++) { for (int j = 0; j < 27; j++) { for (int k = 0; k < 27; k++) { delta_P1[i][j][k] = 0; } } } //デルタの計算 δ(n+1)基準で計算する(つまりimg_C2のピクセルごとに走査する) for (int i = 0; i < 48; i++) { for (int j = 0; j < 13; j++) { for (int k = 0; k < 13; k++) { //img_P1上の対応する中心座標を求める int p_P1Ce_h = j * SLIDE + 1; int p_P1Ce_w = k * SLIDE + 1; for (int l = 0; l < 5; l++) { for (int m = 0; m < 5; m++) { //フィルタ位置に対応するimg_P1上の座標を求める int p_P1_h = p_P1Ce_h + l - 2; int p_P1_w = p_P1Ce_w + m - 2; //端処理 if (p_P1_h < 0 || p_P1_h >= 27 || p_P1_w < 0 || p_P1_w >= 27) { continue; } //delta_C2からdelta_P1を求める delta_P1[i][p_P1_h][p_P1_w] += delta_C2[i][j][k] * fil_C2[i][l][m] * 1; } } } } } }
backpro_conv1.h
// // backpro_conv1.h // CNN06_01 // // Created by 大政 孝充 on 2015/10/04. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #ifndef __CNN06_01__backpro_conv1__ #define __CNN06_01__backpro_conv1__ #include <stdio.h> class backpro_conv1{ public: void learn_conv1(int img_In[300][227][227][3], double fil_C1[16][11][11], double cowei_P1[16][27][27][3][3], double delta_C1[16][55][55], double delta_P1[16][27][27], double epsilon); //訓練データの番号 int num_trai; }; #endif /* defined(__CNN06_01__backpro_conv1__) */
backpro_conv1.cpp
// // backpro_conv1.cpp // CNN06_01 // // Created by 大政 孝充 on 2015/10/04. // Copyright (c) 2015年 大政 孝充. All rights reserved. // #include "backpro_conv1.h" #include <iostream> void backpro_conv1::learn_conv1(int img_In[300][227][227][3], double fil_C1[16][11][11], double cowei_P1[16][27][27][3][3], double delta_C1[16][55][55], double delta_P1[16][27][27], double epsilon){ //conv1のストライド const int STRIDE_C = 4; //pool1のストライド const int STRIDE_P = 2; //conv1のδを初期化 for (int i = 0; i < 16; i++) { for (int j = 0; j < 55; j++) { for (int k = 0; k < 55; k++) { delta_C1[i][j][k] = 0; } } } //conv1画像上における座標 int point_h; int point_w; //conv1のδを算出する処理 for (int i = 0; i < 16; i++) { for (int j = 0; j < 27; j++) { for (int k = 0; k < 27; k++) { for (int l = 0; l < 3; l++) { for (int m = 0; m < 3; m++) { if (cowei_P1[i][j][k][l][m] == 0) { continue;//0ならスキップ } //conv1画像上における座標の特定 point_h = STRIDE_P * j + 1 + (l - 1); point_w = STRIDE_P * k + 1 + (m - 1); //conv1のδへ加算 delta_C1[i][point_h][point_w] += delta_P1[i][j][k]; } } } } } //入力画像におけるフィルタ中心に相当する座標 int pRGB_h; int pRGB_w; //フィルタの重み更新 δを格納した配列に沿って加算していく for (int i = 0; i < 16; i++) {//フィルタ16枚 for (int j = 0; j < 55; j++) { for (int k = 0; k < 55; k++) { if (delta_C1[i][j][k] == 0) {//δが0の場合(伝播が来てない)スキップ continue; } //入力画像におけるフィルタ中心に相当する座標の特定 pRGB_h = j * STRIDE_C + 3; pRGB_w = k * STRIDE_C + 3; for (int l = 0; l < 11; l++) { for (int m = 0; m < 11; m++) { //対応点が画像の枠外ならばスキップ if (pRGB_h + l - 5 < 0 || pRGB_h + l - 5 >= 227 || pRGB_w + m - 5 < 0 || pRGB_w + m - 5 >= 227) { continue; } for (int n = 0; n < 3; n++) {//RGB3枚分 //フィルタの値を更新 fil_C1[i][l][m] += (-1) * epsilon * img_In[num_trai][pRGB_h][pRGB_w][n] * delta_C1[i][j][k]; } } } } } } //確認用出力 /* for (int i = 0; i < 10; i++) { for (int j = 0; j < 11; j++) { for (int k = 0; k < 11; k++) { std::cout << "更新されたフィルタの値" << i << "," << j << "は " << fil_img[i][j][k] << std::endl; } } } */ }