第P2周 学习笔记 CIFAR10彩色图片识别
本文为365天深度学习训练营 中的学习记录博客 原作者K同学啊语言环境Python3.12编译器jupyter notebook深度学习环境Pytorch一、 前期准备1. 设置GPU如果设备上支持GPU就使用GPU,否则使用CPUimporttorchimporttorch.nnasnnimportmatplotlib.pyplotaspltimporttorchvision devicetorch.device(cudaiftorch.cuda.is_available()elsecpu)device输出2. 导入数据使用dataset下载CIFAR10数据集并划分好训练集与测试集使用dataloader加载数据并设置好基本的batch_sizetrain_dstorchvision.datasets.CIFAR10(data,trainTrue,transformtorchvision.transforms.ToTensor(),# 将数据类型转化为TensordownloadTrue)test_dstorchvision.datasets.CIFAR10(data,trainFalse,transformtorchvision.transforms.ToTensor(),# 将数据类型转化为TensordownloadTrue)输出batch_size32train_dltorch.utils.data.DataLoader(train_ds,batch_sizebatch_size,shuffleTrue)test_dltorch.utils.data.DataLoader(test_ds,batch_sizebatch_size)# 取一个批次查看数据格式# 数据的shape为[batch_size, channel, height, weight]# 其中batch_size为自己设定channelheight和weight分别是图片的通道数高度和宽度。imgs,labelsnext(iter(train_dl))imgs.shape输出3. 数据可视化transpose((1, 2, 0))详解作用是对NumPy数组进行轴变换transpose函数的参数是一个元组定义了新轴的顺序。原始PyTorch张量通常是以(C, H, W)的格式存储的其中C是通道数例如RGB图像有3个通道。H是图像的高度。W是图像的宽度。transpose((1, 2, 0))将轴的顺序从(C, H, W)转换为(H, W, C)这使得数据格式更适合可视化和处理。即0, 1, 2–(1, 2, 0)。importnumpyasnp# 指定图片大小图像大小为20宽、5高的绘图(单位为英寸inch)plt.figure(figsize(20,5))fori,imgsinenumerate(imgs[:20]):# 进行轴变换npimgimgs.numpy().transpose((1,2,0))# 将整个figure分成2行10列绘制第i1个子图。plt.subplot(2,10,i1)plt.imshow(npimg,cmapplt.cm.binary)plt.axis(off)#plt.show() 如果你使用的是Pycharm编译器请加上这行代码输出二、构建简单的CNN网络对于一般的CNN网络来说都是由特征提取网络和分类网络构成其中特征提取网络用于提取图片的特征分类网络用于将图片进行分类。torch.nn.Conv2d()详解函数原型torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride1, padding0, dilation1, groups1, biasTrue, padding_mode‘zeros’, deviceNone, dtypeNone)关键参数说明in_channels( int ) – 输入图像中的通道数out_channels( int ) – 卷积产生的通道数kernel_size( int or tuple ) – 卷积核的大小stride( int or tuple , optional ) – 卷积的步幅。默认值1padding( int , tuple或str , optional ) – 添加到输入的所有四个边的填充。默认值0dilation(int or tuple, optional) - 扩张操作控制kernel点卷积核点的间距默认值:1。groupsint可选将输入通道分组成多个子组每个子组使用一组卷积核来处理。默认值为 1表示不进行分组卷积。padding_mode(字符串,可选) – ‘zeros’, ‘reflect’, ‘replicate’或’circular’. 默认‘zeros’关于dilation参数图解torch.nn.Linear()详解函数原型torch.nn.Linear(in_features, out_features, biasTrue, deviceNone, dtypeNone)关键参数说明in_features每个输入样本的大小out_features每个输出样本的大小torch.nn.MaxPool2d()详解函数原型torch.nn.MaxPool2d(kernel_size, strideNone, padding0, dilation1, return_indicesFalse, ceil_modeFalse)关键参数说明kernel_size最大的窗口大小stride窗口的步幅默认值为kernel_sizepadding填充值默认为0dilation控制窗口中元素步幅的参数关于卷积层、池化层的计算**下面的网络数据shape变化过程为3, 32, 32输入数据-64, 30, 30经过卷积层1-64, 15, 15经过池化层1-64, 13, 13经过卷积层2-64, 6, 6经过池化层2-128, 4, 4经过卷积层3 -128, 2, 2经过池化层3-512-256-num_classes(10)网络结构图importtorch.nn.functionalasF num_classes10# 图片的类别数classModel(nn.Module):def__init__(self):super().__init__()# 特征提取网络self.conv1nn.Conv2d(3,64,kernel_size3)# 第一层卷积,卷积核大小为3*3self.pool1nn.MaxPool2d(kernel_size2)# 设置池化层池化核大小为2*2self.conv2nn.Conv2d(64,64,kernel_size3)# 第二层卷积,卷积核大小为3*3self.pool2nn.MaxPool2d(kernel_size2)self.conv3nn.Conv2d(64,128,kernel_size3)# 第二层卷积,卷积核大小为3*3self.pool3nn.MaxPool2d(kernel_size2)# 分类网络self.fc1nn.Linear(512,256)self.fc2nn.Linear(256,num_classes)# 前向传播defforward(self,x):xself.pool1(F.relu(self.conv1(x)))xself.pool2(F.relu(self.conv2(x)))xself.pool3(F.relu(self.conv3(x)))xtorch.flatten(x,start_dim1)xF.relu(self.fc1(x))xself.fc2(x)returnx加载并打印模型fromtorchinfoimportsummary# 将模型转移到GPU中我们模型运行均在GPU中进行modelModel().to(device)summary(model)输出三、 训练模型1. 设置超参数loss_fnnn.CrossEntropyLoss()# 创建损失函数learn_rate1e-2# 学习率opttorch.optim.SGD(model.parameters(),lrlearn_rate)2. 编写训练函数在使用 PyTorch 训练神经网络时optimizer.zero_grad()、loss.backward()和optimizer.step()是训练过程中三步关键操作分别负责清零梯度、反向传播、更新权重。optimizer.zero_grad()清空模型参数的梯度缓存。在 PyTorch 中梯度是累加的。如果不手动清除上一个 batch 的梯度会和当前 batch 的梯度叠加导致参数更新错误。loss.backward()执行反向传播自动计算每个参数的梯度。会自动计算所有可学习参数对损失函数的导数梯度这些梯度存储在每个参数的.grad属性中用于后续优化器更新参数。optimizer.step()根据当前计算得到的梯度更新模型的参数即进行一次优化步骤调用的是优化器中定义的算法比如 SGD、Adam 等它会对每个参数执行如下操作以 SGD 为例param.dataparam.data-learning_rate*param.grad# 训练循环deftrain(dataloader,model,loss_fn,optimizer):sizelen(dataloader.dataset)# 训练集的大小一共60000张图片num_batcheslen(dataloader)# 批次数目187560000/32train_loss,train_acc0,0# 初始化训练损失和正确率forX,yindataloader:# 获取图片及其标签X,yX.to(device),y.to(device)# 计算预测误差predmodel(X)# 网络输出lossloss_fn(pred,y)# 计算网络输出和真实值之间的差距targets为真实值计算二者差值即为损失# 反向传播optimizer.zero_grad()# grad属性归零loss.backward()# 反向传播optimizer.step()# 每一步自动更新# 记录acc与losstrain_acc(pred.argmax(1)y).type(torch.float).sum().item()train_lossloss.item()train_acc/size train_loss/num_batchesreturntrain_acc,train_loss3. 编写测试函数测试函数和训练函数大致相同但是由于不进行梯度下降对网络权重进行更新所以不需要传入优化器deftest(dataloader,model,loss_fn):sizelen(dataloader.dataset)# 测试集的大小一共10000张图片num_batcheslen(dataloader)# 批次数目31310000/32312.5向上取整test_loss,test_acc0,0# 当不进行训练时停止梯度更新节省计算内存消耗withtorch.no_grad():forimgs,targetindataloader:imgs,targetimgs.to(device),target.to(device)# 计算losstarget_predmodel(imgs)lossloss_fn(target_pred,target)test_lossloss.item()test_acc(target_pred.argmax(1)target).type(torch.float).sum().item()test_acc/size test_loss/num_batchesreturntest_acc,test_loss4. 正式训练1. model.train()是训练模式的设置方法调用后模型会处于训练模式。Dropout层在训练时启用随机丢弃部分神经元BatchNorm层使用当前 batch 的均值和方差进行标准化并更新其内部的运行均值和方差2. model.eval()是评估模式推理模式的设置方法调用后模型会处于评估/推理模式。Dropout层关闭不再随机丢弃神经元BatchNorm层使用训练时记录的均值和方差不再更新epochs10train_loss[]train_acc[]test_loss[]test_acc[]forepochinrange(epochs):model.train()epoch_train_acc,epoch_train_losstrain(train_dl,model,loss_fn,opt)model.eval()epoch_test_acc,epoch_test_losstest(test_dl,model,loss_fn)train_acc.append(epoch_train_acc)train_loss.append(epoch_train_loss)test_acc.append(epoch_test_acc)test_loss.append(epoch_test_loss)template(Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%Test_loss:{:.3f})print(template.format(epoch1,epoch_train_acc*100,epoch_train_loss,epoch_test_acc*100,epoch_test_loss))print(Done)输出四、 结果可视化importmatplotlib.pyplotasplt#隐藏警告importwarnings warnings.filterwarnings(ignore)#忽略警告信息plt.rcParams[font.sans-serif][SimHei]# 用来正常显示中文标签plt.rcParams[axes.unicode_minus]False# 用来正常显示负号plt.rcParams[figure.dpi]100#分辨率fromdatetimeimportdatetime current_timedatetime.now()# 获取当前时间epochs_rangerange(epochs)plt.figure(figsize(12,3))plt.subplot(1,2,1)plt.plot(epochs_range,train_acc,labelTraining Accuracy)plt.plot(epochs_range,test_acc,labelTest Accuracy)plt.legend(loclower right)plt.title(Training and Validation Accuracy)plt.xlabel(current_time)# 打卡请带上时间戳否则代码截图无效plt.subplot(1,2,2)plt.plot(epochs_range,train_loss,labelTraining Loss)plt.plot(epochs_range,test_loss,labelTest Loss)plt.legend(locupper right)plt.title(Training and Validation Loss)plt.show()五、笔记