Dalam dunia deep learning, memahami apa yang dipelajari oleh model konvolusi (ConvNet) sangat penting untuk memastikan model tidak hanya akurat tetapi juga dapat diinterpretasikan. Dua teknik yang sering digunakan untuk tujuan ini adalah visualisasi intermediate activation dan Grad-CAM (Gradient-weighted Class Activation Mapping). Dalam artikel ini, kita akan membahas kedua teknik tersebut dan menerapkannya pada model ResNet-101 yang telah dilatih sebelumnya. Gambar yang akan kita gunakan untuk visualisasi adalah gambar lubang jalan.
Pendahuluan
Model konvolusi seperti ResNet-101 telah mencapai hasil yang luar biasa dalam berbagai tugas pengenalan gambar. Namun, model ini sering dianggap sebagai "kotak hitam" karena kompleksitasnya yang tinggi. Untuk mengatasi masalah ini, kita dapat menggunakan teknik visualisasi seperti intermediate activation dan Grad-CAM untuk memahami bagaimana model memproses gambar dan bagian mana dari gambar yang paling mempengaruhi prediksi model.
Intermediate Activation
Intermediate activation adalah teknik yang digunakan untuk mengekstrak dan memvisualisasikan keluaran dari lapisan-lapisan tertentu di dalam jaringan konvolusi. Teknik ini membantu kita memahami bagaimana gambar diproses pada berbagai tahap dalam jaringan.
Mengapa Intermediate Activation Penting?
Memahami Fitur yang Dipelajari: Dengan melihat bagaimana gambar diproses oleh setiap lapisan, kita bisa memahami jenis fitur apa yang dipelajari oleh model pada tiap tahap.
Debugging Model: Jika model tidak bekerja sebagaimana mestinya, visualisasi ini dapat membantu dalam mengidentifikasi di mana masalahnya terjadi.
Transparansi Model: Ini memberikan wawasan tentang bagaimana model membuat keputusan, yang sangat penting untuk aplikasi-aplikasi yang memerlukan tingkat transparansi tinggi seperti bidang medis dan otomotif.
Implementasi Intermediate Activation
Berikut adalah langkah-langkah untuk mengimplementasikan visualisasi intermediate activation pada model ResNet-101 menggunakan PyTorch.
import torch
import torchvision.models as models
import torchvision.transforms as transforms
from PIL import Image
import matplotlib.pyplot as plt
# Memuat model ResNet-101 yang telah dilatih sebelumnya
model = models.resnet101(pretrained=True)
model.eval() # Mengatur model ke mode evaluasi
# Mendefinisikan transformasi untuk mengubah gambar menjadi tensor dan menormalkannya
transform = transforms.Compose([
transforms.Resize((224, 224)), # Mengubah ukuran gambar menjadi 224x224 piksel
transforms.ToTensor(), # Mengubah gambar menjadi tensor
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # Menormalkan tensor
])
# Memuat gambar contoh dan menerapkan transformasi
img = Image.open("/path/to/your/image.jpg")
img_t = transform(img) # Menerapkan transformasi pada gambar
img_t = img_t.unsqueeze(0) # Menambahkan dimensi batch
# Fungsi untuk mendapatkan aktivasi intermediate
def get_activations(layer, x):
"""
Mengambil aktivasi intermediate dari lapisan tertentu.
Args:
layer (torch.nn.Module): Lapisan dari mana aktivasi akan diambil.
x (torch.Tensor): Tensor input.
Returns:
torch.Tensor: Aktivasi dari lapisan tersebut.
"""
activations = [] # List untuk menyimpan aktivasi
def hook_fn(module, input, output):
activations.append(output) # Menambahkan output (aktivasi) ke list
handle = layer.register_forward_hook(hook_fn) # Mendaftarkan hook untuk mendapatkan aktivasi
model(x)
handle.remove() # Menghapus hook setelah mendapatkan aktivasi
return activations[0]
# Memilih beberapa lapisan untuk divisualisasikan
layers = [model.layer1[0].conv1, model.layer2[0].conv1, model.layer3[0].conv1, model.layer4[0].conv1]
activations = [get_activations(layer, img_t) for layer in layers] # Mendapatkan aktivasi dari lapisan-lapisan tersebut
# Memvisualisasikan aktivasi
fig, axes = plt.subplots(len(layers), 1, figsize=(12, 24))
for i, activation in enumerate(activations):
activation = activation.squeeze(0).detach().numpy() # Menghilangkan dimensi batch dan mengubah ke numpy array
ax = axes[i]
ax.imshow(activation.mean(axis=0), cmap='viridis') # Menampilkan rata-rata aktivasi dengan colormap 'viridis'
ax.axis('off') # Menonaktifkan sumbu
ax.set_title(f'Layer {i+1} Activation') # Menambahkan judul pada plot
plt.show() # Menampilkan plot
Berikut adalah hasil visualisasi intermediate activation pada lapisan-lapisan yang berbeda di ResNet-101:
Grad-CAM
Grad-CAM adalah teknik yang digunakan untuk menghasilkan peta panas (heatmap) yang menunjukkan area-area dalam gambar yang paling mempengaruhi prediksi akhir dari model. Grad-CAM bekerja dengan menghitung gradien dari kelas target terhadap keluaran dari lapisan konvolusi terakhir. Gradien ini kemudian digunakan untuk membobot aktivasi lapisan tersebut, menghasilkan peta aktivasi yang terfokus pada fitur-fitur penting.
Mengapa Grad-CAM Penting?
Interprestabilitas Model: Grad-CAM membantu dalam mengidentifikasi bagian mana dari gambar yang dianggap penting oleh model untuk membuat prediksi tertentu, meningkatkan kepercayaan pengguna terhadap model.
Diagnostik Model: Dengan mengetahui bagian mana dari gambar yang paling mempengaruhi prediksi, kita dapat mengidentifikasi apakah model benar-benar melihat fitur yang relevan atau hanya fitur-fitur spurious yang tidak penting.
Pengawasan dan Validasi: Dalam banyak aplikasi kritis, seperti diagnosis medis, penting untuk memverifikasi bahwa model membuat keputusan berdasarkan fitur yang tepat, bukan artefak yang tidak relevan.
Implementasi Grad-CAM
Berikut adalah langkah-langkah untuk mengimplementasikan Grad-CAM pada model ResNet-101 menggunakan PyTorch.
import torch.nn.functional as F
import cv2
import numpy as np
# Fungsi untuk mendapatkan peta aktivasi kelas (CAM)
def get_cam(model, img_tensor, target_class):
"""
Menghasilkan peta aktivasi kelas (CAM) untuk kelas target yang ditentukan.
Args:
model (torch.nn.Module): Model yang digunakan untuk inferensi.
img_tensor (torch.Tensor): Tensor gambar input.
target_class (int): Indeks kelas target untuk menghasilkan CAM.
Returns:
np.ndarray: Peta aktivasi kelas (heatmap).
"""
final_conv_layer = model.layer4[2].conv3 # Mendapatkan lapisan konvolusi terakhir
activations = get_activations(final_conv_layer, img_tensor) # Mendapatkan aktivasi dari lapisan tersebut
output = model(img_tensor) # Melakukan forward pass pada model dengan gambar input
model.zero_grad() # Mengatur gradien menjadi nol
target = torch.zeros(output.shape) # Membuat tensor target dengan bentuk yang sama dengan output model
target[0][target_class] = 1 # Menentukan target untuk kelas spesifik
output.backward(gradient=target) # Melakukan backward pass untuk menghitung gradien
gradients = final_conv_layer.weight.grad # Mendapatkan gradien dari lapisan konvolusi terakhir
pooled_gradients = torch.mean(gradients, dim=[0, 2, 3]) # Mengambil rata-rata gradien
activations = activations.squeeze(0).detach() # Menghilangkan dimensi batch dan detach dari komputasi graf
for i in range(512): # Mengalikan setiap aktivasi dengan gradien yang dipool
activations[i, :, :] *= pooled_gradients[i]
heatmap = torch.mean(activations, dim=0).cpu() # Mengambil rata-rata dari semua aktivasi untuk membuat heatmap
heatmap = np.maximum(heatmap, 0) # Menerapkan ReLU pada heatmap
heatmap /= torch.max(heatmap) # Menormalkan heatmap
return heatmap # Mengembalikan heatmap
# Mendapatkan kelas yang diprediksi
output = model(img_t) # Melakukan forward pass pada model dengan gambar input
_, pred = torch.max(output, 1) # Mendapatkan indeks kelas dengan probabilitas tertinggi
# Mendapatkan peta aktivasi kelas (CAM)
heatmap = get_cam(model, img_t, pred.item()) # Menghasilkan heatmap untuk kelas yang diprediksi
# Menumpangkan heatmap pada gambar asli
heatmap = heatmap.numpy() # Mengubah heatmap menjadi numpy array
heatmap = cv2.resize(heatmap, (img.size[0], img.size[1])) # Mengubah ukuran heatmap agar sesuai dengan gambar asli
heatmap = np.uint8(255 * heatmap) # Mengubah heatmap ke uint8
heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET) # Menerapkan colormap JET pada heatmap
img = cv2.imread("/path/to/your/image.jpg") # Membaca gambar asli
superimposed_img = heatmap * 0.4 + img
# Menumpangkan heatmap pada gambar asli dengan bobot 0.4
cv2.imwrite('cam.jpg', superimposed_img) # Menyimpan gambar hasil tumpangan
# Menampilkan gambar hasil tumpangan
cv2_imshow(superimposed_img) # Menampilkan gambar di Google Colab
Berikut adalah hasil visualisasi Grad-CAM yang menunjukkan area penting pada gambar yang mempengaruhi prediksi model:
Visualisasi Filter Konvolusi
Selain intermediate activation dan Grad-CAM, penting juga untuk melihat filter yang dipelajari oleh lapisan pertama dari model konvolusi. Filter ini menangkap fitur-fitur dasar seperti tepi dan tekstur.
Implementasi Visualisasi Filter
# Mendapatkan bobot dari lapisan konvolusi pertama
filters = model.conv1.weight.data
filters = filters - filters.min() # Menormalkan filter ke rentang [0, 1]
filters = filters / filters.max()
# Memvisualisasikan filter
fig, axes = plt.subplots(8, 8, figsize=(12, 12)) # Membuat grid plot 8x8
for i, ax in enumerate(axes.flat):
if i < filters.size(0): # Memastikan indeks tidak melebihi jumlah filter
ax.imshow(filters[i].permute(1, 2, 0)) # Memutar dimensi filter untuk plotting
ax.axis('off') # Menonaktifkan sumbu
plt.show() # Menampilkan plot
Berikut adalah hasil visualisasi filter konvolusi dari lapisan pertama model:
Kesimpulan
Visualisasi intermediate activation, Grad-CAM, dan filter konvolusi adalah alat yang sangat berguna untuk memahami apa yang dipelajari oleh model konvolusi seperti ResNet-101. Dengan menggunakan teknik-teknik ini, kita dapat memastikan model bekerja sebagaimana mestinya dan membuat keputusan berdasarkan fitur yang relevan. Ini sangat penting dalam aplikasi-aplikasi yang memerlukan transparansi dan interpretabilitas tinggi, seperti diagnosis medis dan pengenalan gambar otonom.
Dengan alat-alat ini, kita dapat lebih percaya diri dalam menggunakan model deep learning untuk berbagai tugas kompleks dan memastikan model tidak hanya akurat tetapi juga dapat diinterpretasikan dengan baik.