このページは Cloud Translation API によって翻訳されました。
Switch to English

統合されたグラデーション

TensorFlow.orgで表示 GoogleColabで実行 GitHubで表示ノートブックをダウンロードTFハブモデルを参照してください

このチュートリアルでは、論文Axiomatic Attribution for Deep Networksで紹介された説明可能なAI手法であるIntegratedGradients(IG)を実装する方法を示します。 IGは、モデルの予測間の関係をその特徴の観点から説明することを目的としています。機能の重要性の理解、データの偏りの特定、モデルのパフォーマンスのデバッグなど、多くのユースケースがあります。

IGは、微分可能なモデル(画像、テキスト、構造化データなど)への幅広い適用性、実装の容易さ、理論的正当化、および大規模なネットワークや機能への拡張を可能にする代替アプローチと比較した計算効率により、人気のある解釈可能性手法になりました画像などのスペース。

このチュートリアルでは、画像分類器のピクセル機能の重要性を理解するために、IGの実装を段階的に説明します。例として、消防艇がジェット水を噴霧しているこの画像を考えてみましょう。この画像を消防艇として分類し、ボートと放水砲を構成するピクセルを決定にとって重要であると強調する場合があります。モデルは、このチュートリアルの後半で、この画像を消防艇として分類します。しかし、その決定を説明するときに重要なものと同じピクセルを強調していますか?

以下の「IGアトリビューションマスク」および「オリジナル+ IGマスクオーバーレイ」というタイトルの画像では、モデルが代わりに、ボート自体よりも重要であるとして、ボートの放水砲と水の噴流を構成するピクセルを(紫色で)強調表示していることがわかります。その決定。あなたのモデルはどのように新しい消防艇に一般化されますか?ウォータージェットのない消防艇はどうですか? IGがどのように機能するか、およびモデルにIGを適用して、モデルの予測と基礎となる機能との関係をよりよく理解する方法について詳しくは、以下をお読みください。

出力画像1

セットアップ

import matplotlib.pylab as plt
import numpy as np
import tensorflow as tf
import tensorflow_hub as hub

事前トレーニング済みの画像分類器をTF-Hubからダウンロードします

IGは、微分可能なモデルに適用できます。元の論文の精神に基づきTensorFlowHubからダウンロードする同じモデルの事前トレーニング済みバージョンであるInceptionV1を使用します。

model = tf.keras.Sequential([
    hub.KerasLayer(
        name='inception_v1',
        handle='https://tfhub.dev/google/imagenet/inception_v1/classification/4',
        trainable=False),
])
model.build([None, 224, 224, 3])
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
inception_v1 (KerasLayer)    (None, 1001)              6633209   
=================================================================
Total params: 6,633,209
Trainable params: 0
Non-trainable params: 6,633,209
_________________________________________________________________

モジュールページから、InceptionV1について次の点に注意する必要があります。

入力:モデルに期待される入力形状は(None, 224, 224, 3)です。これは、dtype float32と形状(batch_size, height, width, RGB channels)高密度4Dテンソルであり、その要素は、範囲[0、1]に正規化されたピクセルのRGBカラー値です。最初の要素はNoneで、モデルが任意の整数のバッチサイズを取ることができることを示します。

出力(batch_size, 1001)形をしたtf.Tensorのtf.Tensor。各行は、ImageNetの1,001クラスごとのモデルの予測スコアを表します。モデルの最上位の予測クラスインデックスには、 tf.argmax(predictions, axis=-1)使用できます。さらに、 tf.nn.softmax(predictions, axis=-1)を使用して、モデルのロジット出力をすべてのクラスの予測確率に変換して、モデルの不確実性を定量化し、同様の予測クラスを探索してデバッグすることもできます。

def load_imagenet_labels(file_path):
  labels_file = tf.keras.utils.get_file('ImageNetLabels.txt', file_path)
  with open(labels_file) as reader:
    f = reader.read()
    labels = f.splitlines()
  return np.array(labels)
imagenet_labels = load_imagenet_labels('https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt')
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt
16384/10484 [==============================================] - 0s 0us/step

tf.image使用して画像をロードして前処理しtf.image

ウィキメディアコモンズの2つの画像、消防艇ジャイアントパンダを使用してIGを説明します。

def read_image(file_name):
  image = tf.io.read_file(file_name)
  image = tf.image.decode_jpeg(image, channels=3)
  image = tf.image.convert_image_dtype(image, tf.float32)
  image = tf.image.resize_with_pad(image, target_height=224, target_width=224)
  return image
img_url = {
    'Fireboat': 'http://storage.googleapis.com/download.tensorflow.org/example_images/San_Francisco_fireboat_showing_off.jpg',
    'Giant Panda': 'http://storage.googleapis.com/download.tensorflow.org/example_images/Giant_Panda_2.jpeg',
}

img_paths = {name: tf.keras.utils.get_file(name, url) for (name, url) in img_url.items()}
img_name_tensors = {name: read_image(img_path) for (name, img_path) in img_paths.items()}
Downloading data from http://storage.googleapis.com/download.tensorflow.org/example_images/San_Francisco_fireboat_showing_off.jpg
3956736/3954129 [==============================] - 0s 0us/step
Downloading data from http://storage.googleapis.com/download.tensorflow.org/example_images/Giant_Panda_2.jpeg
811008/802859 [==============================] - 0s 0us/step

plt.figure(figsize=(8, 8))
for n, (name, img_tensors) in enumerate(img_name_tensors.items()):
  ax = plt.subplot(1, 2, n+1)
  ax.imshow(img_tensors)
  ax.set_title(name)
  ax.axis('off')
plt.tight_layout()

png

画像を分類する

これらの画像を分類し、最も信頼できる上位3つの予測を表示することから始めましょう。以下は、上位k個の予測ラベルと確率を取得するための効用関数です。

def top_k_predictions(img, k=3):
  image_batch = tf.expand_dims(img, 0)
  predictions = model(image_batch)
  probs = tf.nn.softmax(predictions, axis=-1)
  top_probs, top_idxs = tf.math.top_k(input=probs, k=k)
  top_labels = imagenet_labels[tuple(top_idxs)]
  return top_labels, top_probs[0]
for (name, img_tensor) in img_name_tensors.items():
  plt.imshow(img_tensor)
  plt.title(name, fontweight='bold')
  plt.axis('off')
  plt.show()

  pred_label, pred_prob = top_k_predictions(img_tensor)
  for label, prob in zip(pred_label, pred_prob):
    print(f'{label}: {prob:0.1%}')

png

fireboat: 32.6%
pier: 12.7%
suspension bridge: 5.7%

png

giant panda: 89.4%
teddy: 0.3%
gibbon: 0.3%

統合された勾配を計算する

モデルのInceptionV1は、入力特徴空間、画像ピクセル値、および0〜1のImageNetクラス確率値によって定義される出力空間間のマッピングを記述する学習関数です。を使用して特徴重要度スコアを割り当てられたニューラルネットワークの初期の解釈可能性メソッド勾配。これは、モデルの予測関数に沿った特定のポイントで、モデルの予測に対してどのピクセルが最も急なローカルを持つかを示します。ただし、勾配は、ピクセル値に関するモデルの予測関数の局所的な変化のみを記述し、モデルの予測関数全体を完全には記述しません。モデルが個々のピクセルの範囲と正しいImageNetクラスとの関係を完全に「学習」すると、このピクセルの勾配は飽和します。つまり、ますます小さくなり、ゼロになることさえあります。以下の単純なモデル関数について考えてみます。

def f(x):
  """A simplified model function."""
  return tf.where(x < 0.8, x, 0.8)

def interpolated_path(x):
  """A straight line path."""
  return tf.zeros_like(x)

x = tf.linspace(start=0.0, stop=1.0, num=6)
y = f(x)
fig = plt.figure(figsize=(12, 5))
ax0 = fig.add_subplot(121)
ax0.plot(x, f(x), marker='o')
ax0.set_title('Gradients saturate over F(x)', fontweight='bold')
ax0.text(0.2, 0.5, 'Gradients > 0 = \n x is important')
ax0.text(0.7, 0.85, 'Gradients = 0 \n x not important')
ax0.set_yticks(tf.range(0, 1.5, 0.5))
ax0.set_xticks(tf.range(0, 1.5, 0.5))
ax0.set_ylabel('F(x) - model true class predicted probability')
ax0.set_xlabel('x - (pixel value)')

ax1 = fig.add_subplot(122)
ax1.plot(x, f(x), marker='o')
ax1.plot(x, interpolated_path(x), marker='>')
ax1.set_title('IG intuition', fontweight='bold')
ax1.text(0.25, 0.1, 'Accumulate gradients along path')
ax1.set_ylabel('F(x) - model true class predicted probability')
ax1.set_xlabel('x - (pixel value)')
ax1.set_yticks(tf.range(0, 1.5, 0.5))
ax1.set_xticks(tf.range(0, 1.5, 0.5))
ax1.annotate('Baseline', xy=(0.0, 0.0), xytext=(0.0, 0.2),
             arrowprops=dict(facecolor='black', shrink=0.1))
ax1.annotate('Input', xy=(1.0, 0.0), xytext=(0.95, 0.2),
             arrowprops=dict(facecolor='black', shrink=0.1))
plt.show();

png

  • :ピクセルxモデルの勾配は0.0から0.8の間で正ですが、0.8から1.0の間で0.0になります。 Pixel xは、モデルを真のクラスで80%の予測確率に向けてプッシュすることに明らかに大きな影響を及ぼします。ピクセルxの重要性が小さい、または不連続であることは理にかなっていますか?

  • :IGの背後にある直感は、ピクセルxのローカル勾配を累積し、その重要性を、モデルの全体的な出力クラスの確率に追加または削除する量のスコアとして帰属させることです。 IGは、次の3つの部分に分けて計算できます。

    1. 0(ベースラインまたは開始点)と1(入力ピクセルの値)の間のフィーチャスペースの直線に沿って小さなステップを補間します。
    2. 各ステップに関するモデルの予測間の各ステップで勾配を計算します
    3. これらの局所的な勾配を累積(累積平均)することにより、ベースラインと入力の間の積分を概算します。

この直感を強化するために、以下の「消防艇」の画像の例にIGを適用して、これら3つの部分を説明します。

ベースラインを確立する

ベースラインは、特徴の重要度を計算するための開始点として使用される入力画像です。直感的には、ベースラインの説明的な役割は、入力画像に存在する場合の「消防艇」予測に対する各ピクセルの影響とは対照的に、「消防艇」予測に対する各ピクセルの不在の影響を表すと考えることができます。その結果、ベースラインの選択は、ピクセル機能の重要性を解釈および視覚化する上で中心的な役割を果たします。ベースライン選択の詳細については、このチュートリアルの下部にある「次のステップ」セクションのリソースを参照してください。ここでは、ピクセル値がすべてゼロの黒い画像を使用します。

実験できる他の選択肢には、 tf.random.uniform(shape=(224,224,3), minval=0.0, maxval=1.0)作成できる、真っ白な画像またはランダムな画像があります。

baseline = tf.zeros(shape=(224,224,3))
plt.imshow(baseline)
plt.title("Baseline")
plt.axis('off')
plt.show()

png

数式をコードに解凍します

統合グラデーションの式は次のとおりです。

$ IntegratedGradients_ {i}(x):: =(x_ {i} --x'_ {i})\ times \ int _ {\ alpha = 0} ^ 1 \ frac {\ partial F(x '+ \ alpha \ times (x-x '))} {\ partial x_i} {d \ alpha} $

どこ:

$ _ {i} $ =機能
$ x $ =入力
$ x '$ =ベースライン
$ \ alpha $ =特徴を摂動させる補間定数

実際には、定積分の計算は常に数値的に可能であるとは限らず、計算コストがかかる可能性があるため、次の数値近似を計算します。

$ IntegratedGrads ^ {approx} _ {i}(x):: =(x_ {i} -x'_ {i})\ times \ sum_ {k = 1} ^ {m} \ frac {\ partial F(x '+ \ frac {k} {m} \ times(x --x'))} {\ partial x_ {i}} \ times \ frac {1} {m} $

どこ:

$ _ {i} $ =機能(個々のピクセル)
$ x $ =入力(画像テンソル)
$ x '$ =ベースライン(画像テンソル)
$ k $ =スケーリングされたフィーチャ摂動定数
$ m $ =積分のリーマン和近似のステップ数
$(x_ {i} -x'_ {i})$ =ベースラインとの差の項。これは、統合されたグラデーションをスケーリングし、元の画像の観点から維持するために必要です。ベースライン画像から入力までのパスはピクセル空間です。 IGを使用すると、直線で積分(線形変換)するため、これは、十分なステップで$ \ alpha $に関する補間画像関数の導関数の積分項とほぼ同等になります。積分は、各ピクセルの勾配にパスに沿ったピクセルの変化を掛けたものを合計します。 $ x:=(x '+ \ alpha(xx'))$を代入して、ある画像から別の画像への均一なステップとしてこの統合を実装する方が簡単です。したがって、変数の変更により、$ dx =(xx ')d \ alpha $が得られます。 $(xx ')$項は定数であり、積分から因数分解されます。

画像を補間する

$ IntegratedGrads ^ {approx} _ {i}(x):: =(x_ {i} -x'_ {i})\ times \ sum_ {k = 1} ^ {m} \ frac {\ partial F(\ overbrace {x '+ \ frac {k} {m} \ times(x --x')} ^ \ text {k間隔でm個の画像を補間})} {\ partial x_ {i}} \ times \ frac {1} {m} $

まず、ベースラインと元の画像の間に線形補間を生成します。補間された画像は、元の方程式の$ \ alpha $で表される、ベースラインと入力の間の特徴空間の小さなステップと考えることができます。

m_steps=50
alphas = tf.linspace(start=0.0, stop=1.0, num=m_steps+1) # Generate m_steps intervals for integral_approximation() below.
def interpolate_images(baseline,
                       image,
                       alphas):
  alphas_x = alphas[:, tf.newaxis, tf.newaxis, tf.newaxis]
  baseline_x = tf.expand_dims(baseline, axis=0)
  input_x = tf.expand_dims(image, axis=0)
  delta = input_x - baseline_x
  images = baseline_x +  alphas_x * delta
  return images

上記の関数を使用して、黒いベースライン画像とサンプルの「消防艇」画像の間のアルファ間隔で線形パスに沿って補間画像を生成してみましょう。

interpolated_images = interpolate_images(
    baseline=baseline,
    image=img_name_tensors['Fireboat'],
    alphas=alphas)

補間された画像を視覚化してみましょう。注:$ \ alpha $定数についての別の考え方は、補間された各画像の強度を一貫して増加させることです。

fig = plt.figure(figsize=(20, 20))

i = 0
for alpha, image in zip(alphas[0::10], interpolated_images[0::10]):
  i += 1
  plt.subplot(1, len(alphas[0::10]), i)
  plt.title(f'alpha: {alpha:.1f}')
  plt.imshow(image)
  plt.axis('off')

plt.tight_layout();

png

勾配を計算する

次に、フィーチャの変更とモデルの予測の変更との関係を測定するために、勾配を計算する方法を見てみましょう。画像の場合、勾配は、モデルの予測クラス確率に最も強い影響を与えるピクセルを示します。

$ IntegratedGrads ^ {approx} _ {i}(x):: =(x_ {i} -x'_ {i})\ times \ sum_ {k = 1} ^ {m} \ frac {\ overbrace {\ partial F(\ text {interpolated images})} ^ \ text {computegradients}} {\ partial x_ {i}} \ times \ frac {1} {m} $

どこ:
$ F()$ =モデルの予測関数
$ \ frac {\ partial {F}} {\ partial {x_i}} $ =各特徴$ x_i $に対するモデルFの予測関数の勾配(偏導関数のベクトル$ \ partial $)

TensorFlowを使用すると、tf.GradientTapeを使用してグラデーションを簡単に計算できます。

def compute_gradients(images, target_class_idx):
  with tf.GradientTape() as tape:
    tape.watch(images)
    logits = model(images)
    probs = tf.nn.softmax(logits, axis=-1)[:, target_class_idx]
  return tape.gradient(probs, images)

正しい出力に関して、補間パスに沿った各画像の勾配を計算してみましょう。モデルは、各クラスの予測確率に変換するロジットを持つ(1、1001 (1, 1001)形状のTensorを返すことを思い出してください。正しいImageNetターゲットクラスインデックスを画像のcompute_gradients関数に渡す必要があります。

path_gradients = compute_gradients(
    images=interpolated_images,
    target_class_idx=555)

(n_interpolated_images, img_height, img_width, RGB)の出力形状に注意してください。これにより、補間パスに沿ったすべての画像のすべてのピクセルの勾配が得られます。これらの勾配は、特徴空間の小さなステップごとにモデルの予測の変化を測定するものと考えることができます。

print(path_gradients.shape)
(51, 224, 224, 3)

勾配飽和の視覚化

上で計算した勾配は、モデルの「消防艇」の予測確率に対する局所的な変化を表しており、飽和する可能性があることを思い出してください。

これらの概念は、下の2つのプロットで上で計算した勾配を使用して視覚化されます。

pred = model(interpolated_images)
pred_proba = tf.nn.softmax(pred, axis=-1)[:, 555]

plt.figure(figsize=(10, 4))
ax1 = plt.subplot(1, 2, 1)
ax1.plot(alphas, pred_proba)
ax1.set_title('Target class predicted probability over alpha')
ax1.set_ylabel('model p(target class)')
ax1.set_xlabel('alpha')
ax1.set_ylim([0, 1])

ax2 = plt.subplot(1, 2, 2)
# Average across interpolation steps
average_grads = tf.reduce_mean(path_gradients, axis=[1, 2, 3])
# Normalize gradients to 0 to 1 scale. E.g. (x - min(x))/(max(x)-min(x))
average_grads_norm = (average_grads-tf.math.reduce_min(average_grads))/(tf.math.reduce_max(average_grads)-tf.reduce_min(average_grads))
ax2.plot(alphas, average_grads_norm)
ax2.set_title('Average pixel gradients (normalized) over alpha')
ax2.set_ylabel('Average pixel gradients')
ax2.set_xlabel('alpha')
ax2.set_ylim([0, 1]);
(0.0, 1.0)

png

  • :このプロットは、「消防艇」クラスに対するモデルの信頼度がアルファ間でどのように変化するかを示しています。最終的な「消防艇」の予測確率である約40%に落ち着く前に、勾配または線の傾きが0.6から1.0の間で大幅に平坦化または飽和することに注目してください。

  • :右のプロットは、アルファ全体の平均勾配の大きさをより直接的に示しています。値が急激に近づき、一時的にゼロを下回ることに注意してください。実際、モデルは、飽和する前に、アルファの値が低い場合の勾配から最も多く「学習」します。直感的には、モデルが正しい予測を行うために放水砲などのピクセルを学習し、これらのピクセルの勾配をゼロに送信したため、これを考えることができますが、アルファ値が近づくにつれて、まだかなり不確実で、偽のブリッジまたはウォータージェットピクセルに焦点を当てています元の入力画像。

これらの重要な放水砲のピクセルが「消防艇」の予測にとって重要であると反映されるようにするには、以下に進み、これらの勾配を累積して、各ピクセルが「消防艇」の予測確率にどのように影響するかを正確に概算する方法を学習します。

勾配の累積(積分近似)

さまざまな関数間での精度と収束のさまざまなトレードオフを使用して、IGの積分の数値近似を計算する方法はたくさんあります。人気のあるクラスのメソッドは、リーマン和と呼ばれます。ここでは、台形公式を使用します(このチュートリアルの最後に、さまざまな近似方法を調べるための追加のコードがあります)。

$ IntegratedGrads ^ {approx} _ {i}(x):: =(x_ {i} -x'_ {i})\ times \ overbrace {\ sum_ {k = 1} ^ {m}} ^ \ text { m個のローカルグラデーションを合計する} \ text {gradients(interpolated images)} \ times \ overbrace {\ frac {1} {m}} ^ \ text {mステップで除算} $

方程式から、 m勾配を合計し、 mステップで除算していることがわかります。 m内挿された予測と入力画像の局所勾配の平均としてパート3の2つの操作を一緒に実装できます

def integral_approximation(gradients):
  # riemann_trapezoidal
  grads = (gradients[:-1] + gradients[1:]) / tf.constant(2.0)
  integrated_gradients = tf.math.reduce_mean(grads, axis=0)
  return integrated_gradients

integral_approximation関数は、ベースラインと元の画像の間の補間画像に関して、ターゲットクラスの予測確率の勾配を取ります。

ig = integral_approximation(
    gradients=path_gradients)

m補間画像の勾配全体の平均化により、元の「ジャイアントパンダ」画像と同じ形状の統合された勾配テンソルが返されることを確認できます。

print(ig.shape)
(224, 224, 3)

すべてを一緒に入れて

ここで、前の3つの一般的な部分をIntegratedGradients関数に結合し、 @ tf.functionデコレータを使用して、高性能の呼び出し可能なTensorFlowグラフにコンパイルします。これは、以下の5つの小さなステップとして実装されます。

$ IntegratedGrads ^ {approx} _ {i}(x):: = \ overbrace {(x_ {i} -x'_ {i})} ^ \ text {5。} \ times \ overbrace {\ sum_ {k = 1} ^ {m}} ^ \ text {4.} \ frac {\ partial \ overbrace {F(\ overbrace {x '+ \ overbrace {\ frac {k} {m}} ^ \ text {1。} \ times(x --x '))} ^ \ text {2.}} ^ \ text {3.}} {\ partial x_ {i}} \ times \ overbrace {\ frac {1} {m}} ^ \ text {4。} $

  1. アルファを生成$ \ alpha $

  2. 補間画像を生成= $(x '+ \ frac {k} {m} \ times(x --x'))$

  3. 入力特徴に関するモデル$ F $出力予測間の勾配を計算する= $ \ frac {\ partial F(\ text {interpolated path input})} {\ partial x_ {i}} $

  4. 平均勾配による積分近似= $ \ sum_ {k = 1} ^ m \ text {gradients} \ times \ frac {1} {m} $

  5. 元の画像に対して統合されたグラデーションをスケーリングする= $(x_ {i} -x'_ {i})\ times \ text {統合されたグラデーション} $。この手順が必要な理由は、複数の補間画像にわたって累積された帰属値が同じ単位にあり、元の画像のピクセルの重要性を忠実に表していることを確認するためです。

@tf.function
def integrated_gradients(baseline,
                         image,
                         target_class_idx,
                         m_steps=50,
                         batch_size=32):
  # 1. Generate alphas.
  alphas = tf.linspace(start=0.0, stop=1.0, num=m_steps+1)

  # Initialize TensorArray outside loop to collect gradients.    
  gradient_batches = tf.TensorArray(tf.float32, size=m_steps+1)

  # Iterate alphas range and batch computation for speed, memory efficiency, and scaling to larger m_steps.
  for alpha in tf.range(0, len(alphas), batch_size):
    from_ = alpha
    to = tf.minimum(from_ + batch_size, len(alphas))
    alpha_batch = alphas[from_:to]

    # 2. Generate interpolated inputs between baseline and input.
    interpolated_path_input_batch = interpolate_images(baseline=baseline,
                                                       image=image,
                                                       alphas=alpha_batch)

    # 3. Compute gradients between model outputs and interpolated inputs.
    gradient_batch = compute_gradients(images=interpolated_path_input_batch,
                                       target_class_idx=target_class_idx)

    # Write batch indices and gradients to extend TensorArray.
    gradient_batches = gradient_batches.scatter(tf.range(from_, to), gradient_batch)    

  # Stack path gradients together row-wise into single tensor.
  total_gradients = gradient_batches.stack()

  # 4. Integral approximation through averaging gradients.
  avg_gradients = integral_approximation(gradients=total_gradients)

  # 5. Scale integrated gradients with respect to input.
  integrated_gradients = (image - baseline) * avg_gradients

  return integrated_gradients
ig_attributions = integrated_gradients(baseline=baseline,
                                       image=img_name_tensors['Fireboat'],
                                       target_class_idx=555,
                                       m_steps=240)

ここでも、IG機能の属性が入力された「Fireboat」画像と同じ形状であることを確認できます。

print(ig_attributions.shape)
(224, 224, 3)

この論文では、例に応じて20〜300の範囲のステップ数を提案しています(ただし、実際には、積分を正確に近似するために、これは1,000秒でより多くなる可能性があります)。このチュートリアルの最後にある「次のステップ」リソースで、適切なステップ数を確認するための追加のコードを見つけることができます。

アトリビューションを視覚化する

これで、帰属を視覚化し、元の画像にオーバーレイする準備が整いました。以下のコードは、カラーチャネル全体の統合されたグラデーションの絶対値を合計して、アトリビューションマスクを生成します。このプロット方法は、モデルの予測に対するピクセルの相対的な影響をキャプチャします。

def plot_img_attributions(baseline,
                          image,
                          target_class_idx,
                          m_steps=50,
                          cmap=None,
                          overlay_alpha=0.4):

  attributions = integrated_gradients(baseline=baseline,
                                      image=image,
                                      target_class_idx=target_class_idx,
                                      m_steps=m_steps)

  # Sum of the attributions across color channels for visualization.
  # The attribution mask shape is a grayscale image with height and width
  # equal to the original image.
  attribution_mask = tf.reduce_sum(tf.math.abs(attributions), axis=-1)

  fig, axs = plt.subplots(nrows=2, ncols=2, squeeze=False, figsize=(8, 8))

  axs[0, 0].set_title('Baseline image')
  axs[0, 0].imshow(baseline)
  axs[0, 0].axis('off')

  axs[0, 1].set_title('Original image')
  axs[0, 1].imshow(image)
  axs[0, 1].axis('off')

  axs[1, 0].set_title('Attribution mask')
  axs[1, 0].imshow(attribution_mask, cmap=cmap)
  axs[1, 0].axis('off')

  axs[1, 1].set_title('Overlay')
  axs[1, 1].imshow(attribution_mask, cmap=cmap)
  axs[1, 1].imshow(image, alpha=overlay_alpha)
  axs[1, 1].axis('off')

  plt.tight_layout()
  return fig

「消防艇」の画像の帰属を見ると、モデルが放水砲と噴出口を正しい予測に貢献していると識別していることがわかります。

_ = plot_img_attributions(image=img_name_tensors['Fireboat'],
                          baseline=baseline,
                          target_class_idx=555,
                          m_steps=240,
                          cmap=plt.cm.inferno,
                          overlay_alpha=0.4)

png

「ジャイアントパンダ」の画像では、属性がパンダの顔の質感、鼻、毛皮を強調しています。

_ = plot_img_attributions(image=img_name_tensors['Giant Panda'],
                          baseline=baseline,
                          target_class_idx=389,
                          m_steps=55,
                          cmap=plt.cm.viridis,
                          overlay_alpha=0.5)

png

用途と制限

ユースケース

  • モデルを展開する前に統合グラデーションなどの手法を採用すると、モデルがどのように、なぜ機能するのかを直感的に理解するのに役立ちます。このテクニックで強調された機能はあなたの直感と一致していますか?そうでない場合は、モデルまたはデータセットのバグ、または過剰適合を示している可能性があります。

制限事項

  • 統合グラデーションは、個々の例で機能の重要性を提供しますが、データセット全体でグローバルな機能の重要性を提供するわけではありません。

  • 統合グラデーションは、個々の機能の重要性を提供しますが、機能の相互作用と組み合わせについては説明していません。

次のステップ

このチュートリアルでは、統合グラデーションの基本的な実装について説明しました。次のステップとして、このノートブックを使用して、さまざまなモデルや画像でこのテクニックを自分で試すことができます。

興味のある読者のために、このチュートリアルのより長いバージョン(さまざまなベースラインのコードを含み、積分近似を計算し、十分な数のステップを決定するため)がここにあります

理解を深めるには、以前のバージョンのTensorFlowの実装が含まれている論文Axiomatic Attribution for Deep Networks andGithubリポジトリを確認してください。 distill.pubで、機能の帰属やさまざまなベースラインの影響を調べることもできます。

機能の重要性、モデルエラー分析、データスキューモニタリングのためにIGを生産機械学習ワークフローに組み込むことに興味がありますか? IGアトリビューションをサポートするGoogleCloudExplainableAI製品をご覧ください。 Google AI PAIR研究グループは、IG機能の属性の視覚化など、モデルのデバッグに使用できるWhat-ifツールもオープンソース化しました。