「人間のニューラルネットワークはどのように機能するのか?」この質問は多くのデータ サイエンティストを困惑させます。単純なニューラル ネットワークの仕組みを説明するのは簡単ですが、コンピューター ビジョン プロジェクトのレイヤー数が 1,000 倍に増えるとどうなるでしょうか。 エンドユーザーがモデルが最終結果に到達する仕組みを理解したい場合、紙と鉛筆だけでディープニューラルネットワークの動作メカニズムを説明するのは現実的ではありません。では、ニューラル ネットワークを「ブラック ボックス」ほど神秘的なものにしないためにはどうすればよいのでしょうか? 視覚化によってこれが可能になります。ニューラル ネットワークのさまざまな機能を視覚化すると、すべてが明確になり、何千もの画像で畳み込みニューラル ネットワーク (CNN) をトレーニングした結果を視覚化しやすくなります。 [[267042]] この記事では、畳み込みニューラル ネットワークを視覚化するためのさまざまな手法を紹介します。さらに、これらの視覚化からさまざまな洞察を抽出し、畳み込みニューラル ネットワーク モデルを改善することに取り組みます。 注: この記事では、ニューラル ネットワークと畳み込みニューラル ネットワークの基礎については説明しません。次の 3 つの記事は、関連する知識ポイントを確認したり理解したりするのに役立ちます。 - 畳み込みニューラル ネットワークをゼロから学ぶための包括的なチュートリアル: https://www.analyticsvidhya.com/blog/2018/12/guide-convolutional-neural-network-cnn/?utm_source=blog&utm_medium=understanding-visualizing-neural-networks
- ディープラーニングとニューラルネットワーク入門ガイド: https://www.analyticsvidhya.com/blog/2018/10/introduction-neural-networks-deep-learning/?utm_source=blog&utm_medium=understanding-visualizing-neural-networks
- ディープラーニングの基礎 – 人工ニューラル ネットワークから始める: https://www.analyticsvidhya.com/blog/2016/03/introduction-deep-learning-fundamentals-neural-networks/?utm_source=blog&utm_medium=understanding-visualizing-neural-networks
ニューラル ネットワークを視覚化する理由 これは研究する価値のある問題です。ニューラル ネットワークの仕組みを理解する方法は数多くありますが、なぜ視覚化という珍しい方法を採用するのでしょうか? この質問に例を挙げて答えてみましょう。たとえば、プロジェクトでは、ユキヒョウやアラビアヒョウなどの動物の画像を分類する必要があるかもしれません。直感的には画像の背景で判別できます。 この2つの動物の生息地は非常に異なります。ユキヒョウの画像のほとんどは雪を背景に撮影されていますが、アラビアヒョウの画像のほとんどは広大な砂漠を背景に撮影されています。 [[267043]] そこで疑問になるのが、モデルが雪と砂漠の画像を分類し始めたら、そのモデルが 2 種類のヒョウを区別する特徴を正しく学習したかどうかをどのように確認するかということです。その答えは視覚化です。 視覚化は、モデルが画像を分類する際にどのような特徴が役立つかを理解するのに役立ちます。 モデルを視覚化する方法はたくさんありますが、この記事ではそのうちのいくつかを紹介します。 モデルアーキテクチャの構築 学習する最良の方法は概念をエンコードすることです。したがって、この記事では Python コードを直接説明し、実用的なコーディング ガイドラインを提供します。 この論文では、VGG16 アーキテクチャを使用し、ImageNet データセットで事前トレーニング済みの重みを使用します。最初のステップは、モデルをプログラムにインポートし、そのアーキテクチャを理解することです。 次に、Keras の「model.summary()」関数を使用してモデル アーキテクチャを視覚化します。これは、モデル構築フェーズに入る前の重要なステップです。入力と出力の形状が問題ステートメントと一致することを確認する必要があるため、モデルの概要を視覚化する必要があります。 - #必要なモジュールのインポート
- keras.applications から VGG16 をインポートします
- #保存したモデルを読み込む
- #完全なアーキテクチャを使用しているため、 include_top = True
- モデル= VGG16 (重み= 'imagenet' 、 include_top = True )
- #モデルの概要を表示
- モデル.要約()
次の表は、上記のコードによって生成されたモデルの概要を示しています。 この表には、モデルの詳細なアーキテクチャと各レイヤーのトレーニング可能なパラメータの数を記録します。少し時間を取ってこれを読んで、私たちが今どこにいるのかを知っていただければ幸いです。 これは、モデル レイヤーのサブセットのみをトレーニングする場合 (特徴抽出) に特に重要です。モデルの概要を生成することで、トレーニングできないパラメータの数が、トレーニングしたくないレイヤーの数と一致することを確認できます。 さらに、開発者はトレーニング可能なパラメータの合計量を使用して、GPU がモデルをトレーニングするのに十分なメモリを割り当てることができるかどうかを確認できます。コンピューターを扱うほとんどの人にとって、この作業は一般的ですが、課題でもあります。 畳み込みニューラルネットワークの層を理解する モデルの全体的なアーキテクチャを理解したので、ニューラル ネットワークの各層をさらに深く掘り下げてみましょう。 実際、Keras モデルのレイヤーにアクセスし、重みやフィルターの数などの他の情報を含む各レイヤーの関連パラメータを抽出するのは非常に簡単です。 まず、レイヤー名を対応する機能と重みにマッピングする辞書を作成します。 - #レイヤー名とレイヤー詳細のマッピングを作成する
- #レイヤー名とその特性をマッピングする辞書layers_infoを作成します
- レイヤー情報= {}
- model.layers 内の i の場合:
- レイヤー情報[i.name] = i.get_config()
-
- #ここで、layer_weights辞書は、すべてのlayer_nameを対応する重みにマッピングします
- レイヤーの重み= {}
- model.layers 内の i の場合:
- レイヤーの重み[i.name] = i.get_weights()
-
- レイヤー情報['block5_conv1']を印刷します
上記コードの出力は、block5_conv1 レイヤーのさまざまなパラメータを含めて次のようになります。 - {'名前': 'block5_conv1',
- 「訓練可能」: 正しい、
- 'フィルター': 512,
- 'カーネルサイズ': (3, 3),
- 「ストライド」: (1, 1)、
- 'パディング': '同じ',
- 'データフォーマット': 'チャンネル最終',
- '膨張率': (1, 1),
- 'アクティベーション': 'relu',
- 'use_bias': 真、
- 'kernel_initializer': {'class_name': 'VarianceScaling',
- 'config': {'scale': 1.0,
- 'モード': 'fan_avg',
- 「分布」: 「均一」、
- 'シード': なし}},
- 'bias_initializer': {'class_name': 'Zeros', 'config': {}},
- 'kernel_regularizer': なし、
- 'bias_regularizer': なし、
- 'activity_regularizer': なし、
- 'kernel_constraint': なし、
- 'bias_constraint': なし}
「block5_conv1」レイヤーのトレーニング可能なパラメータ値は実数であるため、後でモデルをさらにトレーニングすることで重みを更新できます。 フィルター - 畳み込みニューラルネットワークの構成要素の視覚化 フィルターは畳み込みニューラル ネットワークの基本的な構成要素です。下の図に示すように、さまざまなフィルターが画像からさまざまなタイプの特徴を抽出します。 図に示すように、各畳み込み層は複数のフィルターで構成されています。前のセクションで説明した「block5_conv1」レイヤーのパラメータの概要をもう一度見てみると、このレイヤーには 512 個のフィルターが含まれていることがわかります。これは事実です。 各 VGG16 モジュールの最初の畳み込み層の最初のフィルターは、次のエンコーディングを使用してプロットできます。 - レイヤー= model.layers
- レイヤーID = [1,4,7,11,15]
- #フィルターをプロットする
- 図、 ax = plt.subplots ( nrows = 1 、 ncols = 5 )
- iが範囲(5)内にある場合:
- ax[i].imshow(レイヤー[layer_ids[i]].get_weights()[0][:,:,:,0][:,:,0], cmap = 'gray' )
- ax[i].set_title('ブロック'+str(i+1))
- ax[i].set_xticks([])
- ax[i].set_yticks([])
上記の出力結果は、異なるレイヤーのフィルターです。 VGG16 は 3×3 フィルターのみを使用するため、すべてのフィルターの形状とサイズは同じです。 活性化の最大化 - モデルが期待するものを視覚化する 最大活性化の概念を理解するには、次の図を使用します。 [[267046]] ゾウを識別する上で重要な特徴は何ですか? ここでは、より簡単に重要な機能をいくつか紹介します。 これが人間が直感的に象を識別する方法です。しかし、畳み込みニューラル ネットワークを使用してランダムな画像を最適化し、それを象として分類しようとすると、何が得られるのでしょうか? 畳み込みニューラル ネットワークでは、各畳み込み層は前の層の出力で類似のパターンを探します。入力に探しているパターンが含まれている場合に、最大の活性化が達成されます。 活性化最大化技術では、プロセス中の損失が最小化されるように各層への入力が更新されます。 どうすればこれを実行できるでしょうか? まず、入力に対する活性化損失の勾配を計算し、それに応じて入力を更新する必要があります。 [[267047]] メソッドのコードは次のとおりです。 - #必要なモジュールのインポート
- vis.visualization から visualize_activation をインポートします
- vis.utilsからutilsをインポート
- keras からアクティベーションをインポートする
- keras からアプリケーションをインポートする
- matplotlib.pyplot を plt としてインポートします。
- %matplotlib インライン
- plt.rcParams['figure.figsize'] = (18,6)
- #完全結合層を使用してVGG16モデルを作成すると、
- #個々のカテゴリーのパターンを視覚化する
- keras.applications から VGG16 をインポートします
- モデル= VGG16 (重み= 'imagenet' 、 include_top = True )
-
- #レイヤー名を使用してレイヤーインデックスを見つける
- #find_layer_idx関数は、モデルとレイヤー名をパラメータとして受け取り、それぞれのレイヤーのインデックスを返します。
- layer_idx = utils.find_layer_idx (モデル、'予測')
- #レイヤーのアクティブ化を線形に変更する
- model.layers[layer_idx] .activation =アクティベーション.linear
- #モデルに変更を適用する
- モデル= utils.apply_modifications (モデル)
- #インドゾウ
- img3 = visualize_activation (モデル、レイヤーIDX、フィルターインデックス= 385 、 max_iter = 5000 、詳細= True )
- plt.imshow(画像3)
インドゾウクラスに対応するランダム入力を使用するサンプルモデルは、次のように出力します。 [[267048]] 画像からわかるように、モデルが想定する構造は牙、大きな目、そして胴体です。この情報は、データセットの整合性を効果的にチェックするのに役立ちます。たとえば、モデルが関心のある特徴を背景にある木や草などの他の物体であると解釈するとします。インドゾウの生息地には多くの木や草があることが多いため、モデルは間違いを犯す可能性があります。次に、最大活性化を通じて、既存のデータセットはタスクを完了するのに十分ではない可能性があることが判明したため、ゾウの特徴を正確に識別するには、さまざまな生息地に生息するゾウの画像をトレーニング セットに追加する必要があります。 オクルージョンマップ — 入力プロセスの重要な部分を視覚化する 活性化最大化は主に、モデルの期待値を画像に視覚化するために使用されます。画像オクルージョンにより、モデルにとって重要な画像部分を見つけることができます。 ここで、画像オクルージョンがどのように機能するかを理解するために、トヨタ、アウディなどのメーカー別に車を分類できるモデルを構築しました。 [[267049]] 写真の車がどこの会社のものかわかりますか?難しいでしょうね。会社のロゴがある部分がブロックされているからです。明らかに、画像のブロックされた部分は、自動車の製造元を特定する上で非常に重要な手がかりとなります。 同様に、オクルージョン マップを生成するには、画像の一部をブロックし、特定のクラスに属する確率を計算します。確率が下がると、遮蔽された部分が分類を完了するために非常に重要であることを意味します。それ以外の場合、このセクションは重要ではありません。 サンプルプログラムは、画像の各部分のピクセル値に確率を関連付け、それらを正規化してヒートマップを生成します。 - numpyをnpとしてインポートする
-
- keras.utilsからnp_utilsをインポートする
- keras.modelsからSequentialをインポートする
- keras.layers から Dense、Dropout、Flatten、Activation、Conv2D、MaxPooling2D をインポートします
- keras.optimizersからAdamをインポートする
- keras.callbacks から EarlyStopping、ModelCheckpoint をインポートします
- keras.preprocessing.image から ImageDataGenerator をインポートします
- keras.activationsからreluをインポートします
-
- %matplotlib インライン
- matplotlib.pyplot を plt としてインポートします。
- iter_occlusion(画像、サイズ= 8 ):
-
- 閉塞= np.full ((サイズ * 5, サイズ * 5, 1), [0.5], np.float32)
- occlusion_center = np.full ((サイズ, サイズ, 1), [0.5], np.float32)
- occlusion_padding =サイズ* 2
-
- # print('padding...')
- image_padded = np .pad(image, ( \
- (オクルージョンパディング、オクルージョンパディング)、(オクルージョンパディング、オクルージョンパディング)、(0, 0) \
- )、'定数'、定数値= 0 .0)
-
- yが範囲内(occlusion_padding、image.shape[0] + occlusion_padding、サイズ)の場合:
-
- xが範囲内(occlusion_padding、image.shape[1] + occlusion_padding、サイズ)の場合:
- tmp = image_padded .copy()
-
- tmp[y - occlusion_padding:y + occlusion_center.shape[0] + occlusion_padding, \
- x - occlusion_padding:x + occlusion_center.shape[1] + occlusion_padding] \
- = 閉塞
-
- tmp[y:y + occlusion_center.shape[0], x:x + occlusion_center.shape[1]] = occlusion_center
-
- x - occlusion_padding、y - occlusion_padding、\ を生成します。
- tmp[occlusion_padding:tmp.shape[0] - occlusion_padding、occlusion_padding:tmp.shape[1] - occlusion_padding]
上記のコードで定義されている関数 iter_occlusion は、異なる遮蔽部分を持つ画像を生成できます。 これで、画像をインポートして処理することができます。 - keras.preprocessing.image から load_img をインポートします
- # ファイルから画像を読み込む
- 画像= load_img ('car.jpeg'、ターゲットサイズ=(224, 224))
- plt.imshow(画像)
- plt.title('元の画像')
[[267050]] 全部で3つのステップがあります: - 画像を前処理する
- 異なる閉塞部分の確率を計算する
- ヒートマップのプロット
- keras.preprocessing.image から img_to_array をインポートします
- keras.applications.vgg16 から preprocess_input をインポートします
- # 画像のピクセルをnumpy配列に変換する
- image = img_to_array (画像)
- # モデル用にデータを再形成する
- 画像画像= image.reshape((1, image.shape[0], image.shape[1], image.shape[2]))
- # VGGモデル用の画像を準備する
- 画像= preprocess_input (画像)
- # すべての出力クラスにわたる確率を予測する
- yhat = model.predict (画像)
- temp =画像[0]
- print(temp.shape)
- ヒートマップ= np.zeros ((224,224))
- 正しいクラス= np.argmax (yhat)
- n,(x,y,image) を enumerate(iter_occlusion(temp,14)) で指定します:
- ヒートマップ[x:x+14,y:y+14] = model.predict(image.reshape((1, image.shape[0], image.shape[1], image.shape[2])))[0][correct_class]
- print(x,y,n,' - ',image.shape)
- ヒートマップheatmap1 = heatmap / heatmap.max()
- plt.imshow(ヒートマップ)
面白いと思いませんか? 次に、正規化されたヒートマップの確率を使用してオクルージョンを作成し、描画します。 - skimage.io を io としてインポートします
- #標準化されたヒートマップ確率からマスクを作成する
- マスク=ヒートマップ1 < 0.85
- マスクマスク1 = マスク *256
- マスクマスク= mask.astype(int)
- io.imshow(マスク、 cmap = 'gray' )
[[267052]] 最後に、次の手順を使用して入力画像をマスクします。 - cv2をインポート
- #画像を読む
- 画像= cv2.imread ('car.jpeg')
- 画像= cv2.cvtColor (画像、cv2.COLOR_BGR2RGB)
- #画像を適切なサイズに変更する
- 画像= cv2.resize (画像、(224,224))
- マスクマスク= mask.astype('uint8')
- #画像にマスクを適用する
- 最終= cv2.bitwise_and (画像、画像、マスクマスク= マスク)
- 最終= cv2.cvtColor (最終、cv2.COLOR_BGR2RGB)
- #最終画像をプロットする
- plt.imshow(最終)
[[267053]] なぜ特定の部分だけが表示されるのかご想像ください。その通りです。出力される画像タイプの確率に大きく影響する部分だけが表示されるのです。簡単に言えば、それがオクルージョン マップのすべてです。 特徴マップ — 入力特徴の寄与を視覚化する 特徴マップは、もう 1 つの勾配ベースの視覚化手法です。このタイプの画像は、論文「Deep Inside Convolutional Networks: Visualizing Image Classification Models and Saliency Maps」で紹介されています。 特徴マップは、入力画像内の各ピクセルに対する出力の勾配を計算するなど、モデルの出力に対する各ピクセルの影響を計算します。 これは、入力画像のピクセルがわずかに変更された場合に出力クラスがどのように変化するかを示しています。グラデーションのすべての正の値は、ピクセル値の小さな変化によって出力値が増加することを示します。 [[267054]] これらのグラデーションは画像と同じ形状(グラデーションはピクセルごとに計算されます)であり、直感的な効果をもたらします。 では、サリエンシーマップはどのように生成するのでしょうか? まず、次のコードを使用して入力画像を読み取ります。 [[267055]] 次に、VGG16 モデルによって顕著性マップが生成されます。 - # 名前でレイヤーインデックスを検索するユーティリティ。
- # あるいは、最後のレイヤーに対応するため、これを -1 として指定することもできます。
- layer_idx = utils.find_layer_idx (モデル、'予測')
-
- # ソフトマックスを線形に置き換える
- model.layers[layer_idx] .activation =アクティベーション.linear
- モデル= utils.apply_modifications (モデル)
-
- #ガイドなしバックプロパゲーションによるサリエンシーマップの生成
- grads1 = visualize_saliency (モデル、layer_idx、 filter_indices = None 、 seed_input = image )
- #ガイドなしの顕著性マップをプロットする
- plt.imshow(grads1, cmap = 'jet' ) は、
ご覧のとおり、モデルは犬の顔に特に注目しています。次の図は、ガイド付きバックプロパゲーションを使用した後の結果を示しています。 - #ガイド付きバックプロパゲーションによるサリエンシーマップの生成
- grads2 = visualize_saliency (モデル、layer_idx、 filter_indices = None 、 seed_input = image 、 backprop_modifier = 'guided' )
- #顕著性マップをヒートマップとしてプロットする
- plt.imshow(grads2, cmap = 'jet' ) は、
ガイド付きバックプロパゲーションは、すべての負の勾配を 0 に変更します。つまり、クラス確率に正の影響を与えるピクセルのみが更新されます。 CAM (クラス活性化マップ) (勾配加重) CAM は、活性化マップの勾配や出力への貢献度に応じて重み付けするニューラル ネットワーク視覚化技術でもあります。 Grad-CAM 論文からの次の抜粋は、この技術の要点を示しています。 Grad-CAM は、任意のターゲット コンセプトの勾配 (「犬」の非正規化確率や単純なキャプションなど) を最終的な畳み込み層に使用して大まかなローカリゼーション マップを生成し、最終的にコンセプトを予測するために画像内の重要な領域を強調表示します。 |
基本的に、これは、最後の畳み込み層の特徴マップを取得し、特徴マップに対する出力の勾配を使用して各フィルターに重み付け (乗算) するだけで実行されます。重み付け勾配クラス活性化マップを生成するプロセスは、次の手順で構成されます。 - 最後の畳み込み層によって出力された特徴マップを使用します。 VGG16 の場合、特徴マップのサイズは 14x14x512 です。
- 特徴マップに対応する出力の勾配を計算します。
- 勾配のグローバル平均プーリングを実行します。
- 特徴マップを対応するプールされた勾配で乗算します。
入力画像とそれに対応するクラス活性化マップは次のようになります。 [[267058]] 下の図はクラスアクティベーション図です。 [[267059]] プロセスの階層的な出力を視覚化する 畳み込みニューラル ネットワークの最初の層では、通常、エッジなどの小さな詳細を探します。モデルについて詳しく知るにつれて、その特性は変化します。 モデルのさまざまなレイヤーの出力を視覚化することで、対応するレイヤーの画像の強調表示された特徴を直感的に表示できます。後続の問題に合わせてアーキテクチャを微調整するためには、視覚化が非常に重要なステップです。異なるレイヤーのさまざまな特性を確認し、モデルで使用する特定のレイヤーを決定できるためです。 たとえば、ニューラル スタイル転送問題におけるさまざまなレイヤーのパフォーマンスを比較する場合、視覚化出力が非常に役立ちます。 次のプログラムは、VGG16 モデルのさまざまなレイヤーの出力を実装する方法を示しています。 - #必要なライブラリと関数のインポート
- keras.modelsからモデルをインポート
- #出力を取得するレイヤーの名前を定義する
- レイヤー名= ['block1_conv1','block2_conv1','block3_conv1','block4_conv2']
- 出力= []
- 画像画像= image.reshape((1, image.shape[0], image.shape[1], image.shape[2]))
- #出力を抽出して出力に追加する
- レイヤー名内のレイヤー名の場合:
- 中間レイヤーモデル=モデル(入力=モデル.input、出力=モデル.get_layer(layer_name).output)
- 中間出力=中間層モデル.predict(画像)
- 出力.append(中間出力)
- #出力をプロットする
- 図、 ax = plt.subplots ( nrows = 4 、 ncols = 5 、 figsize =(20,20))
-
- iが範囲(4)内にある場合:
- zが範囲内(5)の場合:
- ax[i][z].imshow(出力[i][0,:,:,z])
- ax[i][z].set_title(レイヤー名[i])
- ax[i][z].set_xticks([])
- ax[i][z].set_yticks([])
- plt.savefig('layerwise_output.jpg')
[[267060]] 図に示すように、VGG16 の各レイヤー (ブロック 5 を除く) は画像から異なる特徴を抽出します。開始レイヤーはエッジなどの低レベルの特徴に対応し、次のレイヤーは屋根や排気口などの特徴に対応します。 結論 視覚化は常に素晴らしいです。テクノロジーの仕組みを理解する方法はたくさんありますが、その仕組みを視覚化するとプロセスがさらに楽しくなります。注目に値するホットなトピックは次のとおりです。 - ニューラル ネットワークの特徴抽出プロセスは非常に注目されている研究分野であり、TensorSpace や「Activation Atlases」などの多くのツールの開発も促進してきました。
- TensorSpace は、複数のモデル形式をサポートするニューラル ネットワーク視覚化ツールでもあります。モデルをロードし、インタラクティブな方法で視覚化できます。 TensorSpace には、ユーザーが複数のアーキテクチャと視覚化を使用してブラウザでニューラル ネットワークを試すことができる「プレイグラウンド」もあります。
|