YOLOv5的yolo.py文件的parse函数注释,结合C3和BiFPN对该部分进行注释

 yolo.py的parse部分

# 结合C3和BiFPN对该部分进行注释
def parse_model(d, ch):  # model_dict, input_channels(3)
    # Parse a YOLOv5 model.yaml dictionary
    # 打印表头:from n params module arguments 等信息
    LOGGER.info(f"\n{'':>3}{'from':>18}{'n':>3}{'params':>10}  {'module':<40}{'arguments':<30}")
    # 获取yaml文件内的信息
    anchors, nc, gd, gw, act = d['anchors'], d['nc'], d['depth_multiple'], d['width_multiple'], d.get('activation')
    if act:
        Conv.default_act = eval(act)  # redefine default activation, i.e. Conv.default_act = nn.SiLU()
        LOGGER.info(f"{colorstr('activation:')} {act}")  # print
    # 计算最终输出的每层锚框个数,anchors[0]是第一行列表,它的长度是6,表示3个w - h对,即3个anchor,这里的na也应当为3
    na = (len(anchors[0]) // 2) if isinstance(anchors, list) else anchors  # number of anchors
    # na为锚框数量,nc为目标类别,5为中心点坐标、宽度和高度以及置信度
    no = na * (nc + 5)  # number of outputs = anchors * (classes + 5)
    # layers是一个用来保存经解析后输出的模型及其参数的列表,里面生成最终的网络模型
    # c2是该模块的输出通道数,仅仅是用于在该部分计算,是否传入args列表要看该模块的判断部分
    # ch是一个用来保存之前所有模块输出的channels,最开始ch[]=3,代表输入图像的RGB三个通道,ch[-1]代表着上一个模块的输出通道
    layers, save, c2 = [], [], ch[-1]  # layers, savelist, ch out
    # 解析yaml文件的f n m args,循环完yaml文件的所有f n m args
    for i, (f, n, m, args) in enumerate(d['backbone'] + d['head']):  # from, number, module, args
        m = eval(m) if isinstance(m, str) else m  # eval strings
        for j, a in enumerate(args):
            with contextlib.suppress(NameError):
                args[j] = eval(a) if isinstance(a, str) else a  # eval strings
        # round(n * gd)将结果四舍五入为最接近的整数
        # n_用于输出打印,见LOGGER.info(f'{i:>3}{str(f):>18}{n_:>3}{np:10.0f}  {t:<40}{str(args):<30}')
        # n决定模块重复次数
        n = n_ = max(round(n * gd), 1) if n > 1 else n  # depth gain
        if m in {
                Conv, GhostConv, Bottleneck, GhostBottleneck, SPP, SPPF, DWConv, MixConv2d, Focus, CrossConv,
                BottleneckCSP, C3, C3TR, C3SPP, C3Ghost, nn.ConvTranspose2d, DWConvTranspose2d, C3x}:
            # ch[f]表示从第f层获取输入通道数,args[0]是输出通道数
            c1, c2 = ch[f], args[0]
            # no是网络的最后一层,如果c2不等于最后一层,使c2 * gw是8的倍数
            if c2 != no:  # if not output
                c2 = make_divisible(c2 * gw, 8)

            # 更新args,将c1和c2嵌入至args列表,生成新的args参数,c1是输入通道数,c2是输出通道数
            # 注意,这里的c2既传入了模型中做参数,又用于后面添加至ch列表,见ch.append(c2)
            args = [c1, c2, *args[1:]]
            # 计算该模块内部模块的重复次数,如C3中的Bottleneck,重复n次
            if m in {BottleneckCSP, C3, C3TR, C3Ghost, C3x}:
                # 在args列表的下标为2的位置插入一个元素n,元素n来自上面的n = n_ = max(round(n * gd), 1) if n > 1 else n
                # 这里的n会传参至模型中做具体的参数,指模块内部模块的具体重复次数
                args.insert(2, n)  # number of repeats
                # 将n重新赋值为1,如果m in {BottleneckCSP, C3, C3TR, C3Ghost, C3x},则将n重新赋值为1
                # 这就决定了这几个模块的重复次数就是1,见下面的m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args)
                n = 1
        elif m is nn.BatchNorm2d:
            args = [ch[f]]
        # Concat是输入层的通道数之和
        elif m is Concat:
            c2 = sum(ch[x] for x in f)

        # 该部分个人添加的
        # 注意这里的c2不会更新至args,只会用于后面添加至ch列表,见ch.append(c2)
        # 注意区分这里的c2和BiFPN_Add2的c2不是同一个,但是值应该相同
        elif m in [BiFPN_Add2, BiFPN_Add3]:
            c2 = max(ch[x] for x in f)

        # TODO: channel, gw, gd
        elif m in {Detect, Segment}:
            args.append([ch[x] for x in f])
            if isinstance(args[1], int):  # number of anchors
                args[1] = [list(range(args[1] * 2))] * len(f)
            if m is Segment:
                args[3] = make_divisible(args[3] * gw, 8)
        elif m is Contract:
            c2 = ch[f] * args[0] ** 2
        elif m is Expand:
            c2 = ch[f] // args[0] ** 2
        else:
            c2 = ch[f]

        # 如果n>1,创建含有n个当前模块的Sequential容器;否则,就创建一个当前模块
        m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args)  # module
        t = str(m)[8:-2].replace('__main__.', '')  # module type 获取模块m的类型字符串,并去除一些不必要的字符。
        np = sum(x.numel() for x in m_.parameters())  # number params 计算Sequential容器中所有模块的参数总数
        m_.i, m_.f, m_.type, m_.np = i, f, t, np  # attach index, 'from' index, type, number params
        # 打印信息,其中包含n_
        LOGGER.info(f'{i:>3}{str(f):>18}{n_:>3}{np:10.0f}  {t:<40}{str(args):<30}')  # print
        # 将模型中每个模块的输出索引(f)添加到save列表中
        save.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1)  # append to savelist
        layers.append(m_)  # 将新创建的模块m_添加到layers列表中
        # 如果索引i等于0,则初始化一个空列表ch,用于保存最开始输入时的RGB三通道,与layers, save, c2 = [], [], ch[-1]呼应
        if i == 0:
            ch = []
        # 将变量c2添加到列表ch中,也就是说,除了前面的跟卷积相关的模块有把c2写入至args列表中,其余模块并未将c2写入至args,仅仅是用于将变量c2添加到列表ch中
        ch.append(c2)
    # 返回一个包含所有layers的新的Sequential容器和一个排序后的save列表
    return nn.Sequential(*layers), sorted(save)

BiFPN

# BiFPN
# 两个特征图add操作,c1=c2,c1必须与上两个特征图的通道数相同
class BiFPN_Add2(nn.Module):
    def __init__(self, c1, c2):
        super().__init__()
        # 定义一个可学习参数;创建一个全是1的张量,张量包含两个元素,张量的维度为1;自动求导
        self.w = nn.Parameter(torch.ones(2, dtype=torch.float32), requires_grad=True)
        self.epsilon = 0.0001
        self.cv = Conv(c1, c2, 1, 1)
        self.silu = nn.SiLU()

    def forward(self, x):
        w = self.w
        weight = w / (torch.sum(w, dim=0) + self.epsilon)
        x0 = self.silu(weight[0] * x[0] + weight[1] * x[1])
        return self.cv(x0)

'''
        # 该部分添加至yolo.py
        # 这里的c2不会更新至args
        # 如果用这行命令,则在类中给定c1和c2参数时,必须满足c1=c2,
        elif m in [BiFPN_Add2, BiFPN_Add3]:
            c2 = max(ch[x] for x in f)
        
        # 如果用这行命令,c2可以随意给定
        elif m in [BiFPN_Add2, BiFPN_Add3]:
            c2 = args[1]
'''

 C3

class C3(nn.Module):
    # CSP Bottleneck with 3 convolutions
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion
        super().__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c1, c_, 1, 1)
        self.cv3 = Conv(2 * c_, c2, 1)  # optional act=FReLU(c2)
        self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))  # range(n)会生成一个从0到n-1的整数序列,例如range(5)生成序列0, 1, 2, 3, 4

    def forward(self, x):
        return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), 1))

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值