作业:
kaggle找到一个图像数据集,用cnn网络进行训练并且用grad-cam做可视化
进阶:并拆分成多个文件
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, random_split
from torchvision import datasets, transforms
from torchvision.transforms import ToPILImage
from tqdm import tqdm
import matplotlib.pyplot as plt
from pytorch_grad_cam import GradCAM
from pytorch_grad_cam.utils.image import show_cam_on_image
# 数据处理
transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
# 使用Kaggle的花朵数据集
dataset = datasets.ImageFolder(root='./flower_data', transform=transform)
train_size = int(0.8 * len(dataset))
val_size = len(dataset) - train_size
train_dataset, val_dataset = random_split(dataset, [train_size, val_size])
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False)
# CNN模型
class FlowerCNN(nn.Module):
def __init__(self, num_classes):
super().__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 64, 3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2, 2),
nn.Conv2d(64, 128, 3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2, 2),
nn.Conv2d(128, 256, 3, padding=1), # 用于GradCAM的目标层
nn.ReLU(),
nn.MaxPool2d(2, 2)
)
self.classifier = nn.Sequential(
nn.Flatten(),
nn.Linear(256 * 28 * 28, 512),
nn.ReLU(),
nn.Linear(512, num_classes)
)
def forward(self, x):
x = self.features(x)
x = self.classifier(x)
return x
# 训练函数
def train_model(model, epochs=5):
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=1e-4)
for epoch in range(epochs):
model.train()
train_loss = 0.0
with tqdm(train_loader, desc=f'Epoch {epoch+1}') as pbar:
for imgs, labels in pbar:
imgs, labels = imgs.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(imgs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
train_loss += loss.item() * imgs.size(0)
pbar.set_postfix(loss=loss.item())
# 验证
model.eval()
val_correct = 0
with torch.no_grad():
for imgs, labels in val_loader:
imgs, labels = imgs.to(device), labels.to(device)
outputs = model(imgs)
_, preds = torch.max(outputs, 1)
val_correct += (preds == labels).sum().item()
print(f'Val Acc: {val_correct/len(val_dataset):.4f}')
return model
# 训练模型
num_classes = len(dataset.classes)
model = FlowerCNN(num_classes)
model = train_model(model)
# Grad-CAM可视化
def visualize_grad_cam(model, img_tensor, target_layer):
cam = GradCAM(model=model, target_layer=target_layer)
grayscale_cam = cam(input_tensor=img_tensor.unsqueeze(0))
grayscale_cam = grayscale_cam[0, :]
# 图像转换
img = img_tensor.permute(1, 2, 0).numpy()
img = (img - img.min()) / (img.max() - img.min()) # 归一化
visualization = show_cam_on_image(img, grayscale_cam, use_rgb=True)
plt.imshow(visualization)
plt.axis('off')
plt.show()
# 随机选择一张验证集图片进行可视化
img, label = val_dataset[0]
target_layer = model.features[6] # 选择最后一个卷积层
visualize_grad_cam(model, img, target_layer)
@浙大疏锦行