实验四 卷积神经网络CNN

1.掌握卷积神经网络CNN的基本结构;

2.掌握数据预处理、模型构建、训练与调参;

3.探索CNN在MNIST数据集中的性能表现;

  • 实验内容

实现卷积神经网络CNN。

  • 主要实验步骤及结果

1.搭建一个CNN网络,使用MNIST手写数字数据集进行训练与测试,并体现模型最终结果,CNN网络的具体框架可参考下图,也可自己设计:

图4-1 CNN架构图

搭建了一个三层卷积的 CNN 网络,网络结构如下:

卷积层1:32 个 3×3 卷积核,Batch Normalization,ReLU 激活函数

池化层1:2×2 最大池化

卷积层2:64 个 3×3 卷积核,Batch Normalization,ReLU 激活函数

池化层2:2×2 最大池化

卷积层3:128 个 3×3 卷积核,Batch Normalization,ReLU 激活函数

池化层3:2×2 最大池化

全连接层 1:256 个神经元,LeakyReLU 激活函数,Dropout(0.5)

全连接层 2:128 个神经元,LeakyReLU 激活函数,Dropout(0.5)

输出层:10个神经元(对应 0-9 数字分类)

以下是网络实现的代码片段:

class ImprovedCNN(nn.Module):

    def __init__(self):

        super(ImprovedCNN, self).__init__()

        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, padding=1)

        self.bn1 = nn.BatchNorm2d(32)

        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)

        self.bn2 = nn.BatchNorm2d(64)

        self.conv3 = nn.Conv2d(64, 128, kernel_size=3, padding=1)

        self.bn3 = nn.BatchNorm2d(128)

        self.pool = nn.MaxPool2d(2, 2)

        self.dropout1 = nn.Dropout(0.25)

        self.dropout2 = nn.Dropout(0.5)

        self.fc1 = nn.Linear(128 * 3 * 3, 256)

        self.fc2 = nn.Linear(256, 128)

        self.fc3 = nn.Linear(128, 10)

        self.relu = nn.ReLU()

        self.leaky_relu = nn.LeakyReLU(0.1)

    def forward(self, x):

        x = self.relu(self.bn1(self.conv1(x)))

        x = self.pool(x)

        x = self.relu(self.bn2(self.conv2(x)))

        x = self.pool(x)

        x = self.relu(self.bn3(self.conv3(x)))

        x = self.pool(x)

        x = self.dropout1(x)

        x = x.view(-1, 128 * 3 * 3)

        x = self.leaky_relu(self.fc1(x))

        x = self.dropout2(x)

        x = self.leaky_relu(self.fc2(x))

        x = self.fc3(x)

        return x

  1. 尝试使用不同的数据增强方法、优化器、损失函数、学习率、batch size和迭代次数来进行训练,记录训练过程,评估模型性能,保存最佳模型。

为了提高模型的泛化能力,我实现了多种数据增强方法,包括随机旋转、随机仿射变换、随机透视变换、颜色抖动和随机擦除等。同时,对数据进行了归一化处理,确保输入数据具有相似的分布。

以下是实现的代码片段:

def get_transforms(augment=False):

    if augment:

        return transforms.Compose([

            transforms.RandomRotation(15),

            transforms.RandomAffine(degrees=0, translate=(0.15, 0.15), scale=(0.85, 1.15)),

            transforms.RandomPerspective(distortion_scale=0.2, p=0.5),

            transforms.ColorJitter(brightness=0.2, contrast=0.2),

            transforms.ToTensor(),

            transforms.Normalize((0.1307,), (0.3081,)),

            transforms.RandomErasing(p=0.2, scale=(0.02, 0.1), value='random')

        ])

    else:

        return transforms.Compose([

            transforms.ToTensor(),

            transforms.Normalize((0.1307,), (0.3081,))

        ])

我尝试了不同的优化器、损失函数、学习率、batch size 和迭代次数来训练模型,并记录了训练过程和模型性能。

优化器:使用 AdamW 优化器,结合权重衰减防止过拟合。

损失函数:采用带有标签平滑的交叉熵损失函数,提高模型的泛化能力。

学习率调度:使用 ReduceLROnPlateau 调度器,当验证集损失停滞时降低学习率。

训练配置:

Batch Size:128

学习率:0.001

迭代次数:20

权重衰减:1e-4

训练过程中,我记录了训练损失、验证损失、训练准确率和验证准确率,并在验证集上取得最佳性能时保存模型。

以下是实现的代码片段:

# 训练模型

epochs = 20

train_losses, train_accuracies, val_losses, val_accuracies = train_model(

    model, train_loader, val_loader, criterion, optimizer, device, epochs, scheduler

)

# 保存最佳模型

if val_acc > best_val_acc:

    best_val_acc = val_acc

    save_model(model, 'best_mnist_model.pth', best_val_acc)

    print(f'New best model saved with val acc: {best_val_acc:.2f}%')

部分训练过程和模型性能截图如图4-2所示:

图4-2 部分训练过程和模型性能截图

训练完成后,我使用测试集评估模型性能,并可视化了训练过程和混淆矩阵,以便更直观地了解模型在各个数字上的分类表现。

以下是实现的代码片段:

# 评估模型

test_loss, test_acc = evaluate_model(model, test_loader, device, criterion)

print(f'Test Loss: {test_loss:.4f} | Test Acc: {test_acc:.2f}%')

# 可视化训练结果

visualize_training(train_losses, train_accuracies, val_losses, val_accuracies, test_loss, test_acc, device, model, test_loader)

混淆矩阵如图4-3所示:

图4-3 混淆矩阵

混淆矩阵是一个 10×10 的矩阵,用于展示模型对每个类别的分类效果:

行:表示实际标签(True Label)。

列:表示预测标签(Predicted Label)。

对角线:表示正确预测的样本数,数值越高表示模型对该类别的识别能力越强。

非对角线元素:表示错误预测的样本数,例如第 i 行第 j 列的元素表示实际为 i 但被预测为 j 的样本数。

通过混淆矩阵,可以直观地看出模型在哪些类别上容易混淆,有助于分析模型的弱点并进行针对性的改进。

混淆矩阵实现代码如下所示:

plt.subplot(2, 2, 3)

confusion_matrix = np.zeros((10, 10))

test_loader = main().test_loader  # 获取测试数据加载器

with torch.no_grad():

    for data, target in test_loader:

        data, target = data.to(device), target.to(device)

        output = model(data)

        _, predicted = output.max(1)

        for t, p in zip(target.view(-1), predicted.view(-1)):

            confusion_matrix[t.long(), p.long()] += 1

plt.imshow(confusion_matrix, interpolation='nearest', cmap=plt.cm.Blues)

plt.title('Confusion Matrix')

plt.colorbar()

tick_marks = np.arange(10)

plt.xticks(tick_marks, range(10))

plt.yticks(tick_marks, range(10))

plt.xlabel('Predicted')

plt.ylabel('True')

训练损失和验证损失的变化曲线如图4-4所示:

图4-4 训练损失和验证损失的变化曲线

这张图展示了随着训练轮次(Epoch)的增加,训练损失和验证损失的变化趋势。其中:

训练损失:应该随着训练轮次的增加而逐渐下降。

验证损失:开始时会下降,但如果出现过拟合,验证损失可能会在某个点后开始上升。

测试损失:用红色虚线表示,是模型在测试集上的最终损失值。

通过观察这张图,可以判断模型是否过拟合或欠拟合,以及训练是否收敛。

实现代码如下所示:

plt.subplot(2, 2, 1)

plt.plot(train_losses, label='Train Loss')

plt.plot(val_losses, label='Validation Loss')

plt.axhline(y=test_loss, color='r', linestyle='--', label='Test Loss')

plt.xlabel('Epoch')

plt.ylabel('Loss')

plt.legend()

plt.title('Training and Validation Loss')

训练准确率和验证准确率曲线如图4-5所示:

图4-5 训练准确率和验证准确率曲线

这张图展示了随着训练轮次的增加,训练准确率和验证准确率的变化趋势。其中:

训练准确率:应该随着训练轮次的增加而逐渐上升。

验证准确率:开始时会上升,但如果出现过拟合,验证准确率可能会停滞或下降。

测试准确率:用红色虚线表示,是模型在测试集上的最终准确率。

通过观察这张图,可以评估模型的学习能力和泛化能力。

实现代码如下所示:

plt.subplot(2, 2, 2)

plt.plot(train_accuracies, label='Train Accuracy')

plt.plot(val_accuracies, label='Validation Accuracy')

plt.axhline(y=test_acc, color='r', linestyle='--', label='Test Accuracy')

plt.xlabel('Epoch')

plt.ylabel('Accuracy (%)')

plt.legend()

plt.title('Training and Validation Accuracy')

3.使用画图工具将自己的学号逐个写出,使用保存的最佳模型对每个数字进行推理,比较模型对每个数字的准确率预测,也可以尝试实现一个实时识别手写数字的demo。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值