前言:Hello大家好,我是小哥谈。通过下载YOLOv7源码可知,原始的yolov7.yaml文件是拆开写的,比较混乱,也不好理解,并且为后续改进增添了很多困难。基于此种情况,笔者就给大家介绍一种将yolov7.yaml文件简化的方法,并且参数量和计算量和原来是一致的,区别只是yaml文件的写法不同!~🌈
目录
🚀1.背景介绍
🚀2.封装步骤
🚀3.具体方法
💥💥步骤1:common.py文件修改
💥💥步骤2:yolo.py文件修改
💥💥步骤3:创建自定义yaml文件
💥💥步骤4:修改自定义yaml文件
💥💥步骤5:验证是否加入成功
💥💥步骤6:修改默认参数
💥💥步骤7:实际训练测试
🚀1.背景介绍
YOLOv7是一种广泛用于实时目标检测的单阶段算法,其成功的一部分可以归功于灵活的yaml配置文件。然而,随着项目的发展,配置文件可能变得庞大而复杂。在本博文中,我们将学习如何简化YOLOv7的yaml配置文件,以提高可读性和可维护性。
封装后具体的结构可以参考下列的网络结构图,图中的模块都是和yaml配置文件一一对应的。👇
改进后的代码作者已上传至“下载”中,大家学习的时候可自行下载。 👇
🚀2.封装步骤
针对YOLOv7源码封装,具体步骤如下所示:👇
步骤1:common.py文件修改
步骤2:yolo.py文件修改
步骤3:创建自定义yaml文件
步骤4:修改自定义yaml文件
步骤5:验证是否加入成功
步骤6:修改默认参数
步骤7:实际训练测试
🚀3.具体方法
💥💥步骤1:common.py文件修改
从上述网络结构图上可以看出,我们需要封装的模块分为ELAN、MP1、MP2、ELAN_H这几个模块。
在common.py中添加ELAN、MP1、MP2、ELAN-H这几个模块的代码,所要添加模块的代码如下所示,将其复制粘贴到common.py文件末尾的位置。
# 需要添加模块的代码
class ELAN(nn.Module):def __init__(self, c1, c2,e=0.5):''':param c1: 输入通道:param c2: 这里给的是中间层的输出通道:param flg: 判断是否为backbone的最后一层,因为这里的输出通道数有所改变'''super(ELAN, self).__init__()c_ = int(c1 *e)self.conv1 = Conv(c1, c_, k=1, s=1)self.conv2 = Conv(c1, c_, k=1, s=1)self.conv3 = Conv(c_, c_, k=3, s=1)self.conv4 = Conv(c_, c_, k=3, s=1)self.conv5 = Conv(c_, c_, k=3, s=1)self.conv6 = Conv(c_, c_, k=3, s=1)self.conv7 = Conv(4 * c_, c2, k=1, s=1)def forward(self, x):# 分支一输出output1 = self.conv1(x)# 分支二输出output2_1 = self.conv2(x)output2_2 = self.conv3(output2_1)output2_3 = self.conv4(output2_2)output2_4 = self.conv5(output2_3)output2_5 = self.conv6(output2_4)output_cat = torch.cat((output1, output2_1, output2_3, output2_5), dim=1)return self.conv7(output_cat)class ELAN_H(nn.Module):def __init__(self, c1, c2,e1=0.5,e2=0.25):''':param c1: 输入通道:param c2: 这里给的是中间层的输出通道:param flg: 判断是否为backbone的最后一层,因为这里的输出通道数有所改变'''super(ELAN_H, self).__init__()c_ = int(c1 * e1)c_hidden = int(c1 *e2)self.conv1 = Conv(c1, c_, k=1, s=1)self.conv2 = Conv(c1, c_, k=1, s=1)self.conv3 = Conv(c_, c_hidden, k=3, s=1)self.conv4 = Conv(c_hidden, c_hidden, k=3, s=1)self.conv5 = Conv(c_hidden, c_hidden, k=3, s=1)self.conv6 = Conv(c_hidden, c_hidden, k=3, s=1)self.conv7 = Conv(2 * c1, c2, k=1, s=1)def forward(self, x):''':param x: 输入:return:'''# 分支一输出output1 = self.conv1(x)# 分支二输出output2_1 = self.conv2(x)output2_2 = self.conv3(output2_1)output2_3 = self.conv4(output2_2)output2_4 = self.conv5(output2_3)output2_5 = self.conv6(output2_4)output_cat = torch.cat((output1, output2_1, output2_2, output2_3, output2_4, output2_5), dim=1)return self.conv7(output_cat)class MPConv(nn.Module):def __init__(self, c1, e=0.5):''':param ch_in: 输如通道:param ch_out: 这里给的是中间层的输出通道'''c_ = int(c1 * e)super(MPConv, self).__init__()# 分支一self.conv1 = nn.Sequential(nn.MaxPool2d(2, 2),Conv(c1, c_, 1, 1),)# 分支二self.conv2 = nn.Sequential(Conv(c1, c_, 1, 1),Conv(c_, c_, 3, 2),)self.cat=Concat()def forward(self, x):# 分支一output1 = self.conv1(x)# 分支二output2 = self.conv2(x)output=self.cat((output1, output2))return output
说明:♨️♨️♨️
MPConv模块包括两种结构,分别是MP1和MP2,请注意分别,关于详细差异请参考后续对yolov7.yaml文件的解析文章。
💥💥步骤2:yolo.py文件修改
修改1,在yolo.py文件中找到parse_model函数(443行左右),加入ELAN、ELAN_H。
修改2,在yolo.py文件中找到parse_model函数,然后再找到elif m is nn.BatchNorm2d:语句(493行左右),在其前面加入代码:
elif m is MPConv:if args[0] == 1:c2 = ch[f] * 2args = [ch[f], *args]
关于这两处修改,具体如下图所示:
💥💥步骤3:创建自定义yaml文件
在cfg/traning中复制yolov7.yaml,粘贴并重命名为yolov7_simplify.yaml。具体如下图所示:
💥💥步骤4:修改自定义yaml文件
根据上述YOLOv7网络结构图进行修改。修改后的完整yaml文件如下所示:
# parameters
nc: 80 # number of classes
depth_multiple: 1.0 # model depth multiple
width_multiple: 1.0 # layer channel multiple# anchors
anchors:- [12,16, 19,36, 40,28] # P3/8- [36,75, 76,55, 72,146] # P4/16- [142,110, 192,243, 459,401] # P5/32# yolov7 backbone
backbone:# [from, number, module, args][[-1, 1, Conv, [32, 3, 1]], # 0[-1, 1, Conv, [64, 3, 2]], # 1-P1/2[-1, 1, Conv, [64, 3, 1]],[-1, 1, Conv, [128, 3, 2]], # 3-P2/4[-1, 1, ELAN, [256,0.5]], # 4[-1, 1, MPConv, [0.5]],[-1, 1, ELAN, [512, 0.5]], # 6[-1, 1, MPConv, [0.5]],[-1, 1, ELAN, [1024,0.5]], # 8[-1, 1, MPConv, [0.5]],[-1, 1, ELAN, [1024,0.25]], # 10]# yolov7 head
head:[[-1, 1, SPPCSPC, [512]], # 11 54[-1, 1, Conv, [256, 1, 1]],#12[-1, 1, nn.Upsample, [None, 2, 'nearest']],[8, 1, Conv, [256, 1, 1]], # 14 route backbone P4[[-1, -2], 1, Concat, [1]], # 15[-1, 1, ELAN_H, [256,0.5,0.25]], #16[-1, 1, Conv, [128, 1, 1]], #17[-1, 1, nn.Upsample, [None, 2, 'nearest']], #18[6, 1, Conv, [128, 1, 1]], # route backbone P3 19[[-1, -2], 1, Concat, [1]], # 20[-1, 1, ELAN_H, [128,0.5,0.25]], #21[-1, 1, MPConv, [1]], # 22[[-1, 16], 1, Concat, [1]],# 23[-1, 1, ELAN_H, [256,0.5,0.25]], # 24[-1, 1, MPConv, [1]], # 25[[-1, 11], 1, Concat, [1]], # 26[-1, 1, ELAN_H, [512,0.5,0.25]], #27[21, 1, RepConv, [256, 3, 1]],[24, 1, RepConv, [512, 3, 1]],[27, 1, RepConv, [1024, 3, 1]],[[28,29,30], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)]
💥💥步骤5:验证是否加入成功
在yolo.py文件里,将配置改为我们刚才自定义的yolov7_simplify.yaml。
修改1,位置位于yolo.py文件237行左右,具体如图所示:
修改2,位置位于yolo.py文件529行左右,具体如下图所示:
注意,需要修改的代码格式如下所示:
../cfg/training/yolov7_simplify.yaml
配置完毕之后,点击“运行”,结果如下图所示:
由运行结果可知,与我们前面的封装后的YOLOv7网络结构图相一致,证明封装成功了!✅
💥💥步骤6:修改默认参数
在train.py文件中找到parse_opt函数,然后将第二行 '--cfg' 的default改为 'cfg/training/yolov7_simplify.yaml',然后就可以开始进行训练了。🎈🎈🎈
💥💥步骤7:实际训练测试
经过实际训练测试,没有发生报错,模型正常训练,具体如下图所示:
在安全帽数据集上,经过100轮实际训练测试,得到结果如下: