本实验属于哪门课程 | 中国海洋大学24夏《深度学习》 |
学习内容 | ResNet+ResNeXt |
一、学习目标
1、通过使用深度学习框架(如PyTorch),了解图像分类任务的基本流程,包括数据预处理、数据加载、模型构建、训练与测试等步骤。 2、学习如何在图像分类任务中使用预训练模型(如ResNet18),并掌握如何根据具体问题对预训练模型进行微调(Fine-Tuning)。 3、了解常用的损失函数(如交叉熵损失)和优化算法(如Adam优化器)的原理与使用方法,掌握训练过程中常见的调优策略(如学习率调整、数据增强等)。 4、通过完整的项目实践,从数据下载到最终预测结果的保存,积累实际操作经验,提高编程能力和问题解决能力。
二、学习内容
1.阅读论文Deep Residual Learning for Image Recognition
本文介绍了一种名为“残差学习”的深度神经网络框架,用于训练比以前使用的网络更深的网络。该方法通过将层重新定义为参考输入的学习残差函数,而不是学习未引用的函数,从而更容易地优化网络,并可以从显着增加的深度中获得更高的准确性。作者在ImageNet数据集上评估了具有最152层的残差网络,这是VGG网络8倍深度,但仍然具有更低的复杂度。使用这些残差网络的集成获得3.57%的错误率,在ImageNet测试集中排名第一。此外,作者还分析了CIFAR-10上的100和000层网络。最后,作者证明了深度表示对于许多视觉识别任务至关重要,并且仅由于他们的极深表示,他们在COCO对象检测数据集上获得了28%的相对改进。
该论文提出了深度残差学习(Deep Residual Learning)的方法,通过引入残差函数来解决深度神经网络中的退化问题。具体来说,将原始映射H(x)分解为残差函数F(x)和一个恒等映射x,即H(x)=F(x)+x。这样,网络就可以直接学习残差函数而不是整个映射,从而降低了难度并提高了性能。
该方法的主要改进在于引入了残差连接(shortcut connections),使得网络可以更加容易地学习到恒等映射。此外,还采用了批量归一化(batch normalization)和随机裁剪(scale augmentation)等技术来提高模型的鲁棒性和泛化能力。
该方法主要解决了深度神经网络中的退化问题,即随着网络层数增加,精度反而下降的现象。通过引入残差函数和残差连接,可以使网络更容易地学习到恒等映射,从而避免了退化现象的发生,并提高了网络的性能。同时,批量归一化和随机裁剪等技术也有助于提高模型的鲁棒性和泛化能力。
2.阅读论文Aggregated Residual Transformations for Deep Neural Networks
这篇论文介绍了一种新的深度神经网络架构——ResNeXt,用于图像分类任务。该网络由重复使用一个基本块组成,其中包含一组具有相同拓扑结构的变换操作。这种简单的设计使得网络变得更加模块化和易于调整,同时增加了新的维度“卡度”,即变换集合的大小,以提高分类准确性。在ImageNet-1K数据集上进行实验表明,在保持复杂性不变的情况下,增加卡度可以显著提高分类准确率,并且比增加深度或宽度更加有效。此外,作者还进行了更广泛的实验,包括在ImageNet-5K数据集上的测试以及COCO检测任务中的表现评估,结果均优于其前身ResNet。最后,作者提供了代码和模型供其他人参考。
该论文提出了一种新的神经网络模型——ResNeXt,它基于ResNet的设计思路,并在其中引入了聚合变换的概念。ResNeXt通过将简单的内积(即加权求和)转换为更通用的函数来实现聚合变换,这个函数可以是一个网络本身。与传统的“网络嵌入到网络中”的设计不同,ResNeXt的“网络嵌入到神经元中”的设计使得模型能够更好地扩展和控制复杂度。
相比于传统的ResNet,ResNeXt使用了更加灵活的架构设计,包括两个规则:如果产生相同大小的空间映射,则块具有相同的超参数;每次空间映射被下采样时,块的宽度乘以一个常数因子。这种设计大大缩小了设计空间,使得研究人员能够专注于几个关键因素。同时,ResNeXt还引入了聚合变换的概念,通过将简单的内积转换为更通用的函数来实现。
ResNeXt旨在提高神经网络的性能,特别是在处理大规模数据集时。通过引入聚合变换的概念,ResNeXt能够在不增加计算复杂度的情况下增加模型的容量,从而提高了模型的准确性。此外,ResNeXt的设计也使得模型更容易扩展和调整,这有助于解决实际应用中的问题。
3.使用LeNet网络结构参加这个猫狗大战
3.1. 数据集准备
首先,我们从远程服务器上下载了猫狗分类任务所需的训练集和测试集,并对其进行解压缩。训练集中包含2000张猫的图像和2000张狗的图像,而测试集中包含了一组待分类的图像。
! wget https://gaopursuit.oss-cn-beijing.aliyuncs.com/2021/files/train.zip ! unzip train.zip ! wget https://gaopursuit.oss-cn-beijing.aliyuncs.com/202007/dogs_cats_test.zip ! unzip dogs_cats_test.zip
3.2. 数据预处理
接下来,我们使用自定义的 Dataset
类 catdog_set
对数据进行加载和预处理。在数据加载时,根据文件名("cat"或"dog")对图像进行标注,并将图像路径和标签保存到一个列表中。为了方便模型处理,图像被缩放至128x128大小。
class catdog_set(torch.utils.data.Dataset): def __init__(self, path, transform): self.data_lst = get_data(path) self.trans = torchvision.transforms.Compose(transform) def __len__(self): return len(self.data_lst) def __getitem__(self, index): (img, cls) = self.data_lst[index] image = self.trans(Image.open(img)) label = torch.tensor(cls, dtype=torch.float32) return image, label train_loader = torch.utils.data.DataLoader( catdog_set(train_path, [transforms.Resize((128,128)), transforms.ToTensor()]), batch_size=128, shuffle=True)
3.3. 模型设计
模型使用了一个简单的卷积神经网络(CNN)。该网络包含了五个卷积层和两个全连接层,通过交替使用卷积层、ReLU激活函数和最大池化层进行特征提取。最终输出层使用Softmax函数预测图像的类别(猫或狗)。
class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(3, 6, 5) self.conv2 = nn.Conv2d(6, 16, 5) self.conv3 = nn.Conv2d(16, 32, 4) self.conv4 = nn.Conv2d(32, 32, 4) self.conv5 = nn.Conv2d(32, 32, 5) self.pool = nn.MaxPool2d(2, 2) self.fc1 = nn.Linear(32, 16) self.fc2 = nn.Linear(16, 2) def forward(self, x): x = self.pool(F.relu(self.conv1(x))) x = self.pool(F.relu(self.conv2(x))) x = self.pool(F.relu(self.conv3(x))) x = self.pool(F.relu(self.conv4(x))) x = F.relu(self.conv5(x)) x = x.view(-1, 32) x = F.relu(self.fc1(x)) x = F.softmax(self.fc2(x), dim=1) return x
3.4. 训练过程
使用交叉熵损失函数(CrossEntropyLoss
)和Adam优化器训练模型。模型在训练过程中会自动判断是否有可用的GPU资源,并使用相应的设备进行加速。训练共进行了30个epoch,每个epoch随机打乱数据顺序以增加模型的泛化能力。
net = Net().to(device) criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(net.parameters(), lr=0.001) for epoch in range(30): for i, (inputs, labels) in enumerate(train_loader): inputs, labels = inputs.to(device), labels.to(device) optimizer.zero_grad() outputs = net(inputs) loss = criterion(outputs, labels.long()) loss.backward() optimizer.step() print('Epoch: %d loss: %.6f' % (epoch + 1, loss.item())) print('Finished Training')
3.5. 模型预测
训练完成后,对测试数据集中的图像进行预测。模型对每一张图片进行分类(猫或狗),并将结果保存到一个CSV文件中。
resfile = open('res.csv', 'w') for i in range(0, 2000): img_PIL = Image.open('./test/'+str(i)+'.jpg') img_tensor = transforms.Compose([transforms.Resize((128,128)),transforms.ToTensor()])(img_PIL) img_tensor = img_tensor.reshape(-1, img_tensor.shape[0], img_tensor.shape[1], img_tensor.shape[2]) img_tensor = img_tensor.to(device) out = net(img_tensor).cpu().detach().numpy() if out[0, 0] < out[0, 1]: resfile.write(str(i)+','+str(1)+'\n') else: resfile.write(str(i)+','+str(0)+'\n') resfile.close()
4.使用ResNet网络结构参加这个猫狗大战
4.1. 数据集下载和解压
首先,下载并解压数据集,包括训练集和测试集。训练集包含2000张猫和2000张狗的图像,测试集用于对模型进行验证。
!wget https://gaopursuit.oss-cn-beijing.aliyuncs.com/2021/files/train.zip -O train.zip !wget https://gaopursuit.oss-cn-beijing.aliyuncs.com/202007/dogs_cats_test.zip -O test.zip !unzip train.zip -d ./train/ !unzip test.zip -d ./test/
4.2. 数据预处理
为了训练模型,我们定义了一个自定义数据集类 catdog_set
,并使用了PyTorch的 DataLoader
类来加载数据。数据预处理包括图像大小调整(224x224)、随机水平翻转、随机旋转以及标准化。标准化参数为ImageNet预训练模型的均值和标准差。
class catdog_set(torch.utils.data.Dataset): def __init__(self, path, transform): super(catdog_set, self).__init__() self.data_lst = get_data(path) # 获得数据列表 self.trans = transform # 使用传入的转换 def __len__(self): return len(self.data_lst) def __getitem__(self, index): (img, cls) = self.data_lst[index] image = self.trans(Image.open(img)) label = torch.tensor(cls, dtype=torch.long) return image, label # 图像预处理转换 transform = transforms.Compose([ transforms.Resize((224, 224)), # 调整到224x224大小 transforms.RandomHorizontalFlip(), # 随机水平翻转 transforms.RandomRotation(15), # 随机旋转 transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) # 使用ImageNet的均值和标准差 ])
4.3. 模型选择
在本次实验中,我们使用了预训练的ResNet18模型,修改了最后的全连接层以适应二分类问题(猫或狗)。预训练的权重帮助模型快速收敛,提升分类效果。
net = models.resnet18(pretrained=True) num_ftrs = net.fc.in_features net.fc = nn.Linear(num_ftrs, 2) net = net.to(device)
4.4. 模型训练
我们使用交叉熵损失函数 (CrossEntropyLoss
) 和 Adam 优化器来训练模型。在每个epoch中,模型会通过计算损失、反向传播和更新权重来进行学习。
criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(net.parameters(), lr=0.001) for epoch in range(30): net.train() running_loss = 0.0 for i, (inputs, labels) in enumerate(train_loader): inputs = inputs.to(device) labels = labels.to(device) optimizer.zero_grad() outputs = net(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() print(f'Epoch: {epoch + 1}, Loss: {running_loss / len(train_loader):.6f}') print('Finished Training')
4.5. 模型预测
训练完成后,使用模型对测试集进行预测。将每张图片的预测结果(猫或狗)保存到一个CSV文件中。
resfile = open('res.csv', 'w') for i in range(0, 2000): img_PIL = Image.open(f'./test/{i}.jpg') img_tensor = transform(img_PIL).unsqueeze(0) # 增加batch维度 img_tensor = img_tensor.to(device) out = net(img_tensor).cpu().detach().numpy() if out[0, 0] < out[0, 1]: resfile.write(f'{i},1\n') else: resfile.write(f'{i},0\n') resfile.close()
4.6. 总结
本项目通过使用预训练的ResNet18模型,实现了对猫狗图像的分类。相较于从头开始训练模型,预训练模型的使用显著缩短了训练时间,并提高了分类精度。未来的工作可以尝试使用更深层的网络或其他预训练模型,以进一步提升分类效果。
5.思考题
5.1. Residual Learning 的基本原理
Residual Learning(残差学习)是深度神经网络中一种常见的方法,尤其在 ResNet(残差网络)中得到了广泛应用。其基本原理是引入了“跳跃连接”(skip connections),使得网络可以直接学习输入与输出之间的残差(residual),而不是直接学习输入到输出的完整映射。这种方法有效地解决了深层神经网络在训练时容易出现的梯度消失和退化问题。
5.2. Batch Normalization 的原理,以及 BN、LN、IN 的区别
Batch Normalization(BN)是一种用于加速深度神经网络训练的正则化技术。它通过在每个小批量(mini-batch)上对激活值进行标准化,使其具有零均值和单位方差,从而减小了输入分布的变化(即内部协变量偏移)。BN 的公式如下:
给定一个小批量输入 {x1, x2,……, xm},BN 的标准化步骤为:
-
计算均值
-
计算方差
-
标准化
-
缩放和平移
Layer Normalization(LN) 和 Instance Normalization(IN) 是 Batch Normalization 的变种,主要区别在于归一化的粒度不同:
-
Batch Normalization (BN): 在小批量内的每个特征维度上进行归一化,适用于全连接层和卷积层的训练加速。
-
Layer Normalization (LN): 在一个样本的所有特征上进行归一化,适用于递归神经网络(RNN)等场景,不依赖于小批量大小。
-
Instance Normalization (IN): 在每个样本的每个通道上进行归一化,适用于风格迁移任务中的生成模型。
5.3. 分组卷积提升准确率的原因及其限制
分组卷积(Grouped Convolution)是指将输入通道分成若干组,每组进行独立的卷积操作。它最早应用于 AlexNet,后来在 MobileNet、ResNeXt 等网络结构中进一步推广。
分组卷积提升准确率的原因
-
特征分解和多样性分组卷积将输入通道分成多个组,每组独立学习特征,这样可以学到更为丰富、多样化的特征表示。
-
降低参数数量和计算复杂度分组卷积减少了卷积核的数量和计算量,从而减小了模型的参数数量,减少了过拟合的风险。
-
促进信息流动分组卷积使得更多的层级和通道之间的信息流动更快,尤其是在深度神经网络中,有助于保持梯度稳定性。
分组卷积的限制
-
分组数量的权衡分组数量过多会导致每组的通道数变少,从而每组只能学习到更少的信息特征,导致表达能力下降。极端情况下,每个组只包含一个通道,类似于独立的单一卷积操作,丧失了多通道之间的特征交互。
-
性能的瓶颈当分组数量过多时,虽然计算量减小,但并行计算的效率也可能下降,尤其是在硬件资源不足的情况下。
因此,在实际应用中,需要在准确率和计算量之间进行平衡,选择适当的分组数量。
三、程序运行结果
LeNet
ResNet
四、问题总结与体会
问题总结
-
数据预处理不充分:在训练模型之前,对数据的预处理仅限于图像的缩放和简单的数据增强(如水平翻转、旋转)。这可能导致模型在遇到复杂图像时表现不佳。更为丰富的数据增强策略(如颜色抖动、剪切等)可能会提高模型的鲁棒性。
-
模型选择的局限性:虽然ResNet18是一个有效的预训练模型,但在处理更复杂的分类任务时,可能需要更深层的网络或其他架构(如VGG、DenseNet)来提升分类性能。此外,本项目只调整了最后一层的输出,未来可以尝试冻结和解冻部分层的参数,进行更细粒度的微调。
-
训练时间较长:即使使用了预训练模型,由于数据集较大和深度网络的计算复杂度,训练过程仍然耗时较长。可以通过引入更有效的优化算法(如学习率调度器、动量优化)或减少冗余计算(如使用混合精度训练)来缩短训练时间。
-
过拟合问题:在多次epoch训练过程中,观察到训练损失持续下降,但测试数据的预测准确率提升有限,可能存在过拟合问题。这表明模型在训练数据上表现良好,但在测试数据上泛化能力不足。可以通过正则化技术(如Dropout、权重衰减)或获取更多数据来缓解过拟合。
体会
-
预训练模型的强大之处:通过使用ResNet18等预训练模型,可以显著提高模型的初始性能和收敛速度。在实际的深度学习项目中,合理利用预训练模型可以大幅度减少训练时间和计算资源的消耗。
-
数据的重要性:数据质量和数量在深度学习项目中起着关键作用。良好的数据预处理和丰富的数据增强方法能够有效提高模型的泛化能力。此外,保证训练数据的多样性有助于避免模型的过拟合。
-
训练过程的监控和调优:训练深度学习模型需要对各个环节进行持续监控和调优。包括选择合适的学习率、使用合适的损失函数、选择正确的优化算法等,这些都会直接影响模型的性能。通过不断实验和调整这些参数,可以逐步提高模型的分类准确率。
-
理论与实践结合的重要性:在深度学习的学习过程中,理论知识的学习和实践操作同样重要。通过代码实践,可以将书本中的理论知识与实际应用结合起来,加深对深度学习原理和方法的理解,培养解决实际问题的能力。
-
未来改进方向:为了进一步提高分类性能,可以尝试使用更复杂的模型(如ResNet50或EfficientNet),同时探索使用迁移学习和集成学习方法。此外,可以考虑使用更多的数据增强方法和正则化技术来提高模型的泛化能力。