Dataset
PyTorch中的Dataset类是一个抽象基类,用于表示数据集。它定义了两个必须实现的方法:__len__()
和 __getitem__()
。这个基类是通用的,但它本身无法处理特定类型的数据。因此,当您需要处理特定类型的数据(例如图像、文本等)时,您需要创建一个继承自Dataset类的自定义类,并实现这两个方法,以便根据您的数据加载和处理需求来处理数据。
在您提供的代码示例中,您创建了一个名为ImageDataset的自定义数据集类,它继承自PyTorch的Dataset类。这个类实现了 __len__()
和 __getitem__()
方法,用于处理存储为NumPy格式的图像数据。通过这种方式,您可以使用自定义的数据集类来适应您的特定数据类型和数据处理需求。
总结一下,原因在于PyTorch的Dataset类是一个通用的抽象基类,无法直接处理特n定类型的数据。因此,需要创建自定义数据集类来实现针对特定数据类型的加载和处理。
网络1
1 | class Net(nn.Module): |
效果如下:
1 | Epoch 1 loss: 1.622, train acc: 0.522, test acc: 0.522 |
这种比较简单的网络结构可能存在一些缺陷:
- 模型表达能力有限:该网络的深度相对较浅,层数较少,可能无法充分提取输入数据的特征,从而导致模型表达能力不足。
- 容易出现过拟合:该网络没有使用正则化技术,如dropout等,容易在训练过程中出现过拟合问题,导致模型在测试集上表现不佳。
- 卷积核尺寸较大:该网络使用的卷积核尺寸为5,可能会导致卷积后的特征图失去一些细节信息,从而降低模型性能。
- 没有使用预训练模型:该网络是从头开始训练的,没有使用任何预训练模型,可能会导致训练时间较长,模型性能不佳。
以上是该网络可能存在的一些缺陷,可以通过调整网络结构、添加正则化技术、使用更小的卷积核等方式来提高模型性能。
简单提升CNN网络性能
增加了更多卷积层,批量标准化层和 Dropout 层来提高性能:
1 | class Net(nn.Module): |
效果如下:
1 | Epoch 1 loss: 1.625, train acc: 0.466, test acc: 0.465 |
网络2(Resnet)
拟合速度更快,准确率更高的网络是残差网络(ResNet)。
ResNet是由微软提出的深度残差网络,其主要思想是通过引入残差连接来解决网络退化问题,从而允许网络更深更广,并提高了模型准确率和泛化能力。ResNet常用的版本包括ResNet-18、ResNet-34、ResNet-50、ResNet-101和ResNet-152等。
相比于其他深度神经网络,ResNet的优点有:
- 更快的训练速度:ResNet通过残差连接解决了梯度消失问题,使得网络可以更深更宽,从而能够更好地拟合数据,提高了训练速度。
- 更好的泛化能力:残差连接允许网络跨层直接传递信息,避免了信息的损失,使得网络可以更好地学习到数据的特征,提高了模型的泛化能力。
- 更高的准确率:ResNet通过引入残差连接,使得网络可以更深更宽,提高了模型的表达能力,从而能够更好地拟合数据,提高了模型的准确率。
但是,相比于其他深度神经网络,ResNet占用更多的显存,需要更多的计算资源来训练。
1 | class BasicBlock(nn.Module): |
Resnet存在着如下几个问题
- 网络的深度限制:尽管ResNet的提出解决了深度神经网络的梯度消失问题,但是当网络的深度增加时,ResNet仍然会出现梯度消失和梯度爆炸的问题。这限制了ResNet的深度。
- 特征重复利用不充分:在ResNet中,残差块中的特征并没有充分地被重复利用。相对于DenseNet,ResNet的特征传递方式是逐级传递,即特征只在当前和下一个块之间传递,而不是在所有块之间传递。
- 训练时间较长:由于ResNet是一个非常深的网络,所以它的训练时间会比较长,特别是当训练数据集很大时。
1 | Epoch 1 loss: 1.333, train acc: 0.757, test acc: 0.734 |
网络3(DenseNet)
在DenseNet中,每个层的输出都会被连接到后续所有层的输入中,这使得每个层都可以直接获取到之前所有层的特征图,从而增加了特征重用的程度,避免了特征的浪费。在DenseNet中,特征图之间的连接可以使用张量拼接(concatenate)来实现。
具体地,DenseNet可以由多个密集块(Dense Block)和一个全局池化层(Global Pooling Layer)组成。每个密集块由多个卷积层和一个批量归一化层(Batch Normalization Layer)组成,卷积层的输出将被拼接到后续所有卷积层的输入中。全局池化层的输出将被送入一个全连接层和一个Softmax层中进行分类。
DenseNet的优点包括:
- 特征重用程度高:在DenseNet中,每个层都可以直接获取到之前所有层的特征图,从而增加了特征重用的程度,避免了特征的浪费。
- 模型参数较少:在DenseNet中,由于特征图之间的连接可以使用张量拼接来实现,所以模型参数较少。
- 准确率高:DenseNet在图像分类等任务上表现出色,达到了当时最好的性能。
然而,DenseNet也有一些缺点,如模型计算量较大、模型结构复杂等。
1 | class DenseLayer(nn.Module): |
效果如下;
1 | Epoch 1 loss: 1.401, train acc: 0.684, test acc: 0.669 |
提高准确度方法:
- 调整超参数:尝试不同的学习率、批量大小、优化器和权重衰减。可以使用网格搜索或随机搜索找到最佳超参数组合。同时,可以考虑使用学习率调度器逐渐降低学习率。
- 更深或更宽的模型:尝试使用更复杂的模型,如更深或更宽的 ResNet、DenseNet 或其他现代架构。通常,更复杂的模型具有更大的表示能力,可以提高性能。
- 数据增强:使用数据增强技术,如随机旋转、翻转、缩放、剪裁和亮度调整等,可以扩展训练数据集并提高模型泛化能力。
- 正则化:使用正则化技术,如 L1 或 L2 正则化、Dropout 或 Batch Normalization,可以减轻过拟合并提高模型泛化能力。
- 更多数据:如果可能,尝试收集更多的训练数据。更多的数据有助于模型学习更多的特征,从而提高准确性。
- 早停法:在验证集上监控模型性能,当性能不再提高时,提前停止训练。这有助于防止过拟合。
- 预训练模型:使用预训练的模型作为初始模型,然后在您的数据集上进行微调。这样可以利用在大型数据集上学到的特征,加速收敛并提高性能。
- 集成方法:训练多个模型并将它们的输出结合起来。这可以是简单的平均,或者可以使用更复杂的技术,如投票或模型堆叠。这有助于提高模型的稳定性和准确性。
数据增强
与上文不同,我在加入高斯噪声的基础上加入了图像旋转变换来提高模型的泛化能力
损失函数
要改善模型的训练效果,您可以尝试使用其他损失函数。这里我使用Label Smoothing Cross Entropy损失。可以提高模型的泛化能力,因为它在训练过程中为模型提供了额外的正则化。
1 | class LabelSmoothingCrossEntropy(nn.Module): |
BATCH_SIZE
Batch size的选择对模型的训练效果和收敛速度有很大影响。然而,并没有一个固定的答案来确定最佳的batch size。
- 训练稳定性和收敛速度 :较大的batch size可以让梯度下降过程更稳定,因为每个batch的平均梯度对噪声更不敏感。然而,大的batch size可能会导致训练过程收敛速度变慢,因为每次迭代更新权重的次数减少了。相反,较小的batch size可以提高训练速度,因为每个epoch内权重更新的次数增加,但可能会导致训练过程不稳定,这是由于小batch size中噪声较多。
- 泛化能力 :有研究表明,较小的batch size可能有助于提高模型的泛化能力。这可能是因为较小的batch size在训练过程中引入了随机性和正则化,从而防止模型过拟合。
- 训练时间 :较大的batch size可以减少每个epoch所需的迭代次数,从而减少同步和数据传输的开销,提高计算资源的利用率。但是,如果batch size过大,可能会导致GPU内存不足,进而影响训练速度。