一、数据预处理

数据是机器学习的基石,对数据进行恰当的预处理至关重要。在本次实验中,我们使用了 PyTorch 提供的 torchvision.datasets.CIFAR10 来加载数据集。CIFAR-10 数据集包含 60,000 张 32×32 的彩色图像,分为 10 个类别,每个类别有 6,000 张图像。其中,训练集包含 50,000 张图像,测试集包含 10,000 张图像。

为了提高模型的泛化能力,我们对数据进行了标准化处理。具体来说,我们使用了 torchvision.transforms 模块中的 Compose 函数,将图像转换为张量(ToTensor),并对其进行了标准化(Normalize)。标准化的均值和标准差分别为 (0.5, 0.5, 0.5),这有助于将像素值归一化到 [-1, 1] 的范围内,从而加速模型的收敛。

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

通过 DataLoader,我们可以方便地加载和迭代数据集,设置批量大小为 4,并在训练时对数据进行随机打乱,以增加模型的鲁棒性。

二、模型构建

接下来,我们构建了一个简单的卷积神经网络(CNN)。CNN 在图像处理任务中表现出色,因为它能够自动提取图像的局部特征。我们的网络结构如下:

  • 第一层:包含 16 个卷积核,卷积核大小为 5×5,步长为 1,之后接一个最大池化层,池化窗口大小为 2×2。

  • 第二层:包含 36 个卷积核,卷积核大小为 3×3,步长为 1,之后接一个最大池化层,池化窗口大小为 2×2。

  • 全连接层:将卷积层的输出展平后,先经过一个包含 128 个神经元的全连接层,再经过一个包含 10 个神经元的全连接层,输出类别概率。

class CNNNet(nn.Module):
    def __init__(self):
        super(CNNNet, self).__init__()
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=5, stride=1)
        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.conv2 = nn.Conv2d(in_channels=16, out_channels=36, kernel_size=3, stride=1)
        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.fc1 = nn.Linear(1296, 128)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x):
        x = self.pool1(F.relu(self.conv1(x)))
        x = self.pool2(F.relu(self.conv2(x)))
        x = x.view(-1, 36 * 6 * 6)
        x = F.relu(self.fc2(F.relu(self.fc1(x))))
        return x

三、训练过程

在训练过程中,我们使用了交叉熵损失函数(CrossEntropyLoss)和随机梯度下降优化器(SGD)。学习率设置为 0.001,并引入了动量(momentum=0.9)来加速收敛并减少震荡。

训练过程持续了 10 个 epoch,每个 epoch 包含约 12,500 个 mini-batch。在每个 mini-batch 中,我们计算损失值并进行反向传播,更新模型的权重。通过打印每 2,000 个 mini-batch 的损失值,我们可以观察到模型在训练过程中的收敛情况。

for epoch in range(10):
    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        inputs, labels = data
        inputs, labels = inputs.to(device), labels.to(device)

        optimizer.zero_grad()

        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        if i % 2000 == 1999:
            print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0

从输出的损失值来看,模型的损失在逐渐降低,这表明模型正在学习数据中的模式。

四、测试与评估

训练完成后,我们使用测试集对模型的性能进行了评估。测试集包含了模型未曾见过的数据,因此可以很好地衡量模型的泛化能力。

我们定义了一个函数 imshow 来显示图像,并打印出图像的真实标签。通过观察模型的预测结果与真实标签的对比,我们可以直观地了解模型的分类效果。

def imshow(img):
    img = img / 2 + 0.5
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

datatier = iter(testloader)
images, labels = next(datatier)
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

此外,我们还可以计算模型在测试集上的准确率,以更全面地评估模型的性能。

五、总结

我们可以尝试以下几种方法来进一步优化模型性能:

  1. 数据增强:通过对训练数据进行旋转、裁剪、翻转等操作,增加数据的多样性,提高模型的泛化能力。

  2. 模型优化:尝试更复杂的网络结构,如更深的网络、添加批量归一化(Batch Normalization)等。

  3. 超参数调整:调整学习率、优化器、批量大小等超参数,寻找更优的训练配置。

  4. 正则化技术:引入 Dropout、L2 正则化等技术,防止模型过拟合。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐