Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/pt-br/tutorials/interpretability/integrated_gradients.ipynb
25118 views
Kernel: Python 3
#@title Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.

Gradientes integrados

Este tutorial demonstra como implementar gradientes integrados (GI), uma técnica de IA explicável apresentada no artigo Axiomatic Attribution for Deep Networks (Atribuição axiomática para redes profundas). O GI busca explicar a relação entre as previsões de um modelo em termos de características. Ele tem vários casos de uso, incluindo entender as importâncias das características, identificar a distorção de dados e depurar o desempenho do modelo.

O GI se tornou uma técnica de interpretabilidade popular devido à aplicação abrangente a qualquer modelo diferenciável (por exemplo, imagens, texto e dados estruturados), à facilidade da implementação, às justificações teóricas e à eficiência computacional referente a abordagens alternativas, que permite escalar para redes maiores e espaços de características como imagens.

Neste tutorial, você acompanhará uma implementação de GI passo a passo para entender as importâncias das características de pixels de um classificador de imagens. Como exemplo, considere esta imagem de um barco de bombeiros lançando jatos de água. Você classificaria essa imagem como um barco de bombeiros e talvez destacasse os pixels que compõem o barco e os canhões de água como importantes para sua decisão. Seu modelo também classificará essa imagem como um barco de bombeiros mais tarde neste tutorial. No entanto, ele destaca os mesmos pixels como importantes ao explicar a própria decisão?

Nas imagens abaixo "Máscara de atribuição de GI" e "Overlay de máscara de GI + original", você pode ver que, na verdade, seu modelo destaca (em roxo) os pixels que compõem os canhões e os jatos de água do barco como mais importantes do que o próprio barco para a decisão dele. Como seu modelo generalizará para novos barcos de bombeiros? E os barcos de bombeiros sem jatos de água? Continue lendo para saber mais sobre como o GI funciona e descobrir como aplicá-lo aos seus modelos para entender melhor a relação entre as previsões e as características subjacentes.

Imagem de saída 1

Configuração

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

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

O GI 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 baixará 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()

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

Entradas: o formato de entrada esperado para o modelo é (None, 224, 224, 3). É um tensor 4D denso de dtype float32 e formato (batch_size, height, width, RGB channels), que tem elementos de valores de cores RGB de pixels normalizados para o intervalo [0, 1]. O primeiro elemento é None para indicar que o modelo aceita qualquer tamanho de lote de números inteiros.

Saídas : Um tf.Tensor de logits na forma de (batch_size, 1001) . Cada linha representa a pontuação prevista do modelo para 1.001 classes do ImageNet. Para o principal índice de classe previsto do modelo, você pode usar tf.math.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 e 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')

Carregue e faça o pré-processamento das imagens com tf.image

Você ilustrará o GI usando duas imagens da Wikimedia Commons: um barco de bombeiros 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()}
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()

Classifique as imagens

Comece classificando essas imagens e mostrando as três principais previsões mais confiáveis. A função utilitária a seguir é usada para recuperar os principais k 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%}')

Calcule os gradientes integrados

Seu modelo, Inception V1, é uma função aprendida que descreve um mapeamento entre o espaço da característica de entrada, os valores dos pixels da imagem e o espaço de saída definido pelos valores de probabilidade de classe da ImageNet entre 0 e 1. Os métodos iniciais de interpretabilidade para redes neurais atribuíam pontuações de importância da característica usando gradientes, que dizem quais pixels têm o local mais íngreme relativo à previsão do modelo em um determinado ponto da função de previsão. No entanto, os gradientes só descrevem mudanças locais na função de previsão do modelo em relação aos valores de pixels e não descrevem totalmente a função de previsão do modelo inteiro. Conforme seu modelo "aprende" totalmente a relação entre o intervalo de um pixel individual e a classe correta da ImageNet, o gradiente desse pixel se tornará saturado, ou seja, ficará cada vez menor e até chegará a zero. Considere a simples função de modelo 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)
#@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();
  • esquerda: os gradientes do seu modelo para o pixel x são positivos entre 0.0 e 0.8, mas chegam a 0.0 entre 0.8 e 1.0. Claramente, o pixel x influencia bastante seu modelo a chegar à probabilidade prevista de 80% na classe verdadeira. Faz sentido que a importância do pixel x seja pequena ou descontínua?

  • direita: a intuição por trás do GI é fazer o acumulado dos gradientes locais do pixel x e atribuir a importância como uma pontuação para o quanto ele adiciona ou subtrai da probabilidade geral de classe de saída do modelo. Você pode dividir e computar o GI em 3 partes:

    1. interpole pequenos passos em uma linha reta no espaço de característica entre 0 (uma linha de base ou ponto de partida) e 1 (valor do pixel de entrada)

    2. compute os gradientes em cada passo entre as previsões do modelo referentes à cada passo

    3. aproxime a integral entre a linha de base e a entrada ao fazer o acumulado (média cumulativa) desses gradientes locais.

Para reforçar essa intuição, você passará por essas 3 partes ao aplicar o GI à imagem de exemplo "Barco de bombeiros" abaixo.

Determine uma linha de base

Uma linha de base é uma imagem de entrada usada como ponto de partida para calcular a importância da característica. De maneira intuitiva, pense no papel explicativo da linha de base como a representação do impacto da ausência de cada pixel na previsão "Barco de bombeiros" para contraste com o impacto de cada pixel na previsão "Barco de bombeiros" quando está presente na imagem de entrada. Como resultado, a escolha de uma linha de base tem um papel central na interpretação e visualização das importâncias de características de pixels. Para uma discussão adicional da seleção de linha de base, confira os recursos na seção "Próximos passos" na parte inferior deste tutorial. Aqui, você usará uma imagem preta, em que todos os pixels têm o valor zero.

Outras opções que você pode testar incluem uma imagem totalmente 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()

Descompacte as fórmulas no código

A fórmula para gradientes integrados é a seguinte:

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}

em que:

i_{i} = característica
xx = entrada
xx' = linha de base
α\alpha = constante de interpolação para perturbar as características

Na prática, computar uma integral definida nem sempre é numericamente possível e pode ser computacionalmente caro, então você pode computar a seguinte aproximação 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}

em que:

em0i{em0}{i} = característica (pixel individual)
xx = entrada (tensor da imagem)
xx' = linha de base (tensor da imagem)
kk = constante de perturbação da característica escalada
mm = número de passos na aproximação da soma de Riemann da integral
(x/em0ixi)(x{/em0}{i}-x'_{i}) = um termo para a diferença da linha de base. Isso é necessário para escalar os gradientes integrados e mantê-los nos termos da imagem original. O caminho da imagem de linha de base até a entrada está no espaço do pixel. Já que com o IG você está integrando em uma linha reta (transformação linear), isso acaba sendo equivalente ao termo integral da derivada da função de imagem interpolada em relação a α\alpha com passos suficientes. A integral soma cada gradiente do pixel e multiplica pela mudança no pixel durante o caminho. É mais simples implementar essa integração como passos uniformes de uma imagem para outra, substituindo x:=(x+α(xx))x := (x' + \alpha(x-x')). Assim, a mudança das variáveis gera dx=(xx)dαdx = (x-x')d\alpha. O termo (xx)(x-x') é constante e descontado da integral.

Interpole imagens

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}

Primeiro, gere uma interpolação linear entre a linha de base e a imagem original. Pense nas imagens interpoladas como pequenos passos no espaço da característica entre sua linha de base e a entrada, representadas 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

Use 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" (Barco de bombeiros).

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

Visualize as imagens interpoladas. Observação: outra maneira de pensar sobre a constante α\alpha é que ela aumenta 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();

Compute os gradientes

Esta seção explica como computar os 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 do modelo.

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}

em que:
F()F() = função de previsão do modelo
Fxi\frac{\partial{F}}{\partial{x_i}} = gradiente (vetor das derivadas parciais \partial) da função de previsão F do seu modelo relativa a cada característica xix_i

O TensorFlow facilita a computação de gradientes 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)

Compute os gradientes de 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 formato (1, 1001) com logits que você converte em probabilidades previstas para cada classe. Você precisa passar o índice de classe de destino correto do ImageNet para a função compute_gradients da sua imagem.

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

Observe o formato de saída de (n_interpolated_images, img_height, img_width, RGB), que fornece o gradiente de cada pixel para todas as imagens no caminho de interpolação. Pense nesses gradientes como a medição da mudança nas previsões do seu modelo para cada pequeno passo no espaço da característica.

print(path_gradients.shape)

Visualizando a saturação dos gradientes

Os gradientes que você acabou de calcular acima descrevem mudanças locais na probabilidade prevista do "Barco de bombeiros" do modelo e podem saturar.

Esses conceitos são visualizados usando os gradientes que você calculou acima nos dois plots abaixo.

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]);
  • esquerda: esse plot mostra como a confiança do modelo na classe "Barco de bombeiros" varia nos alfas. Veja como os gradientes, ou a descida da linha, fica bastante reta ou satura entre 0.6 e 1.0 antes de acabar com a probabilidade final de aproximadamente 40% para o "Barco de bombeiros" .

  • direita: o plot à direita mostra as magnitudes médias dos gradientes acima de alfa mais diretamente. Veja como os valores se aproximam subitamente e até caem brevemente abaixo de zero. Seu modelo "aprende" a maioria dos gradientes com valores menores de alfa antes de saturar. De maneira intuitiva, considere que seu modelo aprendeu os pixels, ou seja, canhões de água, para fazer a previsão correta, enviando esses gradientes de pixels até zero. No entanto, ele ainda tem bastante incerteza e foca nos pixels espúrios da ponte ou dos jatos de água conforme os valores de alfa se aproximam da imagem de entrada original.

Para garantir que esses pixels de jatos de água sejam refletidos como importantes para a previsão do "Barco de bombeiros", continue lendo abaixo para aprender como fazer o acumulado desses gradientes para aproximar com exatidão o quanto cada pixel afeta a probabilidade prevista do "Barco de bombeiros".

Acumule gradientes (aproximação de integrais)

Há várias maneiras diferentes de computar a aproximação numérica de uma integral para o GI com diferentes trade-offs na exatidão e na convergência das diversas funções. Uma classe popular de métodos é chamada de somas de Riemann. Aqui, você usará a regra dos trapézios (encontre código adicional para explorar métodos de aproximação diferentes no final deste tutorial).

IntegratedGradsapproxem0i(x)::=(x/em0ixem1i)×/em1k=1mSum m local gradientsgradients(interpolated images)×1mDivide by m stepsIntegratedGrads^{approx}{em0}{i}(x)::=(x{/em0}{i}-x'{em1}{i})\times \overbrace{\sum{/em1}{k=1}^{m}}^\text{Sum m local gradients}\text{gradients(interpolated images)} \times \overbrace{\frac{1}{m}}^\text{Divide by m steps}

Na equação, você pode ver que está somando 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 obtém 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 a média dos gradientes se m imagens interpoladas retorna um tensor de gradientes integrados com o mesmo formato que a imagem original "Panda gigante".

print(ig.shape)

Juntando tudo

Agora, você combinará as 3 partes gerais anteriores em uma função IntegratedGradients e utilizará um decorador @tf.function para fazer a compilação em um grafo do TensorFlow chamável de alto desempenho. Isso é implementado em 5 passos menores abaixo.

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. Gere alfas α\alpha

  2. Gere imagens interpoladas = (x+km×(xx))(x' + \frac{k}{m}\times(x - x'))

  3. Compute os gradientes entre as previsões de saída FF do modelo referentes às características de entrada = F(interpolated path inputs)xi\frac{\partial F(\text{interpolated path inputs})}{\partial x_{i}}

  4. Aproximação da integral com a média dos gradientes = k=1mgradients×1m\sum_{k=1}^m \text{gradients} \times \frac{1}{m}

  5. Escale os gradientes integrados referentes à imagem original = (xixi)×integrated gradients(x_{i}-x'_{i}) \times \text{integrated gradients}. O motivo pelo qual esse passo é necessário é para 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) # 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)

Novamente, você pode conferir se as atribuições de características dos GIs têm o mesmo formato que a imagem de entrada "Barco de bombeiros".

print(ig_attributions.shape)

O artigo sugere que o número de passos varia entre 20 e 300 dependendo do exemplo (embora, na prática, pode ser mais de mil para aproximar a integral com exatidão). Você pode encontrar código adicional para conferir o número de passos apropriado nos recursos "Próximos passos" ao final deste tutorial.

Visualize as atribuições

Você está pronto para visualizar as atribuições e as sobrepor 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 plotting captura o impacto relativo dos pixels nas previsões do 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

Conferindo as atribuições na imagem "Barco de bombeiros", é possível ver que o modelo identifica a contribuição dos canhões e jatos de água para a 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)

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

Usos e limitações

Casos de uso

  • O uso de técnicas como gradientes integrados antes da implementação do modelo pode ajudar você a desenvolver a intuição de como e por que ele funciona. As características destacadas por essa técnica correspondem à sua intuição? Se não, pode indicar um bug no modelo ou no dataset ou overfitting.

Limitações

  • A técnica de gradientes integrados fornece importâncias de características em exemplos individuais. No entanto, ela não fornece as importâncias de características globais em todo o dataset.

  • A técnica de gradientes integrados fornece importâncias de características individuais, mas não explica interações e combinações de características.

Próximos passos

Este tutorial apresentou uma implementação básica dos gradientes integrados. Como próximo passo, você pode usar esse notebook para testar essa técnica com modelos e imagens diferentes por conta própria.

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

Para entender melhor, confira o artigo Axiomatic Attribution for Deep Networks (Atribuição axiomática para redes profundas) e o repositório do GitHub, que contém a implementação em uma versão anterior do TensorFlow. Você também pode explorar a atribuição de características e o impacto de diferentes linhas de base em distill.pub.

Tem interesse em incorporar o GI aos seus fluxos de trabalho de aprendizado de máquina em produção para importâncias de características, análise de erros do modelo e monitoramento da distorção de dados? Confira o produto IA explicável do Google Cloud que é compatível com as atribuições de GI. O grupo de pesquisa AI PAIR do Google também tem uma ferramenta What-if de código aberto que pode ser usada para a depuração do modelo, incluindo a visualização de atribuições de características de GI.