python打卡day43
用于图像分类的标志性乐器的数据集
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms, models
from PIL import Image
from pytorch_grad_cam import GradCAM
from pytorch_grad_cam.utils.image import show_cam_on_image
# 1. 数据准备
class MusicInstrumentDataset(Dataset):def __init__(self, root_dir, csv_file, transform=None):self.root_dir = root_dirself.data = pd.read_csv(csv_file)self.transform = transformself.classes = self.data['class'].unique().tolist()self.total_images = sum(self.data['image_count'])self.image_paths = []self.labels = []for _, row in self.data.iterrows():class_dir = os.path.join(self.root_dir, row['class'])files = [f for f in os.listdir(class_dir) if f.endswith('.jpg')]files.sort(key=lambda x: int(x.split('.')[0]))self.image_paths.extend([os.path.join(class_dir, f) for f in files])self.labels.extend([self.classes.index(row['class'])] * len(files))def __len__(self):return len(self.image_paths)def __getitem__(self, idx):image = Image.open(self.image_paths[idx]).convert('RGB')label = self.labels[idx]if self.transform:image = self.transform(image)return image, label
# 2. 数据预处理
transform = transforms.Compose([transforms.RandomHorizontalFlip(),transforms.RandomRotation(15),transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2), # 新增颜色扰动transforms.RandomResizedCrop(224, scale=(0.8, 1.0)), # 随机裁剪缩放transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
# 3. 创建数据集和数据加载器
dataset = MusicInstrumentDataset(root_dir="d:\\python打卡\\day43\\music_instruments",csv_file="d:\\python打卡\\day43\\music_instruments\\dataset_stats.csv",transform=transform
)train_size = int(0.8 * len(dataset))
test_size = len(dataset) - train_size
generator = torch.Generator().manual_seed(42)
train_dataset, test_dataset = torch.utils.data.random_split(dataset, [train_size, test_size],generator=generator
)train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)
# 4. 定义CNN模型
class MusicInstrumentCNN(nn.Module):def __init__(self, num_classes=10):super(MusicInstrumentCNN, self).__init__()# 特征提取self.features = nn.Sequential(nn.Conv2d(3, 32, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(32, 64, kernel_size=3, padding=1), nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),)# 分类器self.classifier = nn.Sequential(nn.Linear(64 * 56 * 56, 128),nn.ReLU(inplace=True),nn.Dropout(0.5), nn.Linear(128, num_classes))def forward(self, x):# 特征提取x = self.features(x)# 展平特征图x = torch.flatten(x, 1)# 分类x = self.classifier(x)return x
# 5. 训练模型
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = MusicInstrumentCNN(num_classes=10).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.0005, weight_decay=1e-4)def train_model(model, train_loader, test_loader, criterion, optimizer, device, num_epochs):# 初始化记录变量train_losses = []test_losses = []train_accs = []test_accs = []batch_losses = []# 创建学习率调度器scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, 'min', patience=2, factor=0.5)for epoch in range(num_epochs):model.train()running_loss = 0.0correct = 0total = 0# 训练阶段for batch_idx, (images, labels) in enumerate(train_loader):images, labels = images.to(device), labels.to(device)optimizer.zero_grad()outputs = model(images)loss = criterion(outputs, labels)loss.backward()optimizer.step()# 记录每个batch的损失batch_loss = loss.item()batch_losses.append(batch_loss)running_loss += batch_loss# 计算准确率_, predicted = torch.max(outputs.data, 1)total += labels.size(0)correct += (predicted == labels).sum().item()# 每50个batch打印一次信息if (batch_idx + 1) % 50 == 0:print(f'Epoch [{epoch+1}/{num_epochs}], Batch [{batch_idx+1}/{len(train_loader)}], 'f'Batch Loss: {batch_loss:.4f}, Avg Loss: {running_loss/(batch_idx+1):.4f}')# 计算epoch指标epoch_loss = running_loss / len(train_loader)epoch_acc = 100 * correct / totaltrain_losses.append(epoch_loss)train_accs.append(epoch_acc)# 测试阶段model.eval()test_loss = 0.0test_correct = 0test_total = 0with torch.no_grad():for images, labels in test_loader:images, labels = images.to(device), labels.to(device)outputs = model(images)loss = criterion(outputs, labels)test_loss += loss.item()_, predicted = torch.max(outputs.data, 1)test_total += labels.size(0)test_correct += (predicted == labels).sum().item()test_epoch_loss = test_loss / len(test_loader)test_epoch_acc = 100 * test_correct / test_totaltest_losses.append(test_epoch_loss)test_accs.append(test_epoch_acc)# 更新学习率scheduler.step(test_epoch_loss)print(f'Epoch {epoch+1}/{num_epochs} - 'f'Train Loss: {epoch_loss:.4f}, Train Acc: {epoch_acc:.2f}%, 'f'Test Loss: {test_epoch_loss:.4f}, Test Acc: {test_epoch_acc:.2f}%')# 绘制训练曲线plt.figure(figsize=(12, 5))# 损失曲线plt.subplot(1, 2, 1)plt.plot(train_losses, label='Train Loss')plt.plot(test_losses, label='Test Loss')plt.xlabel('Epoch')plt.ylabel('Loss')plt.title('Training and Test Loss')plt.legend()# 准确率曲线plt.subplot(1, 2, 2)plt.plot(train_accs, label='Train Accuracy')plt.plot(test_accs, label='Test Accuracy')plt.xlabel('Epoch')plt.ylabel('Accuracy (%)')plt.title('Training and Test Accuracy')plt.legend()plt.tight_layout()plt.show()return test_accs[-1]num_epochs = 30
print("开始训练模型...")
final_acc = train_model(model, train_loader, test_loader, criterion, optimizer, device, num_epochs)
print(f"训练完成! 最终测试准确率: {final_acc:.2f}%")
开始训练模型...
Epoch 1/30 - Train Loss: 2.9460, Train Acc: 13.37%, Test Loss: 2.2104, Test Acc: 25.14%
Epoch 2/30 - Train Loss: 2.1367, Train Acc: 22.65%, Test Loss: 2.0500, Test Acc: 27.90%
Epoch 3/30 - Train Loss: 1.9983, Train Acc: 29.64%, Test Loss: 1.9392, Test Acc: 28.45%
Epoch 4/30 - Train Loss: 1.9475, Train Acc: 30.89%, Test Loss: 1.8508, Test Acc: 40.88%
Epoch 5/30 - Train Loss: 1.8551, Train Acc: 33.31%, Test Loss: 1.7993, Test Acc: 42.27%
Epoch 6/30 - Train Loss: 1.7731, Train Acc: 37.88%, Test Loss: 1.7242, Test Acc: 41.16%
Epoch 7/30 - Train Loss: 1.7001, Train Acc: 39.20%, Test Loss: 1.6823, Test Acc: 46.13%
Epoch 8/30 - Train Loss: 1.6640, Train Acc: 39.27%, Test Loss: 1.6702, Test Acc: 47.51%
Epoch 9/30 - Train Loss: 1.6542, Train Acc: 42.66%, Test Loss: 1.6524, Test Acc: 45.03%
Epoch 10/30 - Train Loss: 1.6106, Train Acc: 43.84%, Test Loss: 1.5587, Test Acc: 50.28%
Epoch 11/30 - Train Loss: 1.5937, Train Acc: 44.11%, Test Loss: 1.5993, Test Acc: 45.86%
Epoch 12/30 - Train Loss: 1.5644, Train Acc: 44.04%, Test Loss: 1.5558, Test Acc: 48.62%
Epoch 13/30 - Train Loss: 1.5326, Train Acc: 46.12%, Test Loss: 1.5794, Test Acc: 49.17%
Epoch 14/30 - Train Loss: 1.5449, Train Acc: 47.16%, Test Loss: 1.5342, Test Acc: 51.38%
Epoch 15/30 - Train Loss: 1.4615, Train Acc: 47.16%, Test Loss: 1.5037, Test Acc: 51.66%
Epoch 16/30 - Train Loss: 1.4308, Train Acc: 49.31%, Test Loss: 1.5152, Test Acc: 49.45%
Epoch 17/30 - Train Loss: 1.4268, Train Acc: 48.13%, Test Loss: 1.5076, Test Acc: 49.72%
Epoch 18/30 - Train Loss: 1.4646, Train Acc: 48.06%, Test Loss: 1.4967, Test Acc: 52.21%
Epoch 19/30 - Train Loss: 1.3818, Train Acc: 51.32%, Test Loss: 1.5149, Test Acc: 48.90%
Epoch 20/30 - Train Loss: 1.3524, Train Acc: 52.29%, Test Loss: 1.4570, Test Acc: 51.10%
Epoch 21/30 - Train Loss: 1.3269, Train Acc: 52.22%, Test Loss: 1.4431, Test Acc: 51.38%
Epoch 22/30 - Train Loss: 1.3648, Train Acc: 52.08%, Test Loss: 1.4540, Test Acc: 53.87%
Epoch 23/30 - Train Loss: 1.3316, Train Acc: 53.39%, Test Loss: 1.5655, Test Acc: 52.49%
Epoch 24/30 - Train Loss: 1.3121, Train Acc: 54.09%, Test Loss: 1.4549, Test Acc: 51.66%
Epoch 25/30 - Train Loss: 1.2702, Train Acc: 56.51%, Test Loss: 1.4119, Test Acc: 54.14%
Epoch 26/30 - Train Loss: 1.2295, Train Acc: 56.44%, Test Loss: 1.4718, Test Acc: 53.87%
Epoch 27/30 - Train Loss: 1.2020, Train Acc: 57.20%, Test Loss: 1.4213, Test Acc: 56.35%
Epoch 28/30 - Train Loss: 1.2028, Train Acc: 58.59%, Test Loss: 1.4769, Test Acc: 55.80%
Epoch 29/30 - Train Loss: 1.1816, Train Acc: 57.41%, Test Loss: 1.4514, Test Acc: 54.14%
Epoch 30/30 - Train Loss: 1.1956, Train Acc: 58.73%, Test Loss: 1.4667, Test Acc: 53.59%
训练完成! 最终测试准确率: 53.59%
# 6. Grad-CAM可视化
target_layer = model.features[-2] # 选择最后一个卷积层
cam = GradCAM(model=model, target_layers=[target_layer])# 选择一个测试图像进行可视化
images, labels = next(iter(test_loader))
input_tensor = images[0:1].to(device)# 生成CAM
grayscale_cam = cam(input_tensor=input_tensor, targets=None)# 可视化
rgb_img = images[0].permute(1, 2, 0).cpu().numpy()
rgb_img = (rgb_img - rgb_img.min()) / (rgb_img.max() - rgb_img.min()) # 归一化
visualization = show_cam_on_image(rgb_img, grayscale_cam[0], use_rgb=True)plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.title("Original Image")
plt.imshow(rgb_img)
plt.axis('off')
(-0.5, 223.5, 223.5, -0.5)
plt.subplot(1, 2, 2)
plt.title("Grad-CAM")
plt.imshow(visualization)
plt.axis('off')
plt.show()
@浙大疏锦行