CNNの誤差逆伝播とDeconvolutionまとめ
https://qiita.com/bukei_student/items/a3d1bcd429f99942ace4
非常に分かりやくまとまっていた記事だった。
コメント欄も参考になる。
CNNの誤差逆伝播とDeconvolutionまとめ
https://qiita.com/bukei_student/items/a3d1bcd429f99942ace4
非常に分かりやくまとまっていた記事だった。
コメント欄も参考になる。
行列の row (行) の値は y 縦の長さ、 col (列) の値は x 横の長さのイメージ。(2×3) 行列は 2 row, 3 col。
行列計算なので、a_col と b_row が一致していないと計算できない。
要素数が増えてくると混乱するが、float a[5][2] でも、float a[2][5] メモリー上は一緒。なんなら float a[1][1][2][5]とかなってもメモリー上は一緒。
生のアドレス計算(オフセット)だと混乱するため、添字からアクセスするためのレイアウトを定義しているだけ。
float a[50*25]; float a[50][25];
25個の組みが50個並んでる。横に25、縦に50並んでいるイメージを持つ。
float *a = new float[50 * 25]; for (int i = 0; i < 50; ++i ) { for (int j = 0; j < 25; ++j ) { a[i * 25 + j] = i * 25 + j; std::cout << a[i * 25 + j] << " "; } }
float* A = new float[ 2 * 3 ]; float* B = new float[ 3 * 5 ); float* C = new float[ 2 * 5 ]; for (int i = 0; i < 2; ++i ) { for (int j = 0; j < 5; ++j ) { float dc = 0.0; for (int k = 0; k < 3; ++k) { dc += A[ i * 3 + k ] * B[ k * 5 + j ]; } C[i * 5 + j] = dc; } }
参考
https://www.cspp.cc.u-tokyo.ac.jp/hanawa/class/spc2016s/sp20160607-2.pdf
Command Mode (cell が選択されていない状態) で h を押すか、Help -> Keyboard shortcuts を押すとショートカットの一覧が表示される。
Jupyter Notebook は Command Mode (press Esc) と Edit Mode (press Enter) に分かれており、それぞれで使えるショートカットキーが異なる。
# 編集中セルの下に新しいセルを挿入
ESC + B
# 編集中セルの上に新しいセルを挿入
ESC + A
# 編集中セルを削除
ESC + D, D
Tensorflow の tf.nn.convolution などは padding という引数を取り、文字列で VALID か SAME のどちらかを指定する。なぜ SAME/VALID という名前なのだろうか。。
Tensorflow の Document に計算式が載っている。
https://www.tensorflow.org/api_docs/python/tf/nn/convolution
SAME を指定すると 0 パディング が行われて、(ストライドの値にもよるが) 入力が小さくならずに、同じサイズで出力される。VALID にすると、足りない分はサイズが小さくなっていく。
英語に疎くてあれなのですが、SAME だと入力サイズと同じになるという意味で使われるのだとしたらまだ分かるのですが、なぜ Padding が Valid (有効) だと、パディングされない挙動になるのでしょうか?
TensorFlow – 畳み込み演算の関数 tf.nn.conv2d
https://blog.logicky.com/2017/01/18/tensorflow-%E7%95%B3%E3%81%BF%E8%BE%BC%E3%81%
ちょっとしたコードを確認するのに凄く参考になります。
a = np.arange(18).reshape(2, 3, 3)
という指定を行うと、下記のような 18個の (2, 3, 3) 配列が返る。
a もしくは a[:, :, :] でアクセスする。[:, :, :] って凄い指定の仕方。。
array([[[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8]], [[ 9, 10, 11], [12, 13, 14], [15, 16, 17]]])
この結果に対して a[0] もしくは a[0, :, :] でアクセスすると
array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
a[1][0] もしくは a[1, :, :] にアクセスすると、
array([ 9, 10, 11])
になる。
[:, :, :] は範囲指定を行える。[begin:end] 指定。t = np.arange([2, 3, 4, 5, 6, 7]) で t[2:5] とした場合は、index が 2, 3, 4 までの値 array([4, 5, 6]) が返る。
a と a[:, :, :] と a[0:2, 0:3, 0:3] は全て同じ結果になる。a[0, :, :] にアクセスすると [[ 0, 1, 2], [3, 4, 5], [6, 7, 8]] が返る。a[:, 1, :] にアクセスすると [[ 3, 4, 5], [12, 13, 14]] が返る。3次元配列の 2つ目が 1 で固定されるため。a[:, :, 0] にアクセスすると [[ 0, 3, 6], [ 9, 12, 15]] が返る。3要素のベクトルの先頭 [0] のみが返るため。
a[-1, -1, -1] という風に -1 を指定すると、値としては 17 が帰ってくる。-1 は配列要素の最後の要素。
a.transpose(0, 2, 1) という風に transpose() と入れ替える軸を指定すると、この結果は下記になる。1, 2 -> 2, 1 に順番にしたので転置されてる。
array([[[ 0, 3, 6], [ 1, 4, 7], [ 2, 5, 8]], [[ 9, 12, 15], [10, 13, 16], [11, 14, 17]]])
a.flatten() 命令を使用すると、ndim 1 の 1次元ベクトルになる。
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17])
平均 0, 分散 1 の正規分布 (normal distribution) に従う乱数値を返す。引数には任意の次元を指定できる。randn(d0, d1, …, dn) を指定することができ、仮に randn(2, 2) とすれば、4つの乱数が返る。
0 で初期化された配列を返す。zeros((2,2)) とすれば、要素4つ、shape が (2,2) ndim 2 の配列が返す。
第一引数は乱数の最大値、第二引数は個数。(5, 20) と引数に指定した場合は、5 以下 (0~4) までの乱数が 20 個返る。
指定した変数の前後にパディングを追加出来る。np.pad([5,6], (2, 3), ‘constant’) みたいに指定すれば、array([0, 0, 5, 6, 0, 0, 0]) という要素が返る。5 の前に 2個、6 の後に 3個のパディングが追加される。
Github Keras Examples
https://github.com/keras-team/keras/tree/master/examples
公式のサンプルプログラム
サンプルコードはここにある。
https://github.com/keras-team/keras/blob/master/examples/mnist_cnn.py
Summary を出力するとこのようになる。
_________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d_1 (Conv2D) (None, 26, 26, 32) 320 _________________________________________________________________ conv2d_2 (Conv2D) (None, 24, 24, 64) 18496 _________________________________________________________________ max_pooling2d_1 (MaxPooling2 (None, 12, 12, 64) 0 _________________________________________________________________ dropout_1 (Dropout) (None, 12, 12, 64) 0 _________________________________________________________________ flatten_1 (Flatten) (None, 9216) 0 _________________________________________________________________ dense_1 (Dense) (None, 128) 1179776 _________________________________________________________________ dropout_2 (Dropout) (None, 128) 0 _________________________________________________________________ dense_2 (Dense) (None, 10) 1290 ================================================================= Total params: 1,199,882 Trainable params: 1,199,882 Non-trainable params: 0 _________________________________________________________________
Conv2D() で畳み込みレイヤーの追加。
Conv2D filters 32, kernel size (3, 3) -> ReLU
Conv2D filters 64, kernel size (3, 3) -> ReLU
MaxPooling (2, 2)
(28, 28, 1) の input を、32 filters, size(3, 3) の Conv2D に入力すると、(26, 26, 32) になる。(26, 26, 32) を 64 filters, size(3, 3) に入れれば、(24, 24, 64) になる。
KerasのConv2Dの行列式演算
https://qiita.com/nishiha/items/bfd5dfcd7fffd3c529bc
How flatten layer works in keras?
https://stackoverflow.com/questions/44176982/how-flatten-layer-works-in-keras
どのように展開されるか疑問だったので、凄く分かりやすかった。
KerasのConv2DTransposeの動作について
https://qiita.com/takurooo/items/9a9f387390f5fcf5a516
An Introduction to different Types of Convolutions in Deep Learning
https://towardsdatascience.com/types-of-convolutions-in-deep-learning-717013397f4d
How to use the UpSampling2D and Conv2DTranspose Layers in Keras
https://machinelearningmastery.com/upsampling-and-transpose-convolution-layers-for-generative-adversarial-networks/
分かりやすい記事に感謝。
オートエンコーダーとしてのU-Net
https://qiita.com/koshian2/items/603106c228ac6b7d8356
def backward(self, dout): # dout (3x10)
dx = np.dot(dout, self.W.T) # W (50x10) W.T (10x50)
self.dW = np.dot(self.x.T, dout) # x (3x50) x.T (50x3)
self.db = np.sum(dout, axis=0)
dx = dx.reshape(*self.original_x_shape)
return dx
self.x は affine 変換する前のレイヤーに入力される値。
全然関係無いですが、ニューラルネットワークをレイヤーの概念図で表すと、入力する値の位置とアフィン変換する位置が、コードと一階層(半階層)ズレてる感覚を受ける。コードで記述する時は、レイヤーの層を表す図から左側の接続の線までを含んでいるイメージを持つようにする。
自身のレイヤーの重みの勾配は np.dot(self.x.T, dout) で求まり、バイアスは np.sum(dout) で総和を取る。
参照コード
ゼロから作る Deep Learningn から
疑問に思って調べたら分かりやすかった。
GroupMemoryBarrier? GroupMemoryBarrierWithGroupSync?
https://github.com/Microsoft/DirectX-Graphics-Samples/issues/140
質問者さんと同じ疑問があったので、すごく勉強になった。