P66页作业

点击查看代码
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import matplotlib.pyplot as plt

# 数据预处理:标准化+数据增强(仅用于训练集)
transform_train = transforms.Compose([
    transforms.RandomCrop(32, padding=4),  # 随机裁剪
    transforms.RandomHorizontalFlip(),  # 随机水平翻转
    transforms.ToTensor(),  # 转为Tensor
    transforms.Normalize((0.4914, 0.4822, 0.4465),  # CIFAR-10均值
                         (0.2023, 0.1994, 0.2010))  # CIFAR-10标准差
])

transform_test = 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=transform_train
)
test_dataset = datasets.CIFAR10(
    root='./data', train=False, download=True, transform=transform_test
)

# 构建数据加载器
train_loader = DataLoader(
    train_dataset, batch_size=128, shuffle=True, num_workers=2
)
test_loader = DataLoader(
    test_dataset, batch_size=128, shuffle=False, num_workers=2
)

# 类别名称
classes = ('plane', 'car', 'bird', 'cat', 'deer',
           'dog', 'frog', 'horse', 'ship', 'truck')


class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        # 卷积层:3输入通道(RGB),32输出通道,5x5卷积核
        self.conv1 = nn.Conv2d(3, 32, 5, padding=2)
        # 池化层:2x2最大池化
        self.pool = nn.MaxPool2d(2, 2)
        # 卷积层:32输入通道,64输出通道
        self.conv2 = nn.Conv2d(32, 64, 5, padding=2)
        # 全连接层:展平后连接1024神经元
        self.fc1 = nn.Linear(64 * 8 * 8, 1024)  # 32/2/2=8(两次池化)
        # 全连接层:输出10类(CIFAR-10)
        self.fc2 = nn.Linear(1024, 10)
        # ReLU激活函数
        self.relu = nn.ReLU()

    def forward(self, x):
        x = self.pool(self.relu(self.conv1(x)))  # 卷积+激活+池化
        x = self.pool(self.relu(self.conv2(x)))
        x = x.view(-1, 64 * 8 * 8)  # 展平特征图
        x = self.relu(self.fc1(x))
        x = self.fc2(x)
        return x


# 初始化模型并移动到GPU(如果可用)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model = SimpleCNN().to(device)

# 交叉熵损失(适用于分类任务)
criterion = nn.CrossEntropyLoss()
# Adam优化器(学习率0.001)
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练参数
epochs = 20  # 可根据需要调整

# 记录训练过程
train_losses = []
train_accs = []
test_accs = []

for epoch in range(epochs):
    model.train()  # 训练模式
    running_loss = 0.0
    correct = 0
    total = 0

    for i, data in enumerate(train_loader, 0):
        # 获取输入数据和标签
        inputs, labels = data[0].to(device), data[1].to(device)

        # 清零梯度
        optimizer.zero_grad()

        # 前向传播
        outputs = model(inputs)
        loss = criterion(outputs, labels)

        # 反向传播+参数更新
        loss.backward()
        optimizer.step()

        # 统计损失和准确率
        running_loss += loss.item()
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

        # 每100批次打印一次信息
        if i % 100 == 99:
            print(f'[{epoch + 1}, {i + 1}] loss: {running_loss / 100:.3f}')
            running_loss = 0.0

    # 计算训练集准确率
    train_acc = 100 * correct / total
    train_losses.append(running_loss / len(train_loader))
    train_accs.append(train_acc)

    # 测试集评估
    model.eval()  # 评估模式
    correct = 0
    total = 0
    with torch.no_grad():  # 关闭梯度计算
        for data in test_loader:
            images, labels = data[0].to(device), data[1].to(device)
            outputs = model(images)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()

    test_acc = 100 * correct / total
    test_accs.append(test_acc)
    print(f'Epoch {epoch + 1} - 训练准确率: {train_acc:.2f}%  测试准确率: {test_acc:.2f}%')

print('训练完成')

# 1. 测试集整体精度
model.eval()
correct = 0
total = 0
with torch.no_grad():
    for data in test_loader:
        images, labels = data[0].to(device), data[1].to(device)
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print(f'测试集整体准确率: {100 * correct / total:.2f}%')

# 2. 各类别精度
class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():
    for data in test_loader:
        images, labels = data[0].to(device), data[1].to(device)
        outputs = model(images)
        _, predicted = torch.max(outputs, 1)
        c = (predicted == labels).squeeze()
        for label, prediction in zip(labels, c):
            class_correct[label] += prediction.item()
            class_total[label] += 1

for i in range(10):
    print(f'类别 {classes[i]} 的准确率: {100 * class_correct[i] / class_total[i]:.2f}%')

plt.figure(figsize=(12, 4))

# 损失曲线
plt.subplot(1, 2, 1)
plt.plot(train_losses, label='训练损失')
plt.title('训练损失曲线')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

# 准确率曲线
plt.subplot(1, 2, 2)
plt.plot(train_accs, label='训练准确率')
plt.plot(test_accs, label='测试准确率')
plt.title('准确率曲线')
plt.xlabel('Epoch')
plt.ylabel('准确率 (%)')
plt.legend()

plt.tight_layout()
plt.show()

image

posted @ 2025-10-16 17:11  四季歌镜  阅读(9)  评论(0)    收藏  举报