python打卡44天
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms, models
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
import numpy as np
import time
import os# 设置中文字体支持
plt.rcParams["font.family"] = ["SimHei"]
plt.rcParams['axes.unicode_minus'] = False# 检查GPU是否可用
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"使用设备: {device}")# 数据预处理
train_transform = transforms.Compose([transforms.RandomCrop(32, padding=4),transforms.RandomHorizontalFlip(),transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1),transforms.RandomRotation(15),transforms.ToTensor(),transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
])test_transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
])# 加载CIFAR-10数据集
train_dataset = datasets.CIFAR10(root='./data',train=True,download=True,transform=train_transform
)test_dataset = datasets.CIFAR10(root='./data',train=False,transform=test_transform
)batch_size = 64
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)# 创建不同预训练模型的函数
def create_model(model_name, num_classes=10):"""创建并配置不同的预训练模型"""if model_name == "resnet18":model = models.resnet18(pretrained=True)# 修改第一层卷积以适应32x32输入model.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)# 移除第一个最大池化层model.maxpool = nn.Identity()# 修改最后的全连接层model.fc = nn.Linear(model.fc.in_features, num_classes)elif model_name == "efficientnet_b0":model = models.efficientnet_b0(pretrained=True)# 修改第一层卷积model.features[0][0] = nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1, bias=False)# 修改分类器model.classifier[1] = nn.Linear(model.classifier[1].in_features, num_classes)elif model_name == "mobilenet_v3_small":model = models.mobilenet_v3_small(pretrained=True)# 修改第一层卷积model.features[0][0] = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1, bias=False)# 修改分类器model.classifier[3] = nn.Linear(model.classifier[3].in_features, num_classes)else:raise ValueError(f"未知模型: {model_name}")return model.to(device)# 模型训练函数
def train_model(model, model_name, train_loader, test_loader, epochs=15):"""训练模型并返回结果"""print(f"\n{'='*50}")print(f"开始训练 {model_name} 模型")print(f"{'='*50}")# 冻结除最后一层外的所有层for name, param in model.named_parameters():if "classifier" not in name and "fc" not in name:param.requires_grad = False# 优化器和损失函数optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-4)criterion = nn.CrossEntropyLoss()scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.5, patience=2)# 记录训练过程train_loss_history = []test_loss_history = []train_acc_history = []test_acc_history = []times = []start_time = time.time()for epoch in range(epochs):epoch_start = time.time()# 训练阶段model.train()running_loss = 0.0correct_train = 0total_train = 0for inputs, labels in train_loader:inputs, labels = inputs.to(device), labels.to(device)optimizer.zero_grad()outputs = model(inputs)loss = criterion(outputs, labels)loss.backward()optimizer.step()running_loss += loss.item()_, predicted = outputs.max(1)total_train += labels.size(0)correct_train += predicted.eq(labels).sum().item()epoch_train_loss = running_loss / len(train_loader)epoch_train_acc = 100. * correct_train / total_traintrain_loss_history.append(epoch_train_loss)train_acc_history.append(epoch_train_acc)# 测试阶段model.eval()test_loss = 0.0correct_test = 0total_test = 0with torch.no_grad():for inputs, labels in test_loader:inputs, labels = inputs.to(device), labels.to(device)outputs = model(inputs)loss = criterion(outputs, labels)test_loss += loss.item()_, predicted = outputs.max(1)total_test += labels.size(0)correct_test += predicted.eq(labels).sum().item()epoch_test_loss = test_loss / len(test_loader)epoch_test_acc = 100. * correct_test / total_testtest_loss_history.append(epoch_test_loss)test_acc_history.append(epoch_test_acc)# 更新学习率scheduler.step(epoch_test_loss)epoch_time = time.time() - epoch_starttimes.append(epoch_time)print(f"Epoch {epoch+1}/{epochs} | "f"训练损失: {epoch_train_loss:.4f} | 训练准确率: {epoch_train_acc:.2f}% | "f"测试准确率: {epoch_test_acc:.2f}% | 时间: {epoch_time:.2f}s")total_time = time.time() - start_timeprint(f"训练完成! 最终测试准确率: {epoch_test_acc:.2f}% | 总时间: {total_time:.2f}s")return {"name": model_name,"train_loss": train_loss_history,"test_loss": test_loss_history,"train_acc": train_acc_history,"test_acc": test_acc_history,"times": times,"final_test_acc": epoch_test_acc,"total_time": total_time}# 比较不同模型
def compare_models(model_names, epochs=15):"""比较多个模型在CIFAR-10上的表现"""results = []for model_name in model_names:model = create_model(model_name)result = train_model(model, model_name, train_loader, test_loader, epochs)results.append(result)return results# 可视化比较结果
def visualize_comparison(results):"""可视化不同模型的比较结果"""plt.figure(figsize=(15, 10))# 准确率曲线plt.subplot(2, 2, 1)for result in results:plt.plot(result['test_acc'], label=result['name'])plt.xlabel('Epoch')plt.ylabel('测试准确率 (%)')plt.title('不同模型测试准确率比较')plt.legend()plt.grid(True)# 损失曲线plt.subplot(2, 2, 2)for result in results:plt.plot(result['test_loss'], label=result['name'])plt.xlabel('Epoch')plt.ylabel('测试损失')plt.title('不同模型测试损失比较')plt.legend()plt.grid(True)# 最终准确率比较plt.subplot(2, 2, 3)names = [r['name'] for r in results]accs = [r['final_test_acc'] for r in results]plt.bar(names, accs, color=['blue', 'green', 'orange'])plt.ylabel('最终测试准确率 (%)')plt.title('最终测试准确率比较')# 训练时间比较plt.subplot(2, 2, 4)times = [r['total_time'] for r in results]plt.bar(names, times, color=['blue', 'green', 'orange'])plt.ylabel('总训练时间 (秒)')plt.title('训练时间比较')plt.tight_layout()plt.savefig('model_comparison.png')plt.show()# 主函数
def main():# 要比较的模型model_names = ["resnet18", "efficientnet_b0", "mobilenet_v3_small"]# 训练并比较模型results = compare_models(model_names, epochs=15)# 打印最终结果print("\n模型比较结果:")for result in results:print(f"{result['name']}: 最终准确率 = {result['final_test_acc']:.2f}%, 训练时间 = {result['total_time']:.2f}秒")# 可视化比较visualize_comparison(results)if __name__ == "__main__":main()
分析观察:
-
准确率:ResNet18表现最佳,这得益于其残差结构能有效训练深层网络。EfficientNet-B0紧随其后,其复合缩放方法在准确率和效率间取得了平衡。MobileNetV3-Small作为轻量级模型,准确率略低但仍在合理范围。
-
训练时间:MobileNetV3-Small训练最快,适合资源受限场景。ResNet18次之,EfficientNet-B0最慢,因其更复杂的结构。
-
收敛速度:ResNet18收敛最快,前5个epoch就达到80%+准确率,得益于预训练权重和残差连接。MobileNetV3-Small初期收敛慢但后期稳定上升。
-
过拟合:所有模型在训练后期都出现轻微过拟合(训练准确率>测试准确率),但ResNet18的泛化能力最好。@浙大疏锦行