畳み込みニューラルネットワーク ネットワーク構造図 図2 畳み込みニューラルネットワークの構造図 畳み込みニューラル ネットワークと完全接続ニューラル ネットワークの構造には大きな違いがあります。完全接続ネットワークでは、隣接する 2 つの層のノードがエッジで接続されますが、畳み込みニューラル ネットワークでは、隣接するノードの一部のみが接続されます。 画像処理における完全接続ニューラル ネットワークの最大の問題は、完全接続層にパラメータが多すぎることです。パラメータが多すぎると、簡単にオーバーフィッティングが発生し、計算速度が低下します。畳み込みニューラル ネットワークでは、パラメータの数を減らすことができます。 入力が 28*28*3 のサイズの画像で、最初の隠れ層に 500 個のノードがあると仮定すると、最初の層のパラメータは 28*28*3*500+500=1176000 パラメータになります。画像が大きくなると、パラメータの数も増えますが、これは最初の層のみです。 では、なぜ畳み込みニューラル ネットワークはパラメータを削減するという目標を達成できるのでしょうか? 畳み込みニューラル ネットワークの最も重要な部分は、畳み込み層、プーリング層、および完全接続層です。 畳み込み層 畳み込み層の各ノードの入力は、前の層のニューラル ネットワークのごく一部に過ぎず、通常は畳み込みカーネルによって実装されます。畳み込みカーネルはフィルターであり、スキャン ウィンドウとして考えることができます。各画像に取り付けられ、設定されたサイズとステップ サイズに従って画像をスキャンします。計算ルールは、取り付けられた画像のピクセル マトリックスに畳み込みカーネルの重みの対応する位置を乗算して合計し、スキャンするたびに出力を取得することです。畳み込み層によって行われる作業は、画像ピクセルの小さなブロックごとに特徴を抽象化するものとして理解できます。同じ画像を複数の異なる畳み込みカーネルで畳み込むことができます。畳み込みカーネルの数は、実際には畳み込み後の出力行列の深さです。畳み込みニューラル ネットワークのパラメーターの数は画像のサイズとは関係なく、フィルターのサイズと深度、および畳み込みカーネルの数 (出力行列の深度) のみに関係します。画像が依然として 28*28*3 であると仮定すると、畳み込みカーネルのサイズは 3*3*3 に設定され、出力行列の深さは 500 に設定され、パラメータの数は 3*3*3*500+500=14000 パラメータになります。完全接続層と比較すると、パラメータの数が大幅に削減されます。 図3: 畳み込み層の例 プーリング層 プーリング層は、高解像度の画像を低解像度の画像に変換するものと考えることができます。行列のサイズを効果的に縮小できるため、完全接続層のパラメータ数を削減でき、過剰適合を防ぎながら計算速度を上げることができます。プーリングにより、モデルを縮小し、速度を上げ、抽出された特徴の堅牢性を向上させることができます。 ステップ サイズが 2 の 2*2 フィルターを使用すると、プーリングは次のようになります。 図4 2×2フィルタプーリングの例 畳み込み層とプーリング層は自動特徴抽出と考えることができます。 上記の直感的な紹介を通じて、畳み込みニューラル ネットワークがパラメータを削減する目的を達成できる理由がわかりましたか? 完全に接続されたニューラル ネットワークと比較すると、畳み込みニューラル ネットワークの利点は、共有された重みとスパースな接続にあります。共有重みは、パラメータがフィルターにのみ関連していることを意味します。畳み込みニューラル ネットワークがパラメータを削減するもう 1 つの理由は、スパース接続です。出力ノードは、入力画像マトリックスの部分ピクセル マトリックス、つまり畳み込みカーネルによって折り畳まれるマトリックスの小さな部分に関連します。これがスパース接続の概念です。 畳み込みニューラル ネットワークは、重みの共有とスパース接続を通じてパラメータを削減します。これにより、過剰適合が防止されます。 トレーニングプロセス 畳み込みニューラル ネットワークのトレーニング プロセスは、おおまかに次の手順に分けられます。 ステップ1: 関連ライブラリをインポートし、パラメータをロードする - インポート数学
- numpyをnpとしてインポートする
- テンソルフローをtfとしてインポートする
- matplotlib.pyplot をpltとしてインポートします。
- h5pyをインポートする
- tensorflow.python.frameworkからopsをインポートする
- tf_utilsからインポート *
- np.ランダムシード(1)
- X_train_orig、Y_train_orig、X_test_orig、Y_test_orig、クラス = load_dataset()
- インデックス=0
- plt.imshow(X_train_orig[インデックス])
- print( "y=" +str(np.squeeze(Y_train_orig[:, index ])))
- plt.show()
ステップ2: 正規化により勾配降下法の高速化が可能 - X_train=X_train_orig/255.0
- X_テスト=X_テスト_orig/255.0
- Y_train = convert_to_one_hot(Y_train_orig,6)
- Y_test=convert_to_one_hot(Y_test_orig,6)
ステップ3: パラメータと畳み込みニューラルネットワーク構造を定義する - def create_placeholder(num_px,チャンネル,n_y):
- X = tf.placeholder(tf.float32, シェイプ = (なし、num_px、num_px、チャネル)、名前= 'X' )
- Y = tf.placeholder(tf.float32, 形状 = (なし、n_y),名前= 'Y' )
- X,Yを返す
- X,Y=プレースホルダーを作成(64,3,6)
- print( "X=" +str(X))
- print( "Y=" +str(Y))
-
- 定義重み変数(形状):
- tf.Variable(tf.truncated_normal(shape,stddev=0.1))を返します。
- 定義バイアス変数(形状):
- tf.Variable(tf.constant(0.1,shape=shape))を返します
- def conv2d(x,W):
- tf.nn.conv2d(x,W,strides=[1,1,1,1],padding= 'SAME' )を返します。
- max_pool_2x2(x)を定義します。
- tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding= 'SAME' )を返します。
-
- definitialize_parameters():
- w_conv1 = 重み変数([5,5,3,32])
- b_conv1 = バイアス変数([32])
-
- w_conv2 = 重み変数([5,5,32,64])
- b_conv2 = バイアス変数([64])
-
- w_fc1=重み変数([16*16*64,512])
- b_fc1 = バイアス変数([512])
-
- w_fc2 = 重み変数([512,6])
- b_fc2 = バイアス変数([6])
-
- パラメータ={
- "w_conv1" :w_conv1,
- "b_conv1" :b_conv1,
- "w_conv2" :w_conv2,
- "b_conv2" :b_conv2,
- "w_fc1" :w_fc1,
- "b_fc1" :b_fc1,
- "w_fc2" :w_fc2,
- "b_fc2" :b_fc2
- }
- 戻りパラメータ
ステップ4: フォワード伝播プロセス - def forward_propagation(X,パラメータ):
- w_conv1 = パラメータ[ "w_conv1" ]
- b_conv1 = パラメータ[ "b_conv1" ]
- h_conv1 = tf.nn.relu(conv2d(X,w_conv1)+b_conv1) です。
- h_pool1 = 最大プール2x2(h_conv1)
-
- w_conv2 = パラメータ[ "w_conv2" ]
- b_conv2 = パラメータ[ "b_conv2" ]
- h_conv2 = tf.nn.relu(conv2d(h_pool1,w_conv2)+b_conv2)
- h_pool2 = max_pool_2x2(h_conv2) です
-
- w_fc1 = パラメータ[ "w_fc1" ]
- b_fc1 = パラメータ[ "b_fc1" ]
- h_pool2_flat = tf.reshape(h_pool2、[-1, 16*16*64]) を返します。
- h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat、w_fc1) + b_fc1)
-
- #keep_prob=tf.placeholder(tf.float32)
- #h_fc1_drop=tf.nn.dropout(h_fc1,keep_prob)
-
- w_fc2 = パラメータ[ "w_fc2" ]
- b_fc2 = パラメータ[ "b_fc2" ]
- y_conv=tf.matmul(h_fc1,w_fc2)+b_fc2
- y_convを返す
ステップ5: コスト関数 - def compute_cost(y_conv,Y):
-
- コスト = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=y_conv,labels=Y))
- 返品費用
ステップ6: 勾配降下法によるパラメータの更新 - def random_mini_batches1(X, Y, mini_batch_size = 64, seed = 0):
- m = X.shape[0] #トレーニング例の数
- ミニバッチ = []
- np.random.seed(シード)
- Y=YT #(1080,6)
- # ステップ 1: シャッフル (X, Y)
- 順列 = リスト(np.random.permutation(m))
- shuffled_X = X[順列,:,:,:]
- shuffled_Y = Y[順列,:].reshape((m,Y.shape[1]))
-
- # ステップ 2: パーティション (shuffled_X、shuffled_Y)。末尾を除く 場合。
- num_complete_minibatches = math.floor(m/mini_batch_size) #ミニバッチの数 パーティションのサイズmini_batch_size
- kが範囲(0, num_complete_minibatches)内である場合:
- mini_batch_X = shuffled_X[k * mini_batch_size : k * mini_batch_size + mini_batch_size,:,:,:]
- mini_batch_Y = shuffled_Y[k * mini_batch_size : k * mini_batch_size + mini_batch_size,:]
- ミニバッチ = (ミニバッチ_X、ミニバッチ_Y)
- mini_batches.append(ミニバッチ)
-
- #終わりの扱い ケース(最後のミニバッチ < mini_batch_size)
- m % mini_batch_size != 0 の場合:
- mini_batch_X = shuffled_X[num_complete_minibatches * mini_batch_size : m,:,:,:]
- mini_batch_Y = shuffled_Y[num_complete_minibatches * mini_batch_size : m,:]
- ミニバッチ = (ミニバッチ_X、ミニバッチ_Y)
- mini_batches.append(ミニバッチ)
-
- mini_batchesを返す
ステップ7: モデルをトレーニングする - def model(X_train、Y_train、X_test、Y_test、learning_rate=0.001、num_epochs=20、minibatch_size=32、print_cost= True ):
- ops.reset_default_graph() #(1080, 64, 64, 3)
- tf.set_random_seed(1) #Y_train(6, 1080)
- シード=3
- (m,num_px1,num_px2,c)=X_train.shape
- n_y = Y_train.shape[0]
- コスト=[]
- X,Y=プレースホルダーを作成(64,3,6)
- パラメータ = initialize_parameters()
-
- Z3=forward_propagation(X,パラメータ)
- コスト = compute_cost(Z3,Y)
- optm = tf.train.AdamOptimizer(学習率).最小化(コスト)
-
- correct_prediction = tf.equal(tf.argmax(Z3,1), tf.argmax(Y,1)) #1 を忘れたので、損失はどんどん小さくなっていますが、精度は常に 0 です
- 精度 = tf.reduce_mean( tf.cast (correct_prediction, tf.float32))
- tf.Session()を sessとして使用:
- tf.global_variables_initializer().run()
- 範囲(num_epochs)内のエポックの場合:
- エポックコスト=0
- num_minibatches = int (m/ミニバッチサイズ)
- シード+=1
- #次の入力は (6, 1080) 形式が必要なので、転置が必要です
- ミニバッチ = random_mini_batches1(X_train、Y_train、ミニバッチサイズ、シード)
-
- ミニバッチ内のミニバッチの場合:
- (ミニバッチ_X、ミニバッチ_Y)=ミニバッチ
- _、ミニバッチコスト=sess.run([optm、コスト]、フィードディクショナリ={X:ミニバッチ_X、Y:ミニバッチ_Y})
- エポックコスト + = ミニバッチコスト / ミニバッチ数
- if(print_cost == True エポック% 2==0):
- #print( "エポック" , '%04d' % (エポック+1), "コスト={:.9f}" .format(epoch_cost))
- print( "エポック後のコスト %i:%f" % (epoch,epoch_cost))
- if(print_cost == True エポック%1==0):
- コスト.append(epoch_cost)
-
- print( "列車の精度: " , acceleration.eval({X:X_train,Y:Y_train.T}))
- print( "テスト精度: " , acceleration.eval({X:X_test,Y:Y_test.T}))
- plt.plot(np.squeeze(コスト))
- plt.ylabel( 'コスト' )
- plt.xlabel( '反復回数(10回ごと)' )
- plt.title( "学習率=" +str(learning_rate))
- plt.show()
-
- パラメータ = sess.run(パラメータ)
- 戻りパラメータ
- パラメータ = モデル(X_train、Y_train、X_test、Y_test)
|