Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/es-419/tutorials/interpretability/integrated_gradients.ipynb
25118 views
Kernel: Python 3
#@title Con licencia de Apache License, versión 2.0 (la "Licencia"); # no puede usar este archivo excepto de conformidad con la Licencia. # Puede obtener una copia de la Licencia en # # https://www.apache.org/licenses/LICENSE-2.0 # # A menos que lo exija la ley aplicable o se acuerde por escrito, el software # distribuido bajo la Licencia se distribuye "TAL CUAL", # SIN GARANTÍAS NI CONDICIONES DE NINGÚN TIPO, ya sean expresas o implícitas. # Consulte la Licencia para conocer el idioma específico que rige los permisos y # limitaciones bajo la Licencia.

Gradientes integrados

En este tutorial se demuestra cómo implementar gradientes integrados (IG), una técnica de inteligencia artificial explicable presentada en la publicación Axiomatic Attribution for Deep Networks (Atribución axiomática para redes profundas). Los gradientes integrados tienen como objetivo explicar la relación entre las predicciones de un modelo en lo que respecta a sus atributos. Tiene muchos usos, incluidos los de entender la importancia de los atributos, identificar la asimetría estadística de los datos (conocida también como "data screw") y la depuración del rendimiento del modelo.

Los gradientes integrados se han vuelto una técnica de interpretabilidad muy popular gracias a su amplia aplicabilidad en cualquier modelo diferenciable (p. ej., en imágenes, textos o datos estructurados), a su fácil implementación, a las justificaciones teóricas y a la eficiencia computacional relativa a enfoques alternativos, que permiten escalar el modelo a redes grandes y trabajar espacios como imágenes.

Con este tutorial se mostrará en detalle una implementación paso a paso de gradientes integrados para entender la importancia de los atributos de pixeles de un clasificador de imágenes. Como ejemplo, tome esta imagen de un barco contraincendios que despide chorros de agua. Seguramente, clasificaría esta imagen como barco contraincendios y destacaría los pixeles que forman el barco y los cañones hidrantes como importantes para la toma de decisiones. Su modelo también clasificará esta imagen como barco contraincendios más adelante, en este mismo tutorial. Sin embargo, ¿destaca los mismos pixeles como importantes cuando explica la decisión?

En las imágenes que se encuentran a continuación, bajo el título "IG Attribution Mask" (Máscara de atribuciones de gradientes integrados) y "Original + IG Mask Overlay" (Original + Superposición de máscara de gradientes integrados) podrá ver que en su modelo se destacan (en violeta) los pixeles que comprenden a los cañones hidrantes del barco y a los chorros de agua como más importantes que el barco mismo para la decisión. ¿Cómo hará el modelo para lograr la generalización que lo lleve a nuevos barcos contraincendios? ¿Qué pasará entonces con los barcos contraincendios que no tengan chorros de agua? Siga leyendo para conocer más acerca de cómo funciona el gradiente integrado y cómo aplicar los gradientes integrados a los modelos para entender mejor la relación entre las predicciones de estos gradientes y los atributos subyacentes.

Imagen de salida 1

Preparación

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

Descarga de un clasificador de imágenes previamente entrenado del repositorio TF-Hub

El gradiente integrado se puede aplicar en cualquier modelo diferenciable. Para seguir con el mismo tenor de la publicación original, se usará una versión previamente entrenada del mismo modelo, Inception V1, que descargará del repositorio 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()

En la página del módulo, deberá tener en cuenta lo siguiente sobre Inception V1:

Las entradas: la forma esperada de las entradas para el modelo es (None, 224, 224, 3). Se trata de un tensor 4D denso de tipo d flotante 32 (dtype float32) y forma (batch_size, height, width, RGB channels) cuyos elementos son los valores de color RGB de los pixeles normalizados para el rango [0, 1]. El primer elemento es None para indicar que el modelo puede tomar cualquier tamaño de lote entero.

Las salidas: un tf.Tensor de logits (funciones logísticas) en forma de (batch_size, 1001). Cada una de las filas representa el puntaje predicho del modelo por cada una de las 1001 clases de ImageNet. Para el índice de clase predicho superior del modelo puede usar tf.math.argmax(predictions, axis=-1). Además, también puede convertir la salida de la función logística del modelo a probabilidades pronosticadas en todas las clases con tf.nn.softmax(predictions, axis=-1) para cuantificar la incertidumbre del modelo y, a la vez, explorar clases predichas similares para la depuración.

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

Carga y procesamiento previo de imágenes con tf.image

Ilustrará los gradientes integrados con dos imágenes de Wikimedia Commons: un barco contraincendios y 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()}
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()

Clasificación de imágenes

Empiece por clasificar estas imágenes y mostrar las 3 predicciones más confiables. A continuación, hay una función de utilidad para extraer las etiquetas y las probabilidades predichas top 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%}')

Cálculo de gradientes integrados

El modelo, Inception V1, es una función aprendida que describe un mapeo entre el espacio de los atributos de entrada, los valores de los pixeles de la imagen y un espacio de salida definido por los valores probabilísticos de clase ImageNet entre 0 y 1. Los primeros métodos de interpretabilidad para redes neuronales asignaban puntajes de importancia de atributos usando gradientes, con lo que se obtiene qué pixeles tienen el local más inclinado en relación con las predicciones de su modelo en un punto dado a lo largo de la función de predicción del modelo. Sin embargo, los gradientes solamente describen los cambios de locales en la función de predicción del modelo con respecto a los valores de pixeles y no describen completamente la función de predicción del modelo entero. En cuanto su modelo "aprende" totalmente la relación entre el rango de un pixel individual y la clase ImageNet correcta, el gradiente para este pixel saturará, es decir, se volverá cada vez más pequeño, incluso hasta llegar a cero. Considere la función del modelo simple que se encuentra a continuación:

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)
#@title 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();
  • La izquierda: los gradientes de su modelo para el pixel x son positivos entre 0.0 y 0.8, pero van a 0.0 entre 0.8 y 1.0. El pixel x claramente tiene un impacto significativo en impulsar el modelo hacia una probabilidad prevista del 80% en la clase verdadera. ¿Tiene sentido que la importancia del pixel x sea poca o discontinua?

  • La derecha: la intuición detrás de IG consiste en acumular los gradientes del local del pixel x y atribuirles la importancia como un puntaje de cuánto suma o resta a la probabilidad de clase de salida general del modelo. Se puede descomponer y calcular IG en tres partes:

    1. Interpolar pequeños pasos a lo largo de una línea recta en el espacio de atributos entre 0 (una línea de base o un punto de inicio) y 1 (valor de pixel de entrada)

    2. Calcular los gradientes a cada paso entre las predicciones del modelo con respecto a cada uno de los pasos

    3. Aproximar la integral entre la línea de base y la entrada mediante la acumulación (promedio acumulativo) de estos gradientes de locales.

Para reforzar esta intuición, realizará estas 3 partes mediante la aplicación de IG al ejemplo de la imagen del "barco contraincendios" que se encuentra a continuación.

Establecimiento de una línea de base

La línea de base es una imagen de entrada que se usa como punto de partida para calcular la importancia de los atributos. Intuitivamente, puede pensar que el rol explicativo de la línea de base es el de representar el impacto de la ausencia de cada pixel en la predicción del "barco contraincendios" para contrastarlo con el impacto de cada pixel en la predicción (del "barco contraincendios") cuando se encuentra presente en la imagen de entrada. Como resultado, la elección de la línea de base juega un rol central en la interpretación y visualización de la importancia de los atributos de los pixeles. Para más detalles sobre la selección de la línea de base, consulte la sección "Próximos pasos" al final de este tutorial. Aquí usará una imagen negra cuyos valores de pixeles son todos cero.

Podría probar con otras opciones como con una imagen toda blanca o una imagen aleatoria que puede crear 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()

Desempaquetamiento de fórmulas en código

La fórmula para gradientes integrados es la siguiente:

IntegratedGradientsi(x)::=(xixem0i)×/em0α=01F(x+α×(xx))xidαIntegratedGradients_{i}(x) ::= (x_{i} - x'{em0}{i})\times\int{/em0}{\alpha=0}^1\frac{\partial F(x'+\alpha \times (x - x'))}{\partial x_i}{d\alpha}

donde:

i_{i} = atributo
xx = entrada
xx' = línea de base
α\alpha = constante de interpolación para perturbar atributos

En la práctica, el cálculo de una integral definida no siempre es numéricamente posible y puede tener un alto costo con respecto al cálculo, por lo tanto, calcule la siguiente aproximación numérica:

IntegratedGradsapproxem0i(x)::=(x/em0ixem1i)×/em1k=1mF(x+km×(xx))xi×1mIntegratedGrads^{approx}{em0}{i}(x)::=(x{/em0}{i}-x'{em1}{i})\times\sum{/em1}{k=1}^{m}\frac{\partial F(x' + \frac{k}{m}\times(x - x'))}{\partial x_{i}} \times \frac{1}{m}

donde:

em0i{em0}{i} = atributo (pixel individual)
xx = entrada (tensor de imagen)
xx' = línea de base (tensor de imagen)
kk = constante de perturbación de atributo escalada
mm = número de pasos en la aproximación de la suma de Riemann de la integral
(x/em0ixi)(x{/em0}{i}-x'_{i}) = un término para la diferencia con respecto a la línea de base. Es necesario para escalar los gradientes integrados y para mantenerlos dentro de los términos de la imagen original. La ruta desde la imagen de base a la entrada se encuentra en espacio de pixeles. Como en los gradientes integrados lo que se hace es integrar en una línea recta (transformación lineal), termina siendo prácticamente equivalente al término integral de la derivada de la función de la imagen interpolada con respecto a α\alpha, con los pasos suficientes. La integral suma el gradiente de cada pixel multiplicado por el cambio del pixel a lo largo de la ruta. Es más simple implementar esta integración como pasos uniformes de una imagen a otra, sustituyendo x:=(x+α(xx))x := (x' + \alpha(x-x')). Entonces, el cambio de variables da dx=(xx)dαdx = (x-x')d\alpha. El término (xx)(x-x') es constante y se factoriza fuera de la integral.

Interpolación de imágenes

IntegratedGradsapproxem0i(x)::=(x/em0ixem1i)×/em1k=1mF(x+km×(xx)interpolate m images at k intervals)xi×1mIntegratedGrads^{approx}{em0}{i}(x)::=(x{/em0}{i}-x'{em1}{i})\times\sum{/em1}{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}

Primero, generará una interpolación lineal entre la línea de base y la imagen original. Puede pensar en la imágenes interpoladas como pequeños pasos en el espacio de atributos entre la línea de base y la entrada, representado por α\alpha en la ecuación lineal.

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

Use la función que figura arriba para generar imágenes interpoladas a lo largo de una ruta lineal a intervalos alfa entre una imagen de base negra y la imagen de ejemplo del "barco contraincendios".

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

Observe las imágenes interpoladas. Nota: otra forma de pensar la constante α\alpha es considerando que aumente consistentemente la intensidad de cada imagen interpolada.

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

Cálculo de gradientes

En esta sección se explica cómo se calculan los gradientes para medir la relación entre los cambios hechos a una característica y los cambios en las predicciones del modelo. En el caso de las imágenes, el gradiente nos dice qué pixeles tienen mayor efecto en las probabilidades de clase predichas de los modelos.

IntegratedGradsapproxem0i(x)::=(x/em0ixem1i)×/em1k=1mF(interpolated images)compute gradientsxi×1mIntegratedGrads^{approx}{em0}{i}(x)::=(x{/em0}{i}-x'{em1}{i})\times\sum{/em1}{k=1}^{m}\frac{\overbrace{\partial F(\text{interpolated images})}^\text{compute gradients}}{\partial x_{i}} \times \frac{1}{m}

donde:
F()F() = la función de predicción del modelo
Fxi\frac{\partial{F}}{\partial{x_i}} = gradiente (vector de derivadas parciales \partial) de la función de predicción del modelo F relativa a cada atributo xix_i

TensorFlow facilita el cálculo de gradientes con 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)

Calcule los gradientes de cada imagen junto con la ruta de interpolación, con respecto a la salida correcta. Recuerde que su modelo devuelve un Tensor con forma (1, 1001) con logits que convierte a las probabilidades predichas para cada clase. Para la imagen, debe pasar el índice correcto de la clase de destino ImageNet a la función compute_gradients.

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

Tenga en cuenta la forma de salida de (n_interpolated_images, img_height, img_width, RGB), que proporciona el gradiente para los pixeles de cada imagen a lo largo de la ruta de interpolación. Puede pensar en estos gradientes como que miden el cambio en las predicciones del modelo para cada pequeño paso en el espacio del atributo.

print(path_gradients.shape)

Visualización de la saturación de gradiente

Recuerde que los gradientes que acaba de calcular arriba describen cambios de locales en las probabilidades predictivas de su modelo de "barco contraincendios" y pueden saturar.

Estos conceptos se visualizan con los gradientes que calculó arriba aplicados en los dos gráficos que se encuentran a continuación.

pred = model(interpolated_images) pred_proba = tf.nn.softmax(pred, axis=-1)[:, 555]
#@title 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]);
  • Izquierda: en este gráfico se muestra cómo varía la confianza de su modelo en la clase "barco contraincendios" a través de los alfas. Tenga en cuenta de qué modo los gradientes, o la pendiente de la línea, se aplana o satura entre 0.6 y 1.0 antes de instalarse en la probabilidad predictiva final para "barco contraincendios" de alrededor del 40%.

  • Derecha: en el gráfico de la derecha se muestran más directamente la magnitudes de los gradientes promedio sobre alfa. Tenga en cuenta el modo en que los valores se acercan abruptamente a cero e incluso caen brevemente por debajo de cero. De hecho, su modelo, de lo que más "aprende" es de los gradientes a valores inferiores de alfa antes de saturar. Intuitivamente, se puede pensar que el modelo ha aprendido los pixeles (p. ej., cuáles son los cañones hidrantes) para hacer las predicciones correctas, enviando estos gradientes de pixeles a cero. Pero todavía resulta bastante incierto y enfocado en pixeles espurios del puente o de los chorros de agua, a medida que los valores alfa se acercan a la imagen de entrada original.

Para asegurarnos de que estos pixeles importantes de los cañones hidrantes se vean realmente reflejados como importantes en la predicción de "barco contraincendios", deberá continuar debajo para aprender cómo acumular estos gradientes para aproximar con exactitud de qué manera impacta cada pixel en la probabilidad predictiva del "barco contraincendios".

Acumulación de gradientes (aproximación de enteros)

Hay muchas formas diferentes de abordar el cálculo de la aproximación numérica de una integral para gradientes integrados con diferentes compensaciones (tradeoffs) en cuanto a exactitud y convergencia a través de funciones variables. Una clase popular de métodos son las sumas Riemann. A continuación, usará la regla trapezoidal (al final de este tutorial, puede hallar código adicional para explorar diferentes métodos de aproximación).

IntegratedGradsapproxem0i(x)::=(x/em0ixem1i)×/em1k=1m(Sumar m gradientes de local)gradients(imaˊgenes interpoladas)×1mDividir por m pasos)IntegratedGrads^{approx}{em0}{i}(x)::=(x{/em0}{i}-x'{em1}{i})\times \overbrace{\sum{/em1}{k=1}^{m}}^\text{(Sumar m gradientes de local)}\text{gradients(imágenes interpoladas)} \times \overbrace{\frac{1}{m}}^\text{Dividir por m pasos)}

A partir de la ecuación, puede ver que está sumando m gradientes y dividiendo por m pasos. Puede implementar dos operaciones juntas para la parte 3 como el promedio de los gradientes de locales de las m predicciones interpoladas y las imágenes de entrada.

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

La función integral_approximation toma gradientes de la probabilidad predicha de la clase de destino con respecto a las imágenes interpoladas entre la línea de base y la original.

ig = integral_approximation( gradients=path_gradients)

Puede confirmar que el promedio de los gradientes de m imágenes interpoladas devuelve un tensor de gradientes integrados con la misma forma de la imagen del "Panda gigante".

print(ig.shape)

Unimos todo

Ahora combinaremos las 3 partes generales anteriores juntas en una función IntegratedGradients y utilizaremos un decorador @tf.function para compilarlas en un gráfico de TensorFlow invocable de alto rendimiento. Todo esto se implementa con los siguientes 5 pequeños pasos:

IntegratedGradsapproxem0i(x)::=(x/em0ixem1i)5.×/em1k=1m4.F(x+km1.×(xx))2.3.xi×1m4.IntegratedGrads^{approx}{em0}{i}(x)::=\overbrace{(x{/em0}{i}-x'{em1}{i})}^\text{5.}\times \overbrace{\sum{/em1}{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. Generar alfas α\alpha

  2. Generar imágenes interpoladas = (x+km×(xx))(x' + \frac{k}{m}\times(x - x'))

  3. Calcular los gradientes entre las predicciones de salida del modelo FF con respecto a los atributos de entrada= F(interpolated path inputs)xi\frac{\partial F(\text{interpolated path inputs})}{\partial x_{i}}

  4. Lograr la aproximación integral a través del cálculo de promedios de gradientes = k=1mgradients×1m\sum_{k=1}^m \text{gradients} \times \frac{1}{m}

  5. Escalar los gradientes integrados con respecto a la imagen original = (xixi)×integrated gradients(x_{i}-x'_{i}) \times \text{integrated gradients}. Este paso es necesario para garantizar que los valores de atribución acumulados en múltiples imágenes interpoladas están en las mismas unidades y representan fielmente la importancia de los pixeles en la imagen original.

def integrated_gradients(baseline, image, target_class_idx, m_steps=50, batch_size=32): # Generate alphas. alphas = tf.linspace(start=0.0, stop=1.0, num=m_steps+1) # Collect gradients. gradient_batches = [] # 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] gradient_batch = one_batch(baseline, image, alpha_batch, target_class_idx) gradient_batches.append(gradient_batch) # Concatenate path gradients together row-wise into single tensor. total_gradients = tf.concat(gradient_batches, axis=0) # Integral approximation through averaging gradients. avg_gradients = integral_approximation(gradients=total_gradients) # Scale integrated gradients with respect to input. integrated_gradients = (image - baseline) * avg_gradients return integrated_gradients
@tf.function def one_batch(baseline, image, alpha_batch, target_class_idx): # Generate interpolated inputs between baseline and input. interpolated_path_input_batch = interpolate_images(baseline=baseline, image=image, alphas=alpha_batch) # Compute gradients between model outputs and interpolated inputs. gradient_batch = compute_gradients(images=interpolated_path_input_batch, target_class_idx=target_class_idx) return gradient_batch
ig_attributions = integrated_gradients(baseline=baseline, image=img_name_tensors['Fireboat'], target_class_idx=555, m_steps=240)

Una vez más, puede controlar que las atribuciones de las características de los gradientes integrados tienen la misma forma que la imagen de entrada del "barco contraincendios".

print(ig_attributions.shape)

El documento sugiere que la cantidad de pasos se encuentra entre 20 y 300 dependiendo del ejemplo (a pesar de que en la práctica, puede ser mayor a 1000 para aproximar la integral con más exactitud). Puede buscar más códigos para controlar la cantidad adecuada de pasos entre los recursos de la sección "Próximos pasos" al final de este tutorial.

Visualización de atribuciones

Está todo listo para visualizar atribuciones y superponerlas con la imagen original. El código que se encuentra a continuación suma los valores absolutos de los gradientes integrados a través de los canales de colores para producir una máscara de atribución. Este método de trazado captura el impacto relativo de los pixeles en las predicciones del modelo.

#@title 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

Si mira las atribuciones en la imagen del "barco contraincendios", podrá observar que el modelo identifica que los cañones hidrantes y los chorros contribuyen a la correcta predicción.

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

En la imagen del "panda gigante", las atribuciones destacan la textura, el hocico y el pelo de la cara 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)

Usos y limitaciones

Casos de uso

  • Emplear técnicas como las de los gradientes integrados antes de implementar su modelo lo ayudará a desarrollar la intuición sobre cómo y por qué funciona. ¿Los atributos destacados por esta técnica coinciden con su intuición? De no ser así, puede ser indicador de que hay un error en su modelo o conjunto de datos, o sobreajuste.

Limitaciones

  • La técnica de gradientes integrados aporta la importancia de las características en ejemplos individuales. Sin embargo, no la proveen para los atributos globales de un conjunto de datos entero.

  • La técnica de gradientes integrados aporta la importancia de las características en ejemplos individuales; pero no explica las interacciones ni las combinaciones de esas características.

Próximos pasos

En este tutorial se presentó una implementación básica de Gradientes Integrados. En el próximo paso podrá usar estas notas para probar usted mismo esta técnica con diferentes modelos e imágenes.

Para los lectores interesados, hay una versión más larga de este tutorial (que incluye código para diferentes bases de referencia, para calcular aproximaciones integrales y para determinar la cantidad suficiente de pasos) que puede encontrar aquí.

Para profundizar su comprensión, revise la publicación Axiomatic Attribution for Deep Networks (Atribución axiomática para redes profundas) y el repositorio de Github, que contiene una implementación con una versión anterior de TensorFlow. También puede explorar la atribución de características y el impacto de diferentes líneas de base en distill.pub.

¿Le interesa incorporar los gradientes integrados a la producción de los flujos de aprendizaje automático para determinar la importancia de los atributos, el análisis de errores del modelo y el monitoreo de asimetría estadística (data skew)? Consulte el producto Explainable AI de Google Cloud que es compatible con las atribuciones de los gradientes integrados. El grupo de investigaciones PAIR de inteligencia artificial de Google también compartió en código abierto la herramienta What-if tool que se puede usar para la depuración de modelos, incluida la visualización de las atribuciones de las características de gradientes integrados.