IAsmin: Manutenção Inteligente

Mapeamento de Ativação de Classe Ponderado por Gradiente (Grad-CAM)

A técnica Grad-CAM (Gradient-weighted Class Activation Mapping) é uma abordagem para a visualização de áreas de interesse em imagens analisadas por redes neurais convolucionais (CNNs). Ela permite entender melhor quais partes da imagem estão contribuindo para a decisão da rede, fornecendo um mapa de calor que destaca essas regiões. Como funciona? O Grad-CAM fundamenta-se nos gradientes dos neurônios da última camada convolucional de uma CNN em relação à saída de interesse. Ao calcular o gradiente da pontuação da classe prevista em relação aos mapas de ativação da última camada convolucional, o Grad-CAM consegue estimar o quanto cada mapa de características contribui para a predição de uma determinada classe. Trata-se de uma generalização do Class Activation Mapping (CAM), técnica empregada para localizar regiões discriminativas em algumas arquiteturas de CNNs que não possuem camadas totalmente conectadas. No entanto, a abordagem CAM sacrifica parte do desempenho do modelo para obter uma maior interpretabilidade em seu processo de decisão. O Grad-CAM, por sua vez, aproveita os gradientes que se propagam até a última camada convolucional da rede para atribuir relevância a cada neurônio, com base em uma decisão específica de interesse. Para gerar o mapa de localização discriminativo por classe referente a uma classe ***c***, inicia-se com o cálculo do gradiente da pontuação dessa classe com respeito às ativações da camada convolucional. Esses gradientes são, então, agregados por meio de uma média global (global average pooling) ao longo das dimensões espaciais, resultando nos pesos que indicam a importância de cada neurônio. Durante esse processo, à medida que os gradientes são retropropagados em relação às ativações, o cálculo envolve multiplicações sucessivas entre os gradientes e as matrizes de pesos correspondentes às funções de ativação, até alcançar a última camada convolucional considerada. O peso resultante fornece uma linearização parcial da rede a partir de ***A***, refletindo a relevância do mapa de características ***k*** para a classe-alvo ***c***. Aplica-se uma função ReLU à combinação linear dos mapas de características, pois o interesse reside apenas nas ativações que influenciam positivamente a predição da classe em questão, ou seja, nos pixels cuja intensidade contribui para elevar a pontuação da classe ***c***. Ativações negativas tendem a estar associadas a outras classes presentes na imagem. A ausência dessa função ReLU pode fazer com que o mapa de localização evidencie áreas que não estão diretamente relacionadas à classe desejada, o que prejudica a acurácia na localização.

Implementando Grad-CAM em Python

Neste exemplo, será utilizado uma base de dados contendo imagens de gatos e cachorros no formato “.jpg”. Este é um conjunto utilizado para estudos, onde seu foco é gerar um modelo capaz de classificar diferentes seres vivos e veículos. Podemos encontrar este conjunto de dados no kaggle. Para simplificar este conjunto, as pastas que não são referentes a gatos ou cachorros foram removidas. É possível baixar o dataset aqui.

Passo 1: Preparação do Ambiente

# Importando bibliotecas necessárias
import zipfile
import numpy as np
import tensorflow as tf
import matplotlib.cm as cm
import matplotlib.pyplot as plt
from tensorflow import keras
from google.colab import drive
from IPython.display import Image, display

Passo 2: Preparação dos Dados

# Extraindo o dataset de meu Google Drive
drive.mount('/content/drive')

# Utilize o caminho do arquivo em seu drive
zf = zipfile.ZipFile('/content/drive/MyDrive/IC_Datasets/PetImages.zip') 
zf.extractall()
zf.close()

# Utilizando o modelo Xception como exemplo
model_builder = keras.applications.xception.Xception
preprocess_input = keras.applications.xception.preprocess_input
decode_predictions = keras.applications.xception.decode_predictions
last_conv_layer_name = "block14_sepconv2_act"

# Carregando os dados
img_size = (299, 299)
train_dataset = keras.utils.image_dataset_from_directory(
    '/content/PetImages',
    image_size=img_size,
    batch_size=32
)
train_dataset = train_dataset.map(lambda x, y: (preprocess_input(x), y))

Passo 3: Preparando o GRAD-CAM

# Convertendo uma imagem em array
def get_img_array(img_path, size): 
    img = keras.utils.load_img(img_path, target_size=size)
    array = keras.utils.img_to_array(img)
    array = np.expand_dims(array, axis=0)
    return array

 # Método para mapear a imagem de entrada para as ativações da última camada
 # convolucional e previsões de saída
def make_gradcam_heatmap(img_array, model, last_conv_layer_name, pred_index=None):
    grad_model = keras.models.Model(
        model.inputs, [model.get_layer(last_conv_layer_name).output, model.output]
    )

    # Cálculo do gradiente da classe prevista em relação às ativações da última
    # camada convolucional
    with tf.GradientTape() as tape:
        last_conv_layer_output, preds = grad_model(img_array)
        if pred_index is None:
            pred_index = tf.argmax(preds[0])
        class_channel = preds[:, pred_index]

    # Gradiente do neurônio de saída
    grads = tape.gradient(class_channel, last_conv_layer_output)

    # Vetor onde cada entrada é a intensidade média do gradiente sobre um canal
    # específico do mapa de características
    pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2))

    # Multiplica-se cada canal no mapa de características por sua importância
    # depois são somados todos os canais para obter o heatmap da classe
    last_conv_layer_output = last_conv_layer_output[0]
    heatmap = last_conv_layer_output @ pooled_grads[..., tf.newaxis]
    heatmap = tf.squeeze(heatmap)

    # Normalização do heatmap
    heatmap = tf.maximum(heatmap, 0) / tf.math.reduce_max(heatmap)
    return heatmap.numpy()

# Carrega a imagem original
def save_and_display_gradcam(img_path, heatmap, cam_path="cam.jpg", alpha=0.4):
    img = keras.utils.load_img(img_path)
    img = keras.utils.img_to_array(img)

    # Redimensiona o heatmap para o intervalo de 0 a 255
    heatmap = np.uint8(255 * heatmap)

    # Usa o mapa de cores "jet" para colorir o heatmap
    jet = cm.get_cmap("jet")

    # Usa os valores RGB do mapa de cores
    jet_colors = jet(np.arange(256))[:, :3]
    jet_heatmap = jet_colors[heatmap]

    # Cria uma imagem com o heatmap colorido em RGB
    jet_heatmap = keras.utils.array_to_img(jet_heatmap)
    jet_heatmap = jet_heatmap.resize((img.shape[1], img.shape[0]))
    jet_heatmap = keras.utils.img_to_array(jet_heatmap)

    # Sobrepõe o heatmap na imagem original
    superimposed_img = jet_heatmap * alpha + img
    superimposed_img = keras.utils.array_to_img(superimposed_img)

    # Salva a imagem sobreposta
    superimposed_img.save(cam_path)

    # Exibe o Grad-CAM
    display(Image(cam_path))

Passo 4: Testando o GRAD-CAM

# Caminho das imagens para visualização do GRAD-CAM
cat_img_path = '/content/PetImages/cats/cat.1.jpg'
dog_img_path = '/content/PetImages/dogs/dog.1.jpg'

# Preparação das imagens
cat_img_array = preprocess_input(get_img_array(cat_img_path, size=(299, 299)))
dog_img_array = preprocess_input(get_img_array(dog_img_path, size=(299, 299)))


# Utilizando pesos da imagenet
model = model_builder(weights="imagenet")

# Removendo softmax da última camada
model.layers[-1].activation = None

# Predição e print da classe mais provável
preds = model.predict(cat_img_array)
print("Predicted:", decode_predictions(preds, top=1)[0])
preds = model.predict(dog_img_array)
print("Predicted:", decode_predictions(preds, top=1)[0])

# Gerando heatmaps
cat_heatmap = make_gradcam_heatmap(cat_img_array, model, last_conv_layer_name)
dog_heatmap = make_gradcam_heatmap(dog_img_array, model, last_conv_layer_name)

# Exibindo heatmaps
plt.matshow(cat_heatmap)
plt.matshow(dog_heatmap)
plt.show()

# Exibindo heatmap aplicado nas imagens
save_and_display_gradcam(cat_img_path, cat_heatmap)
save_and_display_gradcam(dog_img_path, dog_heatmap)

As imagens acima evidenciam que, para a rede, as regiões próximas aos rostos dos animais foram as mais relevantes para a predição, indicando que o modelo considera essas áreas, como as mais discriminativas para identificar corretamente a classe.

Conclusão

Neste post, observamos que o Grad-CAM é uma ferramenta poderosa para interpretar decisões de redes neurais convolucionais, ao destacar visualmente as regiões da imagem mais relevantes para a predição. Sua aplicação contribui para maior transparência, confiança e entendimento dos modelos de visão computacional.

É possível testar este código, a partir do meu notebook no Google Colab. Basta duplicar o notebook e ajustá-lo conforme seus dados e objetivos. Caso surjam dúvidas, consulte a documentação oficial do GRAD-CAM no Keras ou entre em contato comigo.

Referências

Grad-CAM class activation visualization. Keras, 07 mar. 2021. Disponível em: https://keras.io/examples/vision/grad_cam/

Ramprasaath R Selvaraju, Abhishek Das, Ramakrishna Vedantam, Michael Cogswell, Devi Parikh, & Dhruv Batra. (2017). Grad-CAM: Why did you say that?.

Powered by wisp

4/28/2025
Related Posts
SHAP: Desvendando as Decisões dos Modelos de Machine Learning

SHAP: Desvendando as Decisões dos Modelos de Machine Learning

Descubra como o SHAP pode desvendar as decisões dos modelos de machine learning e tornar suas previsões mais transparentes e confiáveis. (João Assaoka)

Read Full Story
Efeitos Locais Acumulados (ALE)

Efeitos Locais Acumulados (ALE)

Descubra como os Efeitos Locais Acumulados (ALE) oferecem uma análise rápida e precisa, superando os desafios dos Partial Dependence Plots (PDPs). (João Assaoka)

Read Full Story
Importância por Permutação

Importância por Permutação

Quer saber quais variáveis realmente importam para o seu modelo de machine learning? A importância por permutação é a técnica que você precisa. Neste post, vamos explorar como essa abordagem simples e eficaz pode transformar suas previsões. (João Assaoka)

Read Full Story
© IAsmin - Unifesp 2025