欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Pytorch如何利用yaml定義卷積網(wǎng)絡(luò)

 更新時(shí)間:2022年10月20日 11:27:46   作者:Vertira  
大多數(shù)卷積神經(jīng)網(wǎng)絡(luò)都是直接通過(guò)寫(xiě)一個(gè)Model類來(lái)定義的,這樣寫(xiě)的代碼其實(shí)是比較好懂,也很方便。但是本文將介紹另一個(gè)方法:利用yaml定義卷積網(wǎng)絡(luò),感興趣的可以了解一下

大多數(shù)卷積神經(jīng)網(wǎng)絡(luò)都是直接通過(guò)寫(xiě)一個(gè)Model類來(lái)定義的,這樣寫(xiě)的代碼其實(shí)是比較好懂的,特別是在魔改網(wǎng)絡(luò)的時(shí)候也很方便。然后也有一些會(huì)通過(guò)cfg配置文件進(jìn)行模型的定義。在yolov5中可以看到是通過(guò)yaml文件進(jìn)行網(wǎng)絡(luò)的定義【個(gè)人感覺(jué)通過(guò)配置文件魔改網(wǎng)絡(luò)有些不方便,當(dāng)然每個(gè)人習(xí)慣不同】,可能很多人也用過(guò),如果自己去寫(xiě)一個(gè)yaml文件,自己能不能定義出來(lái)呢?很多人不知道是如何具體通過(guò)yaml文件將里面的參數(shù)傳入自己定義的網(wǎng)絡(luò)中,這也就給自己修改網(wǎng)絡(luò)帶來(lái)了不便。這篇文章將仿照yolov5的方式,利用yaml定義一個(gè)自己的網(wǎng)絡(luò)。

定義卷積塊

我們可以先定義一個(gè)卷積塊CBL,C指卷積Conv,B指BN層,L為激活函數(shù),這里我用ReLu.

class BaseConv(nn.Module):
    def __init__(self, in_channels, out_channels, k=1, s=1, p=None):
        super().__init__()
        self.in_channels = in_channels
        self.out_channels = out_channels
        self.conv = nn.Conv2d(in_channels, out_channels, k, s, autopad(k, p))
        self.bn = nn.BatchNorm2d(out_channels)
        self.act_fn = nn.ReLU(inplace=True)
 
    def forward(self, x):
        return self.act_fn(self.bn(self.conv(x)))

卷積中的autopad是自動(dòng)補(bǔ)充pad,代碼如下:

def autopad(k, p=None):
    if p is None:
        p = k // 2 if isinstance(k, int) else [x // 2 for x in k]
    return p

定義一個(gè)Bottleneck 

可以仿照yolov5定義一個(gè)Bottleneck,參考了殘差塊的思想。

class Bottleneck(nn.Module):
    def __init__(self, in_channels, out_channels, shortcut=True):
        super(Bottleneck, self).__init__()
        self.conv1 = BaseConv(in_channels, out_channels, k=1, s=1)
        self.conv2 = BaseConv(out_channels, out_channels, k=3, s=1)
        self.add = shortcut and in_channels == out_channels
 
    def forward(self, x):
        """
        x-->conv1-->conv2-->add
          |_________________|
        """
        return x + self.conv2(self.conv1(x)) if self.add else self.conv2(self.conv1(x))

攥寫(xiě)yaml配置文件

然后我們來(lái)寫(xiě)一下yaml配置文件,網(wǎng)絡(luò)不要很復(fù)雜,就由兩個(gè)卷積和兩個(gè)Bottleneck組成就行。同理,仿v5的方法,我們的網(wǎng)絡(luò)中的backone也是個(gè)列表,每行為一個(gè)卷積層,每列有4個(gè)參數(shù),分別代表from(指該層的輸入通道數(shù)為上一層的輸出通道數(shù),所以是-1),number【yaml中的1,1,2指該層的深度,或者說(shuō)是重復(fù)幾次】,Module_nams【該層的名字】,args【網(wǎng)絡(luò)參數(shù),包含輸出通道數(shù),k,s,p等設(shè)置】

# define own model
backbone:
  [[-1, 1, BaseConv, [32, 3, 1]],  # out_channles=32, k=3, s=1
   [-1, 1, BaseConv, [64, 1, 1]],
   [-1, 2, Bottleneck, [64]]
  ]

我們現(xiàn)在用yaml工具來(lái)打開(kāi)我們的配置文件,看看都有什么內(nèi)容

    import yaml
    # 獲得yaml文件名字
    yaml_file = Path('Model.yaml').name
    with open(yaml_file,errors='ignore') as f:
        yaml_ = yaml.safe_load(f)
    print(yaml_)

輸出: 

 {'backbone': [[-1, 1, 'BaseConv', [32, 3, 1]], [-1, 1, 'BaseConv', [64, 1, 1]], [-1, 2, 'Bottleneck', [64]]]}

然后我們可以定義下自己Model類,也就是定義自己的網(wǎng)絡(luò)??梢钥吹脚c前面讀取yaml文件相比,多了一行    ch = self.yaml["ch"] = self.yaml["ch"] = 3   這個(gè)是在原yaml內(nèi)容中加入一個(gè)key和valuse,3指的3通道,因?yàn)槲覀兊膱D像是3通道。parse_model是下面要說(shuō)的傳參過(guò)程。

class Model(nn.Module):
    def __init__(self, cfg='./Model.yaml', ch=3, ):
        super().__init__()
        self.yaml = cfg
        import yaml
        yaml_file = Path(cfg).name
        with open(yaml_file, errors='ignore')as f:
            self.yaml = yaml.safe_load(f)
 
        ch = self.yaml["ch"] = self.yaml["ch"] = 3
        self.backbone = parse_model(deepcopy(self.yaml), ch=[ch])
 
    def forward(self, x):
        output = self.backbone(x)
        return output

傳入?yún)?shù)

這一步也是最關(guān)鍵的一步,我們需要定義傳參的函數(shù),將yaml中的卷積參數(shù)傳入我們定義的網(wǎng)絡(luò)中,這里會(huì)用的一個(gè)非常非常重要的函數(shù)eval(),后面也會(huì)介紹到這個(gè)函數(shù)的用法。

這里先附上完整代碼:

def parse_model(yaml_cfg, ch):
    """
    :param yaml_cfg: yaml file
    :param ch: init in_channels default is 3
    :return: model
    """
 
    layer, out_channels = [], ch[-1]
    for i, (f, number, Module_name, args) in enumerate(yaml_cfg['backbone']):
        """
        f:上一層輸出通道
        number:該模塊有幾層,就是該模塊要重復(fù)幾次
        Mdule_name:卷積層名字
        args:參數(shù),包含輸出通道數(shù),k,s,p等
        """
        # 通過(guò)eval,將str類型轉(zhuǎn)自己定義的BaseConv
        m = eval(Module_name) if isinstance(Module_name, str) else Module_name
        for j, a in enumerate(args):
            # 通過(guò)eval,將str轉(zhuǎn)int,獲得輸出通道數(shù)
            args[j] = eval(a) if isinstance(a, str) else a
        # 更新通道
        # args[0]是輸出通道
        if m in [BaseConv, Bottleneck]:
            in_channels, out_channels = ch[f], args[0]
            args = [in_channels, out_channels, *args[1:]]  # args=[in_channels, out_channels, k, s, p]
 
        # 將參數(shù)傳入模型
        model_ = nn.Sequential(*[m(*args) for _ in range(number)]) if number > 1 else m(*args)
        # 更新通道列表,每次獲取輸出通道
        ch.append(out_channels)
        layer.append(model_)
    return nn.Sequential(*layer)

下面開(kāi)始分析代碼 。

這行代碼是通過(guò)列表用來(lái)存放每層內(nèi)容以及輸出通道數(shù)。

# 這行代碼是通過(guò)列表用來(lái)存放每層內(nèi)容以及輸出通道數(shù)
layer, out_channels = [], ch[-1]

然后進(jìn)入我們的for循環(huán),在每一次循環(huán)中可以獲得我們yaml文件中的每一層網(wǎng)絡(luò):f是上一層網(wǎng)絡(luò)的輸出通道【用來(lái)作為本層的輸入通道】,number【網(wǎng)絡(luò)深度,也就是該層重復(fù)幾次而已】,Module_name是該層的名字,args是該層的一些參數(shù)。

for i, (f, number, Module_name, args) in enumerate(yaml_cfg['backbone']):

接下來(lái)會(huì)碰到一個(gè)很重要的函數(shù)eval()。下行的代碼首先需要判斷一下我們的Module_name類型是不是字符串類型,也就是判斷一下yaml中“BaseConv”是不是字符串類型,如果是,則用eval進(jìn)行對(duì)應(yīng)類型的轉(zhuǎn)化,轉(zhuǎn)成我們的BaseConv類型。 

m = eval(Module_name) if isinstance(Module_name, str) else Module_name

這里我將對(duì)eval函數(shù)在深入點(diǎn),如果知道這個(gè)函數(shù)用法的,就可以略去這部分。

我們先舉個(gè)例子,比如我現(xiàn)在有個(gè)變量a="123",這個(gè)a的類型是什么呢?他是一個(gè)str類型,不是int類型。 現(xiàn)在我們用eval函數(shù)轉(zhuǎn)一下,看看會(huì)變成什么樣子。

>>> b = eval(a) if isinstance(a,str) else a
>>> b
123
>>> type(b)
<class 'int'>

我們可以看到,經(jīng)過(guò)eval函數(shù)以后,會(huì)自動(dòng)識(shí)別并轉(zhuǎn)為int類型。那么我繼續(xù)舉例子,如果現(xiàn)在a="BaseConv",經(jīng)過(guò)eval以后會(huì)變成什么?可以看到,這里報(bào)錯(cuò)了!這是為什么?這是因?yàn)槲覀儧](méi)有導(dǎo)入BaseConv這個(gè)類,所以eval函數(shù)并不知道我們希望轉(zhuǎn)為什么類型。所以我們需要用import導(dǎo)入BaseConv這個(gè)類才可以。

>>> a="BaseConv"
>>> b = eval(a) if isinstance(a,str) else a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'BaseConv' is not defined

當(dāng)我們導(dǎo)入BaseConv以后,在經(jīng)過(guò)eval就可以獲得:

<class 'models.BaseConv'> 

接下來(lái)是獲得args中的網(wǎng)絡(luò)參數(shù),也是通過(guò)eval進(jìn)行轉(zhuǎn)化

        for j, a in enumerate(args):
            # 通過(guò)eval,將str轉(zhuǎn)int,獲得輸出通道數(shù)
            args[j] = eval(a) if isinstance(a, str) else a

獲取通道數(shù),并在每次循環(huán)中對(duì)通道進(jìn)行更新:可以仔細(xì)看一下ch[f]指的上一層輸出通道,剛開(kāi)始默認(rèn)為[3],那么ch[-1]=3,我們yaml中第一層的BaseConv args[0]為32,表示輸出32通道。因此在第一次循環(huán)中有in_channels = 3,out_channels=32。args也要更新,*args前面的"*"并不是指針的意思,也不是乘的意思,而是解壓操作,因此我們第一次循環(huán)中得到的args=[3,32,3,1]。

# 更新通道
# args[0]是輸出通道
if m in [BaseConv, Bottleneck]:
    in_channels, out_channels = ch[f], args[0]
    args = [in_channels, out_channels, *args[1:]]  # args=[in_channels, out_channels, k, s, p]

將參數(shù)傳入模型

這里用for _ in range(number)來(lái)判斷網(wǎng)絡(luò)的深度【或者說(shuō)該模塊重復(fù)幾次】,這里的m就是前面經(jīng)過(guò)eval轉(zhuǎn)化的 <class 'models.BaseConv'>。通過(guò)*args解壓操作將args列表中的內(nèi)容放入m中,再通過(guò)*解壓操作放入nn.Sequential。

model_ = nn.Sequential(*[m(*args) for _ in range(number)]) if number > 1 else m(*args)

這樣就可以獲得我們第一次循環(huán)BaseConv了。后面的循環(huán)也是同樣的反復(fù)操作而已。

BaseConv(
  (conv): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (bn): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (act_fn): ReLU(inplace=True)
)

然后是更新通道列表和layer列表,為的是獲取每次循環(huán)的輸出通道,沒(méi)有這一步,再下一次循環(huán)的時(shí)候?qū)⒉荒苷_得到通道數(shù)。

# 更新通道列表,每次獲取輸出通道
ch.append(out_channels)
layer.append(model_)

然后我們就可以對(duì)模型調(diào)用進(jìn)行實(shí)例化了,可以打印下模型:

Model(
  (backbone): Sequential(
    (0): BaseConv(
      (conv): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (bn): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (act_fn): ReLU(inplace=True)
    )
    (1): BaseConv(
      (conv): Conv2d(32, 64, kernel_size=(1, 1), stride=(1, 1))
      (bn): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (act_fn): ReLU(inplace=True)
    )
    (2): Sequential(
      (0): Bottleneck(
        (conv1): BaseConv(
          (conv): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1))
          (bn): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (act_fn): ReLU(inplace=True)
        )
        (conv2): BaseConv(
          (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (bn): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (act_fn): ReLU(inplace=True)
        )
      )
      (1): Bottleneck(
        (conv1): BaseConv(
          (conv): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1))
          (bn): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (act_fn): ReLU(inplace=True)
        )
        (conv2): BaseConv(
          (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (bn): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (act_fn): ReLU(inplace=True)
        )
      )
    )
  )
)

同時(shí)我們也可以對(duì)模型每層可視化看一下。可以看到和我們定義的模型是一樣的。

完整的代碼

from copy import deepcopy
 
from models import BaseConv, Bottleneck
import torch.nn as nn
import os
 
path = os.getcwd()
from pathlib import Path
import torch
 
 
def parse_model(yaml_cfg, ch):
    """
    :param yaml_cfg: yaml file
    :param ch: init in_channels default is 3
    :return: model
    """
 
    layer, out_channels = [], ch[-1]
    for i, (f, number, Module_name, args) in enumerate(yaml_cfg['backbone']):
        """
        f:上一層輸出通道
        number:該模塊有幾層,就是該模塊要重復(fù)幾次
        Mdule_name:卷積層名字
        args:參數(shù),包含輸出通道數(shù),k,s,p等
        """
        # 通過(guò)eval,將str類型轉(zhuǎn)自己定義的BaseConv
        m = eval(Module_name) if isinstance(Module_name, str) else Module_name
        for j, a in enumerate(args):
            # 通過(guò)eval,將str轉(zhuǎn)int,獲得輸出通道數(shù)
            args[j] = eval(a) if isinstance(a, str) else a
        # 更新通道
        # args[0]是輸出通道
        if m in [BaseConv, Bottleneck]:
            in_channels, out_channels = ch[f], args[0]
            args = [in_channels, out_channels, *args[1:]]  # args=[in_channels, out_channels, k, s, p]
 
        # 將參數(shù)傳入模型
        model_ = nn.Sequential(*[m(*args) for _ in range(number)]) if number > 1 else m(*args)
        # 更新通道列表,每次獲取輸出通道
        ch.append(out_channels)
        layer.append(model_)
    return nn.Sequential(*layer)
 
 
class Model(nn.Module):
    def __init__(self, cfg='./Model.yaml', ch=3, ):
        super().__init__()
        self.yaml = cfg
        import yaml
        yaml_file = Path(cfg).name
        with open(yaml_file, errors='ignore')as f:
            self.yaml = yaml.safe_load(f)
 
        ch = self.yaml["ch"] = self.yaml["ch"] = 3
        self.backbone = parse_model(deepcopy(self.yaml), ch=[ch])
 
    def forward(self, x):
        output = self.backbone(x)
        return output
 
 
if __name__ == "__main__":
    cfg = path + '/Model.yaml'
    model = Model()
    model.eval()
    print(model)
    x = torch.ones(1, 3, 512, 512)
    output = model(x)
    torch.save(model, "model.pth")
 
 
 
    # model = torch.load('model.pth')
    # model.eval()
    # x = torch.ones(1,3,512,512)
    # input_name = ['input']
    # output_name = ['output']
    # torch.onnx.export(model, x, 'myonnx.onnx', verbose=True)

以上就是詳解Pytorch如何利用yaml定義卷積網(wǎng)絡(luò)的詳細(xì)內(nèi)容,更多關(guān)于Pytorch yaml定義卷積網(wǎng)絡(luò)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 神經(jīng)網(wǎng)絡(luò)訓(xùn)練采用gpu設(shè)置的方式

    神經(jīng)網(wǎng)絡(luò)訓(xùn)練采用gpu設(shè)置的方式

    這篇文章主要介紹了神經(jīng)網(wǎng)絡(luò)訓(xùn)練采用gpu設(shè)置的方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-03-03
  • Python實(shí)現(xiàn)定期檢查源目錄與備份目錄的差異并進(jìn)行備份功能示例

    Python實(shí)現(xiàn)定期檢查源目錄與備份目錄的差異并進(jìn)行備份功能示例

    這篇文章主要介紹了Python實(shí)現(xiàn)定期檢查源目錄與備份目錄的差異并進(jìn)行備份功能,涉及Python基于filecmp模塊的文件比較及讀寫(xiě)等相關(guān)操作技巧,需要的朋友可以參考下
    2019-02-02
  • Python搭建HTTP服務(wù)器和FTP服務(wù)器

    Python搭建HTTP服務(wù)器和FTP服務(wù)器

    這篇文章主要為大家詳細(xì)介紹了Python搭建HTTP服務(wù)器和FTP服務(wù)器的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • python實(shí)現(xiàn)定時(shí)發(fā)送郵件

    python實(shí)現(xiàn)定時(shí)發(fā)送郵件

    這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)定時(shí)發(fā)送郵件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • Pycharm安裝python庫(kù)的方法

    Pycharm安裝python庫(kù)的方法

    這篇文章主要介紹了Pycharm安裝python庫(kù)的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • 利用Python繪制隨機(jī)游走圖的詳細(xì)過(guò)程

    利用Python繪制隨機(jī)游走圖的詳細(xì)過(guò)程

    隨機(jī)游走(random walk)也稱隨機(jī)漫步,隨機(jī)行走等,是以隨機(jī)的方式采取連續(xù)步驟的過(guò)程,下面這篇文章主要給大家介紹了關(guān)于利用Python繪制隨機(jī)游走圖的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-02-02
  • 使用python實(shí)現(xiàn)壓縮PDF文件大小的方法

    使用python實(shí)現(xiàn)壓縮PDF文件大小的方法

    壓縮 PDF 文件能有效減小文件大小并提高文件傳輸?shù)男?同時(shí)還能節(jié)省計(jì)算機(jī)存儲(chǔ)空間,除了使用一些專業(yè)工具對(duì)PDF文件進(jìn)行壓縮,我們還可以通過(guò) Python 來(lái)執(zhí)行該操作,本文將分享一個(gè)簡(jiǎn)單有效的使用 Python 壓縮 PDF 文件的方法,需要的朋友可以參考下
    2024-06-06
  • 和孩子一起學(xué)習(xí)python之變量命名規(guī)則

    和孩子一起學(xué)習(xí)python之變量命名規(guī)則

    這篇文章我們給大家總結(jié)了關(guān)于兒童學(xué)習(xí)python中的變量命名規(guī)則相關(guān)知識(shí)點(diǎn)內(nèi)容,有興趣的朋友跟著參考學(xué)習(xí)下。
    2018-05-05
  • Django框架視圖函數(shù)設(shè)計(jì)示例

    Django框架視圖函數(shù)設(shè)計(jì)示例

    這篇文章主要介紹了Django框架視圖函數(shù)設(shè)計(jì),結(jié)合實(shí)例形式分析了Django框架視圖函數(shù)處理流程、原理與相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2019-07-07
  • Python實(shí)現(xiàn)超快窗口截圖功能詳解

    Python實(shí)現(xiàn)超快窗口截圖功能詳解

    這篇文章主要為大家詳細(xì)介紹了如何利用Python語(yǔ)言實(shí)現(xiàn)超快窗口截圖功能,可以自動(dòng)獲取當(dāng)前活動(dòng)窗口并展示截圖,感興趣的可以了解一下
    2022-05-05

最新評(píng)論