Aiuto proteggere la Grande Barriera Corallina con tensorflow sul Kaggle Join Sfida

Gradienti integrati

Visualizza su TensorFlow.org Esegui in Google Colab Visualizza su GitHub Scarica taccuino Vedi il modello del mozzo TF

Questo tutorial illustrato come implementare Sfumature integrati (IG), uno spiegabile AI tecnica introdotta nella carta assiomatica Attribuzione per Deep Networks . IG mira a spiegare la relazione tra le previsioni di un modello in termini di caratteristiche. Ha molti casi d'uso, tra cui la comprensione dell'importanza delle funzionalità, l'identificazione dell'asimmetria dei dati e il debug delle prestazioni del modello.

IG è diventata una tecnica di interpretabilità popolare grazie alla sua ampia applicabilità a qualsiasi modello differenziabile (ad es. immagini, testo, dati strutturati), facilità di implementazione, giustificazioni teoriche ed efficienza computazionale rispetto ad approcci alternativi che gli consentono di scalare su reti di grandi dimensioni e funzionalità spazi come le immagini.

In questo tutorial, esaminerai un'implementazione di IG passo dopo passo per comprendere l'importanza delle caratteristiche dei pixel di un classificatore di immagini. Come esempio, si consideri l' immagine di un fireboat spruzzano getti d'acqua. Classificheresti questa immagine come una nave dei pompieri e potresti evidenziare i pixel che compongono la barca e i cannoni ad acqua come importanti per la tua decisione. Il tuo modello classificherà anche questa immagine come un battello dei pompieri più avanti in questo tutorial; tuttavia, evidenzia gli stessi pixel come importanti quando spiega la sua decisione?

Nelle immagini sottostanti intitolate "IG Attribution Mask" e "Original + IG Mask Overlay" puoi vedere che il tuo modello evidenzia invece (in viola) i pixel che comprendono i cannoni ad acqua e i getti d'acqua della barca come più importanti della barca stessa per sua decisione. In che modo il tuo modello si generalizzerà ai nuovi battelli antincendio? E le motoscafi senza getti d'acqua? Continua a leggere per saperne di più su come funziona IG e su come applicare IG ai tuoi modelli per comprendere meglio la relazione tra le loro previsioni e le caratteristiche sottostanti.

Immagine di uscita 1

Impostare

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

Scarica un classificatore di immagini preaddestrato da TF-Hub

IG può essere applicato a qualsiasi modello differenziabile. Nello spirito del documento originale, si utilizzerà una versione pre-formati dello stesso modello, Inception V1, che si scarica da tensorflow Hub .

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
_________________________________________________________________

Dalla pagina del modulo, è necessario tenere presente quanto segue su Inception V1:

Ingressi: La forma di ingresso previsto per il modello è (None, 224, 224, 3) . Questo è un denso 4D tensore di DTYPE Float32 e forma (batch_size, height, width, RGB channels) i cui elementi sono valori di colore RGB dei pixel normalizzati alla gamma [0, 1]. Il primo elemento è None per indicare che il modello può assumere qualsiasi dimensione intero lotto.

Uscite: Una tf.Tensor di logit a forma di (batch_size, 1001) . Ogni riga rappresenta il punteggio previsto del modello per ciascuna delle 1.001 classi di ImageNet. Per top indice di classe previsto del modello è possibile utilizzare tf.argmax(predictions, axis=-1) . Inoltre, è possibile anche convertire l'uscita logit del modello di probabilità previste in tutte le classi utilizzando tf.nn.softmax(predictions, axis=-1) per quantificare l'incertezza del modello così come esplorare le classi previste analoghe per il debug.

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
24576/10484 [======================================================================] - 0s 0us/step

Carico e pre-elaborare le immagini con tf.image

Si illustrano IG utilizzando due immagini da Wikimedia Commons : un Fireboat , e un panda gigante .

def read_image(file_name):
  image = tf.io.read_file(file_name)
  image = tf.io.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
3964928/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
819200/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

Classificare le immagini

Iniziamo classificando queste immagini e visualizzando le prime 3 previsioni più sicure. Di seguito è riportata una funzione di utilità per recuperare le prime k etichette e probabilità previste.

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%

Calcola gradienti integrati

Il tuo modello, Inception V1, è una funzione appresa che descrive una mappatura tra lo spazio delle funzionalità di input, i valori dei pixel dell'immagine e uno spazio di output definito dai valori di probabilità della classe ImageNet compresi tra 0 e 1. I primi metodi di interpretabilità per le reti neurali assegnati punteggi di importanza delle funzionalità utilizzando gradienti, che indicano quali pixel hanno il locale più ripido rispetto alla previsione del modello in un determinato punto lungo la funzione di previsione del modello. Tuttavia, gradienti descrivono solo cambiamenti locali in funzione di stima del vostro modello rispetto ai valori dei pixel e non descrivono appieno la vostra intera funzione modello di previsione. Come il vostro modello completamente "impara" il rapporto tra la portata di un singolo pixel e la classe IMAGEnet corretta, il gradiente per questo pixel saturerà, che significa diventano sempre più piccoli e anche andare a zero. Considera la semplice funzione del modello di seguito:

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

  • a sinistra: pendenze del modello per pixel x sono positivi compreso tra 0,0 e 0,8, ma vanno a 0.0 tra 0,8 e 1,0. Pixel x chiaramente ha un impatto significativo a spingere il tuo modello verso l'80% del predetto probabilità sulla vera classe. Ha senso che pixel x l' importanza è piccolo o discontinuo?

  • a destra: L'intuizione dietro IG è quello di accumulare pixel x 's gradienti locali e attribuiscono la sua importanza come un punteggio per quanto aggiunge o sottrae alla probabilità complessiva di classe di uscita del vostro modello. Puoi scomporre e calcolare IG in 3 parti:

    1. interpolare piccoli passi lungo una linea retta nello spazio delle caratteristiche tra 0 (una linea di base o punto iniziale) e 1 (valore del pixel di input)
    2. calcola i gradienti ad ogni passaggio tra le previsioni del tuo modello rispetto a ogni passaggio
    3. approssimare l'integrale tra la linea di base e l'input accumulando (media cumulativa) questi gradienti locali.

Per rafforzare questa intuizione, camminerai attraverso queste 3 parti applicando IG all'immagine di esempio "Fireboat" qui sotto.

Stabilire una linea di base

Una linea di base è un'immagine di input utilizzata come punto di partenza per calcolare l'importanza delle caratteristiche. Intuitivamente, puoi pensare al ruolo esplicativo della linea di base come rappresentante dell'impatto dell'assenza di ogni pixel sulla previsione "Fireboat" in contrasto con il suo impatto di ciascun pixel sulla previsione "Fireboat" quando presente nell'immagine di input. Di conseguenza, la scelta della linea di base svolge un ruolo centrale nell'interpretazione e nella visualizzazione dell'importanza delle caratteristiche dei pixel. Per ulteriori discussioni sulla selezione della linea di base, vedere le risorse nella sezione "Passaggi successivi" nella parte inferiore di questa esercitazione. Qui utilizzerai un'immagine nera i cui valori in pixel sono tutti zero.

Altre scelte si potrebbe sperimentare includono un'immagine completamente bianca, o un'immagine casuale, che è possibile creare con 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

Decomprimi le formule nel codice

La formula per i gradienti integrati è la seguente:

\(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}\)

dove:

\(_{i}\) = funzione
\(x\) = input
\(x'\) = basale
\(\alpha\) = interpolazione costante alle caratteristiche perturbare da

In pratica, il calcolo di un integrale definito non è sempre numericamente possibile e può essere computazionalmente costoso, quindi si calcola la seguente approssimazione numerica:

\(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}\)

dove:

\(_{i}\) = funzione (singolo pixel)
\(x\) = input (tensore immagine)
\(x'\) = basale (immagine tensore)
\(k\) = di scala costante perturbazione
\(m\) = numero di passi nella somma ravvicinamento Riemann dell'integrale
\((x_{i}-x'_{i})\) = un termine per la differenza dalla linea di fondo. Ciò è necessario per ridimensionare i gradienti integrati e mantenerli in termini di immagine originale. Il percorso dall'immagine della linea di base all'input è nello spazio dei pixel. Poiché con IG si integra in una linea retta (trasformazione lineare) Questo finisce per essere approssimativamente equivalente al termine integrale della derivata della funzione immagine interpolata rispetto \(\alpha\) con gradini abbastanza. L'integrale somma il gradiente di ogni pixel per il cambiamento nel pixel lungo il percorso. E 'più semplice da implementare questa integrazione come passi uniformi da un'immagine all'altra, sostituendo \(x := (x' + \alpha(x-x'))\). Quindi il cambio di variabili dà \(dx = (x-x')d\alpha\). Il \((x-x')\) termine è costante e viene fattorizzato dell'integrale.

Interpola immagini

\(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{interpolate m images at k intervals})}{\partial x_{i} } \times \frac{1}{m}\)

In primo luogo, si genererà un'interpolazione lineare tra la linea di base e l'immagine originale. Si può pensare di immagini interpolati come piccoli passi nello spazio funzione tra il basale e di ingresso, rappresentata da \(\alpha\) nell'equazione originale.

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

Usiamo la funzione sopra per generare immagini interpolate lungo un percorso lineare a intervalli alfa tra un'immagine di base nera e l'immagine di esempio "Fireboat".

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

Visualizziamo le immagini interpolate. Nota: un altro modo di pensare il \(\alpha\) costante è che è costantemente in aumento l'intensità di ogni immagine interpolata.

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

Calcola gradienti

Ora diamo un'occhiata a come calcolare i gradienti per misurare la relazione tra le modifiche a una feature e le modifiche nelle previsioni del modello. Nel caso delle immagini, il gradiente ci dice quali pixel hanno l'effetto più forte sulle probabilità di classe previste dai modelli.

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

dove:
\(F()\) = funzione di previsione del modello
\(\frac{\partial{F} }{\partial{x_i} }\) = gradiente (vettore delle derivate parziali \(\partial\)) del modello di F funzione di stima rispetto a ciascuna caratteristica \(x_i\)

Tensorflow rende gradienti di calcolo facile per voi con un 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)

Calcoliamo i gradienti per ogni immagine lungo il percorso di interpolazione rispetto all'output corretto. Ricordiamo che il modello restituisce un (1, 1001) a forma di Tensor con logit che si converte probabilità previste per ogni classe. È necessario passare la corretta destinazione IMAGEnet indice di classe al compute_gradients funzioni per la vostra immagine.

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

Nota la forma di uscita (n_interpolated_images, img_height, img_width, RGB) , che ci dà il gradiente per ogni pixel di ogni immagine lungo il percorso di interpolazione. Puoi pensare a questi gradienti come alla misurazione del cambiamento nelle previsioni del tuo modello per ogni piccolo passo nello spazio delle caratteristiche.

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

Visualizzazione della saturazione del gradiente

Ricordiamo che i gradienti appena sopra calcolato descrivono modifiche locali alle probabilità prevista del vostro modello di "Battello antincendio" e può saturare.

Questi concetti vengono visualizzati utilizzando i gradienti calcolati sopra nei 2 grafici sottostanti.

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]);

png

  • sinistra: Questo grafico mostra come la fiducia del vostro modello nella classe "Fireboat" varia a seconda alfa. Notare come i gradienti, o pendenza della linea, si appiattiscano o si saturano ampiamente tra 0,6 e 1,0 prima di stabilizzarsi alla probabilità prevista "Fireboat" finale di circa il 40%.

  • a destra: a destra mostra trama i gradienti medi di grandezze oltre alfa più direttamente. Nota come i valori si avvicinano bruscamente e scendono anche brevemente sotto lo zero. In effetti, il tuo modello "impara" di più dai gradienti a valori più bassi di alfa prima della saturazione. Intuitivamente, puoi pensare a questo poiché il tuo modello ha appreso i pixel, ad esempio i cannoni ad acqua per fare la previsione corretta, inviando questi pixel gradienti a zero, ma è ancora abbastanza incerto e focalizzato su ponti spuri o pixel a getto d'acqua mentre i valori alfa si avvicinano al immagine di ingresso originale.

Per assicurarti che questi importanti pixel del cannone ad acqua si riflettano come importanti per la previsione "Fireboat", continuerai di seguito per imparare come accumulare questi gradienti per approssimare con precisione l'impatto di ogni pixel sulla tua probabilità prevista "Fireboat".

Gradienti accumulati (approssimazione integrale)

Esistono molti modi diversi per calcolare l'approssimazione numerica di un integrale per IG con diversi compromessi in termini di precisione e convergenza tra funzioni variabili. Una classe popolare di metodi si chiama somme di Riemann . Qui utilizzerai la regola trapezoidale (puoi trovare codice aggiuntivo per esplorare diversi metodi di approssimazione alla fine di questo tutorial).

$ IntegratedGrads ^ {circa} {i} (x) :: = (x {i} -x'{i}) \ times \ overbrace {\ sum {k = 1} ^ {m}} ^ \ text {Sum m gradienti locali} \text{gradients(immagini interpolate)} \times \overbrace{\frac{1}{m} }^\text{Dividi per m passi}$

Dalla equazione, si può vedere che si sta sommando su m gradienti e dividendo per m passi. È possibile implementare le due operazioni insieme per parte 3 come media dei gradienti locali di m interpolati predizioni e immagini in ingresso.

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 funzione prende i gradienti della probabilità prevista della classe bersaglio rispetto alle immagini interpolati tra la base e l'immagine originale.

ig = integral_approximation(
    gradients=path_gradients)

È possibile confermare la media attraverso i gradienti di m interpolati immagini restituisce un gradienti integrati tensore con la stessa forma l'immagine originale "Giant Panda".

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

Mettere tutto insieme

Ora si combinare le parti generali precedenti 3 insieme in un IntegratedGradients funzione e utilizzare un @ tf.function decoratore a compilarlo in un grafico richiamabile tensorflow ad alte prestazioni. Questo è implementato come 5 passaggi più piccoli di seguito:

\(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. Genera alfa \(\alpha\)

  2. Generare immagini interpolati = \((x' + \frac{k}{m}\times(x - x'))\)

  3. Gradienti di calcolo tra modello \(F\) predizioni di uscita rispetto all'ingresso caratteristiche = \(\frac{\partial F(\text{interpolated path inputs})}{\partial x_{i} }\)

  4. Ravvicinamento integrale attraverso gradienti averaging = \(\sum_{k=1}^m \text{gradients} \times \frac{1}{m}\)

  5. Scala gradienti integrati rispetto all'immagine originale = \((x_{i}-x'_{i}) \times \text{integrated gradients}\). Il motivo per cui questo passaggio è necessario è assicurarsi che i valori di attribuzione accumulati su più immagini interpolate siano nelle stesse unità e rappresentino fedelmente l'importanza dei pixel sull'immagine originale.

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

Ancora una volta, puoi verificare che le attribuzioni delle funzionalità IG abbiano la stessa forma dell'immagine "Fireboat" di input.

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

Il documento suggerisce che il numero di passaggi sia compreso tra 20 e 300 a seconda dell'esempio (sebbene in pratica questo possa essere maggiore nei 1.000 per approssimare con precisione l'integrale). Puoi trovare codice aggiuntivo per verificare il numero appropriato di passaggi nelle risorse "Passaggi successivi" alla fine di questo tutorial.

Visualizza le attribuzioni

Sei pronto per visualizzare le attribuzioni e sovrapporle all'immagine originale. Il codice seguente somma i valori assoluti dei gradienti integrati attraverso i canali di colore per produrre una maschera di attribuzione. Questo metodo di tracciamento cattura l'impatto relativo dei pixel sulle previsioni del modello.

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

Guardando le attribuzioni sull'immagine "Fireboat", puoi vedere che il modello identifica i cannoni ad acqua e i beccucci che contribuiscono alla sua corretta previsione.

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

Nell'immagine "Panda gigante", le attribuzioni evidenziano la trama, il naso e la pelliccia della faccia del Panda.

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

Usi e limitazioni

Casi d'uso

  • L'utilizzo di tecniche come i gradienti integrati prima di distribuire il modello può aiutarti a sviluppare l'intuizione su come e perché funziona. Le caratteristiche evidenziate da questa tecnica corrispondono al tuo intuito? In caso contrario, potrebbe essere indicativo di un bug nel modello o nel set di dati o di un adattamento eccessivo.

Limitazioni

  • I gradienti integrati forniscono importanza delle funzionalità su singoli esempi, tuttavia, non fornisce importanza delle funzionalità globali su un intero set di dati.

  • I gradienti integrati forniscono l'importanza delle singole funzionalità, ma non spiega le interazioni e le combinazioni di funzionalità.

Prossimi passi

Questo tutorial ha presentato un'implementazione di base dei gradienti integrati. Come passaggio successivo, puoi utilizzare questo quaderno per provare tu stesso questa tecnica con diversi modelli e immagini.

Per i lettori interessati, c'è una versione più lunga di questo tutorial (che include il codice per le diverse linee di base, per calcolare approssimazioni integrali, e di determinare un numero sufficiente di passi), che potete trovare qui .

Per approfondire la vostra comprensione, controlla la carta assiomatica Attribuzione per Deep reti e repository GitHub , che contiene un'implementazione in una versione precedente di tensorflow. Puoi anche esplorare funzione di attribuzione, e l'impatto delle diverse linee di base, sulla distill.pub .

Interessato a incorporare IG nei flussi di lavoro di machine learning di produzione per l'importanza delle funzionalità, l'analisi degli errori del modello e il monitoraggio dell'inclinazione dei dati? Scopri di Google Cloud spiegabile AI prodotto che supporta attribuzioni IG. Il gruppo di ricerca di Google AI COPPIA anche open-source il What-if strumento che può essere utilizzato per il modello di debug, tra cui la visualizzazione IG caratteristica attribuzioni.