前回の記事では、畳み込みニューラルネットワークの基本原理について、いくつかの基本層の定義、動作ルールなどについて説明しました。この記事では、主に、順方向伝播と逆方向伝播を含む畳み込みニューラル ネットワークの完全なトレーニングを実行する方法と、畳み込みニューラル ネットワークを手動で記述する方法について説明します。基本的な原理が分からない方は、まず前回の記事をお読みください: [ディープラーニングシリーズ] 畳み込みニューラルネットワーク(CNN)の原理を詳しく解説(I) - 基本原理 畳み込みニューラルネットワークの順伝播 まず、最も単純な畳み込みニューラル ネットワークを見てみましょう。 1. 入力層---->畳み込み層 前のセクションの例を例にとると、入力は 4*4 画像であり、2 つの 2*2 畳み込みカーネルによる畳み込み演算の後、2 つの 3*3 feature_maps に変換されます。 畳み込みカーネル filter1 を例に挙げます (ストライド = 1)。 最初の畳み込み層ニューロンo11の入力を計算します。 neto11 = conv ( 入力、フィルタ) = i11 × h11 + i12 × h12 + i21 × h21 + i22 × h22 = 1 × 1 + 0 × ( −1 ) + 1 × 1 + 1 × ( −1 ) = 1ニューロン o 11の出力: (ここでは Relu 活性化関数が使用されています) outo11 =アクティベーター( neto11 ) =最大( 0 、 neto11 ) = 1 ( 2 ) ( 2 ) outo11 = アクティベーター( neto11 ) =最大( 0 、 neto11 ) = 1他のニューロンも同様に計算される 2. 畳み込み層 ----> プーリング層 プーリング層の入力m 11を計算する(ウィンドウを2 * 2とする)。プーリング層には活性化関数がない。 netm11 = max ( o11 、 o12 、 o21 、 o22 ) = 1 outm11 = netm11 = 1 ( 3 ) ( 3 ) netm11 = max ( o11 、 o12 、 o21 、 o22 ) = 1 outm11 = netm11 = 13.プーリング層 ----> 完全結合層 プーリング層の出力は、すべての要素を「平坦化」する平坦化層に送られ、その後、完全接続層に送られます。 4.全結合層---->出力層 出力層への完全結合層は、ニューロン間の通常の接続です。ニューロンはソフトマックス関数によって計算され、出力に出力され、異なるカテゴリの確率値を取得します。画像のカテゴリは、出力確率値が最大のカテゴリです。
畳み込みニューラルネットワークのバックプロパゲーション 従来のニューラルネットワークは完全に接続されています。バックプロパゲーションを実行すると、前の層の偏微分を次の層から連続的に計算する、つまり連鎖偏微分を計算することで、各層のエラーに敏感な項目を取得できます。次に、重みとバイアス項目の勾配を計算して重みを更新できます。畳み込みニューラル ネットワークには、畳み込み層とプーリング層という 2 つの特殊な層があります。プーリング層の出力は活性化関数を経由する必要はありません。これはスライディング ウィンドウの最大値と定数であるため、偏微分は 1 です。プーリング層は、上位層の画像を圧縮することと同等です。この逆の方法でエラーに敏感な項目を計算する方法は、従来のバックプロパゲーション法とは異なります。畳み込み feature_map から前のレイヤーに逆伝播する場合、feature_map は順伝播中に畳み込みカーネルを介した畳み込み演算によって取得されるため、逆伝播は従来のものと異なり、畳み込みカーネルのパラメータを更新する必要があります。次に、プーリング層と畳み込み層がどのようにバックプロパゲーションを行うのかを紹介します。 はじめに、まず従来のバックプロパゲーション法について確認してみましょう。 1.順伝播により各層の入力値neti 、 jを計算する。 (例えば、 畳み込み後のfeature_mapの最初のニューロンの入力: neti11 neti11 ) 2. バックプロパゲーションは各ニューロンの誤差項δi , jを計算する。 , δ i , j = ∂ E ∂ n e t i , j δi,j=∂E∂neti,j 、ここで E は損失関数によって計算された全体的な誤差であり、二乗差、交差エントロピーなどで表すことができます。 3. 各ニューロンの重みwi 、 jを計算する ηi 、 j = ∂E∂neti 、 j⋅∂neti 、 j∂wi 、 j = δi 、 j⋅outi 、 j の勾配 4. 重みを更新するwi , j = wi , j − λ ⋅ ηi , j wi , j =wi,j−λ⋅ηi,j (ここでλ は学習率) 畳み込み層のバックプロパゲーション 順方向伝播から次の結果が得られます。 私11 n e t o 11 o u t o 11 = o u t i 11 = actions ( n e t i 11 ) = con v ( in p u t , filter ) = i 11 × h 11 + i 12 × h 12 + i 21 × h 21 + i 22 × h 22 = actions ( n e t o 11 ) = max ( 0 , n e t o 11 ) ( 4 ) ( 4 ) i11 = outi11 = activators ( neti11 ) neto11=conv(入力,フィルタ)=i11×h11+i12×h12+i21×h21+i22×h22outo11=アクティベーター(neto11)=max(0,neto11)neti11は前の層の入力を表し、 outo11は前の層の出力を表す。 まず、畳み込みの前の層の最初の要素i 11 i11の誤差項δ 11 δ11を計算します。 注: ここで入力層の誤差項が計算されると書くのは正確ではありません。ここで、 i 11 i11 は畳み込み層の前の層を指します。 まず∂ E ∂ i 11 ∂E∂i11を計算します。 ここでは∂ E ∂ i 11 ∂E∂i11の計算方法がわからないため、入力層が畳み込みカーネルを介して畳み込み演算を完了した後、まず出力 feature_map を書き出すことができます。 32 × H 22 N E T O 22 = I 22 × H 11 + I 23 × H 12 + I 22 × H 21 + I 23 × H 22 N E T O 21 = I 21 × H 11 + I 22 × H 12 + I 31 × H 21 + I 12 + I 31 × h 21 + I 32 × H 22 N E T O 22 = I 22 × H 11 + I 23 × H 12 + I 32 × H 21 + I 33 × H 22 33 = i 33 × h 11 + i 34 × h 12 + i 43 × h 21 + i 44 × h 22 4 = i 33 × h 11 + i 34 × h 12 + i 43 × h 21 + i 44 × h 22 4 = i 31 × h 11 + i 32 × h 12 + i 41 × h 21 + i 42 × h 22 4 = i 32 × h 11 + i 33 × h 12 + i 42 × h 21 + i 43 × h 22 (5)neto11=i11×h11+i12×h12+i21×h21+i22×h22neto12=i12×h11+i13×h12+i22×h21+i23×h22neto12=i13×h11+i14×h12+i23×h21+i24×h22neto21=i21×h11+i22×h12+i31×h21+i32×h22neto22=i22×h11+i2 3×h12+i32×h21+i33×h22neto23=i23×h11+i24×h12+i33×h21+i34×h22neto31=i31×h11+i32×h12+i41×h21+i42×h22neto32=i32×h11+i33×h12+i42×h21+i43×h22neto33=i33×h11+i34×h12+i43×h21+i44×h22次に、入力要素i i 、 j ii,jの偏微分を順に計算する。 i 11 i11の偏微分は次のようになります。 ∂E∂i11 = ∂E∂n e t o 11⋅∂n e t o 11∂i11 = δ11⋅h11 ( 6 ) ( 6 ) ∂E∂i11 = ∂E∂neto11⋅∂neto11∂i11 = δ11⋅h11i 12 i12の偏微分は次のようになります。 ∂E∂i12 = ∂E∂neto11⋅∂neto11∂i12 + ∂E∂neto12⋅∂neto12∂i12 = δ11⋅h12 + δ12⋅h11 ( 7 ) ( 7 ) ∂E∂i12 = ∂E∂neto11⋅∂neto11∂i12 + ∂E∂neto12⋅∂neto12∂i12 = δ11⋅h12 + δ12⋅h11i 13 i13の偏微分は次のようになります。 ∂E∂i13 = ∂E∂neto12⋅∂neto12∂i13 + ∂E∂neto13⋅∂neto13∂i13 = δ12⋅h12 + δ13⋅h11 ( 8 ) ( 8 ) ∂E∂i13 = ∂E∂neto12⋅∂neto12∂i13 + ∂E∂neto13⋅∂neto13∂i13 = δ12⋅h12 + δ13⋅h11i 21 i21の偏微分は次のようになります。 ∂E∂i21 = ∂E∂neto11⋅∂neto11∂i21 + ∂E∂neto21⋅∂neto21∂i21 = δ11⋅h21 + δ21⋅h11 ( 9 ) ( 9 ) ∂E∂i21 = ∂E∂neto11⋅∂neto11∂i21 + ∂E∂neto21⋅∂neto21∂i21 = δ11⋅h21 + δ21⋅h11i 22 i22の偏微分は次のようになります。 ∂ E ∂ i 22 = ∂ E ∂ n e t o 11 ∂ n e t o 11 ∂ i 22 + ∂ E ∂ n e t o 12 ⋅ ∂ n e t o 12 ∂ i 22 + ∂ E ∂ n e t o 21 ⋅ ∂ n e t o 21 ∂ i 22 + ∂ E ∂ n e t o 22 ⋅ ∂ n e t o 22 ∂ i 22 = δ 11 ⋅ h 22 + δ 12 ⋅ h 21 + δ 21 ⋅ h 12 + δ 22 ⋅ h 11 ( 10) (10)∂E∂i22=∂E∂neto11⋅∂neto11∂i22+∂E∂neto12⋅∂neto12∂i22+∂E∂neto21⋅∂neto21∂i22+∂E∂neto22⋅∂neto22∂i22=δ11⋅h22+δ12⋅h21+δ21⋅h12+δ22⋅h11上記の式の規則を観察し、それをまとめると次の式が得られます。 ⎡⎣⎢⎢⎢⎢⎢⎢⎢ 0 0 0 0 0 δ 11 δ 21 δ 31 0 0 δ 12 δ 22 δ 32 0 0 δ 13 δ 23 δ 33 0 0 0 0 0 0 ⎤⎦⎥⎥⎥⎥⎥⎥⎥ ⋅ [ h 22 h 12 h 21 h 11 ] = ⎡⎣⎢⎢⎢⎢⎢⎢⎢ ∂ E ∂ i 11 ∂ E ∂ i 21 ∂ E ∂ i 31 ∂ E ∂ i 41 ∂ E ∂ i 12 ∂ E ∂ i 22 ∂ E ∂ i 32 ∂ E ∂ i 42 ∂ E ∂ i 13 ∂ E ∂ i 23 ∂ E ∂ i 33 ∂ E ∂ i 43 ∂ E ∂ i 14 ∂ E ∂ i 24 ∂ E ∂ i 34 ∂ E ∂ i 44 ⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥ (11) (11)[000000δ11δ12δ1300δ21δ22δ2300δ31δ32δ33000000]⋅[h22h21h12h11]=[∂E∂i11∂E∂i12∂E∂i13∂E∂i14∂E∂i21∂E∂i22∂E∂i23∂E∂i24∂E∂i31∂E∂i32∂E∂i33∂E∂i34∂E∂i41∂E∂i42∂E∂i43∂E∂i44]図の畳み込みカーネルは180°反転され、この層の誤差に敏感な項目行列delta i , j delta i , j )とゼロパディングで畳み込まれ、 ∂E∂i11∂E∂i11が得られる。つまり、 ∂E∂ii 、 j = ∑m⋅∑nhm 、 nδi + m 、 j + n 最初の項を解いた後、2番目の項を解きましょう: ∂ i 11 ∂ n e t i 11 ∂i11∂neti11 ∵ i 11 ∴ ∂ i 11 ∂ n e t i 11 ∴ δ 11 = o t i 11 = a ct i v a t o s ( n e t i 11 ) = f ′ ( n e t i 11 ) = ∂ E ∂ n e t o 11 = ∂ E ∂ i 11 ⋅ ∂ i 11 ∂ n e t i 11 = ∑ m ⋅ ∑ n h m , n δ i + m , j + n ⋅ f ′ ( n e t i 11 ) ( 12 ) (12)∵i11=outi11=アクティベーター(neti11)∴∂i11∂neti11=f′(neti11)∴δ11=∂E∂neto11=∂E∂i11⋅∂i11∂neti11=∑m⋅∑nhm,nδi+m,j+n⋅f′(neti11)この時点で、エラー感度行列が計算されました。エラー感度行列を取得したら、重みの勾配を計算できます。 畳み込み層の入力neto11 neto11と重みhi 、 j hi 、j の間の式は上に書かれているので、次の式を直接求めることができます。 ∂E ∂ h 11 = ∂ E ∂ n e t o 11 ⋅ ∂ n e t o 11 ∂ h 11 + . . . + ∂ E ∂ n e t o 33 ⋅ ∂ n e t o 33 ∂ h 11 = δ 11 ⋅ h 11 + . . . + δ 33 ⋅ h 11 ( 13) (13)∂E∂h11=∂E∂neto11⋅∂neto11∂h11+...+∂E∂neto33⋅∂neto33∂h11=δ11⋅h11+...+δ33⋅h11重みの勾配を推測する: ∂E∂hi , j = ∑m∑nδm , noutoi + m , j + n ( 14 ) ( 14 ) ∂E∂hi , j = ∑m∑nδm , noutoi + m ,j+nバイアス項の勾配: ∂ E ∂ b = ∂ E ∂ n e t o 11 ∂ n e t o 11 ∂ w b + ∂ E ∂ n e t o 12 ∂ n e t o 12 ∂ w b + ∂ E ∂ n e t o 21 ∂ n e t o 21 ∂ w b + ∂ E ∂ n e t o 22 ∂ n e t o 22 ∂ w b = δ 11 + δ 12 + δ 21 + δ 22 = ∑ i ∑ j δ i , j (15) (15)∂E∂b=∂E∂neto11∂neto11∂wb+∂E∂neto12∂neto12∂wb+∂E∂neto21∂neto21∂wb+∂E∂neto22∂neto22∂wb=δ11+δ12+δ21+δ22=∑i∑jδi,jバイアス項の偏微分は、このレイヤー内のすべてのエラーに敏感な項の合計に等しいことがわかります。重みとバイアス項の勾配を取得した後、勾配降下法に従って重みと勾配を更新できます。 プーリング層のバックプロパゲーション プーリング層のバックプロパゲーションは見つけやすいです。下の図を見ると、左側が前の層の出力、つまり畳み込み層の出力 feature_map で、右側がプーリング層の入力です。計算しやすいように、フォワードプロパゲーションに基づいた式を書き出してみましょう。 前の層のスライディングウィンドウの最大値がo u t o 11 outo11であると仮定します。 ∵n e t m 11 = max ( o u t o 11 、 o u t o 12 、 o u t o 21 、 o u t o 22 ) これ 11 δ l − 1 12 = δ l − 1 21 = δ l − 1 22 = 0 (16) (16)∵netm11=max(outo11,outo12,outo21,outo22)∴∂netm11∂outo11=1∂netm11∂outo12=∂netm11∂outo21=∂netm11∂outo22=0∴δ11l−1=∂E∂outo11=∂E∂netm11⋅∂netm11∂outo11=δ11lδ12l−1=δ21l−1=δ22l−1=0このようにして、プーリング層のエラーに敏感な項行列が得られます。同様に、各ニューロンの勾配を計算し、重みを更新することができます。 畳み込みニューラルネットワークを手書きする 1. 畳み込み層を定義する まず、ConvLayer を使用して畳み込み層を実装し、畳み込み層のハイパーパラメータを定義します。 1 クラス ConvLayer(オブジェクト): 2 ''' 3 パラメータの意味: 4 input_width: 入力画像サイズ - 幅 5 input_height: 入力画像サイズ - 長さ 6 channel_number: チャンネル数。カラーの場合は 3、グレーの場合は 1 7 filter_width: 畳み込みカーネルの幅 8 filter_height: 畳み込みカーネルの長さ 9 filter_number: 畳み込みカーネルの数 10 zero_padding: ゼロパディングの長さ 11 stride: ステップの長さ 12 activator: 活性化関数 13 learning_rate: 学習率 14 ''' 15 def __init__(self, input_width, input_height, 16 チャンネル番号、フィルター幅、 17 フィルター高さ、フィルター番号、 18 ゼロパディング、ストライド、アクティベーター、 19 学習率): 20 自己.input_width = 入力幅 21 自己.input_height = 入力高さ 22 自己.チャンネル番号 = チャンネル番号 23 自己.filter_width = フィルター幅 24 自己.filter_height = フィルター高さ 25 自己.フィルター番号 = フィルター番号 26 自己ゼロパディング = ゼロパディング 27 自己ストライド = ストライド 28 自己.出力幅 = \ 29 ConvLayer.calculate_output_size( 30 自己入力幅、フィルター幅、ゼロパディング、 31歩) 32 自己.出力高さ = \ 33 ConvLayer.calculate_output_size( 34 自己入力高さ、フィルター高さ、ゼロパディング、 35歩) 36 self.output_array = np.zeros((self.filter_number, 37 自己.出力の高さ、自己.出力の幅)) 38 自己フィルター = [] 39 i が範囲内(フィルター番号): 40 self.filters.append(Filter(filter_width, 41 フィルター高さ、自己チャンネル番号)) 42 self.activator = アクティベーター 43 自己学習率 = 学習率 Calculate_output_size は、畳み込み演算後の feature_map サイズ出力を計算するために使用されます。 1 @静的メソッド 2 def calculate_output_size(input_size, 3 フィルターサイズ、ゼロパディング、ストライド): 4 戻り値 (input_size - filter_size + 5 2 * zero_padding) / ストライド + 1 2. 活性化関数を構築する ここでは RELU 活性化関数が使用されるため、activators.py で定義します。forward は順方向計算、backforward は計算式の導関数です。 1 クラス ReluActivator(オブジェクト): 2 定義forward(self, weighted_input): 3 #重み付けされた入力を返す 4 max(0, weighted_input)を返す 5 6 後方定義(自分自身、出力): 7 出力 > 0 の場合は 1 を返し、それ以外の場合は 0 を返す 次のように定義できるシグモイド関数など、他の一般的な活性化関数をアクティベーターに入れることもできます。 1 クラス SigmoidActivator(オブジェクト): 2 定義forward(self, weighted_input): 3 1.0 / (1.0 + np.exp(-weighted_input)) を返す 4 #シグモイドの部分 5 後方定義(自分自身、出力): 6 出力を返す * (1 - 出力) 他の活性化関数を自動的に使用する必要がある場合は、activator.py でクラスを定義できます。 3. 畳み込み層のパラメータと勾配を保存するクラスを定義する 1 クラス フィルター(オブジェクト): 2 __init__(self, 幅, 高さ, 深さ): 3 # 初期重み 4 self.weights = np.random.uniform(-1e-4, 1e-4, 5(奥行き、高さ、幅) 6 # 初期バイアス 7 self.bias = 0 8 自己重み勾配 = np.zeros( 9 自己重み形状) 10 自己バイアス勾配 = 0 11 12 __repr__(self)を定義します: 13 'フィルタの重み:\n%s\nバイアス:\n%s' % を返す ( 14 repr(自己重み)、repr(自己バイアス)) 15 16 定義 get_weights(self): 17 self.weightsを返す 18 19 定義 get_bias(self): 20 自己バイアスを返す 21 22 定義更新(自己、学習率): 23 自己重み -= 学習率 * 自己重み勾配 24 自己バイアス -= 学習率 * 自己バイアス勾配 4. 畳み込み層の順方向伝播 1). 畳み込み領域を取得する 1 # 畳み込み領域を取得 2 def get_patch(input_array, i, j, filter_width, 3 フィルター高さ、ストライド): 4 ''' 5 入力配列からこの畳み込みの面積を取得し、 6 入力を2Dおよび3Dの状況に自動的に適応させる 7 ''' 8 start_i = i * ストライド 9 start_j = j * ストライド 10 input_array.ndim == 2の場合: 11 input_array_conv = 入力配列[ 12 start_i : start_i + filter_height、 13 start_j : start_j + フィルター幅] 14 印刷 "input_array_conv:",input_array_conv 15 input_array_convを返す 16 17 elif input_array.ndim == 3: 18 input_array_conv = 入力配列[:, 19 start_i : start_i + フィルター高さ、 20 start_j : start_j + フィルター幅] 21 印刷 "input_array_conv:",input_array_conv 22 input_array_convを返す 2) 畳み込み演算を実行する 1 def conv(入力配列, 2 カーネル配列、 3 出力配列、 4 ストライド、バイアス): 5 ''' 6 畳み込みを計算し、入力を2Dと3Dに自動的に適応させる 7 ''' 8 チャネル番号 = 入力配列.ndim 9 出力幅 = 出力配列の形状[1] 10 出力高さ = 出力配列.shape[0] 11 カーネル幅 = カーネル配列の形状[-1] 12 カーネルの高さ = カーネルの配列の形状[-2] 13 i が範囲内(出力高さ)の場合: 14 範囲内のj(出力幅)の場合: 15 出力配列[i][j] = ( 16 get_patch(入力配列、i、j、カーネル幅、 17 カーネルの高さ、ストライド) * カーネルの配列 18 ).sum() + バイアス 3). zero_paddingを増やす 1 #ゼロパディングを追加 2 def padding(input_array, zp): 3 ''' 4 配列にゼロパディングを追加し、2Dと3Dの入力に自動的に適応します 5 ''' 6 zp == 0の場合: 7 入力配列を返す 8 その他: 9 input_array.ndim == 3の場合: 10 入力幅 = 入力配列の形状[2] 11 input_height = input_array.shape[1] 12 input_depth = input_array.shape[0] 13 パディング配列 = np.zeros(( 14 入力深度、 15 入力高さ + 2 * zp、 16 入力幅 + 2 * zp)) 17 パディング配列[:, 18 zp : zp + 入力高さ、 19 zp : zp + input_width] = input_array 20 padded_arrayを返す 21 elif input_array.ndim == 2: 22 input_width = input_array.shape[1] 23 input_height = input_array.shape[0] 24 パディング配列 = np.zeros(( 25 入力高さ + 2 * zp、 26 入力幅 + 2 * zp)) 27 padded_array[zp : zp + input_height, 28 zp : zp + input_width] = input_array 29 padded_arrayを返す 4) 順伝播を実行する 1 定義forward(self, input_array): 2 ''' 3 畳み込み層の出力を計算する 4 出力結果はself.output_arrayに保存される 5 ''' 6 自己.input_array = 入力配列 7 self.padded_input_array = padding(input_array, 8 自己ゼロパディング) 9 範囲内のfの場合(self.filter_number): 10 フィルター = self.filters[f] 11 conv(self.padded_input_array, 12 filter.get_weights()、self.output_array[f]、 13 self.stride、filter.get_bias()) 14 要素ごとの演算(自己.出力配列, 15 自己アクティベーターフォワード) element_wise_op 関数は、各グループの要素をそれに応じて乗算します。 1 # numpy配列に対して要素ごとの演算を実行し、行列の各要素を乗算します 2 def element_wise_op(array, op): 3 i を np.nditer(配列、 4 op_flags=['readwrite']): 5 i[...] = op(i) 5. 畳み込み層のバックプロパゲーション 1). エラーを前のレイヤーに渡す 1 定義bp_sensitivity_map(self, 感度配列, 2 アクティベーター): 3 ''' 4 前のレイヤーに渡された感度マップを計算する 5 感度配列: このレイヤーの感度マップ 6 アクティベーター: 前のレイヤー 7 の活性化関数 ''' 8 # 畳み込みステップを処理し、元の感度マップを拡張します 9 expand_array = self.expand_sensitivity_map( 10 感度配列) 11 # 完全な畳み込み、感度マップのゼロパディング 12 # 元の入力のゼロパディングユニットも残差を取得します 13 # ただし、この残差は渡す必要がないため、計算されません 14 expand_width = expand_array.shape[2] 15 zp = (self.input_width + 16 自己.filter_width - 1 - 拡張幅) / 2 17 padded_array = padding(expanded_array, zp) 18 # delta_array を初期化して、前のレイヤーに渡された 19 # 感度マップを保存します 20 自己.デルタ_配列 = 自己.作成_デルタ_配列() 21 # 複数のフィルターを持つ畳み込み層の場合、最終的に前の層に渡される 22 # 感度マップは、すべてのフィルターの 23 # 感度マップの合計に相当します 24 for f in range(self.filter_number): 25 フィルター = self.filters[f] 26 # フィルタの重みを180度反転します 27 flipped_weights = np.array(map( 28 ラムダ i: np.rot90(i, 2), 29 filter.get_weights())) 30 # フィルターに対応するdelta_arrayを計算する 31 delta_array = self.create_delta_array() 32 範囲(delta_array.shape[0])内のdの場合: 33 変換(パディング配列[f]、反転重み[d]、 34 デルタ配列[d], 1, 0) 35 自己デルタ配列 += デルタ配列 36 # 計算結果と活性化関数の偏微分を要素ごとに乗算します 37 derived_array = np.array(self.input_array) 38 要素ごとの演算(導関数配列、 39 アクティベーター(後方) 40 自己デルタ配列 *= 派生配列 2) 前のレイヤーに渡された感度マップの配列を保存する 1 def create_delta_array(self): 2 np.zeros((self.channel_number, を返す 3 自己.input_height、自己.input_width)) 3). コード勾配を計算する 1 定義bp_gradient(self, 感度配列): 2 # 畳み込みステップを処理し、元の感度マップを拡張します 3 expand_array = self.expand_sensitivity_map( 4 感度配列) 5 範囲内の f の場合(self.filter_number): 6 # 各重みの勾配を計算する 7 filter = self.filters[f] 8 dが範囲内(filter.weights.shape[0])の場合: 9 conv(self.padded_input_array[d], 10 拡張配列[f], 11 フィルター.weights_grad[d], 1, 0) 12 # バイアス項の勾配を計算する 13 filter.bias_grad = expand_array[f].sum() 4). 勾配降下法に従ってパラメータを更新する 1 定義更新(自己): 2 ''' 3 勾配降下法に従って重みを更新する 4 ''' 5 self.filters のフィルター: 6 フィルターを更新(自己学習率) 6. MaxPooling層のトレーニング 1). MaxPoolingクラスを定義する 1 クラス MaxPoolingLayer(オブジェクト): 2 def __init__(self, input_width, input_height, 3 チャンネル番号、フィルター幅、 4 フィルター高さ、ストライド): 5 自己.input_width = 入力幅 6 自己.input_height = 入力高さ 7 自己.チャンネル番号 = チャンネル番号 8 自己.filter_width = フィルター幅 9 自己.filter_height = フィルター高さ 10 自己ストライド = ストライド 11 self.output_width = (input_width - 12 フィルター幅) / 自己ストライド + 1 13 self.output_height = (input_height - 14 フィルター高さ) / 自己ストライド + 1 15 self.output_array = np.zeros((self.channel_number, 16 自己.出力の高さ、自己.出力の幅)) 2). 順伝播計算 1 # 順方向伝播 2 def forward(self, input_array): 3 d が範囲(self.channel_number)内にある場合: 4 i が範囲内(self.output_height)の場合: 5 範囲内のjの場合(self.output_width): 6 自己.出力配列[d,i,j] = ( 7 get_patch(入力配列[d], i, j, 8 自己.フィルター幅、 9 自己.フィルター高さ、 10 自己ストライド).max()) 3). バックプロパゲーション計算 1 #後方伝播 2 def backward(self, input_array, acceleration_array): 3 自己.デルタ配列 = np.ゼロ(入力配列.シェイプ) 4 d が範囲(self.channel_number)内にある場合: 5 範囲内の i の場合(self.output_height): 6 範囲内のjの場合(self.output_width): 7 パッチ配列 = get_patch( 8 入力配列[d], i, j, 9 自己.フィルター幅、 10 自己.フィルター高さ、 11 自己ストライド) 12 k, l = get_max_index(パッチ配列) 13 自己.デルタ配列[d, 14 i * 自己ストライド + k、 15 j * 自己ストライド + l] = \ 16 感度配列[d,i,j] 完全なコードについては、cnn.py (https://github.com/huxiaoman7/PaddlePaddle_code/blob/master/1.mnist/cnn.py) を参照してください。 コードを表示最後に、前の 4 * 4 画像データを使用して、畳み込みニューラル ネットワークを介した順方向伝播と逆方向伝播後の出力結果を確認します。 1 定義 init_test(): 2 a = np.array( 3 [[[0,1,1,0,2], 4 [2,2,2,2,1], 5 [1,0,0,2,0], 6 [0,1,1,0,0], 7 [1,2,0,0,2]], 8 [[1,0,2,2,0], 9 [0,0,0,2,0], 10 [1,2,1,2,1], 11 [1,0,0,0,0], 12 [1,2,1,1,1]], 13 [[2,1,2,0,0], 14 [1,0,0,1,0], 15 [0,2,1,0,1], 16 [0,1,2,2,2], 17 [2,1,0,0,1]]]) 18 b = np.array( 19 [[[0,1,1], 20 [2,2,2], 21 [1,0,0]], 22 [[1,0,2], 23 [0,0,0], 24 [1,2,1]]]) 25 cl = ConvLayer(5,5,3,3,3,2,1,2,IdentityActivator(),0.001) 26 cl.filters[0].weights = np.array( 27 [[[-1,1,0], 28 [0,1,0], 29 [0,1,1]], 30 [[-1,-1,0], 31 [0,0,0], 32 [0,-1,0]], 33 [[0,0,-1], 34 [0,1,0], 35 [1,-1,-1]]], dtype=np.float64) 36 cl.filters[0].bias=1 37 cl.filters[1].weights = np.array( 38 [[[1,1,-1], 39 [-1,-1,1], 40 [0,-1,1]], 41 [[0,1,0], 42 [-1,0,-1], 43 [-1,1,0]], 44 [[-1,0,0], 45 [-1,0,1], 46 [-1,0,0]]], dtype=np.float64) 47 a、b、clを返す 実行します: 1 定義テスト(): 2 a、b、cl = init_test() 3 cl.forward(a) 4 「順方向伝播結果:」を印刷します。cl.output_array 5 cl.backward(a, b, IdentityActivator()) 6 cl.update() 7 print "Filter1 はバックプロパゲーション後に更新されました:",cl.filters[0] 8 print "Filter2 はバックプロパゲーション後に更新されました:",cl.filters[1] 9 10 __name__ == "__main__" の場合: 11 テスト() 操作結果: 1 順伝播結果: [[[ 6. 7. 5.] 2 [ 3. -1. -1.] 3 [ 2. -1. 4.]] 4 5 [[ 2. -5. -8.] 6 [ 1. -4. -4.] 7 [ 0. -5. -5.]]] 8 バックプロパゲーション後、フィルタ1が更新されます: フィルタの重み: 9 配列([[[-1.008, 0.99, -0.009], 10 [-0.005, 0.994, -0.006], 11 [-0.006, 0.995, 0.996]], 12 13 [[-1.004, -1.001, -0.004], 14 [-0.01, -0.009, -0.012], 15 [-0.002, -1.002, -0.002]], 16 17 [[-0.002, -0.002, -1.003], 18 [-0.005, 0.992, -0.005], 19 [ 0.993, -1.008, -1.007]]]) 20 バイアス: 21 0.99099999999999999 22 バックプロパゲーション後、フィルタ2が更新されます: フィルタの重み: 23 配列([[[ 9.98000000e-01, 9.98000000e-01, -1.00100000e+00], 24 [ -1.00400000e+00, -1.00700000e+00, 9.97000000e-01], 25 [ -4.00000000e-03, -1.00400000e+00, 9.98000000e-01]], 26 27 [[ 0.00000000e+00, 9.99000000e-01, 0.00000000e+00], 28 [ -1.00900000e+00, -5.00000000e-03, -1.00400000e+00], 29 [ -1.00400000e+00, 1.00000000e+00, 0.00000000e+00]], 30 31 [[ -1.00400000e+00, -6.00000000e-03, -5.00000000e-03], 32 [ -1.00200000e+00, -5.00000000e-03, 9.98000000e-01], 33 [ -1.00200000e+00, -1.00000000e-03, 0.00000000e+00]]]) 34 バイアス: 35 -0.00700000000000000001 PaddlePaddle 畳み込みニューラル ネットワーク ソース コード分析 畳み込み層 前回の記事では、畳み込みニューラルネットワークを実装するためのpaddlepaddleの機能について簡単に紹介しました。手書き数字認識では、CNN のネットワーク構造を設計する際に、 simple_img_conv_pool という関数を呼び出しました (前回の記事のリンクはフレームワークが fluid に変更され、更新速度が速すぎるため無効です = =)。使い方は以下のとおりです。 1 conv_pool_1 = paddle.networks.simple_img_conv_pool( 2 入力=画像、 3 フィルターサイズ=5、 4 フィルター数=20, 5 num_channel=1、 6 プールサイズ=2、 7 プールストライド=2、 8 行為 = paddle.activation.Relu()) この関数は畳み込み層とプーリング層を一緒にカプセル化します。 1 つの関数を呼び出すだけで実行できるため、非常に便利です。畳み込み層だけを使用する必要がある場合は、この関数 img_conv_layer を呼び出して、次のように使用できます。 1 変換 = img_conv_layer(入力=データ、フィルターサイズ=1、フィルターサイズy=1、 2 チャンネル数=8、 3 num_filters=16、ストライド=1、 4 バイアス属性=False、 5 行為=ReluActivation()) この関数の特定のパラメータを見てみましょう(コメントでパラメータの意味と使用方法を説明しています) 1 定義img_conv_layer(入力, 2 フィルターサイズ、 3 num_filters、 4 名前=なし、 5 num_channels=なし、 6 行為=なし、 7グループ=1、 8 ストライド=1、 9 パディング=0、 10 膨張=1、 11 バイアス属性=なし、 12 param_attr=なし、 13 共有バイアス=True、 14 レイヤー属性=なし、 15 filter_size_y=なし、 16 stride_y=なし、 17 padding_y=なし、 18 dilation_y=なし、 19 トランス=偽、 20 レイヤータイプ=なし): 21 """ 画像に適した 22 個の畳み込み層。パドルは、正方形と長方形の 2 つの画像サイズの入力をサポートできます。 24 画像の逆畳み込み(畳み込み転置、deconv)にも適用できます。 25 正方形と長方形の入力サイズもサポートします。 26 27 num_channel: 入力画像のチャンネル数。 1 または 3、あるいは前の層のチャネル数(畳み込みカーネルの数 * グループ数)になります。 28 各グループは画像のいくつかのチャネルを処理します。例えば、入力のnum_channelが256で4つのグループが設定されている場合、 29 32 畳み込みカーネルの場合、入力画像を処理するために 32*4 = 128 個の畳み込みカーネルが作成されます。チャネルは 4 つのブロックに分割され、32 個の畳み込みカーネルが最初に 64 (256/4=64) チャネルを処理します。残りのカーネル グループは残りのチャネルを処理します。 31 32 name: レイヤーの名前。オプション、カスタム。 33 タイプ:ベース文字列 34 35 入力: このレイヤーの入力 36 型:LayerOutPut 37 38 filter_size: 畳み込みカーネルの x 次元。幅として理解できます。 39 正方形の場合は、画像のサイズを表すタプルグループを直接入力できます 40 型: int/ タプル/ リスト 41 42 filter_size_y: 畳み込みカーネルの y 次元。高さとして理解できます。 43 PaddlePaddleは長方形の画像サイズをサポートしているので、畳み込みカーネルのサイズは(filter_size, filter_size_y)です。 44 45 タイプ:int/なし 46 47 act: 活性化関数の種類。 Reluはデフォルトで選択されています 48 タイプ:BaseActivation 49 50 グループ: 畳み込みカーネルのグループ数 51 型: int 52 53 54 ストライド: 水平方向のスライドステップのサイズ。または、ワールドがタプルを入力すると、水平方向の値のスライド ステップは同じになります。 55 タイプ:int/タプル/リスト 56 57 stride_y: 垂直方向のスライディングステップの長さ。 58 タイプ:int 59 60 パディング: ゼロパディングの水平方向の寸法。水平方向と垂直方向に同じゼロパディングの寸法を持つタプルを直接入力することもできます。 61 タイプ:int/タプル/リスト 62 63 padding_y: 垂直ゼロパディング寸法 64 型: int 65 66 膨張: 水平方向の拡張寸法。水平値と初期値が同じ拡張次元67であることを示すタプルを入力することもできます:type:int/ tuple/ list 68 69 dilation_y: 垂直方向の拡張寸法 70 型: int 71 72bias_attr: バイアス属性 73False: バイアスは定義されていません True: バイアスは0に初期化されています 74 タイプ: ParameterAttribute/ None/ bool/ Any 75 76 num_channel: 入力画像のチャンネル。 Noneに設定すると、上位層出力用に自動的に生成されるチャネル数は77になります。タイプ: int 78 79 param_attr: 畳み込みパラメータ属性。デフォルト属性80を示すにはNoneに設定します param_attr:ParameterAttribute 81 82 shared_bias: バイアス項目を畳み込みカーネルで共有するかどうかを設定します 83 型: bool 84 85 layer_attr: レイヤーの追加属性 86 タイプ:ExtraLayerAttribute 87 88 パラメータ trans: convTransLayer の場合は True に設定、convlayer の場合は conv に設定 89 タイプ:bool 90 91 layer_type: レイヤータイプを指定します。デフォルトは None です。 92 trans=Trueの場合はexconvtまたはcudnn_convtでなければなりません。それ以外の場合はexconvまたはcudnn_convのいずれかです。 93 PS:デフォルトの場合、パドルはCPUのexpandConvlayerを自動的に選択し、GPUにはCudnnconvlayerを選択します 94もちろん、95タイプのタイプ:文字列を明示的に選択することもできます 96戻り:レイヤーアウトプットオブジェクト 97 rtype:layeroutput 98 99 "" " 100 101 102 def img_conv_layer(入力、 103 filter_size、 104 num_filters、 105 name = none、 106 num_channels = none、 107 act = none、 108グループ= 1、 109ストライド= 1、 110パディング= 0、 111拡張= 1、 112 bias_attr = none、 113 param_attr = none、 114 shared_biase = true、 115 layer_attr = none、 116 filter_size_y = none、 117 stride_y = none、 118 padding_y = none、 119拡張_y = none、 120 trans = false、 121 layer_type = none): 122 123 num_channelsがなしである場合: 124 assert input.num_filtersは誰でもありません 125 num_channels = input.num_filters 126 127 filter_size_yが存在しない場合: 128 if isinstance(filter_size、collections.sequence): 129 Assert Len(filter_size)== 2 130 filter_size、filter_size_y = filter_size 131 else: 132 filter_size_y = filter_size 133 134 stride_yがなしである場合: 135 If Isinstance(Stride、Collections.Sequence): 136 Assert Len(Stride)== 2 137ストライド、Stride_y = Stride 138 else: 139 Stride_y = Stride 140 141 padding_yが存在しない場合: 142 IF ISINSTANCE(パディング、コレクション。シーケンス): 143 Assert Len(パディング)== 2 144パディング、パディング_y=パディング 145 else: 146 PADDING_Y =パディング 147 148拡張_yがなしである場合: 149 If Isinstance(拡張、収集。シーケンス): 150アサートレン(拡張)== 2 151拡張、拡張_y =拡張 152他のもの: 153拡張_y =拡張 154 155 if param_attr.attr.get( 'initial_smart'): 156#convレイヤーの特別な初期。 157 init_w =(2.0 /(filter_size ** 2*num_channels))** 0.5 158 param_attr.attr ["initial_mean"] = 0.0 159 param_attr.attr ["initial_std"] = init_w 160 param_attr.attr ["initial_strategy"] = 0 161 param_attr.attr ["initial_smart"] = false 162 163 layer_typeの場合: 164拡張> 1または拡張_y> 1の場合: 165 assert layer_type in [ 166 "cudnn_conv"、 "cudnn_convt"、 "exconv"、 "exconvt" 167] 168トランスの場合: 169 ["exconvt"、 "cudnn_convt"のassert layer_type] 170 else: 171 ["exconv"、 "cudnn_conv"のassert layer_type] 172 lt = layer_type 173他のもの: 174 lt = layertype.convtrans_layer trans relse layertype.conv_layerの場合 175 176 L =レイヤー( 177 name = name、 178入力=入力( 179 input.name、 180 conv = conv( 181 filter_size = filter_size、 182パディング=パディング、 183拡張=拡張、 184ストライド=ストライド、 185チャンネル= num_channels、 186グループ=グループ、 187 filter_size_y = filter_size_y、 188 padding_y = padding_y、 189拡張_y =拡張_y、 190 stride_y = stride_y)、 191 ** param_attr.attr)、 192 Active_type = act.name、 193 num_filters = num_filters、 194バイアス= paramattr.to_bias(bias_attr)、 195 shared_biase = shared_biase、 196タイプ= lt、 197 ** extrayerattribute.to_kwargs(layer_attr)) 198 Return LayerOutput( 199名、 200 lt、 201の両親= [入力]、 202 activation = act、 203 num_filters = num_filters、 204サイズ= l.config.size) これらのパラメーターの意味を理解した後、手で書いたCNNと比較して、パドルパドルにはいくつかの利点があることがわかります。
CNNでは、正方形の画像の長さのみがサポートされている場合、エラーが報告されます。スライドステップサイズ、ゼロパッジング寸法などは、水平方向と垂直方向の同じ寸法のみをサポートします。畳み込みレイヤーのパラメーターの意味を理解した後、基礎となるソースコードがどのように実装されているかを見てみましょう。 同じことが、以前のアイデアに従ってそれを分析することができます。 (明日、Tensorflowのソースコードの実装を補うためにZhankeng) 要約する この記事では、主に、畳み込み層のバックプロパゲーションとプーリング層と従来のバックプロパゲーションの違いを含む、畳み込みニューラルネットワークにおけるバックプロパゲーションのいくつかの手法を説明し、将来的には完全なCNNを実装できます。私が書いたCNNと比較して、私は4つの利点をまとめました。文章は比較的大まかな質問がある場合は、メッセージを残してください:) 参考記事: 1.https://www.cnblogs.com/pinard/p/6494810.html 2.https://www.zybuluo.com/hanbingtao/note/476663 |
<<: DGX-2 および SXM3 カードが GTC 2018 で発表されました
>>: 人工知能が教育改革にどのように貢献しているかをご覧ください
人工知能は、脳内の機能的接続のスキャンを分析するだけで、人の政治的イデオロギーを予測することができま...
色をどのように表現するか考えたことはありますか?最新の研究によると、人間は個別の記号を使用して領域の...
7月18日のニュース、Businessinsiderによると、米国の若い労働者はキャリア危機に直面し...
データによれば、わが国には60歳以上の高齢者が2億6,400万人以上おり、そのうち1億8,000万人...
現代の産業環境にはセンサーやスマート コンポーネントが満載されており、それらすべてが組み合わさって大...
「スマート+」時代の到来とともに、人工知能、5G、モノのインターネット、ビッグデータなどの技術が徐...
地球上で最も複雑な知能の担い手である人間の脳の最大の特徴の 1 つは、高いエネルギー効率で知能を生み...
[[390934]] AI と機械学習の最近の研究では、一般的な学習と、ますます大規模なトレーニング...
人工知能は、知能機械や機械知能とも呼ばれ、人間が作った機械が示す知能を指します。人工知能は、医療、テ...
[[314711]]ボストン・ダイナミクス社が開発したスポットは、ノルウェーの石油会社アーケル社で独...
2019年、21歳の中国人学生、李凡は自身の微博に書き込みをした後、薬を飲んで自殺した。その後の調査...
人工知能データ収集およびラベリングのリーディングカンパニーであるYunce Dataは最近、「202...
機械学習は基本的に、既存のデータを使用して新しいデータについて予測を行う人工知能のサブセットです。も...