Gradientes integrados

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

Este tutorial demonstra como implementar Gradientes Integrados (IG) , uma técnica de IA explicável apresentada no artigo Axiomatic Attribution for Deep Networks . O 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 entender a importância dos recursos, identificar a distorção de dados e depurar o desempenho do modelo.

O 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 do IG passo a passo para entender a importância dos recursos de pixel de um classificador de imagens. Como exemplo, considere esta imagem de um barco de bombeiros pulverizando jatos de água. Você classificaria esta imagem como um barco de bombeiros 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 "IG Attribution Mask" e "Original + IG Mask Overlay", você pode ver que seu modelo destaca (em roxo) os pixels que compõem 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 seu modelo será generalizado para novos barcos de bombeiros? E os barcos de bombeiros sem jatos de água? Continue lendo para saber mais sobre como o IG funciona e como aplicar o IG aos seus modelos para entender melhor a relação entre as previsões e os 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

O IG pode ser aplicado a qualquer modelo diferenciável. No espírito do artigo original, você usará uma versão pré-treinada do mesmo modelo, Inception V1, que será baixada do 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 esperada para o modelo é (None, 224, 224, 3) . Este é um tensor 4D denso de dtype float32 e forma (batch_size, height, width, RGB channels) cujos elementos são valores de cores RGB de pixels normalizados 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 logits 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 o índice de classe previsto superior do modelo, você pode usar tf.argmax(predictions, axis=-1) . Além disso, você também pode converter a saída logit do modelo em probabilidades previstas em todas as classes usando tf.nn.softmax(predictions, axis=-1) para quantificar a incerteza do modelo, bem como explorar classes previstas 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')

Carregar e pré-processar imagens com tf.image

Você irá ilustrar o IG usando duas imagens do Wikimedia Commons : um Fireboat e um Giant Panda .

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 principais 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 recursos 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. Os primeiros métodos de interpretabilidade para redes neurais atribuíam pontuações de importância de recursos 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, os gradientes descrevem apenas as alterações locais na função de previsão do seu modelo em relação aos valores de pixel e não descrevem totalmente a função de previsão do modelo inteiro. À medida que seu modelo "aprende" totalmente a relação entre o intervalo de um pixel individual e a classe ImageNet correta, o gradiente desse pixel saturará , o que significa que ficará cada vez menor e até chegará 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)

png

  • left : os gradientes do seu modelo para o pixel x são positivos entre 0,0 e 0,8, mas vão para 0,0 entre 0,8 e 1,0. O Pixel x claramente tem um impacto significativo em empurrar seu modelo para 80% de probabilidade prevista na classe real. Faz sentido que a importância do pixel x seja pequena ou descontínua?

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

    1. interpolar pequenos passos ao longo de uma linha reta no espaço de feição entre 0 (uma linha de base ou ponto inicial) e 1 (valor do pixel de entrada)
    2. calcular gradientes em cada etapa entre as previsões do seu modelo em relação a cada etapa
    3. aproxime 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 3 partes aplicando IG à imagem de exemplo "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 no papel explicativo da linha de base como representando o impacto da ausência de cada pixel na previsão "Fireboat" para contrastar com o 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 da importância dos recursos do pixel. Para obter uma discussão adicional sobre a seleção de 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, 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}\) = recurso
\(x\) = entrada
\(x'\) = linha de base
\(\alpha\) = constante de interpolação para perturbar recursos por

Na prática, calcular uma integral definida nem sempre é numericamente possível e pode ser computacionalmente caro, 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}\) = recurso (pixel individual)
\(x\) = entrada (tensor de imagem)
\(x'\) = linha de base (tensor de imagem)
\(k\) = constante de perturbação de recurso dimensionado
\(m\) = número de passos na aproximação da soma de Riemann da integral
\((x_{i}-x'_{i})\) = um termo para a diferença 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 para a entrada está no espaço de pixel. Como com IG você está integrando em uma linha reta (transformação linear), isso acaba sendo aproximadamente equivalente ao termo integral da derivada da função de imagem interpolada em relação 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 implementar essa integração como etapas uniformes de uma imagem para outra, substituindo \(x := (x' + \alpha(x-x'))\). Então a mudança de variáveis ​​dá \(dx = (x-x')d\alpha\). O termo \((x-x')\) é constante e é fatorado da 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ê 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 recursos entre sua linha de base e a entrada, representada 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 o exemplo de imagem "Fireboat".

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

Vamos visualizar as imagens interpoladas. Nota: outra maneira de pensar sobre a constante \(\alpha\) é que ela está aumentando consistentemente a 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 alterações em um recurso e as alterações 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 pelos 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 (vetor de derivadas parciais \(\partial\)) da função de previsão do seu modelo F em relação a cada recurso \(x_i\)

O TensorFlow facilita a computação de gradientes com uma 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 de que seu modelo retorna um Tensor em forma (1, 1001) com logits que você converte em probabilidades previstas para cada classe. Você precisa passar o índice de classe de destino ImageNet correto para a função compute_gradients para 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) , que nos dá o gradiente para cada pixel de cada imagem ao longo do caminho de interpolação. Você pode pensar nesses gradientes como uma medida da 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 do gradiente

Lembre-se de que os gradientes que você calculou acima descrevem mudanças locais na probabilidade prevista de "Fireboat" do seu modelo e podem saturar .

Esses conceitos são visualizados usando os gradientes calculados acima nos 2 gráficos abaixo.

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

png

  • left : 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, em grande parte achatam ou saturam entre 0,6 e 1,0 antes de se estabelecerem na probabilidade final prevista do "Fireboat" de cerca de 40%.

  • right : O gráfico da direita mostra as magnitudes médias dos gradientes sobre alfa mais diretamente. Observe como os valores se aproximam acentuadamente e até mergulham 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 como seu modelo aprendeu os pixels, por exemplo, canhões de água para fazer a previsão correta, enviando esses gradientes de pixels para zero, mas ainda é bastante incerto e focado em pixels espúrios de ponte ou jato de água à medida que os valores alfa se aproximam do imagem de entrada original.

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

Acumular gradientes (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 várias funções. Uma classe popular de métodos é chamada 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^{approx} {i}(x)::=(x {i}-x' {i})\times \overbrace{\sum {k=1}^{m} }^\text{Sum m gradientes locais} \text{gradients(imagens interpoladas)} \times \overbrace{\frac{1}{m} }^\text{Dividir por m passos}$

A partir da equação, você pode ver que está somando em m gradientes e dividindo por m passos. Você pode implementar as duas operações juntas para a parte 3 como uma média dos gradientes locais de m previsões interpoladas e 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

A função integral_approximation pega os gradientes da probabilidade prevista da classe alvo em relação às imagens interpoladas entre a linha de base e a imagem original.

ig = integral_approximation(
    gradients=path_gradients)

Você pode confirmar que a média entre os gradientes de m imagens interpoladas retorna um tensor de gradientes integrado com a mesma forma que a imagem original do "Panda Gigante".

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

Juntando tudo

Agora você combinará as 3 partes gerais anteriores em uma função IntegratedGradients e utilizará um decorador @tf.function para compilá-lo em um gráfico TensorFlow de alto desempenho que pode ser chamado. 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 alfas \(\alpha\)

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

  3. Computar gradientes entre as previsões de saída do modelo \(F\) em relação aos recursos de entrada = \(\frac{\partial F(\text{interpolated path inputs})}{\partial x_{i} }\)

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

  5. Escalar gradientes integrados em relação à imagem original = \((x_{i}-x'_{i}) \times \text{integrated gradients}\). A razão pela 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.

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)

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

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

Novamente, você pode verificar se as atribuições do recurso IG têm a mesma forma que a imagem "Fireboat" de entrada.

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

O artigo sugere que o número de etapas varia de 20 a 300, dependendo do exemplo (embora na prática isso possa ser maior em 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.

Visualizar atribuições

Você está pronto para visualizar as atribuições e sobrepô-las na 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. Esse método de plotagem captura o impacto relativo dos pixels nas previsões do modelo.

Observando as atribuições na imagem "Fireboat", você pode ver que o modelo identifica os canhões e bicas de água 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 a pelagem 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

  • O emprego de técnicas como gradientes integrados antes de implantar seu modelo pode ajudá-lo a desenvolver a 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 indicativo de um bug em seu modelo ou conjunto de dados, ou overfitting.

Limitações

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

  • Os gradientes integrados fornecem importâncias de recursos individuais, mas não explicam interações e combinações de recursos.

Próximos passos

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

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

Para aprofundar sua compreensão, confira o artigo Axiomatic Attribution for Deep Networks and Github repository , 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 disstill.pub .

Interessado em incorporar IG em seus fluxos de trabalho de aprendizado de máquina de produção para importância de recursos, análise de erros de modelo e monitoramento de distorção de dados? Confira o produto Explainable AI do Google Cloud que oferece suporte a atribuições de IG. O grupo de pesquisa do Google AI PAIR também abriu o código-fonte da ferramenta What-if, que pode ser usada para depuração de modelos, incluindo a visualização de atribuições de recursos do IG.