Interpretabilidade e compreensão do modelo para PyTorch usando Captum
Introdução
Os métodos de interpretabilidade dos modelos ganharam importância crescente nos últimos anos como consequência direta do aumento da complexidade do modelo e da falta de transparência associada. A compreensão do modelo é um tema importante de estudo e uma área focal para aplicações práticas que empregam aprendizado de máquina em vários setores.
Captum fornece a acadêmicos e desenvolvedores técnicas de ponta, como Gradientes Integrados, que simplificam a identificação dos elementos que contribuem para a saída de um modelo. Captum torna mais fácil para os pesquisadores de ML usar modelos PyTorch para construir métodos de interpretabilidade.
Ao facilitar a identificação dos muitos elementos que contribuem para a saída de um modelo, o Captum pode ajudar os desenvolvedores de modelos a criar modelos melhores e corrigir modelos que fornecem resultados inesperados.
Descrições de algoritmos
Captum é uma biblioteca que permite a implementação de diversas abordagens de interpretabilidade. É possível classificar os algoritmos de atribuição do Captum em três grandes categorias:
- atribuição primária: determina a contribuição de cada recurso de entrada para a saída de um modelo.
- Atribuição de camada: cada neurônio em uma camada específica é avaliado por sua contribuição para a saída do modelo.
- Atribuição de neurônios: a ativação de um neurônio oculto é determinada pela avaliação da contribuição de cada recurso de entrada.
A seguir está uma breve visão geral dos vários métodos atualmente implementados no Captum para atribuição primária, de camada e de neurônio. Também está incluída uma descrição do túnel de ruído, que pode ser usado para suavizar os resultados de qualquer método de atribuição. Captum fornece métricas para estimar a confiabilidade das explicações do modelo, além de seus algoritmos de atribuição. Neste momento, eles fornecem métricas de infidelidade e sensibilidade que auxiliam na avaliação da precisão das explicações.
Técnicas de atribuição primária
Gradientes Integrados
Digamos que temos uma representação formal de uma rede profunda, F : Rn → [0, 1]. Seja x ∈ Rn a entrada atual e x′ ∈ Rn a entrada da linha de base. A linha de base em redes de imagens pode ser a imagem preta, enquanto pode ser o vetor de incorporação zero em modelos de texto. Da linha de base x′ até a entrada x, calculamos gradientes em todos os pontos ao longo do caminho em linha reta (em Rn). Ao acumular esses gradientes, pode-se gerar gradientes integrados. Gradientes integrados são definidos como a integral do caminho dos gradientes ao longo de um caminho direto da linha de base x′ até a entrada x.
As duas suposições básicas, sensibilidade e invariância de implementação, formam a base deste método. Consulte o artigo original para saber mais sobre esses axiomas.
Gradiente SHAP
Os valores Shapley na teoria dos jogos cooperativos são usados para calcular os valores Gradient SHAP, que são calculados usando uma abordagem de gradiente. O Gradient SHAP adiciona ruído gaussiano a cada amostra de entrada várias vezes e, em seguida, escolhe um ponto aleatório no caminho entre a linha de base e a entrada para determinar o gradiente das saídas. Como resultado, os valores finais do SHAP representam o valor esperado dos gradientes. * (entradas - linhas de base). Os valores SHAP são aproximados com base na premissa de que os recursos de entrada são independentes e que o modelo explicativo é linear entre as entradas e as linhas de base fornecidas.
Deep LIFT
É possível usar DeepLIFT (uma técnica de retropropagação) para atribuir alterações de entrada com base nas diferenças entre as entradas e sua referência correspondente (ou linha de base). DeepLIFT tenta explicar a disparidade entre a saída da referência usando a disparidade entre as entradas da referência. DeepLIFT emprega a ideia de multiplicadores para “culpar” neurônios individuais pela diferença nos resultados. Para um determinado neurônio de entrada x com diferença da referência ∆x, e neurônio alvo t com diferença da referência ∆t para o qual desejamos calcular a contribuição, definimos o multiplicador m∆x∆t como:
Formato DeepLIFT
DeepLIFT SHAP é uma extensão do DeepLIFT baseada nos valores de Shapley estabelecidos na teoria dos jogos cooperativos. DeepLIFT SHAP calcula a atribuição do DeepLIFT para cada par de entrada-linha de base e calcula a média das atribuições resultantes por exemplo de entrada usando uma distribuição de linhas de base. As regras de não linearidade do DeepLIFT ajudam a linearizar as funções não lineares da rede, e a aproximação dos valores SHAP do método também se aplica à rede linearizada. Os recursos de entrada também são considerados independentes neste método.
Saliência
Calcular a atribuição de insumos por meio de saliência é um processo simples que produz o gradiente da saída em relação à entrada. Uma expansão de rede de Taylor de primeira ordem é usada na entrada, e os gradientes são os coeficientes de cada característica na representação linear do modelo. O valor absoluto desses coeficientes pode ser usado para indicar a relevância de um recurso. Você pode encontrar mais informações sobre a abordagem de saliência no artigo original.
Entrada X Gradiente
Input X Gradient é uma extensão da abordagem de saliência, pegando os gradientes da saída em relação à entrada e multiplicando pelos valores do recurso de entrada. Uma intuição para esta abordagem considera um modelo linear; os gradientes são simplesmente os coeficientes de cada entrada, e o produto da entrada com um coeficiente corresponde à contribuição total do recurso para a saída do modelo linear.
Retropropagação e deconvolução guiadas
A computação do gradiente é realizada por meio de retropropagação e deconvolução guiadas, embora a retropropagação das funções ReLU seja substituída de forma que apenas gradientes não negativos sejam retropropagados. Embora a função ReLU seja aplicada aos gradientes de entrada na retropropagação guiada, ela é aplicada diretamente aos gradientes de saída na desconvolução. É prática comum empregar esses métodos em conjunto com redes convolucionais, mas eles também podem ser usados em outros tipos de arquitetura de redes neurais.
GradCAM guiado
As atribuições de retropropagação guiada calculam o produto elemento a elemento de atribuições guiadas do GradCAM (GradCAM guiado) com atribuições do GradCAM com amostragem aumentada (camada). O cálculo de atribuição é feito para uma determinada camada e ampliado para caber no tamanho da entrada. Redes neurais convolucionais são o foco desta técnica. No entanto, qualquer camada que possa ser alinhada espacialmente com a entrada pode ser fornecida. Normalmente, a última camada convolucional é fornecida.
Ablação de recursos
Para calcular a atribuição, uma técnica conhecida como “ablação de recursos” emprega um método baseado em perturbação que substitui uma “linha de base” ou “valor de referência” conhecido (como 0) para cada recurso de entrada antes de calcular a diferença de saída. Agrupar e eliminar recursos de entrada é uma alternativa melhor do que fazê-lo individualmente, e muitos aplicativos diferentes podem se beneficiar disso. Ao agrupar e remover segmentos de uma imagem, podemos determinar a importância relativa do segmento.
Permutação de recursos
A permutação de recursos é um método baseado em perturbação no qual cada recurso é permutado aleatoriamente dentro de um lote, e a mudança na saída (ou perda) é calculada como resultado dessa modificação. Os recursos também podem ser agrupados em vez de individualmente, da mesma forma que a ablação de recursos. Observe que, em contraste com os outros algoritmos disponíveis no Captum, este algoritmo é o único que pode fornecer atribuições adequadas quando é fornecido com um lote de múltiplos exemplos de entrada. Outros algoritmos precisam apenas de um único exemplo como entrada.
Oclusão
A oclusão é uma abordagem baseada em perturbações para atribuição computacional, substituindo cada região retangular contígua por uma determinada linha de base/referência e calculando a diferença na saída. Para feições localizadas em múltiplas áreas (hiperretângulos), é calculada a média das diferenças de saída correspondentes para calcular a atribuição para essa feição. A oclusão é mais útil em casos como imagens, onde os pixels em uma região retangular contígua provavelmente estão altamente correlacionados.
Amostragem de valor Shapley
A técnica de atribuição de valor de Shapley é baseada na teoria dos jogos cooperativos. Esta técnica pega cada permutação dos recursos de entrada e os adiciona um por um a uma linha de base especificada. A diferença na saída após a adição de cada recurso corresponde à sua contribuição, e essas diferenças são somadas em todas as permutações para determinar a atribuição.
Lima
Um dos métodos de interpretabilidade mais amplamente utilizados é o Lime, que treina um modelo substituto interpretável amostrando pontos de dados em torno de um exemplo de entrada e usando avaliações de modelo nesses pontos para treinar um modelo “substituto” interpretável mais simples, como um modelo linear.
KernelSHAP
Kernel SHAP é uma técnica para calcular valores Shapley que usa a estrutura LIME. Os valores Shapley podem ser obtidos de forma mais eficiente na estrutura LIME, definindo a função de perda, ponderando o kernel e regularizando os termos adequadamente.
Técnicas de atribuição de camadas
Condutância da Camada
A condutância da camada é um método que constrói uma imagem mais abrangente da importância de um neurônio, combinando a ativação do neurônio com as derivadas parciais do neurônio em relação à entrada e da saída em relação ao neurônio. Através do neurônio oculto, a condutância se baseia no fluxo de atribuição dos Gradientes Integrados (IG). A condutância total de um neurônio oculto y é definida da seguinte forma no artigo original:
Influência Interna
Usando a Influência Interna, pode-se estimar a integral dos gradientes ao longo do caminho desde uma entrada de linha de base até a entrada fornecida. Essa técnica é semelhante à aplicação de gradientes integrados, que envolve a integração do gradiente em relação à camada (em vez da entrada).
Ativação do gradiente X da camada
A ativação do Layer Gradient X é o equivalente da rede à técnica Input X Gradient para camadas ocultas em uma rede… Ele multiplica a ativação da camada elemento por elemento pelos gradientes da saída alvo em relação à camada especificada.
GradCAM
GradCAM é uma técnica de atribuição de camada de rede neural convolucional que normalmente é aplicada à última camada convolucional. O GradCAM calcula os gradientes da saída alvo em relação à camada especificada, calcula a média de cada canal de saída (dimensão de saída 2) e multiplica o gradiente médio de cada canal pelas ativações da camada. Uma ReLU é aplicada à saída para garantir que apenas atribuições não negativas sejam retornadas da soma dos resultados em todos os canais.
Técnicas de atribuição de neurônios
Condutância de Neurônios
A condutância combina a ativação do neurônio com derivadas parciais do neurônio em relação à entrada e da saída em relação ao neurônio para fornecer uma imagem mais abrangente da relevância do neurônio. Para determinar a condutância de um neurônio específico, examina-se o fluxo de atribuição de IG de cada entrada que passa por esse neurônio. A seguir está a definição formal do artigo original de condutância do neurônio y dada a atribuição de entrada i:
De acordo com esta definição, deve-se notar que a soma da condutância de um neurônio (em todos os recursos de entrada) é sempre igual à condutância da camada na qual esse neurônio específico está localizado.
Gradiente de Neurônio
A abordagem do gradiente de neurônios é a equivalência do método de saliência para um único neurônio na rede. Ele simplesmente calcula o gradiente da saída do neurônio em relação à entrada do modelo. Este método, como Saliency, pode ser pensado como fazendo uma expansão de Taylor de primeira ordem da saída do neurônio em uma determinada entrada, com os gradientes correspondendo aos coeficientes de cada característica na representação linear do modelo.
Gradientes Integrados de Neurônios
É possível estimar a integral dos gradientes de entrada em relação a um neurônio específico ao longo do caminho desde uma entrada de linha de base até a entrada de interesse usando uma técnica chamada “Gradientes Integrados de Neurônios”. ” Gradientes integrais são equivalentes a este método, assumindo que a saída é apenas a do neurônio identificado. Você pode encontrar mais informações sobre a abordagem de gradiente integrado no artigo original aqui.
Gradiente de NeurônioSHAP
Neuron GradientSHAP é equivalente ao GradientSHAP para um neurônio específico. O Neuron GradientSHAP adiciona ruído gaussiano a cada amostra de entrada várias vezes, escolhe um ponto aleatório ao longo do caminho entre a linha de base e a entrada e calcula o gradiente do neurônio alvo em relação a cada ponto escolhido aleatoriamente. Os valores SHAP resultantes estão próximos dos valores de gradiente previstos*. (entradas - linhas de base).
Neurônio DeepLIFT SHAP
Neuron DeepLIFT SHAP é equivalente ao DeepLIFT para um neurônio específico. Usando a distribuição de linhas de base, o algoritmo DeepLIFT SHAP calcula a atribuição do Neuron DeepLIFT para cada par de entrada-linha de base e calcula a média das atribuições resultantes por exemplo de entrada.
Túnel de Ruído
Noise Tunnel é uma técnica de atribuição que pode ser usada em conjunto com outros métodos. O túnel de ruído calcula a atribuição várias vezes, adicionando ruído gaussiano à entrada a cada vez e, em seguida, mescla as atribuições resultantes dependendo do tipo escolhido. Os seguintes tipos de túnel de ruído são suportados:
- Smoothgrad: a média das atribuições amostradas é retornada. Suavizar a técnica de atribuição especificada usando um Kernel Gaussiano é uma aproximação desse processo.
- Smoothgrad Squared: A média das atribuições da amostra ao quadrado é retornada.
- Vargrad: A variação das atribuições da amostra é retornada.
Métricas
Infidelidade
A infidelidade mede o erro quadrático médio entre as explicações do modelo nas magnitudes das perturbações de entrada e as mudanças da função preditora nessas perturbações de entrada. A infidelidade é definida da seguinte forma:
A partir de técnicas de atribuição bem conhecidas, como o gradiente integrado, este é um conceito computacionalmente mais eficiente e estendido de Sensitivy-n. Este último analisa as correlações entre a soma das atribuições e as diferenças da função preditora na sua entrada e uma linha de base predefinida.
Sensibilidade
A sensibilidade, que é definida como o grau de mudança explicativa para pequenas perturbações de entrada usando a aproximação baseada em amostragem de Monte Carlo, é medida da seguinte forma:
Por padrão, amostramos de um subespaço de uma bola L-Infinity com um raio padrão para aproximar a sensibilidade. Os usuários podem alterar o raio da bola e a função de amostra.
Interpretação de modelo para modelo ResNet pré-treinado
Este tutorial mostra como usar métodos de interpretabilidade do modelo em um modelo ResNet pré-treinado com uma imagem escolhida e visualiza as atribuições de cada pixel sobrepondo-as à imagem. Neste tutorial, utilizaremos os algoritmos de interpretação Gradientes Integrados, GradientShape, Atribuição com Camada GradCAM e Oclusão.
Antes de começar, você deve ter um ambiente Python que inclua:
- Python versão 3.6 ou superior
- PyTorch versão 1.2 ou superior (a versão mais recente é recomendada)
- TorchVision versão 0
- .6 ou superior (a versão mais recente é recomendada)
- Captum (a versão mais recente é recomendada)
Dependendo se você está usando o ambiente virtual Anaconda ou pip, os seguintes comandos irão ajudá-lo a configurar o Captum:
Com conda
:
conda install pytorch torchvision captum -c pytorch
Com pip
:
pip install torch torchvision captum
Vamos importar bibliotecas.
import torch
import torch.nn.functional as F
from PIL import Image
import os
import json
import numpy as np
from matplotlib.colors import LinearSegmentedColormap
import os, sys
import json
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
import torchvision
from torchvision import models
from torchvision import transforms
from captum.attr import IntegratedGradients
from captum.attr import GradientShap
from captum.attr import Occlusion
from captum.attr import LayerGradCam
from captum.attr import NoiseTunnel
from captum.attr import visualization as viz
from captum.attr import LayerAttribution
Carrega o modelo Resnet pré-treinado e o define para o modo de avaliação
model = models.resnet18(pretrained=True)
model = model.eval()
O ResNet é treinado no conjunto de dados ImageNet. Baixa e lê a lista de classes/rótulos do conjunto de dados ImageNet na memória.
wget -P $HOME/.torch/models https://s3.amazonaws.com/deep-learning-models/image-models/imagenet_class_index.json
labels_path = os.getenv("HOME") + '/.torch/models/imagenet_class_index.json'
with open(labels_path) as json_data:
idx_to_labels = json.load(json_data)
Agora que completamos o modelo, podemos baixar a imagem para análise. No meu caso, escolhi uma imagem de gato.
fonte
Sua pasta de imagens deve conter o arquivo cat.jpg. Como podemos ver abaixo, Image.open() abre e identifica o arquivo de imagem fornecido e np.asarry() o converte em um array.
test_img = Image.open('path/cat.jpg')
test_img_data = np.asarray(test_img)
plt.imshow(test_img_data)
plt.show()
No código abaixo definiremos transformadores e funções de normalização para a imagem. Para treinar nosso modelo ResNet, usamos o conjunto de dados ImageNet, que exige que as imagens tenham um tamanho específico, com dados do canal normalizados para um intervalo especificado de valores. transforms.Compose() compõe várias transformações juntas e transforms.Normalize() normaliza uma imagem tensorial com média e desvio padrão.
# model expectation is 224x224 3-color image
transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224), #crop the given tensor image at the center
transforms.ToTensor()
])ImageNet normalization
transform_normalize = transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
img = Image.open('path/cat.jpg')
transformed_img = transform(img)
input = transform_normalize(transformed_img)
#unsqueeze returns a new tensor with a dimension of size one inserted at the #specified position.
input = input.unsqueeze(0)
Agora, vamos prever a classe da imagem de entrada. A pergunta que pode ser feita é: “O que nosso modelo acha que esta imagem representa? ”
#call our model
output = model(input)
## applied softmax() function
output = F.softmax(output, dim=1)
#torch.topk returns the k largest elements of the given input tensor along a given #dimension.K here is 1
prediction_score, pred_label_idx = torch.topk(output, 1)
pred_label_idx.squeeze_()
#convert into a dictionnary of keyvalues pair the predict label, convert it #into a string to get the predicted label
predicted_label = idx_to_labels[str(pred_label_idx.item())][1]
print('Predicted:', predicted_label, '(', prediction_score.squeeze().item(), ')')
saída :
Predicted: tabby ( 0.5530276298522949 )
O fato de a ResNet pensar que nossa imagem de um gato representa um gato real foi verificado. Mas o que dá à modelo a impressão de que se trata da imagem de um gato? Para obter a solução para essa questão, consultaremos Captum.
Atribuição de recursos com gradientes integrados
Uma das várias técnicas de atribuição de recursos no Captum são os Gradientes Integrados. Os Gradientes Integrados concedem a cada recurso de entrada uma pontuação de relevância, estimando a integral dos gradientes da saída do modelo em relação às entradas.
Para o nosso caso, pegaremos um componente específico do vetor de saída - aquele que indica a confiança do modelo na categoria selecionada - e usaremos gradientes integrados para descobrir quais aspectos da imagem de entrada contribuíram para essa saída. Isso nos permitirá determinar quais partes da imagem foram mais importantes na produção desse resultado. Depois de obtermos o mapa de importância dos Gradientes Integrados, usaremos as ferramentas de visualização capturadas pelo Captum para fornecer uma representação clara e compreensível do mapa de importância.
Gradientes integrados determinarão a integral dos gradientes da saída do modelo para a classe prevista pred_label_idx em relação aos pixels da imagem de entrada ao longo do caminho da imagem preta até nossa imagem de entrada.
print('Predicted:', predicted_label, '(', prediction_score.squeeze().item(), ')')
#Create IntegratedGradients object and get attributes
integrated_gradients = IntegratedGradients(model)
#Request the algorithm to assign our output target to
attributions_ig = integrated_gradients.attribute(input, target=pred_label_idx, n_steps=200)
Saída :
Predicted: tabby ( 0.5530276298522949 )
Vamos ver a imagem e as atribuições que a acompanham, sobrepondo esta última no topo da imagem. O método visualize_image_attr() que Captum oferece oferece um conjunto de possibilidades para adaptar a apresentação dos dados de atribuição às suas preferências. Aqui, passamos um mapa de cores Matplotlib personalizado (consulte LinearSegmentedColormap()).
#result visualization with custom colormap
default_cmap = LinearSegmentedColormap.from_list('custom blue',
[(0, '#ffffff'),
(0.25, '#000000'),
(1, '#000000')], N=256)use visualize_image_attr helper method for visualization to show the #original image for comparison
_ = viz.visualize_image_attr(np.transpose(attributions_ig.squeeze().cpu().detach().numpy(), (1,2,0)),
np.transpose(transformed_img.squeeze().cpu().detach().numpy(), (1,2,0)),
method='heat_map',
cmap=default_cmap,
show_colorbar=True,
sign='positive',
outlier_perc=1)
Saída :
Você deve notar na imagem que mostramos acima que a área ao redor do gato na imagem é onde o algoritmo de Gradientes Integrados nos fornece o sinal mais forte.
Vamos calcular atribuições usando Gradientes Integrados e, em seguida, suavizá-las em várias imagens que foram produzidas por um túnel de ruído. Este último modifica a entrada adicionando ruído gaussiano com desvio padrão de um, 10 vezes (nt_samples=10). A abordagem smoothgrad_sq é usada pelo túnel de ruído para tornar as atribuições consistentes em todos os nt_samples de amostras ruidosas. O valor de smoothgrad_sq é a média das atribuições quadradas nas amostras nt_samples. visualize_image_attr_multiple() visualiza a atribuição para uma determinada imagem normalizando os valores de atribuição do sinal especificado (positivo, negativo, valor absoluto ou todos) e, em seguida, exibindo-os em uma figura matplotlib usando o modo selecionado.
noise_tunnel = NoiseTunnel(integrated_gradients)
attributions_ig_nt = noise_tunnel.attribute(input, nt_samples=10, nt_type='smoothgrad_sq', target=pred_label_idx)
_ = viz.visualize_image_attr_multiple(np.transpose(attributions_ig_nt.squeeze().cpu().detach().numpy(), (1,2,0)),
np.transpose(transformed_img.squeeze().cpu().detach().numpy(), (1,2,0)),
["original_image", "heat_map"],
["all", "positive"],
cmap=default_cmap,
show_colorbar=True)
Saída :
Posso ver nas imagens acima que o modelo se concentra na cabeça do gato.
Vamos terminar usando GradientShap. GradientShap é uma abordagem de gradiente que pode ser usada para calcular valores SHAP e também é uma ferramenta fantástica para adquirir insights sobre o comportamento global. É um modelo de explicação linear que explica as previsões do modelo usando uma distribuição de amostras de referência. Determina os gradientes esperados para uma entrada escolhida aleatoriamente entre a entrada e uma linha de base. A linha de base é escolhida aleatoriamente na distribuição de linhas de base fornecida.
torch.manual_seed(0)
np.random.seed(0)
gradient_shap = GradientShap(model)
Definition of baseline distribution of images
rand_img_dist = torch.cat([input * 0, input * 1])
attributions_gs = gradient_shap.attribute(input,
n_samples=50,
stdevs=0.0001,
baselines=rand_img_dist,
target=pred_label_idx)
_ = viz.visualize_image_attr_multiple(np.transpose(attributions_gs.squeeze().cpu().detach().numpy(), (1,2,0)),
np.transpose(transformed_img.squeeze().cpu().detach().numpy(), (1,2,0)),
["original_image", "heat_map"],
["all", "absolute_value"],
cmap=default_cmap,
show_colorbar=True)
Saída :
Atribuição de camada com Layer GradCAM
Você pode relacionar a atividade das camadas ocultas dentro do seu modelo aos recursos da sua entrada com a ajuda da Atribuição de Camada. Aplicaremos um algoritmo de atribuição de camadas para investigar a atividade de uma das camadas convolucionais incluídas em nosso modelo. GradCAM é responsável por calcular os gradientes da saída alvo em relação à camada especificada. A média desses gradientes é então calculada para cada canal de saída (dimensão 2 da saída) e as ativações da camada são multiplicadas pelo gradiente médio de cada canal. Os resultados são somados em todos os canais. Como a atividade das camadas convolucionais geralmente é mapeada espacialmente para a entrada, as atribuições do GradCAM são frequentemente ampliadas e usadas para mascarar a entrada. É importante notar que o GradCAM foi desenvolvido explicitamente para redes neurais convolucionais (convnets). A atribuição de camadas é configurada da mesma forma que a atribuição de entradas, com a exceção de que além do modelo, você deve fornecer uma camada oculta dentro do modelo que deseja analisar. Semelhante ao que foi discutido antes, quando chamamos attribute(), indicamos a classe alvo de interesse.
layer_gradcam = LayerGradCam(model, model.layer3[1].conv2)
attributions_lgc = layer_gradcam.attribute(input, target=pred_label_idx)
_ = viz.visualize_image_attr(attributions_lgc[0].cpu().permute(1,2,0).detach().numpy(),
sign="all",
title="Layer 3 Block 1 Conv 2")
Para fazer uma comparação mais precisa entre a imagem de entrada e esses dados de atribuição, faremos um upsample com a ajuda da função interpolate(), localizada na classe base LayerAttribution.
upsamp_attr_lgc = LayerAttribution.interpolate(attributions_lgc, input.shape[2:])
print(attributions_lgc.shape)
print(upsamp_attr_lgc.shape)
print(input.shape)
_ = viz.visualize_image_attr_multiple(upsamp_attr_lgc[0].cpu().permute(1,2,0).detach().numpy(),
transformed_img.permute(1,2,0).numpy(),
["original_image","blended_heat_map","masked_image"],
["all","positive","positive"],
show_colorbar=True,
titles=["Original", "Positive Attribution", "Masked"],
fig_size=(18, 6))
Saída :
Visualizações como esta têm o potencial de fornecer insights exclusivos sobre como suas camadas ocultas respondem às informações fornecidas.
Atribuição de recursos com oclusão
Métodos baseados em gradientes ajudam a compreender o modelo em termos de calcular diretamente as mudanças na saída em relação à entrada. A técnica conhecida como atribuição baseada em perturbações adota uma abordagem mais direta para esse problema, fazendo modificações na entrada para quantificar o impacto que tais mudanças têm na saída. Uma dessas estratégias é chamada de oclusão. Implica trocar pedaços da imagem de entrada e analisar como essa mudança afeta o sinal produzido na saída.
A seguir configuraremos a atribuição de oclusão. Assim como na configuração de uma rede neural convolucional, você pode escolher o tamanho da região alvo e o comprimento da passada, que determina o espaçamento das medições individuais. Usaremos a função visualize_image_attr_multiple() para visualizar os resultados de nossa atribuição de Oclusão. Esta função exibirá mapas de calor de atribuição positiva e negativa por região e mascarará a imagem original com as regiões de atribuição positiva. A máscara fornece uma visão muito esclarecedora das regiões da foto do nosso gato que o modelo identificou como mais “parecidas com gatos”. ”
occlusion = Occlusion(model)
attributions_occ = occlusion.attribute(input,
target=pred_label_idx,
strides=(3, 8, 8),
sliding_window_shapes=(3,15, 15),
baselines=0)
_ = viz.visualize_image_attr_multiple(np.transpose(attributions_occ.squeeze().cpu().detach().numpy(), (1,2,0)),
np.transpose(transformed_img.squeeze().cpu().detach().numpy(), (1,2,0)),
["original_image", "heat_map", "heat_map", "masked_image"],
["all", "positive", "negative", "positive"],
show_colorbar=True,
titles=["Original", "Positive Attribution", "Negative Attribution", "Masked"],
fig_size=(18, 6)
)
Saída :
A parte da imagem que contém o gato parece ter um nível de importância mais elevado.
Conclusão
Captum é uma biblioteca de interpretabilidade de modelo para PyTorch que é versátil e simples. Ele oferece técnicas de última geração para compreender como neurônios e camadas específicas impactam as previsões. Possui três tipos principais de técnicas de atribuição: Técnicas de Atribuição Primária, Técnicas de Atribuição de Camada e Técnicas de Atribuição de Neurônios.
Referências
https://pytorch.org/tutorials/beginner/introyt/captumyt.html https://gilberttanner.com/blog/interpreting-pytorch-models-with-captum/ https://arxiv.org/pdf/1805.12233.pdf https://arxiv.org/pdf/1704.02685.pdf