Merken Sie den Termin vor! Google I / O kehrt vom 18. bis 20. Mai zurück Registrieren Sie sich jetzt
Diese Seite wurde von der Cloud Translation API übersetzt.
Switch to English

Integrierte Farbverläufe

Ansicht auf TensorFlow.org In Google Colab ausführen Ansicht auf GitHub Notizbuch herunterladen Siehe TF Hub-Modell

Dieses Tutorial zeigt, wie Integrated Gradients (IG) implementiert wird, eine erklärbare KI- Technik, die in der Veröffentlichung Axiomatic Attribution for Deep Networks vorgestellt wurde . IG zielt darauf ab, die Beziehung zwischen den Vorhersagen eines Modells in Bezug auf seine Merkmale zu erklären. Es gibt viele Anwendungsfälle, einschließlich des Verständnisses der Wichtigkeit von Funktionen, des Erkennens von Datenversatz und des Debuggens der Modellleistung.

IG hat sich aufgrund seiner breiten Anwendbarkeit auf jedes differenzierbare Modell (z. B. Bilder, Text, strukturierte Daten), seiner einfachen Implementierung, seiner theoretischen Begründungen und seiner Recheneffizienz im Vergleich zu alternativen Ansätzen, die eine Skalierung auf große Netzwerke und Funktionen ermöglichen, zu einer beliebten Interpretierbarkeitstechnik entwickelt Räume wie Bilder.

In diesem Tutorial werden Sie Schritt für Schritt durch die Implementierung von IG geführt, um die Wichtigkeit von Pixelmerkmalen eines Bildklassifizierers zu verstehen. Betrachten Sie als Beispiel dieses Bild eines Feuerlöschboots, das Wasserstrahlen sprüht. Sie würden dieses Bild als Feuerlöschboot klassifizieren und die Pixel, aus denen das Boot und die Wasserwerfer bestehen, als wichtig für Ihre Entscheidung hervorheben. Ihr Modell wird dieses Bild später in diesem Tutorial auch als Feuerlöschboot klassifizieren. Markiert es jedoch dieselben Pixel als wichtig, wenn es seine Entscheidung erklärt?

In den folgenden Bildern mit dem Titel "IG Attribution Mask" und "Original + IG Mask Overlay" können Sie sehen, dass Ihr Modell stattdessen (in lila) die Pixel hervorhebt, aus denen die Wasserwerfer und Wasserstrahlen des Bootes bestehen, die wichtiger sind als das Boot selbst seine Entscheidung. Wie wird sich Ihr Modell auf neue Feuerlöschboote verallgemeinern lassen? Was ist mit Feuerlöschbooten ohne Wasserstrahlen? Lesen Sie weiter, um mehr darüber zu erfahren, wie IG funktioniert und wie Sie IG auf Ihre Modelle anwenden, um die Beziehung zwischen ihren Vorhersagen und den zugrunde liegenden Merkmalen besser zu verstehen.

Ausgabebild 1

Einrichten

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

Laden Sie einen vorgefertigten Bildklassifizierer von TF-Hub herunter

IG kann auf jedes differenzierbare Modell angewendet werden. Im Sinne des Originalpapiers verwenden Sie eine vorgefertigte Version desselben Modells, Inception V1, die Sie von TensorFlow Hub herunterladen.

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
_________________________________________________________________

Auf der Modulseite müssen Sie Folgendes zu Inception V1 beachten:

Eingaben : Die erwartete Eingabeform für das Modell ist (None, 224, 224, 3) . Dies ist ein dichter 4D-Tensor vom Typ float32 und Form (batch_size, height, width, RGB channels) dessen Elemente RGB-Farbwerte von Pixeln sind, die auf den Bereich [0, 1] normalisiert sind. Das erste Element ist None um anzugeben, dass das Modell eine beliebige ganzzahlige Stapelgröße annehmen kann.

Ausgaben : Ein tf.Tensor von Protokollen in Form von (batch_size, 1001) . Jede Zeile repräsentiert die vorhergesagte Punktzahl des Modells für jede der 1.001 Klassen von ImageNet. Für den besten vorhergesagten Klassenindex des Modells können Sie tf.argmax(predictions, axis=-1) . Darüber hinaus können Sie die Logit-Ausgabe des Modells mithilfe von tf.nn.softmax(predictions, axis=-1) in vorhergesagte Wahrscheinlichkeiten für alle Klassen tf.nn.softmax(predictions, axis=-1) , um die Unsicherheit des Modells zu quantifizieren und ähnliche vorhergesagte Klassen für das Debuggen zu untersuchen.

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

Laden Sie Bilder mit tf.image und verarbeiten tf.image

Sie werden IG anhand von zwei Bildern aus Wikimedia Commons veranschaulichen: einem Feuerlöschboot und einem Riesenpanda .

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

Bilder klassifizieren

Beginnen wir mit der Klassifizierung dieser Bilder und der Anzeige der drei sichersten Vorhersagen. Im Folgenden finden Sie eine Utility-Funktion zum Abrufen der Top-k-vorhergesagten Bezeichnungen und Wahrscheinlichkeiten.

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%

Integrierte Verläufe berechnen

Ihr Modell, Inception V1, ist eine erlernte Funktion, die eine Zuordnung zwischen Ihrem Eingabemerkmalraum, Bildpixelwerten und einem Ausgaberaum beschreibt, der durch ImageNet-Klassenwahrscheinlichkeitswerte zwischen 0 und 1 definiert ist Gradienten, die Ihnen sagen, welche Pixel an einem bestimmten Punkt entlang der Vorhersagefunktion Ihres Modells die steilste lokale Relation zur Vorhersage Ihres Modells aufweisen. Gradienten beschreiben jedoch nur lokale Änderungen in der Vorhersagefunktion Ihres Modells in Bezug auf Pixelwerte und beschreiben nicht vollständig Ihre gesamte Modellvorhersagefunktion. Wenn Ihr Modell die Beziehung zwischen dem Bereich eines einzelnen Pixels und der richtigen ImageNet-Klasse vollständig "lernt", wird der Gradient für dieses Pixel gesättigt , was bedeutet, dass er immer kleiner wird und sogar auf Null geht. Betrachten Sie die folgende einfache Modellfunktion:

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

  • links : Die Farbverläufe Ihres Modells für Pixel x sind zwischen 0,0 und 0,8 positiv, gehen jedoch zwischen 0,8 und 1,0 auf 0,0. Pixel x eindeutig einen signifikanten Einfluss darauf, Ihr Modell in Richtung einer vorhergesagten Wahrscheinlichkeit von 80% für die wahre Klasse zu bewegen. Ist es sinnvoll, dass die Bedeutung von Pixel x gering oder diskontinuierlich ist?

  • rechts : Die Intuition hinter IG besteht darin, die lokalen Gradienten von Pixel x zu akkumulieren und seine Bedeutung als Punktzahl dafür zuzuweisen, wie viel es zur Gesamtwahrscheinlichkeit der Ausgabeklasse Ihres Modells addiert oder subtrahiert. Sie können IG in 3 Teile zerlegen und berechnen:

    1. Interpolieren Sie kleine Schritte entlang einer geraden Linie im Merkmalsraum zwischen 0 (Grundlinie oder Startpunkt) und 1 (Wert des Eingabepixels).
    2. Berechnen Sie bei jedem Schritt Gradienten zwischen den Vorhersagen Ihres Modells in Bezug auf jeden Schritt
    3. Nähern Sie das Integral zwischen Ihrer Basislinie und der Eingabe, indem Sie diese lokalen Gradienten akkumulieren (kumulativer Durchschnitt).

Um diese Intuition zu verstärken, werden Sie durch diese 3 Teile gehen, indem Sie IG auf das Beispielbild "Feuerlöschboot" unten anwenden.

Stellen Sie eine Basislinie auf

Eine Grundlinie ist ein Eingabebild, das als Ausgangspunkt für die Berechnung der Merkmalsbedeutung verwendet wird. Intuitiv können Sie sich vorstellen, dass die erklärende Rolle der Grundlinie die Auswirkung des Fehlens jedes Pixels auf die "Fireboat" -Vorhersage darstellt, um sich von der Auswirkung jedes Pixels auf die "Fireboat" -Vorhersage zu kontrastieren, wenn sie im Eingabebild vorhanden ist. Infolgedessen spielt die Wahl der Grundlinie eine zentrale Rolle bei der Interpretation und Visualisierung der Bedeutung von Pixelmerkmalen. Weitere Informationen zur Auswahl der Baseline finden Sie in den Ressourcen im Abschnitt "Nächste Schritte" am Ende dieses Lernprogramms. Hier verwenden Sie ein schwarzes Bild, dessen Pixelwerte alle Null sind.

Andere Möglichkeiten, mit denen Sie experimentieren können, sind ein tf.random.uniform(shape=(224,224,3), minval=0.0, maxval=1.0) weißes Bild oder ein zufälliges Bild, das Sie mit 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

Entpacken Sie Formeln in Code

Die Formel für integrierte Farbverläufe lautet wie folgt:

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

wo:

$ _ {i} $ = Funktion
$ x $ = Eingabe
$ x '$ = Grundlinie
$ \ alpha $ = Interpolationskonstante, um Features durch zu stören

In der Praxis ist die Berechnung eines bestimmten Integrals nicht immer numerisch möglich und kann rechenintensiv sein. Berechnen Sie daher die folgende numerische Näherung:

$ IntegratedGrads ^ {ungefähr} _ {i} (x) :: = (x_ {i} -x '_ {i}) \ times \ sum_ {k = 1} ^ {m} \ frac {\ partielles F (x '+ \ frac {k} {m} \ times (x - x'))} {\ partielle x_ {i}} \ times \ frac {1} {m} $

wo:

$ _ {i} $ = Feature (einzelnes Pixel)
$ x $ = Eingabe (Bildtensor)
$ x '$ = Grundlinie (Bildtensor)
$ k $ = skalierte Merkmalsstörungskonstante
$ m $ = Anzahl der Schritte in der Riemannschen Summennäherung des Integrals
$ (x_ {i} -x '_ {i}) $ = ein Begriff für die Differenz zur Basislinie. Dies ist erforderlich, um die integrierten Farbverläufe zu skalieren und im Hinblick auf das Originalbild beizubehalten. Der Pfad vom Grundlinienbild zur Eingabe befindet sich im Pixelraum. Da Sie mit IG in eine gerade Linie integrieren (lineare Transformation), entspricht dies in etwa dem Integralterm der Ableitung der interpolierten Bildfunktion in Bezug auf $ \ alpha $ mit genügend Schritten. Das Integral summiert den Gradienten jedes Pixels mal die Änderung des Pixels entlang des Pfades. Es ist einfacher, diese Integration als einheitliche Schritte von einem Bild zum anderen zu implementieren und $ x: = (x '+ \ alpha (xx')) $ zu ersetzen. Die Änderung der Variablen ergibt also $ dx = (xx ') d \ alpha $. Der Term $ (xx ') $ ist konstant und wird aus dem Integral herausgerechnet.

Bilder interpolieren

$ IntegratedGrads ^ {ungefähr} _ {i} (x) :: = (x_ {i} -x '_ {i}) \ times \ sum_ {k = 1} ^ {m} \ frac {\ partielles F (\ Überbrücken Sie {x '+ \ frac {k} {m} \ times (x - x')} ^ \ text {interpolieren Sie m Bilder in k Intervallen})} {\ partielle x_ {i}} \ times \ frac {1} {m} $

Zunächst erzeugen Sie eine lineare Interpolation zwischen der Grundlinie und dem Originalbild. Sie können sich interpolierte Bilder als kleine Schritte im Merkmalsraum zwischen Ihrer Grundlinie und der Eingabe vorstellen, die in der ursprünglichen Gleichung durch $ \ alpha $ dargestellt werden.

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

Verwenden Sie die obige Funktion, um interpolierte Bilder entlang eines linearen Pfades in Alpha-Intervallen zwischen einem schwarzen Grundlinienbild und dem Beispielbild "Fireboat" zu erzeugen.

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

Lassen Sie uns die interpolierten Bilder visualisieren. Hinweis: Eine andere Art, über die $ \ alpha $ -Konstante nachzudenken, besteht darin, dass sie die Intensität jedes interpolierten Bildes konstant erhöht.

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

Gradienten berechnen

Schauen wir uns nun an, wie Gradienten berechnet werden, um die Beziehung zwischen Änderungen an einem Feature und Änderungen in den Vorhersagen des Modells zu messen. Bei Bildern gibt der Gradient an, welche Pixel den stärksten Einfluss auf die vorhergesagten Klassenwahrscheinlichkeiten des Modells haben.

$ IntegratedGrads ^ {approx} _ {i} (x) :: = (x_ {i} -x '_ {i}) \ times \ sum_ {k = 1} ^ {m} \ frac {\ overbrace {\ partiell F (\ text {interpolierte Bilder})} ^ \ text {Gradienten berechnen}} {\ partielle x_ {i}} \ times \ frac {1} {m} $

wo:
$ F () $ = Vorhersagefunktion Ihres Modells
$ \ frac {\ partielle {F}} {\ partielle {x_i}} $ = Gradient (Vektor partieller Ableitungen $ \ partielle $) der Vorhersagefunktion Ihres Modells F relativ zu jedem Merkmal $ x_i $

TensorFlow erleichtert Ihnen das Berechnen von Verläufen mit einemtf.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)

Berechnen wir die Verläufe für jedes Bild entlang des Interpolationspfads in Bezug auf die korrekte Ausgabe. Denken Sie daran, dass Ihr Modell einen (1, 1001) -förmigen Tensor mit Protokollen zurückgibt, die Sie für jede Klasse in vorhergesagte Wahrscheinlichkeiten konvertieren. Sie müssen den korrekten ImageNet- compute_gradients an die Funktion compute_gradients für Ihr Bild übergeben.

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

Beachten Sie die Ausgabeform von (n_interpolated_images, img_height, img_width, RGB) , die den Gradienten für jedes Pixel jedes Bildes entlang des Interpolationspfads angibt. Sie können sich diese Verläufe als Messung der Änderung der Vorhersagen Ihres Modells für jeden kleinen Schritt im Feature-Space vorstellen.

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

Visualisierung der Gradientensättigung

Denken Sie daran, dass die soeben berechneten Gradienten lokale Änderungen der vorhergesagten Wahrscheinlichkeit Ihres Modells für "Feuerlöschboot" beschreiben und sättigen können .

Diese Konzepte werden anhand der Gradienten visualisiert, die Sie oben in den beiden folgenden Darstellungen berechnet haben.

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

  • links : Dieses Diagramm zeigt, wie sich das Vertrauen Ihres Modells in die "Fireboat" -Klasse je nach Alphas unterscheidet. Beachten Sie, wie sich die Steigungen oder Steigungen der Linie zwischen 0,6 und 1,0 weitgehend abflachen oder sättigen, bevor Sie sich auf die endgültige "Feuerlöschboot" -Wahrscheinlichkeit von etwa 40% einstellen.

  • rechts : Das rechte Diagramm zeigt die durchschnittlichen Gradientengrößen über Alpha direkter. Beachten Sie, wie sich die Werte scharf nähern und sogar kurz unter Null fallen. Tatsächlich "lernt" Ihr Modell am meisten aus Verläufen bei niedrigeren Alpha-Werten, bevor es gesättigt wird. Intuitiv können Sie sich das vorstellen, da Ihr Modell die Pixel, z. B. Wasserwerfer, gelernt hat, um die richtige Vorhersage zu treffen, und diese Pixelgradienten auf Null sendet. Es ist jedoch immer noch ziemlich unsicher und konzentriert sich auf falsche Brücken- oder Wasserstrahlpixel, wenn sich die Alpha-Werte dem nähern Original-Eingabebild.

Um sicherzustellen, dass diese wichtigen Wasserwerferpixel als wichtig für die Vorhersage "Feuerlöschboot" wiedergegeben werden, werden Sie weiter unten lernen, wie Sie diese Gradienten akkumulieren, um genau zu schätzen, wie sich jedes Pixel auf Ihre vorhergesagte Wahrscheinlichkeit für "Feuerlöschboote" auswirkt.

Gradienten akkumulieren (integrale Approximation)

Es gibt viele verschiedene Möglichkeiten, die numerische Approximation eines Integrals für IG mit unterschiedlichen Kompromissen in Bezug auf Genauigkeit und Konvergenz über verschiedene Funktionen hinweg zu berechnen. Eine beliebte Klasse von Methoden heißt Riemann-Summen . Hier verwenden Sie die Trapezregel (zusätzlichen Code zum Erkunden verschiedener Approximationsmethoden finden Sie am Ende dieses Tutorials).

$ IntegratedGrads ^ {approx} _ {i} (x) :: = (x_ {i} -x '_ {i}) \ times \ overbrace {\ sum_ {k = 1} ^ {m}} ^ \ text { Summiere m lokale Verläufe} \ text {Verläufe (interpolierte Bilder)} \ times \ overbrace {\ frac {1} {m}} ^ \ text {Teilen durch m Schritte} $

Aus der Gleichung können Sie ersehen, dass Sie über m Gradienten summieren und durch m Schritte dividieren. Sie können die beiden Operationen zusammen für Teil 3 als Durchschnitt der lokalen Gradienten von m interpolierten Vorhersagen und Eingabebildern implementieren.

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

Die integral_approximation nimmt die Gradienten der vorhergesagten Wahrscheinlichkeit der Zielklasse in Bezug auf die interpolierten Bilder zwischen der Grundlinie und dem Originalbild.

ig = integral_approximation(
    gradients=path_gradients)

Sie können bestätigen, dass die Mittelung über die Gradienten von m interpolierten Bildern einen integrierten Gradiententensor mit der gleichen Form wie das ursprüngliche "Giant Panda" -Bild zurückgibt.

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

Alles zusammenfügen

Jetzt kombinieren Sie die drei vorherigen allgemeinen Teile zu einer IntegratedGradients Funktion und verwenden einen @ tf.function- Dekorator, um sie zu einem hochleistungsfähigen aufrufbaren TensorFlow-Diagramm zu kompilieren. Dies wird in 5 kleineren Schritten unten implementiert:

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

  1. Generieren Sie Alphas $ \ alpha $

  2. Generiere interpolierte Bilder = $ (x '+ \ frac {k} {m} \ times (x - x')) $

  3. Berechnen Sie Gradienten zwischen Modell $ F $ -Ausgabevorhersagen in Bezug auf Eingabemerkmale = $ \ frac {\ partielles F (\ text {interpolierte Pfadeingaben})} {\ partielles x_ {i}} $

  4. Integrale Approximation durch Mittelung von Gradienten = $ \ sum_ {k = 1} ^ m \ text {Gradienten} \ times \ frac {1} {m} $

  5. Skalieren Sie integrierte Farbverläufe in Bezug auf das Originalbild = $ (x_ {i} -x '_ {i}) \ times \ text {integrierte Farbverläufe} $. Der Grund, warum dieser Schritt erforderlich ist, besteht darin, sicherzustellen, dass die über mehrere interpolierte Bilder akkumulierten Attributionswerte in denselben Einheiten liegen und die Pixelbedeutungen auf dem Originalbild genau wiedergeben.

@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)

Auch hier können Sie überprüfen, ob die IG-Feature-Zuordnungen dieselbe Form haben wie das eingegebene "Fireboat" -Bild.

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

Das Papier schlägt die Anzahl der Schritte vor, die je nach Beispiel zwischen 20 und 300 liegen sollen (obwohl dies in der Praxis in den 1000er Jahren höher sein kann, um das Integral genau zu approximieren). Zusätzlichen Code zum Überprüfen der entsprechenden Anzahl von Schritten finden Sie in den Ressourcen "Nächste Schritte" am Ende dieses Lernprogramms.

Zuschreibungen visualisieren

Sie können Attributionen visualisieren und über das Originalbild legen. Der folgende Code summiert die absoluten Werte der integrierten Farbverläufe über die Farbkanäle, um eine Attributionsmaske zu erstellen. Diese Darstellungsmethode erfasst den relativen Einfluss von Pixeln auf die Vorhersagen des Modells.

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

Wenn Sie sich die Zuschreibungen auf dem Bild "Feuerlöschboot" ansehen, können Sie sehen, dass das Modell die Wasserwerfer und Ausgüsse als Beitrag zur korrekten Vorhersage identifiziert.

_ = 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

Auf dem Bild "Giant Panda" heben die Zuschreibungen die Textur, die Nase und das Fell des Panda-Gesichts hervor.

_ = 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

Verwendungen und Einschränkungen

Anwendungsfälle

  • Der Einsatz von Techniken wie Integrated Gradients vor der Bereitstellung Ihres Modells kann Ihnen dabei helfen, eine Vorstellung davon zu entwickeln, wie und warum es funktioniert. Stimmen die durch diese Technik hervorgehobenen Merkmale mit Ihrer Intuition überein? Wenn nicht, kann dies auf einen Fehler in Ihrem Modell oder Datensatz oder auf eine Überanpassung hinweisen.

Einschränkungen

  • Integrierte Verläufe bieten Feature-Wichtigkeiten für einzelne Beispiele, jedoch keine globalen Feature-Wichtigkeiten für ein gesamtes Dataset.

  • Integrierte Verläufe bieten individuelle Feature-Wichtigkeiten, erklären jedoch nicht die Interaktionen und Kombinationen von Features.

Nächste Schritte

In diesem Tutorial wurde eine grundlegende Implementierung von Integrated Gradients vorgestellt. Als nächsten Schritt können Sie dieses Notizbuch verwenden, um diese Technik mit verschiedenen Modellen und Bildern selbst auszuprobieren.

Für interessierte Leser gibt es eine längere Version dieses Tutorials (die Code für verschiedene Baselines enthält, um integrale Approximationen zu berechnen und eine ausreichende Anzahl von Schritten zu bestimmen), die Sie hier finden.

Um Ihr Verständnis zu vertiefen, lesen Sie das Dokument Axiomatic Attribution for Deep Networks und Github Repository , das eine Implementierung in einer früheren Version von TensorFlow enthält. Sie können auch die Feature-Zuordnung und die Auswirkungen verschiedener Baselines auf destill.pub untersuchen .

Möchten Sie IG in Ihre Workflows für maschinelles Lernen in der Produktion integrieren, um Funktionen zu importieren, Modellfehler zu analysieren und Datenversatz zu überwachen? Schauen Sie sich das Explainable AI- Produkt von Google Cloud an, das IG-Zuweisungen unterstützt. Die Google AI PAIR-Forschungsgruppe hat auch das Was-wäre-wenn-Tool als Open-Source -Tool bereitgestellt, das zum Debuggen von Modellen verwendet werden kann, einschließlich der Visualisierung von IG-Funktionszuordnungen.