ディープラーニングなどのエンドツーエンドのモデルの場合、トレーニングプロセスをどのように説明し理解するかは、ほとんどの研究者にとって注目の話題の 1 つです。この問題は、医療や軍事などの高リスクの業界にとって特に重要です。ディープラーニングでは、この問題は「ブラックボックス」と呼ばれます。モデルがどのように機能するかを説明できない場合、モデルの出力をどのようにして簡単に信頼できるのでしょうか? がんの腫瘍を検出するディープラーニング モデルを例に挙げてみましょう。このモデルは、がんを最大 99% の精度で検出できると説明していますが、どのように機能して判断を下すのかは説明していません。それで、モデルは MRI スキャンで重要な手がかりを見つけたのでしょうか?それとも、単にスキャン画像上の汚れが誤って腫瘍と判定されたのでしょうか?モデルの出力は患者の生死や治療計画に影響するため、医師はそのようなミスを犯すわけにはいかない。 [[224830]] この記事では、コンピューター ビジョンで最も広く使用されているネットワークである畳み込みニューラル ネットワーク (CNN) を視覚化する方法について説明します。まず、CNN モデルの視覚化の重要性を理解します。次に、いくつかの視覚化方法を紹介し、ユースケースを使用して、読者がモデルの視覚化の概念をよりよく理解できるようにします。 1. 畳み込みニューラルネットワークモデルの可視化の重要性 上記の癌腫瘍診断の例に見られるように、研究者は設計したモデルがどのように機能し、何を行うのかを明確に理解することが重要です。一般的に、ディープラーニングの研究者は次の点に留意する必要があります。 1. モデルの仕組みを理解する 2. モデルのパラメータを調整する 3. モデルが失敗した理由を調べる 4. モデルの決定を消費者/エンドユーザーまたは経営幹部に説明する ここで、ニューラル ネットワーク モデルを視覚化することで、その仕組みを理解し、モデルのパフォーマンスを向上させることができる例を見てみましょう。 かつて、米軍はニューラル ネットワークを使用して、カモフラージュされた敵の戦車を自動的に検出したいと考えていました。研究者たちは、迷彩柄の戦車の写真50枚と森林の写真50枚を使ってニューラルネットワークを訓練した。このモデルは、教師あり学習法を使用してトレーニングされました。研究者がネットワーク パラメータをトレーニングすると、ネットワーク モデルはトレーニング セットに対して正しい判断を下すことができました。カモフラージュされた戦車の写真 50 枚すべてが「はい」を出力し、木の写真 50 枚すべてが「いいえ」を出力しました。しかし、これはモデルが新しいサンプルを正しく分類できることを保証するものではありません。研究者たちは賢明にも、まず最初に迷彩柄の戦車の写真 100 枚と木の写真 100 枚を含む 200 枚の写真を撮影した。 50 枚の写真 (合計 100 枚の写真) をトレーニング セットとして選択し、残りの 100 枚の写真をテスト セットとして選択します。モデルはテスト セットも正しく分類できることが分かりました。そのため、研究者たちはモデルに問題はないと判断し、最終結果を軍に提出した。軍は研究結果に非常に満足するだろうと思っていましたが、軍からのフィードバックは、テスト後に効果は良くなかったというものでした。 [[224831]] 研究者たちは、この件が少し奇妙だと感じました。前回のテストでは 100% 正確だったのに、軍によるテストでは再び失敗したのはなぜでしょうか?最終的に、研究者のデータセットに問題があることが発覚しました。迷彩戦車が収集されたときは曇りでしたが、森が収集されたときは晴れていました。ニューラルネットワークは最終的に、迷彩戦車と森を区別するのではなく、晴れた日と曇りの日を区別することを学習しました。これは本当に笑えます。この問題の主な原因は、モデルの具体的な動作原理と機能がまだわかっていないことです。 2. CNNモデルの可視化方法 CNN 視覚化手法は、その内部動作原理に応じて、おおよそ次の 3 つのカテゴリに分類できます。 1. 予備的アプローチ: 訓練されたモデルの全体構造を示す簡単な方法 2. 活性化に基づく方法:単一または複数のニューロンの活性化状態を解読し、その動作プロセスを理解する 3. 勾配ベースの方法: トレーニング中に前方伝播と後方伝播によって形成された勾配を操作する 上記 3 つの方法については、以下で詳しく説明します。ここで示す例は、Keras ディープラーニング ライブラリを使用して実装されています。また、この記事で使用されているデータセットは、「Recognize Numbers」コンテストで提供されています。したがって、この記事の例を再現する場合は、Kears をインストールし、これらの手順を実行したことを確認してください。 1 予備的な方法 1.1 モデル構造図を描く 研究者ができる最も簡単なことは、モデル構造の図を描くことです。さらに、ニューラル ネットワークの各層の形状とパラメータに注釈を付けることもできます。 keras では、次のコマンドを使用してモデル構造図を描画できます。 - モデル.要約()
- _________________________________________________________________
- レイヤー(タイプ)出力形状パラメータ#
- =================================================================
- conv2d_1 (Conv2D) (なし、26、26、32) 320
- _________________________________________________________________
- conv2d_2 (Conv2D) (なし、24、24、64) 18496
- _________________________________________________________________
- max_pooling2d_1 (MaxPooling2 (なし、12、12、64) 0
- _________________________________________________________________
- dropout_1 (ドロップアウト) (なし、12、12、64) 0
- _________________________________________________________________
- flatten_1 (フラット化) (なし、9216) 0
- _________________________________________________________________
- 密_1 (密) (なし、128) 1179776
- _________________________________________________________________
- dropout_2 (ドロップアウト) (なし、128) 0
- _________________________________________________________________
- preds (密集) (なし、10) 1290
- =================================================================
- 合計パラメータ: 1,199,882
- トレーニング可能なパラメータ: 1,199,882
- トレーニング不可能なパラメータ: 0
モデル構造図をよりクリエイティブで表現力豊かな方法で提示することもできます。keras.utils.vis_utils 関数を使用して、モデル アーキテクチャ図の描画を完了できます。 1.2 視覚化フィルター もう 1 つのアプローチは、トレーニング済みモデルのフィルターをプロットして、これらのフィルターがどのように動作するかを把握することです。たとえば、最初のレイヤーの最初のフィルターは次のようになります。 - top_layer = モデル.layers[0]
- plt.imshow(top_layer.get_weights()[0][:, :, :, 0].squeeze(), cmap= 'gray' )
一般的に、ニューラル ネットワークの最下層は主にエッジ検出器として機能します。層の数が深くなるにつれて、フィルターは顔などのより抽象的な概念を捉えることができるようになります。 2. アクティベーション方法
2.1 活性化の最大化 ニューラル ネットワークの仕組みを理解するには、入力画像にフィルターを適用し、その畳み込み出力をプロットします。これにより、フィルターの特定のアクティブ化パターンが何であるかを理解できます。たとえば、下の画像は顔フィルターで、入力画像が顔画像の場合に有効になります。 - vis.visualizationからvisualize_activation をインポートします
- vis.utilsからutilsをインポート
- kerasからアクティベーションをインポートする
- matplotlibからpyplotをpltとしてインポートします
- %matplotlib インライン
- plt.rcParams[ 'figure.figsize' ] = (18, 6)
- #レイヤーインデックスを検索するユーティリティ による 名前。
- # あるいは、最後のレイヤーに対応するため、これを -1として指定することもできます。
- layer_idx = utils.find_layer_idx(モデル、 'preds' )
- # ソフトマックスを線形に置き換える
- model.layers[layer_idx].activation = activations.linear
- モデル = utils.apply_modifications(モデル)
- # これは最大化したい出力ノードです。filter_idx = 0
- img = visualize_activation(モデル、レイヤーID、フィルターインデックス=フィルターID)
- plt.imshow(画像[..., 0])
同様に、この考え方をすべてのカテゴリに適用し、そのパターンがどのようになるかを確認できます。 - np.arange(10)のoutput_idxの場合:
- #今回は詳細出力をオフにしましょう 混乱を避けて出力だけを確認します。
- img = visualize_activation(モデル、レイヤーIDX、フィルターインデックス=出力IDX、入力範囲=(0.、1.))
- plt.figure()
- plt.title( '{} のネットワーク認識' .format(output_idx))
- plt.imshow(画像[..., 0])
2.2 画像の遮蔽 画像分類の問題では、対象オブジェクトが遮蔽され、オブジェクトのごく一部しか見えない状況に遭遇することがあります。画像オクルージョンベースの方法では、灰色の四角形を通して画像のさまざまな部分を体系的に入力し、分類器の出力を監視します。これらの例は、モデルがシーン内のオブジェクトを見つけるときに、オブジェクトが遮蔽されている場合は、オブジェクトを正しく分類する確率が大幅に低下することを明確に示しています。 この概念を理解するには、データセットから画像をランダムにサンプリングし、その画像のヒートマップをプロットしてみるとよいでしょう。これにより、モデルが実際のクラスを明確に区別するために、画像のどの部分が重要であるかを直感的に理解できるようになります。 - def iter_occlusion(画像、サイズ=8):
- # https://www.kaggle.com/blargl/simple-occlusion-and-saliency-mapsより引用
-
- 閉塞 = np.full ((サイズ* 5,サイズ* 5, 1), [0.5], np.float32)
- 閉塞中心 = np.full ((サイズ,サイズ, 1), [0.5], np.float32)
- occlusion_padding =サイズ* 2
-
- # print( 'padding...' )
- image_padded = np.pad(image, ( \ (occlusion_padding, occlusion_padding), (occlusion_padding, occlusion_padding), (0, 0) \ ), 'constant' , constant_values = 0.0)
-
- yが範囲内(occlusion_padding、image.shape[0] + occlusion_padding、 size )の場合:
- 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]i = 23 #たとえばdata = val_x[i]correct_class = np.argmax(val_y[i])
-
- # 入力テンソルfor model.predictinp = data.reshape(1, 28, 28, 1) # 画像データfor matplotlib's imshowimg = data.reshape(28, 28)
- # 閉塞img_size = img.shape[0]
-
- occlusion_size = 4print( 'occluding...' )heatmap = np.zeros((img_size, img_size), np.float32)class_pixels = np.zeros((img_size, img_size), np.int16)
- コレクションからdefaultdictをインポート
- counters = defaultdict( int ) 、 n 、(x、y、img_float)、 enumerate (iter_occlusion(data、 size =occlusion_size)) の場合:
- X = img_float.reshape(1, 28, 28, 1)
- 出力= モデル.予測(X)
- #print( '#{}: {} @ {} (正しいクラス: {})' .format(n, np.argmax( out ), np.amax( out ), out [0][correct_class]))
- #print( 'x {} - {} | y {} - {}' .format(x, x + occlusion_size, y, y + occlusion_size))
- ヒートマップ[y:y + occlusion_size, x:x + occlusion_size] =出力[0][correct_class]
- class_pixels[y:y + occlusion_size, x:x + occlusion_size] = np.argmax(出力)
- カウンター[np.argmax( out )] += 1
3. 勾配法
3.1 顕著性マップ 先ほどのタンクの例で見たように、モデルが予測のどの部分に重点を置くべきかをどうやって知るのでしょうか?この目的のために、顕著性マップを使用してこの問題を解決することができます。この論文では、サリエンシーマップが初めて紹介されています。 顕著性マップを使用する概念は非常に単純です。入力画像に対する出力クラスの勾配を計算します。これにより、入力画像のピクセルの小さな変化に対して出力クラス値がどのように変化するかがわかります。グラデーションのすべての正の値は、ピクセルの小さな変化によって出力値が増加することを示しています。したがって、これらの勾配を視覚化することで、ある程度の直感が得られ、このアプローチにより、出力に最も貢献する顕著な画像領域が強調表示されます。 - class_idx = 0 インデックス = np。ここで(val_y[:, class_idx] == 1.)[0]
- # ここからランダムな入力を選択します。idx = indices[0]
- #選択した画像の妥当性をチェックします。from matplotlib import pyplot as plt%matplotlib inline
- plt.rcParams[ 'figure.figsize' ] = (18, 6)plt.imshow(val_x[idx][..., 0])
-
- vis.visualizationからvisualize_saliency をインポートします
- from vis.utils import utilsfrom keras import activations#レイヤーインデックスを検索するユーティリティ による 名前。
- # あるいは、最後のレイヤーに対応するため、これを -1として指定することもできます。
- layer_idx = utils.find_layer_idx(モデル、 'preds' )
- # softmax をlinearmodel.layers[layer_idx].activation = activations.linearと入れ替えます
- モデル = utils.apply_modifications(モデル) grads = visualize_saliency(モデル、layer_idx、filter_indices=class_idx、seed_input=val_x[idx])
-
- #プロット ヒートマップとして視覚化するための'jet'カラーマップ。plt.imshow(grads, cmap= 'jet' )
-
- # これは高密度線形層に対応します。 for class_idx in np.arange(10):
- インデックス = np.where (val_y[:, class_idx] == 1.)[0]
- idx = インデックス[0]
-
- f, ax = plt.subplots(1, 4)
- ax[0].imshow(val_x[idx][..., 0])
-
- iの場合、 enumerate([None, 'guided' , 'relu' ])の修飾子:
- grads = visualize_saliency(モデル、layer_idx、filter_indices=class_idx、
- seed_input=val_x[idx]、backprop_modifier=修飾子)
- 修飾子がNoneの場合:
- 修飾子 = 'バニラ'
- ax[i+1].set_title(修飾子)
- ax[i+1].imshow(grads, cmap= 'jet' )
3.2 勾配ベースのクラス活性化マッピング クラス アクティベーション マップ (CAM) または grad-CAM は、モデルを視覚化する別の方法ですが、勾配の出力を使用する代わりに、この方法では最後から 2 番目の畳み込み層の出力を使用して、最後から 2 番目の層に格納されている空間情報を利用します。 - vis.visualizationからvisualize_camをインポートします
- # これは高密度線形層に対応します。 for class_idx in np.arange(10):
- インデックス = np.where (val_y[:, class_idx] == 1.)[0]
- idx = インデックス[0]f、ax = plt.subplots(1, 4)
- ax[0].imshow(val_x[idx][..., 0])
- iの場合、 enumerate([None, 'guided' , 'relu' ])の修飾子:
- grads = visualize_cam(モデル、layer_idx、filter_indices=class_idx、
- seed_input=val_x[idx]、backprop_modifier=修飾子)
- 修飾子がNoneの場合:
- 修飾子 = 'バニラ'
- ax[i+1].set_title(修飾子)
- ax[i+1].imshow(grads, cmap= 'jet' )
要約する この記事では、CNN モデルの視覚化の重要性について簡単に説明し、CNN ネットワーク モデルを視覚化するいくつかの方法を紹介します。この記事が読者の皆さんの役に立つことを願っています。読者の皆さんが、今後のディープラーニング アプリケーションでより優れたモデルを構築できるようになります。 |