知方号

知方号

[Pytorch] CIFAR

本篇文章借鉴了我的朋友Jc的报告,他是一个十分优秀的人。 本篇文章记录了第一次完整训练优化的过程

0 关于数据集

在CIFAR-10 dataset的介绍中,cifar-10数据集一共10类图片,每一类有6000张图片,加起来就是60000张图片,每张图片的尺寸是32x32,图片是彩色图,整个数据集被分为5个训练批次和1个测试批次,每一批10000张图片。测试批次包含10000张图片,是由每一类图片随机抽取出1000张组成的集合。剩下的50000张图片每一类的图片数量都是5000张,训练批次是由剩下的50000张图片打乱顺序,然后随机分成5份,所以可能某个训练批次中10个种类的图片数量不是对等的,会出现一个类的图片数量比另一类多的情况。

1数据处理 1.1 加载数据集和归一化

transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))的作用 使用Normalize()函数,将数据转换为标准正太分布,使得模型更加容易收敛,其中 mean 和 std 的3个值分表表示图像的3个通道。对于单通道的灰度图,可以写成 transforms.Normalize(mean=[0.5], std=[0.5])。

## 加载数据集归一化transform = transforms.Compose( [transforms.ToTensor(), # transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])# 66.98 transforms.Normalize(mean=[0.5, 0.5, 0.5],std=[0.5, 0.5, 0.5]) ])

PyTorch 中我们经常看到 mean = [0.485, 0.456, 0.406], std = [0.229, 0.224, 0.225] ,是从 ImageNet 数据集的数百万张图片中随机抽样计算得到的。

train_data = torchvision.datasets.CIFAR10("dataset", train=True, transform=transform, download=True)test_data = torchvision.datasets.CIFAR10("dataset", train=False, transform=transform, download=True) 1.2 把训练的数据二八分成 训练集和验证集

【深度学习】训练集、测试集和验证集的相关概念

train_size = int(0.8 * len(train_data))val_size = len(train_data) - train_sizetrain_data, val_data = torch.utils.data.random_split(train_data, [train_size, val_size] , generator=torch.Generator().manual_seed(123)) 1.3利用DataLoader来加载数据集

获得图片数据集后,我们就可以利用torch.utils.data.DataLoader将数据集包装成一个数据加载器

train_dataloader = DataLoader(train_data, shuffle=True, batch_size=64)test_dataloader = DataLoader(test_data, batch_size=64)val_dataloader = DataLoader(val_data, batch_size=64) 2 模型的搭建 2.1 神经网络模型构建

在Pytorch中,可以选择使用Sequential来进行贯序模型的搭建。分析目标神经网络的网络结构,可以得出神经网络各层的具体情况如下表所示。 值得注意的是,通道数为3的32×32的图像,在经过5×5的卷积核时,默认值的情况下输出的图像大小应该为28×28,若想图片的大小仍让为32×32,我们需要计算padding进行填补。 pytorch的conv2d卷积操作有公式可以进行计算: 使用默认的dilation=1,stride=1,计算得出padding为2,同理宽也一样。 在经过三轮的卷积、最大池化的操作后,需要 Flatten 层作为卷积层与全连接层的过渡,处理后的数据从多维变成一维,尺寸为 1×1024。最后通过两个全连接层,将输出个数从 1024 个降到 64 个再到 10 个并实现十分类的概率输出。

def __init__(self): super(Model_CIFAR10, self).__init__() self.model = nn.Sequential(OrderedDict([ ("conv1", nn.Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2)), ("maxPool1", nn.MaxPool2d(2)), ("conv2", nn.Conv2d(in_channels=32, out_channels=32, kernel_size=5, padding=2)), ("maxPool2", nn.MaxPool2d(2)), ("conv3", nn.Conv2d(in_channels=32, out_channels=64, kernel_size=5, padding=2)), ("maxPool3", nn.MaxPool2d(2)), ("Flatten", nn.Flatten()), ("Linear1", nn.Linear(1024, 64)), ("Linear2", nn.Linear(64, 10)) ]))

对于神经网络可以通俗地理解为一个函数近似器,它需要近似一个输入x到输入y的映射函数。我们要训练的网络参数其实就是在拟合这个映射函数的未知量。神经网络的训练可以分为两个步骤,一个是前向传播,另外一个是反向传播。 同时,由于该模型为贯序模型,因此可以直接使用Mudule默认的前向传播函数forward(),可以不进行forward函数的重定义。

def forward(self, x): x = self.model(x) return x

前向传播计算出了输出值(也即预测值),就可以根据输出值与目标值的差别来计算损失loss。 而反向传播就是根据损失函数loss来反方向地计算每一层的偏导数,从最后一层逐层向前去改变每一层的权重,也就是更新参数,核心就是损失函数对每一网络层的每一个参数求偏导的链式求导法则。 步骤可以概括为: 1.根据模型的输出与真实的target标签计算误差 2.调动反向传播,得到每个要更新参数的梯度 3.调动优化器,对卷积的一些参数进行调整

这里我们选用的损失函数为交叉熵损失函数 CrossEntropyLoss() ,其结合了nn.LogSoftmax() 和 nn.NLLLoss() 两个函数,在做分类(具体几类)训练的时候是非常有用的。

loss_fn = nn.CrossEntropyLoss()

损失函数上,分别对神经网络参数的常见优化器SGD(stochastic gradient descent 随机梯度下降)、和Adam(Adaptive Moment Estimation 自适应矩估计)两种优化器进行了挑选,在实际效果上,发现SGD随机梯度下降对本实验的优化更好。

# optimizer = torch.optim.Momentum(model_u.parameters(), lr=learning_rate)optimizer = torch.optim.SGD(model_u.parameters(), lr=learning_rate)

在训练过程中使用反向传播优化网络参数。 在使用pytorch的backward()函数进行计算,网络参数进行反馈,梯度是被积累而不是被替换掉,训练时每一个batch只需要更新网络参数,并不需要将上一个batch的梯度继续叠加到下一个batch。因此在每一轮batch中都需要设置optimizer.zero_grad()对梯度初始化为0,把loss关于weight的导数变为0。

outputs = model_u(imgs)loss = loss_fn(outputs, targets)optimizer.zero_grad()loss.backward()optimizer.step() 2.2 TensorBoard 模型的可视化

我们可以通过安装TensorBoard来进行神经网络结构的可视化。 (1)安装TensorBoard可视化工具

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至lizi9903@foxmail.com举报,一经查实,本站将立刻删除。