东拼西凑网站谁做的,网站为什么改版,水果 网站源码,襄樊建设网站#x1f368; 本文为[#x1f517;365天深度学习训练营]内部限免文章#xff08;版权归 *K同学啊* 所有#xff09; #x1f356; 作者#xff1a;[K同学啊] 本周任务#xff1a; ●1.请根据本文 TensorFlow 代码#xff08;训练营内部阅读#xff09;#xff0c;编写… 本文为[365天深度学习训练营]内部限免文章版权归 *K同学啊* 所有 作者[K同学啊] 本周任务 ●1.请根据本文 TensorFlow 代码训练营内部阅读编写出相应的 Pytorch 代码 ●2.了解残差结构 ●3.是否可以将残差模块融入到C3当中自由探索
思路因为本章是识别四种鸟类拿pytorch写数据集下文有下载链接结构没有划分训练集和测试集。很类似week8的咖啡豆识别因此本章思路代码参考深度学习Week8-咖啡豆识别Pytorch_牛大了2022的博客-CSDN博客
理论知识储备 深度残差网络ResNetdeep residual network在2015年由何恺明等提出因为它简单与实用并存随后很多研究都是建立在ResNet-50或者ResNet-101基础上完成的。 ResNet主要解决深度卷积网络在深度加深时候的“退化”问题。在一般的卷积神经网络中增大网络深度后带来的第一个问题就是梯度消失、爆炸这个问Szegedy提出BN后被顺利解决。BN层能对各层的输出做归一化这样梯度在反向层层传递后仍能保持大小稳定不会出现过小或过大的情况。 但是作者发现加了BN后再加大深度仍然不容易收敛其提到了第二个问题--准确率下降问题层级大到一定程度时准确率就会饱和然后迅速下降这种下降即不是梯度消失引起的也不是过拟合造成的而是由于网络过于复杂以至于光靠不加约束的放养式的训练很难达到理想的错误率。准确率下降问题不是网络结构本身的问题而是现有的训练方式不够理想造成的。当前广泛使用的训练方法无论是SGD还是RMSProp或是Adam都无法在网络深度变大后达到理论上最优的收敛结果。还可以证明只要有理想的训练方式更深的网络肯定会比较浅的网络效果要好。证明过程也很简单假设在一种网络的后面添加几层形成新的网络如果增加的层级只是对的输出做了个恒等映射identity mapping即的输出经过新增的层级变成的输出后没有发生变化这样网絡和网络的错误率就是相等的也就证明了加深后的网络不会比加深前的网络效果差。 何恺明提出了一种残差结构来实现上述恒等映射图1整个模块除了正常的卷积层输出外还有一个分支把输入直接连到输出上该分支输出和卷积的输出做算术相加得到最终的输出用公式表达就是H(x)F(x)x是输入F(x)是卷积分支的输出H(x)是整个结构的输出。可以证明F(x)分支中所有参数都是0。H(x)就是个恒等映射。残差结构人为制造了恒等映射就能让整个结构朝着恒等映射的方向去收敛确保最终的错误率不会因为深度的变大而越来越差。如果一个网络通过简单的手工设置参数值就能达到想要的结果那这种结构就很容易通过训练来收敛到该结果这是一条设计复杂的网络时通用的规则。 一、环境配置
1. 设置GPU
如果设备上支持GPU就使用GPU,否则使用CPU。尽量配置好GPU使用。
import torch
import torch.nn as nn
import torchvision.transforms as transforms
import torchvision
from torchvision import transforms, datasets
import os,PIL,pathlib,warningswarnings.filterwarnings(ignore) #忽略警告信息device torch.device(cuda if torch.cuda.is_available() else cpu)2. 导入数据
本地数据集位于./data/bird_photos/目录下。数据集下载百度网盘 请输入提取码提取码0mhm data_dir ./data/bird_photos/
data_dir pathlib.Path(data_dir)data_paths list(data_dir.glob(*))
classeNames [str(path).split(\\)[2] for path in data_paths]
print(classeNames)[Bananaquit, Black Skimmer, Black Throated Bushtiti, Cockatoo] image_count len(list(data_dir.glob(*/*)))
print(图片总数为,image_count) 图片总数为 565 图形变换输出一下用到torchvision.transforms.Compose()类有兴趣的朋友可以参考这篇博客torchvision.transforms.Compose()详解【Pytorch手册】
train_transforms transforms.Compose([transforms.Resize([224, 224]), # 将输入图片resize成统一尺寸# transforms.RandomHorizontalFlip(), # 随机水平翻转transforms.ToTensor(), # 将PIL Image或numpy.ndarray转换为tensor并归一化到[0,1]之间transforms.Normalize( # 标准化处理--转换为标准正太分布高斯分布使模型更容易收敛mean[0.485, 0.456, 0.406],std[0.229, 0.224, 0.225]) # 其中 mean[0.485,0.456,0.406]与std[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
])test_transform transforms.Compose([transforms.Resize([224, 224]), # 将输入图片resize成统一尺寸transforms.ToTensor(), # 将PIL Image或numpy.ndarray转换为tensor并归一化到[0,1]之间transforms.Normalize( # 标准化处理--转换为标准正太分布高斯分布使模型更容易收敛mean[0.485, 0.456, 0.406],std[0.229, 0.224, 0.225]) # 其中 mean[0.485,0.456,0.406]与std[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
])total_data datasets.ImageFolder(./data/bird_photos/, transformtrain_transforms)
print(total_data.class_to_idx){Bananaquit: 0, Black Skimmer: 1, Black Throated Bushtiti: 2, Cockatoo: 3} 3. 划分数据集
划分训练集和测试集.
train_size int(0.8 * len(total_data))
test_size len(total_data) - train_size
train_dataset, test_dataset torch.utils.data.random_split(total_data, [train_size, test_size])
batch_size 32
train_dl torch.utils.data.DataLoader(train_dataset,batch_sizebatch_size,shuffleTrue,num_workers0)
test_dl torch.utils.data.DataLoader(test_dataset,batch_sizebatch_size,shuffleTrue,num_workers0)
for X, y in test_dl:print(Shape of X [N, C, H, W]: , X.shape)print(Shape of y: , y.shape, y.dtype)break Shape of X [N, C, H, W]: torch.Size([32, 3, 224, 224]) Shape of y: torch.Size([32]) torch.int64 二、残差网络ResNet介绍
1. 残差网络解决了什么
残差网络是为了解决神经网络隐藏层过多时而引起的网络退化问题。退化degradation问题是指当网络隐藏层变多时网络的准确度达到饱和然后急剧退化而且这个退化不是由于过拟合引起的。 拓展 深度神经网络的“两朵乌云” ●梯度弥散/爆炸 简单来讲就是网络太深了会导致模型训练难以收敛。这个问题可以被标准初始化和中间层正规化的方法有效控制。现阶段知道这么一回事就好了 ●网络退化 随着网络深度增加网络的表现先是逐渐增加至饱和然后迅速下降这个退化不是由于过拟合引起的。 2. ResNet-50介绍
ResNet-50有两个基本的块分别名为Conv Block和Identity Block 三、构建ResNet-50网络模型
这里可以参考深度学习Week9-YOLOv5-C3模块实现Pytorch_牛大了2022的博客-CSDN博客 的构建思路虽然像week4week6也有CNN网络的构建但略微粗糙。week9这篇分成四个类构建同时用到卷积中的autopad这个函数自动补充pad这个思路我们也可以用到。
def autopad(k, pNone): # kernel, padding# Pad to sameif p is None:p k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-padreturn p
1.identity block
结合上图的结构conv2d、BN、ReLuconv2d、BN、ReLuconv2d、BN三个模块组成最后再加个relu层
class IdentityBlock(nn.Module):def __init__(self, in_channel, kernel_size, filters):super(IdentityBlock, self).__init__()filters1, filters2, filters3 filtersself.conv1 nn.Sequential(nn.Conv2d(in_channel, filters1, 1, stride1, padding0, biasFalse),nn.BatchNorm2d(filters1),nn.ReLU(True))self.conv2 nn.Sequential(nn.Conv2d(filters1, filters2, kernel_size, stride1, paddingautopad(kernel_size), biasFalse),nn.BatchNorm2d(filters2),nn.ReLU(True))self.conv3 nn.Sequential(nn.Conv2d(filters2, filters3, 1, stride1, padding0, biasFalse),nn.BatchNorm2d(filters3))self.relu nn.ReLU(True)def forward(self, x):x1 self.conv1(x)x1 self.conv2(x1)x1 self.conv3(x1)x x1 xself.relu(x)return x
2.conv block
比前者多一个conv2d、BN层
class ConvBlock(nn.Module):def __init__(self, in_channel, kernel_size, filters, stride2):super(ConvBlock, self).__init__()filters1, filters2, filters3 filtersself.conv1 nn.Sequential(nn.Conv2d(in_channel, filters1, 1, stridestride, padding0, biasFalse),nn.BatchNorm2d(filters1),nn.ReLU(True))self.conv2 nn.Sequential(nn.Conv2d(filters1, filters2, kernel_size, stride1, paddingautopad(kernel_size), biasFalse),nn.BatchNorm2d(filters2),nn.ReLU(True))self.conv3 nn.Sequential(nn.Conv2d(filters2, filters3, 1, stride1, padding0, biasFalse),nn.BatchNorm2d(filters3))self.conv4 nn.Sequential(nn.Conv2d(in_channel, filters3, 1, stridestride, padding0, biasFalse),nn.BatchNorm2d(filters3))self.relu nn.ReLU(True)def forward(self, x):x1 self.conv1(x)x1 self.conv2(x1)x1 self.conv3(x1)x2 self.conv4(x)x x1 x2self.relu(x)return x
3.ResNet50
注意def forward上面一行的 4 是识别种类的数目
class ResNet50(nn.Module):def __init__(self, classes1000):super(ResNet50, self).__init__()self.conv1 nn.Sequential(nn.Conv2d(3, 64, 7, stride2, padding3, biasFalse, padding_modezeros),nn.BatchNorm2d(64),nn.ReLU(),nn.MaxPool2d(kernel_size3, stride2, padding0))self.conv2 nn.Sequential(ConvBlock(64, 3, [64, 64, 256], stride1),IdentityBlock(256, 3, [64, 64, 256]),IdentityBlock(256, 3, [64, 64, 256]))self.conv3 nn.Sequential(ConvBlock(256, 3, [128, 128, 512]),IdentityBlock(512, 3, [128, 128, 512]),IdentityBlock(512, 3, [128, 128, 512]),IdentityBlock(512, 3, [128, 128, 512]))self.conv4 nn.Sequential(ConvBlock(512, 3, [256, 256, 1024]),IdentityBlock(1024, 3, [256, 256, 1024]),IdentityBlock(1024, 3, [256, 256, 1024]),IdentityBlock(1024, 3, [256, 256, 1024]),IdentityBlock(1024, 3, [256, 256, 1024]),IdentityBlock(1024, 3, [256, 256, 1024]))self.conv5 nn.Sequential(ConvBlock(1024, 3, [512, 512, 2048]),IdentityBlock(2048, 3, [512, 512, 2048]),IdentityBlock(2048, 3, [512, 512, 2048]))self.pool nn.AvgPool2d(kernel_size7, stride7, padding0)self.fc nn.Linear(2048, 4)#4是识别种类的数目def forward(self, x):x self.conv1(x)x self.conv2(x)x self.conv3(x)x self.conv4(x)x self.conv5(x)x self.pool(x)x torch.flatten(x, start_dim1)x self.fc(x)return x
4. 查看模型详情
打印下模型
model ResNet50().to(device)
print(model) ResNet50( (conv1): Sequential( (0): Conv2d(3, 64, kernel_size(7, 7), stride(2, 2), padding(3, 3), biasFalse) (1): BatchNorm2d(64, eps1e-05, momentum0.1, affineTrue, track_running_statsTrue) (2): ReLU() (3): MaxPool2d(kernel_size3, stride2, padding0, dilation1, ceil_modeFalse) ) (conv2): Sequential(……………… 统计模型参数量以及其他指标
import torchsummary as summary
summary.summary(model, (3, 224, 224)) 四、训练与运行
1. 编写训练和测试函数
两个基本上不怎么变。训练部分代码和之前cnn网络一样
# 训练循环
def train(dataloader, model, loss_fn, optimizer):size len(dataloader.dataset) # 训练集的大小num_batches len(dataloader) # 批次数目, (size/batch_size向上取整)train_loss, train_acc 0, 0 # 初始化训练损失和正确率for X, y in dataloader: # 获取图片及其标签X, y X.to(device), y.to(device)# 计算预测误差pred model(X) # 网络输出loss loss_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_loss loss.item()train_acc / sizetrain_loss / num_batchesreturn train_acc, train_loss
训练函数和测试函数差别不大但是由于不进行梯度下降对网络权重进行更新所以不用优化器
所以测试函数代码部分和之前几周一样
def test (dataloader, model, loss_fn):size len(dataloader.dataset) # 测试集的大小num_batches len(dataloader) # 批次数目test_loss, test_acc 0, 0# 当不进行训练时停止梯度更新节省计算内存消耗with torch.no_grad():for imgs, target in dataloader:imgs, target imgs.to(device), target.to(device)# 计算losstarget_pred model(imgs)loss loss_fn(target_pred, target)test_loss loss.item()test_acc (target_pred.argmax(1) target).type(torch.float).sum().item()test_acc / sizetest_loss / num_batchesreturn test_acc, test_loss
2.训练器的选择和训练
结合之前的实验经验使用Adam模型。按照实验要求10轮训练。记得加上4可视化后再运行。
学习率试了1e-7效果并不好所以用了1e-5设置动态学习率也许会好一点点。
import copyoptimizer torch.optim.Adam(model.parameters(), lr 1e-4)
loss_fn nn.CrossEntropyLoss() # 创建损失函数epochs 10train_loss []
train_acc []
test_loss []
test_acc []best_acc 0 # 设置一个最佳准确率作为最佳模型的判别指标for epoch in range(epochs):# 更新学习率使用自定义学习率时使用# adjust_learning_rate(optimizer, epoch, learn_rate)model.train()epoch_train_acc, epoch_train_loss train(train_dl, model, loss_fn, optimizer)# scheduler.step() # 更新学习率调用官方动态学习率接口时使用model.eval()epoch_test_acc, epoch_test_loss test(test_dl, model, loss_fn)# 保存最佳模型到 best_modelif epoch_test_acc best_acc:best_acc epoch_test_accbest_model copy.deepcopy(model)train_acc.append(epoch_train_acc)train_loss.append(epoch_train_loss)test_acc.append(epoch_test_acc)test_loss.append(epoch_test_loss)# 获取当前的学习率lr optimizer.state_dict()[param_groups][0][lr]template (Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%, Test_loss:{:.3f}, Lr:{:.2E})print(template.format(epoch 1, epoch_train_acc * 100, epoch_train_loss,epoch_test_acc * 100, epoch_test_loss, lr))# 保存最佳模型到文件中
PATH ./best_model.pth # 保存的参数文件名
torch.save(model.state_dict(), PATH)print(Done) 3.结果可视化
import matplotlib.pyplot as plt
#隐藏警告
import warnings
warnings.filterwarnings(ignore) #忽略警告信息
plt.rcParams[font.sans-serif] [SimHei] # 用来正常显示中文标签
plt.rcParams[axes.unicode_minus] False # 用来正常显示负号
plt.rcParams[figure.dpi] 100 #分辨率epochs_range range(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.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() 五、模型预测
预测时候可以把上面训练大部分注释掉。
from PIL import Imageclasses list(total_data.class_to_idx)def predict_one_image(image_path, model, transform, classes):test_img Image.open(image_path).convert(RGB)plt.imshow(test_img) # 展示预测的图片test_img transform(test_img)img test_img.to(device).unsqueeze(0)model.eval()output model(img)_,pred torch.max(output,1)pred_class classes[pred]print(f预测结果是{pred_class})# 预测训练集中的某张照片predict_one_image(image_path./data/bird_photos/Bananaquit/007.jpg,modelmodel,transformtrain_transforms,classesclasses) 模型评估
以往都是看看最后几轮得到准确率但是跳动比较大就不太好找准确率最高的一回所以我们用函数返回进行比较。
best_model.eval()
epoch_test_acc, epoch_test_loss test(test_dl, best_model, loss_fn)
print(epoch_test_acc, epoch_test_loss)
print(epoch_test_acc)