【人工智能】【Python】多层感知机应用实验

1.导入必要的库。

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
from sklearn.metrics import confusion_matrix, classification_report
import seaborn as sns
图片[1] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

2.定义数据转换并下载并加载数据集

定义将图像转为张量并标准化的转换方式,代码会自动下载Fashion-MNIST训练集和测试集并加载,同时定义10个服装类别的名称。

# 定义数据转换:将图像数据转换为张量,并标准化(使用MNIST的均值和标准差)
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))  # 单通道,均值和标准差都是0.5
])
​
# 下载并加载训练集和测试集
trainset = torchvision.datasets.FashionMNIST(root='./data', train=True,
                                            download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64,
                                          shuffle=True, num_workers=2)
​
testset = torchvision.datasets.FashionMNIST(root='./data', train=False,
                                           download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=64,
                                         shuffle=False, num_workers=2)
​
# 定义类别名称
classes = ('T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
           'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot')
图片[2] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

3.获取一批训练数据并显示图像

从训练数据加载器中获取一批数据,通过子图展示5张图像及对应类别名称,实现样本数据可视化。

# 获取一批训练数据
dataiter = iter(trainloader)
images, labels = next(dataiter)
​
# 显示图像
fig, axes = plt.subplots(1, 5, figsize=(12, 3))
for i in range(5):
    ax = axes[i]
    # 反标准化:img = (img * 0.5) + 0.5
    ax.imshow(images[i].squeeze(), cmap='gray')
    ax.set_title(classes[labels[i].item()])
    ax.axis('off')
plt.show()
图片[3] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

4.定义MLP并初始化

定义含输入层到隐藏层全连接、ReLU激活、Dropout正则化及隐藏层到输出层全连接的MLP模型,初始化模型并打印结构。

class MLP(nn.Module):
    def __init__(self, input_size, hidden_size, num_classes):
        super(MLP, self).__init__()
        # 第一个全连接层: 输入层 -> 隐藏层
        self.fc1 = nn.Linear(input_size, hidden_size)
        # 非线性激活函数 ReLU
        self.relu = nn.ReLU()
        # Dropout层,随机丢弃一部分神经元,防止过拟合
        self.dropout = nn.Dropout(0.5)
        # 第二个全连接层: 隐藏层 -> 输出层
        self.fc2 = nn.Linear(hidden_size, num_classes)
        # 注意:我们不在最后一层使用Softmax,因为CrossEntropyLoss自带Softmax
​
    def forward(self, x):
        # 将图像展平 [batch_size, 1, 28, 28] -> [batch_size, 784]
        x = x.view(-1, 28*28)
        # 前向传播
        out = self.fc1(x)
        out = self.relu(out)
        out = self.dropout(out)  # 只在训练时起作用
        out = self.fc2(out)
        return out
​
# 尝试使用GPU设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(f"使用设备: {device}")
​
# 初始化模型并移动到指定设备
input_size = 784  # 28x28
hidden_size = 256  # 隐藏层神经元数量,可调整
num_classes = 10
model = MLP(input_size, hidden_size, num_classes).to(device)
​
# 打印模型结构
print(model)
图片[4] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

和源代码不同的是,我这里使用GPU设备进行训练。

5.损失函数和优化器

选择交叉熵损失函数(适配多分类)和Adam优化器(设置学习率0.001)用于模型训练。

# 损失函数:交叉熵损失,适用于多分类问题
criterion = nn.CrossEntropyLoss()
​
# 优化器:Adam,一种自适应学习率的优化算法,通常效果比SGD更好
optimizer = optim.Adam(model.parameters(), lr=0.001)
图片[5] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

6.进行训练并绘制曲线

设置训练轮次为10,每轮将模型设为训练模式,遍历训练数据,完成前向传播、损失计算、反向传播及参数优化,统计每轮损失和准确率并绘制训练曲线。

num_epochs = 10
train_losses = []
train_accuracies = []
​
for epoch in range(num_epochs):
    # 设置为训练模式(启用Dropout)
    model.train()
    running_loss = 0.0
    correct = 0
    total = 0
​
    for i, (images, labels) in enumerate(trainloader):
        # 将数据移动到CUDA设备
        images = images.to(device)
        labels = labels.to(device)
​
        # 前向传播
        outputs = model(images)
        loss = criterion(outputs, labels)
​
        # 反向传播和优化
        optimizer.zero_grad()  # 清空梯度
        loss.backward()        # 反向传播,计算梯度
        optimizer.step()       # 更新参数
​
        # 统计损失和准确率
        running_loss += loss.item()
        _, predicted = torch.max(outputs.data, 1)  # 获取预测类别
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
​
    # 计算每个epoch的平均损失和准确率
    epoch_loss = running_loss / len(trainloader)
    epoch_acc = 100 * correct / total
    train_losses.append(epoch_loss)
    train_accuracies.append(epoch_acc)
​
    print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {epoch_loss:.4f}, Accuracy: {epoch_acc:.2f}%')
​
# 绘制训练过程曲线
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
ax1.plot(train_losses)
ax1.set_title('Training Loss')
ax1.set_xlabel('Epoch')
ax1.set_ylabel('Loss')
​
ax2.plot(train_accuracies)
ax2.set_title('Training Accuracy')
ax2.set_xlabel('Epoch')
ax2.set_ylabel('Accuracy (%)')
plt.show()

训练轮数为10时,Train Acc大概在87%,然后我又尝试了epochs改为20。最高Train Acc到了89.34%。改为40,最高Train Acc 91.50%

图片[6] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本
图片[7] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

7.模型评估

将模型设为评估模式,关闭梯度计算,遍历测试数据完成预测,统计测试准确率,生成分类报告和混淆矩阵并可视化。

# 设置为评估模式(禁用Dropout)
model.eval()
all_labels = []
all_preds = []
​
with torch.no_grad():  # 关闭梯度计算
    correct = 0
    total = 0
    for images, labels in testloader:
        # 将测试数据也移动到CUDA设备
        images = images.to(device)
        labels = labels.to(device)
​
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
​
        # 收集所有预测和标签以生成详细报告(需要移回CPU)
        all_labels.extend(labels.cpu().numpy())
        all_preds.extend(predicted.cpu().numpy())
​
test_accuracy = 100 * correct / total
print(f'Test Accuracy of the model on the 10000 test images: {test_accuracy:.2f}%')
​
# 生成并打印分类报告
print("\nClassification Report:")
print(classification_report(all_labels, all_preds, target_names=classes))
​
# 生成混淆矩阵
cm = confusion_matrix(all_labels, all_preds)
plt.figure(figsize=(10, 8))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=classes, yticklabels=classes)
plt.title('Confusion Matrix')
plt.ylabel('True Label')
plt.xlabel('Predicted Label')
plt.xticks(rotation=45)
plt.yticks(rotation=0)
plt.show()
图片[8] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

上图是epochs为20时的测试结果,下图是epochs为40时的测试结果。Test Acc从88.41%提升到了89.01%

图片[9] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

8.模型测试

从测试数据加载器中获取一批数据,用训练好的模型预测,通过子图展示 10 张图像的真实类别与预测类别(正确为绿色、错误为红色)。

# 获取一批测试数据
dataiter = iter(testloader)
images, labels = next(dataiter)
​
# 将数据移动到CUDA设备进行预测
images_cuda = images.to(device)
outputs = model(images_cuda)
_, preds = torch.max(outputs, 1)
​
# 显示图像及预测结果
fig, axes = plt.subplots(2, 5, figsize=(15, 6))
for i, ax in enumerate(axes.flat):
    ax.imshow(images[i].squeeze(), cmap='gray')  # 使用原始CPU上的图像进行显示
    ax.set_title(f'True: {classes[labels[i]]}\nPred: {classes[preds[i].item()]}',
                 color=('green' if preds[i] == labels[i].to(device) else 'red'))
    ax.axis('off')
plt.tight_layout()
plt.show()
图片[10] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

9.观察训练曲线

图片[11] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本
图片[12] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

可以看到Train Loss在随着轮数增加在逐步减小,前期(前 10 轮)下降陡峭,后期(20 轮后)下降速率放缓并伴随震荡。波动可能源于优化器梯度更新的随机性(Adam 自适应调整学习率时的参数震荡),以及 Dropout 层的正则化影响(训练时随机失活神经元,导致每轮前向传播的网络结构略有差异)。曲线未完全平稳,说明 40 轮后模型仍有学习潜力。

然后Train Acc轮数增加在抖动上升。最终在第20轮达到最高89.65%的Train Acc。之后设置为40轮时,Train Acc最高达到了91.50%,但与测试集存在约2.5% 的差距,结合 Dropout 的使用,说明过拟合风险低,差距主要源于 MLP 对图像空间特征的提取能力不足(相较于 CNN,全连接层可能丢失局部结构信息)。

10.记录测试准确率

图片[13] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

上图是epochs为20时的测试结果,下图是epochs为40时的测试结果。Test Acc从88.41%提升到了89.01%

图片[14] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

测试集准确率为89.01%,处于Fashion-MNIST数据集上MLP 模型的合理区间(85%–90%),验证了模型结构有效性。对比训练准确率,差距约 2.5%,反映了模型复杂度依旧不足,因为MLP 无法充分捕捉图像的空间关联性,如边缘、纹理的局部模式。

11.分析混淆矩阵

图片[15] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

上图是epochs为20时的测试结果,下图是epochs为40时的测试结果。Test Acc从88.41%提升到了89.01%,混淆矩阵中对角线上的数值明显增加。

图片[16] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

12.其他调整

除了上面对epochs的两次调整外,下面对学习率和优化器进行调整和替换。均在epochs=20下进行。

初始lr=0.001,修改为lr=0.01,可见Train Acc一直在70%左右波动,最终是71.64%。学习率过大无法拟合特征。观察下方图像也可以看出,振幅明显增大且无拟合趋势。

图片[17] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

初始lr=0.001,修改为lr=0.0001(减小到原来的1/10),训练曲线如下:

图片[18] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

可见学习率减少到原来的1/10后,训练Loss和Acc曲线都更加稳定,但Train Acc变化也较之前慢。

尽管Adam等自适应学习率算法收敛快,但是有研究表明传统的SGD(随机梯度下降)配合动量(Momentum)在训练后期,往往能找到一个更平坦的损失函数区域,从而获得更好的泛化能力。调整Adam优化器为SGD + Momentum,这时代码为。

# 损失函数:交叉熵损失,适用于多分类问题
criterion = nn.CrossEntropyLoss()
​
# 优化器:SGD + Momentum,在某些情况下泛化能力比Adam更好
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)

使用此优化器策略训练模型20轮,训练结果如下,

图片[19] - AI科研 编程 读书笔记 - 【人工智能】【Python】多层感知机应用实验 - AI科研 编程 读书笔记 - 小竹の笔记本

可以看到Loss和Acc曲线都比之前Adam优化器更加平稳和稳定。

© 版权声明
THE END
点赞14 分享
相关推荐
评论 抢沙发

请登录后发表评论

    暂无评论内容