Ajuda a proteger a Grande Barreira de Corais com TensorFlow em Kaggle Junte Desafio

Gradientes integrados

Ver no TensorFlow.org Executar no Google Colab Ver no GitHub Baixar caderno Veja o modelo TF Hub

Este tutorial demonstra como implementar gradientes integrados (IG), um Explicável AI técnica introduzida no jornal Axiomatic Atribuição de profunda Networks . IG visa explicar a relação entre as previsões de um modelo em termos de suas características. Ele tem muitos casos de uso, incluindo compreensão das importâncias dos recursos, identificação de distorção de dados e depuração do desempenho do modelo.

IG tornou-se uma técnica de interpretabilidade popular devido à sua ampla aplicabilidade a qualquer modelo diferenciável (por exemplo, imagens, texto, dados estruturados), facilidade de implementação, justificativas teóricas e eficiência computacional em relação a abordagens alternativas que permitem escalar para grandes redes e recursos espaços como imagens.

Neste tutorial, você percorrerá uma implementação de IG passo a passo para entender as importâncias dos recursos de pixel de um classificador de imagem. Como um exemplo, considere esta imagem de um fireboat pulverização de jactos de água. Você classificaria esta imagem como um fireboat e poderia destacar os pixels que compõem o barco e os canhões de água como sendo importantes para sua decisão. Seu modelo também classificará esta imagem como um fireboat posteriormente neste tutorial; no entanto, ele destaca os mesmos pixels como importantes ao explicar sua decisão?

Nas imagens abaixo intituladas "Máscara de atribuição IG" e "Sobreposição de máscara original + IG", você pode ver que o seu modelo destaca (em roxo) os pixels que compreendem os canhões de água e jatos de água do barco como sendo mais importantes do que o próprio barco para sua decisão. Como o seu modelo vai generalizar para novos fireboats? E os fireboats sem jatos d'água? Continue lendo para aprender mais sobre como o IG funciona e como aplicá-lo aos seus modelos para entender melhor a relação entre suas previsões e recursos subjacentes.

Imagem de saída 1

Configurar

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

Baixe um classificador de imagem pré-treinado do TF-Hub

IG pode ser aplicado a qualquer modelo diferenciável. No espírito do papel original, você vai usar uma versão pré-formados do mesmo modelo, Inception V1, o que você vai baixar a partir 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
_________________________________________________________________

Na página do módulo, você precisa ter em mente o seguinte sobre o Inception V1:

Entradas: A forma de entrada para o modelo esperado é (None, 224, 224, 3) . Esta é uma densa 4D de tensores de DTYPE float32 e forma (batch_size, height, width, RGB channels) , cujos elementos são valores de cor RGB de pixels normalizado para o intervalo [0, 1]. O primeiro elemento é None para indicar que o modelo pode ter qualquer tamanho de lote inteiro.

Saídas: Um tf.Tensor de logitos na forma de (batch_size, 1001) . Cada linha representa a pontuação prevista do modelo para cada uma das 1.001 classes do ImageNet. Para topo índice de classe previsível do modelo pode utilizar tf.argmax(predictions, axis=-1) . Além disso, também é possível converter a saída logit do modelo de probabilidades previstas em todas as classes usando tf.nn.softmax(predictions, axis=-1) para quantificar a incerteza do modelo, bem como classes de explorar preditos semelhantes para depuração.

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

Carga e pré-processar as imagens com tf.image

Você vai ilustrar IG utilizando duas imagens de Wikimedia Commons : um Fireboat , e um 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

Classificar imagens

Vamos começar classificando essas imagens e exibindo as 3 previsões mais confiáveis. A seguir está uma função de utilidade para recuperar os k principais rótulos e probabilidades previstos.

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%

Calcular gradientes integrados

Seu modelo, Inception V1, é uma função aprendida que descreve um mapeamento entre seu espaço de recurso de entrada, valores de pixel de imagem e um espaço de saída definido por valores de probabilidade de classe ImageNet entre 0 e 1. Métodos iniciais de interpretabilidade para redes neurais atribuídas pontuações de importância de recurso usando gradientes, que informam quais pixels têm o local mais inclinado em relação à previsão do seu modelo em um determinado ponto ao longo da função de previsão do seu modelo. No entanto, gradientes apenas descrevem alterações locais em função de previsão do seu modelo com respeito a valores de pixel e não descrevem totalmente a sua função de previsão de modelo inteiro. Como seu modelo totalmente "aprende" a relação entre o intervalo de um pixel individual ea classe IMAGEnet correta, o gradiente para este pixel vai saturar, que significa tornar-se cada vez mais pequenos e até mesmo ir a zero. Considere a função de modelo simples abaixo:

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

  • esquerda: gradientes do seu modelo para Pixel x são positivos entre 0,0 e 0,8, mas ir para 0,0 entre 0,8 e 1,0. Pixel x claramente tem um impacto significativo em empurrar o seu modelo para 80% probabilidade prevista na verdadeira classe. Faz sentido que pixels x importância 's é pequena ou descontínuo?

  • direita: A intuição por trás IG é acumular pixels x 's gradientes locais e atribuem sua importância como uma pontuação para o quanto isso adiciona ou subtrai a probabilidade geral classe de saída do seu modelo. Você pode dividir e calcular IG em 3 partes:

    1. interpolar pequenos passos ao longo de uma linha reta no espaço do recurso entre 0 (uma linha de base ou ponto inicial) e 1 (valor do pixel de entrada)
    2. computar gradientes em cada etapa entre as previsões do seu modelo com relação a cada etapa
    3. aproximar a integral entre sua linha de base e a entrada, acumulando (média cumulativa) esses gradientes locais.

Para reforçar essa intuição, você percorrerá essas três partes aplicando IG ao exemplo da imagem "Fireboat" abaixo.

Estabeleça uma linha de base

Uma linha de base é uma imagem de entrada usada como ponto de partida para calcular a importância do recurso. Intuitivamente, você pode pensar na função explicativa da linha de base como representando o impacto da ausência de cada pixel na previsão "Fireboat" para contrastar com seu impacto de cada pixel na previsão "Fireboat" quando presente na imagem de entrada. Como resultado, a escolha da linha de base desempenha um papel central na interpretação e visualização das importâncias dos recursos de pixel. Para uma discussão adicional sobre a seleção da linha de base, consulte os recursos na seção "Próximas etapas" na parte inferior deste tutorial. Aqui, você usará uma imagem preta cujos valores de pixel são todos zero.

Outras opções que você pode experimentar incluem uma imagem toda branca, ou uma imagem aleatória, o que você pode criar com 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

Descompacte fórmulas em código

A fórmula para gradientes integrados é a seguinte:

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

Onde:

\(_{i}\) = característica
\(x\) = input
\(x'\) = basal
\(\alpha\) constante interpolação para características perturbar por =

Na prática, computar uma integral definida nem sempre é numericamente possível e pode ser caro computacionalmente, então você calcula a seguinte aproximação numérica:

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

Onde:

\(_{i}\) = característica (pixel individual)
\(x\) = entrada (tensor de imagem)
\(x'\) = linha de base (imagem tensor)
\(k\) = característica dimensionado constante perturbação
\(m\) = número de passos na soma aproximação Riemann da integral
\((x_{i}-x'_{i})\) = um termo para a diferença a partir da linha de base. Isso é necessário para dimensionar os gradientes integrados e mantê-los em termos da imagem original. O caminho da imagem de linha de base até a entrada está no espaço de pixel. Uma vez que com IG estiver integrando em uma linha reta (transformação linear) isso acaba sendo mais ou menos equivalente ao termo integral do derivado da função imagem interpolada com respeito a \(\alpha\) com passos suficientes. A integral soma o gradiente de cada pixel vezes a mudança no pixel ao longo do caminho. É mais simples de implementar essa integração como medidas uniformes de uma imagem para o outro, substituindo \(x := (x' + \alpha(x-x'))\). Assim, a mudança de variáveis dá \(dx = (x-x')d\alpha\). O \((x-x')\) prazo é constante e está consignado fora do integral.

Interpolar imagens

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

Primeiro, você vai gerar uma interpolação linear entre a linha de base e a imagem original. Você pode pensar em imagens interpoladas como pequenos passos no espaço de características entre a sua linha de base e de entrada, representado por \(\alpha\) na equação original.

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

Vamos usar a função acima para gerar imagens interpoladas ao longo de um caminho linear em intervalos alfa entre uma imagem de linha de base preta e a imagem de exemplo "Fireboat".

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

Vamos visualizar as imagens interpoladas. Nota: uma outra maneira de pensar sobre a \(\alpha\) constante é que ele é consistentemente o aumento da intensidade de cada imagem 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();

png

Gradientes de computação

Agora, vamos dar uma olhada em como calcular gradientes para medir a relação entre as mudanças em um recurso e as mudanças nas previsões do modelo. No caso de imagens, o gradiente nos diz quais pixels têm o efeito mais forte nas probabilidades de classe previstas dos modelos.

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

Onde:
\(F()\) = função de previsão do seu modelo
\(\frac{\partial{F} }{\partial{x_i} }\) = gradiente (vector de derivados parciais \(\partial\)) do seu modelo de F função de previsão em relação a cada característica \(x_i\)

TensorFlow faz gradientes de computação fácil para você com um 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)

Vamos calcular os gradientes para cada imagem ao longo do caminho de interpolação em relação à saída correta. Lembre-se que o modelo retorna uma (1, 1001) em forma Tensor com logits que você converter para probabilidades previstas para cada classe. Você precisa passar o índice de classe alvo IMAGEnet correto para o compute_gradients função para a sua imagem.

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

Observe a forma de saída de (n_interpolated_images, img_height, img_width, RGB) , o que nos dá o gradiente para cada pixel de cada imagem ao longo do caminho de interpolação. Você pode pensar nesses gradientes como medindo a mudança nas previsões do seu modelo para cada pequena etapa no espaço de recursos.

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

Visualizando a saturação de gradiente

Lembre-se que os gradientes que você acabou de calcular descrever mudanças locais a probabilidade prevista do seu modelo de "Fireboat" e pode saturar.

Esses conceitos são visualizados usando os gradientes que você calculou acima nos 2 gráficos abaixo.

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

  • esquerda: Este gráfico mostra como a confiança do seu modelo na classe "Fireboat" varia entre os alfas. Observe como os gradientes, ou inclinação da linha, achatam ou saturam amplamente entre 0,6 e 1,0 antes de se estabelecerem na probabilidade final prevista "Fireboat" de cerca de 40%.

  • direita: As mostras gráfico da direita os gradientes médios grandezas mais alfa mais directamente. Observe como os valores se aproximam bruscamente e até mesmo caem brevemente abaixo de zero. Na verdade, seu modelo "aprende" mais com gradientes em valores mais baixos de alfa antes de saturar. Intuitivamente, você pode pensar nisso porque seu modelo aprendeu os pixels, por exemplo, canhões de água para fazer a previsão correta, enviando esses gradientes de pixels a zero, mas ainda é bastante incerto e focado em pixels espúrios de ponte ou jato de água conforme os valores alfa se aproximam de imagem de entrada original.

Para garantir que esses pixels de canhão de água importantes sejam refletidos como importantes para a previsão do "Fireboat", você continuará a seguir para aprender como acumular esses gradientes para aproximar com precisão como cada pixel afeta a probabilidade prevista do "Fireboat".

Gradientes de acumulação (aproximação integral)

Há muitas maneiras diferentes de calcular a aproximação numérica de uma integral para IG com diferentes compensações em precisão e convergência em funções variáveis. Uma classe popular de métodos é chamado de somas de Riemann . Aqui, você usará a regra trapezoidal (você pode encontrar código adicional para explorar diferentes métodos de aproximação no final deste tutorial).

$ IntegratedGrads ^ {aprox} {i} (x) :: = (x {i} -x'{i}) \ times \ overbrace {\ sum {k = 1} ^ {m}} ^ \ text {Sum m gradientes locais} \ text {gradientes (imagens interpoladas)} \ times \ overbrace {\ frac {1} {m}} ^ \ text {Dividir por m etapas} $

A partir da equação, você pode ver que você está somando sobre m gradientes e dividindo por m etapas. É possível aplicar as duas operações em conjunto para a parte 3 como uma média dos gradientes locais de m interpolado e previsões de imagens 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

O integral_approximation função leva os gradientes da probabilidade prevista da classe alvo no que diz respeito às imagens interpolados entre a linha de base e a imagem original.

ig = integral_approximation(
    gradients=path_gradients)

Você pode confirmar média através dos gradientes de m interpolados imagens retorna um gradientes integrados tensor com a mesma forma que a imagem original "Panda gigante".

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

Juntando tudo

Agora você vai combinar as 3 partes gerais anteriores juntos em um IntegratedGradients função e utilizar um @ tf.function decorador para compilá-lo em um gráfico TensorFlow exigível alto desempenho. Isso é implementado como 5 etapas menores abaixo:

\(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. Gerar os alfas \(\alpha\)

  2. Gerar imagens interpoladas = \((x' + \frac{k}{m}\times(x - x'))\)

  3. Gradientes de computação entre modelo \(F\) previsões de saída com relação à entrada possui = \(\frac{\partial F(\text{interpolated path inputs})}{\partial x_{i} }\)

  4. Aproximação integrante através de gradientes de média = \(\sum_{k=1}^m \text{gradients} \times \frac{1}{m}\)

  5. Escala gradientes integrados com respeito à imagem original = \((x_{i}-x'_{i}) \times \text{integrated gradients}\). O motivo pelo qual essa etapa é necessária é garantir que os valores de atribuição acumulados em várias imagens interpoladas estejam nas mesmas unidades e representem fielmente as importâncias dos pixels na imagem original.

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

Novamente, você pode verificar se as atribuições do recurso IG têm o mesmo formato da imagem "Fireboat" de entrada.

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

O artigo sugere que o número de etapas varia entre 20 e 300, dependendo do exemplo (embora na prática isso possa ser maior na casa dos 1.000 para aproximar com precisão a integral). Você pode encontrar código adicional para verificar o número apropriado de etapas nos recursos "Próximas etapas" no final deste tutorial.

Visualize atribuições

Você está pronto para visualizar as atribuições e sobrepô-las à imagem original. O código abaixo soma os valores absolutos dos gradientes integrados nos canais de cores para produzir uma máscara de atribuição. Este método de plotagem captura o impacto relativo dos pixels nas previsões do modelo.

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

Olhando as atribuições na imagem do "Fireboat", você pode ver que o modelo identifica os canhões de água e bicas como contribuindo para sua previsão correta.

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

Na imagem do "Panda Gigante", as atribuições destacam a textura, o nariz e o pelo do rosto do 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

Usos e limitações

Casos de uso

  • Empregar técnicas como gradientes integrados antes de implantar seu modelo pode ajudá-lo a desenvolver intuição de como e por que ele funciona. As características destacadas por esta técnica correspondem à sua intuição? Caso contrário, isso pode ser um indicativo de um bug em seu modelo ou conjunto de dados, ou overfitting.

Limitações

  • Gradientes integrados fornece importâncias de recursos em exemplos individuais, no entanto, não fornece importâncias de recursos globais em um conjunto de dados inteiro.

  • Gradientes integrados fornece importâncias de recursos individuais, mas não explica as interações e combinações de recursos.

Próximos passos

Este tutorial apresentou uma implementação básica de Gradientes Integrados. Como uma próxima etapa, você pode usar este notebook para experimentar essa técnica com diferentes modelos e imagens você mesmo.

Para os leitores interessados, há uma versão mais longa deste tutorial (que inclui o código para diferentes linhas de base, para calcular aproximações integrais, e para determinar um número suficiente de passos), que você pode encontrar aqui .

Para aprofundar sua compreensão, confira o papel Axiomatic Atribuição para Deep Networks e repositório Github , que contém uma implementação em uma versão anterior do TensorFlow. Você também pode explorar a atribuição de recursos e o impacto de diferentes linhas de base, em distill.pub .

Interessado em incorporar IG em seus fluxos de trabalho de aprendizado de máquina de produção para importâncias de recursos, análise de erro de modelo e monitoramento de distorção de dados? Confira o Google Cloud Explicável AI produto que suporta atribuições IG. O grupo de pesquisa Google AI PAR também open-source a ferramenta What-se que pode ser usado para o modelo de depuração, incluindo a visualização IG atribuições de recursos.