国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

YOLO系列 --- YOLOV7算法(四):YOLO V7算法網(wǎng)絡(luò)結(jié)構(gòu)解析

這篇具有很好參考價(jià)值的文章主要介紹了YOLO系列 --- YOLOV7算法(四):YOLO V7算法網(wǎng)絡(luò)結(jié)構(gòu)解析。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

YOLO系列 — YOLOV7算法(四):YOLO V7算法網(wǎng)絡(luò)結(jié)構(gòu)解析

今天來(lái)講講YOLO V7算法網(wǎng)絡(luò)結(jié)構(gòu)吧~
train.py中大概95行的地方開始創(chuàng)建網(wǎng)絡(luò),如下圖(YOLO V7下載的時(shí)間不同,可能代碼有少許的改動(dòng),所以行數(shù)跟我不一定一樣)
YOLO系列 --- YOLOV7算法(四):YOLO V7算法網(wǎng)絡(luò)結(jié)構(gòu)解析
我們進(jìn)去發(fā)現(xiàn),其實(shí)就是在yolo.py里面。后期,我們就會(huì)發(fā)現(xiàn)相關(guān)的網(wǎng)絡(luò)結(jié)構(gòu)都是在該py文件里面。這篇blog就主要講講Model這個(gè)類。


def __init__(self, cfg='yolor-csp-c.yaml', ch=3, nc=None, anchors=None): 

先來(lái)說(shuō)下,傳入的參數(shù):

  • cfg:傳入的網(wǎng)絡(luò)結(jié)構(gòu)yaml文件路徑,這里已經(jīng)默認(rèn)的是yolor-csp-c.yaml,就是說(shuō)如果你在train.py中沒(méi)有傳入網(wǎng)絡(luò)結(jié)構(gòu)yaml文件的話默認(rèn)使用yolor-csp-c.yaml這個(gè)網(wǎng)絡(luò)結(jié)構(gòu)
  • ch:預(yù)測(cè)頭的數(shù)量
  • nc:數(shù)據(jù)集類別數(shù)

        super(Model, self).__init__()
        self.traced = False
        if isinstance(cfg, dict):
            self.yaml = cfg  # model dict
        else:  # is *.yaml
            import yaml  # for torch hub
            self.yaml_file = Path(cfg).name
            with open(cfg) as f:
                self.yaml = yaml.load(f, Loader=yaml.SafeLoader)  # model dict

首先判斷傳入的yaml網(wǎng)絡(luò)結(jié)構(gòu)文件是不是字典形式的,我們一般直接就是傳入的是yaml的路徑,所以直接用yaml.load解析yaml文件,如下圖
YOLO系列 --- YOLOV7算法(四):YOLO V7算法網(wǎng)絡(luò)結(jié)構(gòu)解析


        # Define model
        ch = self.yaml['ch'] = self.yaml.get('ch', ch)  # input channels
        if nc and nc != self.yaml['nc']:
            logger.info(f"Overriding model.yaml nc={self.yaml['nc']} with nc={nc}")
            self.yaml['nc'] = nc  # override yaml value
        if anchors:
            logger.info(f'Overriding model.yaml anchors with anchors={anchors}')
            self.yaml['anchors'] = round(anchors)  # override yaml value
        self.model, self.save = parse_model(deepcopy(self.yaml), ch=[ch])  # model, savelist
        self.names = [str(i) for i in range(self.yaml['nc'])]  # default names

這段代碼值得好好看看,首先第一行代碼出現(xiàn)了兩個(gè)=號(hào),其實(shí)代表的含義就是最右邊的變量同時(shí)賦值給中間的和最左邊的。而self.yaml.get('ch', ch)就是在尋找yaml字典中是否存在ch這個(gè)key,這里是不存在的,我們可以在yolov7.yaml中找找看,里面是沒(méi)有的,如果沒(méi)有的話就直接用Model類中__init__初始化定義的ch=3,表示該模型一共有三個(gè)預(yù)測(cè)頭。
然后,判斷一下custom_data.yaml中的nc是否與yolov7.yaml中的nc是否一致,如果不一致的話就默認(rèn)將custom_data.yaml中的nc賦值給self.yaml中的nc。
接著,將yolov7.yaml中的anchors賦值給self.yaml
最后,我們將解析后的yaml字典和預(yù)測(cè)頭數(shù)量傳入parse_model函數(shù),最后一行代碼其實(shí)就沒(méi)什么的了,就是將所有類別變成[0,1,2,…]


進(jìn)入parse_model函數(shù):

    logger.info('\n%3s%18s%3s%10s  %-40s%-30s' % ('', 'from', 'n', 'params', 'module', 'arguments'))
    anchors, nc, gd, gw = d['anchors'], d['nc'], d['depth_multiple'], d['width_multiple']
    na = (len(anchors[0]) // 2) if isinstance(anchors, list) else anchors  # number of anchors
    no = na * (nc + 5)  # number of outputs = anchors * (classes + 5)

首先是從yaml字典中獲取anchor尺寸,類別數(shù),網(wǎng)絡(luò)深度,網(wǎng)絡(luò)寬度。這里,詳細(xì)說(shuō)下網(wǎng)絡(luò)深度,網(wǎng)絡(luò)寬度到底是什么意思:

我們先來(lái)看下yolov7.yaml中backbone和head中每個(gè)列表的含義:
YOLO系列 --- YOLOV7算法(四):YOLO V7算法網(wǎng)絡(luò)結(jié)構(gòu)解析
第一個(gè):表示該層是源自于上面哪一層,一般-1就表示的是上一層
第二個(gè):表示該層一共有幾個(gè),就比如說(shuō)這里有一個(gè)卷積層是2的話,那么這層就是有兩個(gè)串聯(lián)卷積層
第三個(gè):表示該層是什么模塊
第四個(gè):表示的是該層的一些參數(shù),比如說(shuō)如果該層是Conv卷積層的話,那么后面接一個(gè)【32,3,1】就表示的是輸出channel數(shù)是32,卷積核大小為3*3
介紹完每個(gè)參數(shù),我們就可以介紹什么是網(wǎng)絡(luò)深度,網(wǎng)絡(luò)寬度了:

  • 網(wǎng)絡(luò)深度:實(shí)際在構(gòu)建網(wǎng)絡(luò)模型的時(shí)候,并不是直接使用上述第二個(gè)參數(shù),即有幾個(gè)模塊層。而是用網(wǎng)絡(luò)深度去乘以第二個(gè)參數(shù),最終獲得的數(shù)量才是真正的層數(shù)量。舉個(gè)例子,此時(shí)網(wǎng)絡(luò)深度是0.33,某個(gè)層的第二個(gè)參數(shù)是3,那么實(shí)際在構(gòu)建網(wǎng)絡(luò)模型的時(shí)候只創(chuàng)建了0.33*3=1個(gè),并不是三個(gè)。
  • 網(wǎng)絡(luò)寬度:再舉個(gè)例子吧,比如此時(shí)該層是卷積層Conv,輸出channels數(shù)設(shè)置為64,但是同時(shí)網(wǎng)絡(luò)寬度設(shè)置的是0.5,那么在實(shí)際構(gòu)建網(wǎng)絡(luò)模型的時(shí)候,該層最后的輸出channels數(shù)其實(shí)是64*0.5=32。

同時(shí),在yolov7.yaml中anchors表示的是每個(gè)預(yù)測(cè)頭所對(duì)應(yīng)的anchors長(zhǎng)寬大小,如下圖(隨意畫的,能理解含義就ok了):
YOLO系列 --- YOLOV7算法(四):YOLO V7算法網(wǎng)絡(luò)結(jié)構(gòu)解析

YOLO系列 --- YOLOV7算法(四):YOLO V7算法網(wǎng)絡(luò)結(jié)構(gòu)解析
那么na就表示的是每個(gè)預(yù)測(cè)頭有幾組比例不同的anchor,no表示的是最后預(yù)測(cè)頭輸出的通道數(shù),其中5表示的是四個(gè)位置信息和置信度大小。


    layers, save, c2 = [], [], ch[-1]  # layers, savelist, ch out
    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):
            try:
                args[j] = eval(a) if isinstance(a, str) else a  # eval strings
            except:
                pass

遍歷backbone和head所有層,獲取得到每一層到底是什么模塊,然后利用eval函數(shù)進(jìn)行解析。這里簡(jiǎn)單介紹下eval函數(shù),遍歷所得到的m只是一個(gè)字符串,表示該層的類型,但是并不是該層的類,而eval函數(shù)就是實(shí)例化該層類。而args也是同理。


        n = max(round(n * gd), 1) if n > 1 else n  # depth gain
        if m in [nn.Conv2d, Conv, RobustConv, RobustConv2, DWConv, GhostConv, RepConv, RepConv_OREPA, DownC, 
                 SPP, SPPF, SPPCSPC, GhostSPPCSPC, MixConv2d, Focus, Stem, GhostStem, CrossConv, 
                 Bottleneck, BottleneckCSPA, BottleneckCSPB, BottleneckCSPC, 
                 RepBottleneck, RepBottleneckCSPA, RepBottleneckCSPB, RepBottleneckCSPC,  
                 Res, ResCSPA, ResCSPB, ResCSPC, 
                 RepRes, RepResCSPA, RepResCSPB, RepResCSPC, 
                 ResX, ResXCSPA, ResXCSPB, ResXCSPC, 
                 RepResX, RepResXCSPA, RepResXCSPB, RepResXCSPC, 
                 Ghost, GhostCSPA, GhostCSPB, GhostCSPC,
                 SwinTransformerBlock, STCSPA, STCSPB, STCSPC,
                 SwinTransformer2Block, ST2CSPA, ST2CSPB, ST2CSPC]:
            c1, c2 = ch[f], args[0]
            if c2 != no:  # if not output
                c2 = make_divisible(c2 * gw, 8)

第一行代碼就是我上述所說(shuō)的網(wǎng)絡(luò)深度,真正的層數(shù)量取決于yaml文件中的第二個(gè)參數(shù)和網(wǎng)絡(luò)深度的乘積。前提是yaml中層的數(shù)量是大于1才進(jìn)行計(jì)算。然后判斷該層的類型,這個(gè)判斷大多是判斷是不是卷積層。計(jì)算c1和c2,這兩個(gè)數(shù)字分別表示輸入channels數(shù)和輸出channels數(shù)。接著判斷遍歷的該層是不是最后一層,而判斷的標(biāo)注就是看最后輸出的channels數(shù)c2是不是等于no,no上述已經(jīng)說(shuō)過(guò)了表示最后預(yù)測(cè)頭的通道數(shù)。如果不是最后一層,那表示是網(wǎng)絡(luò)中間的層,那么就來(lái)到了上述說(shuō)的網(wǎng)絡(luò)寬度部分了。

c2 = make_divisible(c2 * gw, 8)

該層最終的輸出通道數(shù)其實(shí)就是網(wǎng)絡(luò)寬度和該層第一個(gè)參數(shù)的乘積,make_divisible函數(shù)之前已經(jīng)說(shuō)過(guò)了,作用是自動(dòng)調(diào)整為32的倍數(shù)。


            args = [c1, c2, *args[1:]]
            if m in [DownC, SPPCSPC, GhostSPPCSPC, 
                     BottleneckCSPA, BottleneckCSPB, BottleneckCSPC, 
                     RepBottleneckCSPA, RepBottleneckCSPB, RepBottleneckCSPC, 
                     ResCSPA, ResCSPB, ResCSPC, 
                     RepResCSPA, RepResCSPB, RepResCSPC, 
                     ResXCSPA, ResXCSPB, ResXCSPC, 
                     RepResXCSPA, RepResXCSPB, RepResXCSPC,
                     GhostCSPA, GhostCSPB, GhostCSPC,
                     STCSPA, STCSPB, STCSPC,
                     ST2CSPA, ST2CSPB, ST2CSPC]:
                args.insert(2, n)  # number of repeats
                n = 1

args是拼湊喂入網(wǎng)絡(luò)的參數(shù)列表, 我們可以看下models/common.pyConv類的__init__初始化函數(shù):
YOLO系列 --- YOLOV7算法(四):YOLO V7算法網(wǎng)絡(luò)結(jié)構(gòu)解析
卷積層需要輸入的參數(shù)格式為:[輸入通道數(shù),輸出通道數(shù),卷積核大小,步長(zhǎng)],正好與args = [c1, c2, *args[1:]]相對(duì)應(yīng)上了。然后進(jìn)一步判斷層是否屬于列表中這些層,因?yàn)檫@些層的參數(shù)形式與卷積不一樣。


        elif m is nn.BatchNorm2d:
            args = [ch[f]]
        elif m is Concat:
            c2 = sum([ch[x] for x in f])
        elif m is Chuncat:
            c2 = sum([ch[x] for x in f])
        elif m is Shortcut:
            c2 = ch[f[0]]
        elif m is Foldcut:
            c2 = ch[f] // 2
        elif m in [Detect, IDetect, IAuxDetect, IBin]:
            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)
        elif m is ReOrg:
            c2 = ch[f] * 4
        elif m is Contract:
            c2 = ch[f] * args[0] ** 2
        elif m is Expand:
            c2 = ch[f] // args[0] ** 2
        else:
            c2 = ch[f]

判斷如果不是卷積等操作,再看看是不是屬于下面elif中的哪個(gè)層,這里我就不多說(shuō)了,也沒(méi)啥難的。


        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
        np = sum([x.numel() for x in m_.parameters()])  # number params
        m_.i, m_.f, m_.type, m_.np = i, f, t, np  # attach index, 'from' index, type, number params
        logger.info('%3s%18s%3s%10.0f  %-40s%-30s' % (i, f, n, np, t, args))  # print
        save.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1)  # append to savelist
        layers.append(m_)
        if i == 0:
            ch = []
        ch.append(c2)
    return nn.Sequential(*layers), sorted(save)

torch.nn.Sequential是一個(gè)Sequential容器,模塊將按照構(gòu)造函數(shù)中傳遞的順序添加到模塊中。然后獲取遍歷的每一層的參數(shù)量大小。save.extend這一行代碼是遍歷所有的層,找到第一個(gè)參數(shù)不是等于-1的,也就是該層的數(shù)據(jù)不是來(lái)源于上一層,也就是意味著是來(lái)自上面的多個(gè)層,那么就要進(jìn)行保存操作。最后判斷下是不是輸入層,如果是輸入層的話,就將輸出通道數(shù)放進(jìn)ch里面去。最后返回Sequential容器和需要保存的層的索引號(hào)。


我們回到Model類中:

        # Build strides, anchors
        m = self.model[-1]  # Detect()
        if isinstance(m, Detect):
            s = 256  # 2x min stride
            m.stride = torch.tensor([s / x.shape[-2] for x in self.forward(torch.zeros(1, ch, s, s))])  # forward
            m.anchors /= m.stride.view(-1, 1, 1)
            check_anchor_order(m)
            self.stride = m.stride
            self._initialize_biases()  # only run once
            # print('Strides: %s' % m.stride.tolist())
        if isinstance(m, IDetect):
            s = 256  # 2x min stride
            m.stride = torch.tensor([s / x.shape[-2] for x in self.forward(torch.zeros(1, ch, s, s))])  # forward
            m.anchors /= m.stride.view(-1, 1, 1)
            check_anchor_order(m)
            self.stride = m.stride
            self._initialize_biases()  # only run once
            # print('Strides: %s' % m.stride.tolist())
        if isinstance(m, IAuxDetect):
            s = 256  # 2x min stride
            m.stride = torch.tensor([s / x.shape[-2] for x in self.forward(torch.zeros(1, ch, s, s))[:4]])  # forward
            #print(m.stride)
            m.anchors /= m.stride.view(-1, 1, 1)
            check_anchor_order(m)
            self.stride = m.stride
            self._initialize_aux_biases()  # only run once
            # print('Strides: %s' % m.stride.tolist())
        if isinstance(m, IBin):
            s = 256  # 2x min stride
            m.stride = torch.tensor([s / x.shape[-2] for x in self.forward(torch.zeros(1, ch, s, s))])  # forward
            m.anchors /= m.stride.view(-1, 1, 1)
            check_anchor_order(m)
            self.stride = m.stride
            self._initialize_biases_bin()  # only run once
            # print('Strides: %s' % m.stride.tolist())

得到一個(gè)Sequential容器,里面包含著所有層。我們先獲取到最后一層,一般來(lái)說(shuō),最后一層都是檢測(cè)層,就是將所有的預(yù)測(cè)頭進(jìn)行融合。判斷下是屬于哪種檢測(cè)層。
YOLO系列 --- YOLOV7算法(四):YOLO V7算法網(wǎng)絡(luò)結(jié)構(gòu)解析
我們通過(guò)yolov7.yaml文件可以看到,這里最后一層是Detect類型,那么我就進(jìn)入第一個(gè)if分支里面。我們本身是知道這個(gè)網(wǎng)絡(luò)的步長(zhǎng)是32,16和8的,但是模型是不知道的,所以我們需要喂入一個(gè)測(cè)試tensor進(jìn)行一次正向傳播來(lái)得到網(wǎng)絡(luò)的步長(zhǎng)大小。m.anchors表示的是基于原始圖片的,所以我們要相對(duì)應(yīng)的除以步長(zhǎng),得到的m.anchors才是真正的基于最后預(yù)測(cè)頭特征圖的anchors尺寸。check_anchor_order函數(shù)的作用就是檢測(cè)anchor的順序是不是正確的,在yolov7.yaml中應(yīng)該是從小到大的順序排列。


        # Init weights, biases
        initialize_weights(self)
        self.info()
        logger.info('')

最后,就是初始化一下權(quán)重大小。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-448920.html

到了這里,關(guān)于YOLO系列 --- YOLOV7算法(四):YOLO V7算法網(wǎng)絡(luò)結(jié)構(gòu)解析的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包