丁香五月天婷婷久久婷婷色综合91|国产传媒自偷自拍|久久影院亚洲精品|国产欧美VA天堂国产美女自慰视屏|免费黄色av网站|婷婷丁香五月激情四射|日韩AV一区二区中文字幕在线观看|亚洲欧美日本性爱|日日噜噜噜夜夜噜噜噜|中文Av日韩一区二区

您正在使用IE低版瀏覽器,為了您的雷峰網(wǎng)賬號(hào)安全和更好的產(chǎn)品體驗(yàn),強(qiáng)烈建議使用更快更安全的瀏覽器
此為臨時(shí)鏈接,僅用于文章預(yù)覽,將在時(shí)失效
人工智能開(kāi)發(fā)者 正文
發(fā)私信給skura
發(fā)送

0

PyTorch代碼規(guī)范最佳實(shí)踐和樣式指南

本文作者: skura 2019-04-16 13:02
導(dǎo)語(yǔ):PyTorch的非官方風(fēng)格指南和最佳實(shí)踐摘要

雷鋒網(wǎng) AI 科技評(píng)論按,本文不是 Python 的官方風(fēng)格指南。本文總結(jié)了使用 PyTorch 框架進(jìn)行深入學(xué)習(xí)的一年多經(jīng)驗(yàn)中的最佳實(shí)踐。本文分享的知識(shí)主要是以研究的角度來(lái)看的,它來(lái)源于一個(gè)開(kāi)元的 github 項(xiàng)目。

根據(jù)經(jīng)驗(yàn),作者建議使用 Python 3.6+,因?yàn)橐韵鹿δ苡兄趯?xiě)出干凈簡(jiǎn)單的代碼:

  • 支持 Python 3.6 以后的輸入。

  • 自 Python 3.6 起支持 f 字符串

Python Styleguide 概述

作者嘗試按照 Google Styleguide for Python 進(jìn)行操作,這里是 Google 提供的 python 代碼詳細(xì)樣式指南

常見(jiàn)的命名約定:

PyTorch代碼規(guī)范最佳實(shí)踐和樣式指南

Jupyter Notebook與Python腳本

一般來(lái)說(shuō),建議使用 Jupyternotebook 進(jìn)行初步探索和使用新的模型和代碼。如果你想在更大的數(shù)據(jù)集上訓(xùn)練模型,就應(yīng)該使用 Python 腳本。在這里,復(fù)用性更為重要。

推薦使用的工作流程是:

  1. 從Jupyter筆記本開(kāi)始

  2. 探索數(shù)據(jù)和模型

  3. 在 notebook 的單元格中構(gòu)建類(lèi)/方法

  4. 將代碼移動(dòng)到python腳本中

  5. 在服務(wù)器上訓(xùn)練/部署

注意,不要將所有層和模型放在同一個(gè)文件中。最佳做法是將最終網(wǎng)絡(luò)分離為單獨(dú)的文件(networks.py),并將層、損耗和 ops 保存在各自的文件(layers.py、losses.py、ops.py)中。完成的模型(由一個(gè)或多個(gè)網(wǎng)絡(luò)組成)應(yīng)在一個(gè)文件中引用,文件名為 yolov3.py、dcgan.py 這樣。

在PyTorch中構(gòu)建神經(jīng)網(wǎng)絡(luò)

我們建議將網(wǎng)絡(luò)拆分為更小的可重用部分。網(wǎng)絡(luò)由操作或其它網(wǎng)絡(luò)模塊組成。損失函數(shù)也是神經(jīng)網(wǎng)絡(luò)的模塊,因此可以直接集成到網(wǎng)絡(luò)中。

繼承自 nn.module 的類(lèi)必須有一個(gè) forward 方法來(lái)實(shí)現(xiàn)各個(gè)層或操作的 forward 傳遞。

使用 self.net(input),可以在輸入數(shù)據(jù)上使用 nn.module。這只需使用對(duì)象的 call()方法。

output = self.net(input)

PyTorch 中的一個(gè)簡(jiǎn)單網(wǎng)絡(luò)

對(duì)于具有單個(gè)輸入和單個(gè)輸出的簡(jiǎn)單網(wǎng)絡(luò),請(qǐng)使用以下模式:

class ConvBlock(nn.Module):
    def __init__(self):
        super(ConvBlock, self).__init__()
        block = [nn.Conv2d(...)]
        block += [nn.ReLU()]
        block += [nn.BatchNorm2d(...)]
        self.block = nn.Sequential(*block)
    
    def forward(self, x):
        return self.block(x)

class SimpleNetwork(nn.Module):
    def __init__(self, num_resnet_blocks=6):
        super(SimpleNetwork, self).__init__()
        # here we add the individual layers
        layers = [ConvBlock(...)]
        for i in range(num_resnet_blocks):
            layers += [ResBlock(...)]
        self.net = nn.Sequential(*layers)
    
    def forward(self, x):
        return self.net(x)

需要注意的是:

  • 重用簡(jiǎn)單的、循環(huán)的構(gòu)建塊,例如 ConvBlock,它由相同的循環(huán)模式(卷積、激活、歸一化)組成,并將它們放入單獨(dú)的nn.模塊中。

  • 作者構(gòu)建了一個(gè)所需層的列表,最后使用 nn.Sequential()將它們轉(zhuǎn)換為模型。在 list 對(duì)象之前使用 * 操作符來(lái)展開(kāi)它。

  • 在前向傳導(dǎo)中,我們只是通過(guò)模型運(yùn)行輸入。

pytorch 中跳過(guò)連接的網(wǎng)絡(luò)

class ResnetBlock(nn.Module):
    def __init__(self, dim, padding_type, norm_layer, use_dropout, use_bias):
        super(ResnetBlock, self).__init__()
        self.conv_block = self.build_conv_block(...)

    def build_conv_block(self, ...):
        conv_block = []

        conv_block += [nn.Conv2d(...),
                       norm_layer(...),
                       nn.ReLU()]
        if use_dropout:
            conv_block += [nn.Dropout(...)]
            
        conv_block += [nn.Conv2d(...),
                       norm_layer(...)]

        return nn.Sequential(*conv_block)

    def forward(self, x):
        out = x + self.conv_block(x)
        return out

在這里,ResNet 塊的跳過(guò)連接直接在前向傳導(dǎo)中實(shí)現(xiàn)。PyTorch 允許在前向傳導(dǎo)時(shí)進(jìn)行動(dòng)態(tài)操作。

PyTorch中具有多個(gè)輸出的網(wǎng)絡(luò)

對(duì)于需要多個(gè)輸出的網(wǎng)絡(luò),例如使用預(yù)訓(xùn)練的 VGG 網(wǎng)絡(luò)構(gòu)建感知損失,我們使用以下模式:

class Vgg19(nn.Module):
  def __init__(self, requires_grad=False):
    super(Vgg19, self).__init__()
    vgg_pretrained_features = models.vgg19(pretrained=True).features
    self.slice1 = torch.nn.Sequential()
    self.slice2 = torch.nn.Sequential()
    self.slice3 = torch.nn.Sequential()

    for x in range(7):
        self.slice1.add_module(str(x), vgg_pretrained_features[x])
    for x in range(7, 21):
        self.slice2.add_module(str(x), vgg_pretrained_features[x])
    for x in range(21, 30):
        self.slice3.add_module(str(x), vgg_pretrained_features[x])
    if not requires_grad:
        for param in self.parameters():
            param.requires_grad = False

  def forward(self, x):
    h_relu1 = self.slice1(x)
    h_relu2 = self.slice2(h_relu1)        
    h_relu3 = self.slice3(h_relu2)        
    out = [h_relu1, h_relu2, h_relu3]
    return out

請(qǐng)注意:

  • 這里使用 torchvision 提供的預(yù)訓(xùn)練模型。

  • 這里把網(wǎng)絡(luò)分成三部分,每個(gè)部分由預(yù)訓(xùn)練模型的層組成。

  • 通過(guò)設(shè)置 requires_grad = False 來(lái)凍結(jié)網(wǎng)絡(luò)。

  • 我們返回一個(gè)包含三個(gè)輸出部分的列表。

自定義損失

雖然 PyTorch 已經(jīng)有很多標(biāo)準(zhǔn)的損失函數(shù),但有時(shí)也可能需要?jiǎng)?chuàng)建自己的損失函數(shù)。為此,請(qǐng)創(chuàng)建單獨(dú)的文件 losses.py 并擴(kuò)展 nn.module 類(lèi)以創(chuàng)建自定義的損失函數(shù):

class CustomLoss(nn.Module):
    
    def __init__(self):
        super(CustomLoss,self).__init__()
        
    def forward(self,x,y):
        loss = torch.mean((x - y)**2)
        return loss

推薦使用的用于訓(xùn)練模型的代碼結(jié)構(gòu)

請(qǐng)注意,作者使用了以下模式:

我們使用 prefetch_generator 中的 BackgroundGenerator 在后臺(tái)加載 batch。有關(guān)詳細(xì)信息,請(qǐng)參閱這里。

我們使用 tqdm 來(lái)監(jiān)控訓(xùn)練進(jìn)度并顯示計(jì)算效率。這有助于我們?cè)跀?shù)據(jù)加載管道中找到瓶頸在哪里。

# import statements
import torch
import torch.nn as nn
from torch.utils import data
...

# set flags / seeds
torch.backends.cudnn.benchmark = True
np.random.seed(1)
torch.manual_seed(1)
torch.cuda.manual_seed(1)
...

# Start with main code
if __name__ == '__main__':
    # argparse for additional flags for experiment
    parser = argparse.ArgumentParser(description="Train a network for ...")
    ...
    opt = parser.parse_args()
    
    # add code for datasets (we always use train and validation/ test set)
    data_transforms = transforms.Compose([
        transforms.Resize((opt.img_size, opt.img_size)),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
    ])
    
    train_dataset = datasets.ImageFolder(
        root=os.path.join(opt.path_to_data, "train"),
        transform=data_transforms)
    train_data_loader = data.DataLoader(train_dataset, ...)
    
    test_dataset = datasets.ImageFolder(
        root=os.path.join(opt.path_to_data, "test"),
        transform=data_transforms)
    test_data_loader = data.DataLoader(test_dataset ...)
    ...
    
    # instantiate network (which has been imported from *networks.py*)
    net = MyNetwork(...)
    ...
    
    # create losses (criterion in pytorch)
    criterion_L1 = torch.nn.L1Loss()
    ...
    
    # if running on GPU and we want to use cuda move model there
    use_cuda = torch.cuda.is_available()
    if use_cuda:
        net = net.cuda()
        ...
    
    # create optimizers
    optim = torch.optim.Adam(net.parameters(), lr=opt.lr)
    ...
    
    # load checkpoint if needed/ wanted
    start_n_iter = 0
    start_epoch = 0
    if opt.resume:
        ckpt = load_checkpoint(opt.path_to_checkpoint) # custom method for loading last checkpoint
        net.load_state_dict(ckpt['net'])
        start_epoch = ckpt['epoch']
        start_n_iter = ckpt['n_iter']
        optim.load_state_dict(ckpt['optim'])
        print("last checkpoint restored")
        ...
        
    # if we want to run experiment on multiple GPUs we move the models there
    net = torch.nn.DataParallel(net)
    ...
    
    # typically we use tensorboardX to keep track of experiments
    writer = SummaryWriter(...)
    
    # now we start the main loop
    n_iter = start_n_iter
    for epoch in range(start_epoch, opt.epochs):
        # set models to train mode
        net.train()
        ...
        
        # use prefetch_generator and tqdm for iterating through data
        pbar = tqdm(enumerate(BackgroundGenerator(train_data_loader, ...)),
                    total=len(train_data_loader))
        start_time = time.time()
        
        # for loop going through dataset
        for i, data in pbar:
            # data preparation
            img, label = data
            if use_cuda:
                img = img.cuda()
                label = label.cuda()
            ...
            
            # It's very good practice to keep track of preparation time and computation time using tqdm                to find any issues in your dataloader
            prepare_time = start_time-time.time()
            
            # forward and backward pass
            optim.zero_grad()
            ...
            loss.backward()
            optim.step()
            ...
            
            # udpate tensorboardX
            writer.add_scalar(..., n_iter)
            ...
            
            # compute computation time and *compute_efficiency*

            process_time = start_time-time.time()-prepare_time
            pbar.set_description("Compute efficiency: {:.2f}, epoch: {}/{}:".format(
                process_time/(process_time+prepare_time), epoch, opt.epochs))
            start_time = time.time()
            
        # maybe do a test pass every x epochs
        if epoch % x == x-1:
            # bring models to evaluation mode
            net.eval()
            ...
            #do some tests
            pbar = tqdm(enumerate(BackgroundGenerator(test_data_loader, ...)),
                    total=len(test_data_loader))
            for i, data in pbar:
                ...
                
            # save checkpoint if needed
            ...

用 PyTorch 在多個(gè) GPU 上進(jìn)行訓(xùn)練

PyTorch 中有兩種不同的模式去使用多個(gè) GPU 進(jìn)行訓(xùn)練。根據(jù)經(jīng)驗(yàn),這兩種模式都是有效的。然而,第一種方法得到的結(jié)果更好,需要的代碼更少。由于 GPU 之間的通信較少,第二種方法似乎具有輕微的性能優(yōu)勢(shì)。

分割每個(gè)網(wǎng)絡(luò)的批輸入

最常見(jiàn)的方法是簡(jiǎn)單地將所有網(wǎng)絡(luò)的批劃分為單個(gè) GPU。

因此,在批大小為 64 的 1 個(gè) GPU 上運(yùn)行的模型將在批大小為 32 的 2 個(gè) GPU 上運(yùn)行。這可以通過(guò)使用 nn.dataparallel(model)自動(dòng)包裝模型來(lái)完成。

將所有網(wǎng)絡(luò)打包到超級(jí)網(wǎng)絡(luò)中并拆分輸入批

這種模式不太常用。Nvidia 的 pix2pixhd 實(shí)現(xiàn)中顯示了實(shí)現(xiàn)此方法的存儲(chǔ)庫(kù)

什么該做什么不該做

避免在 nn.Module 的 forward 方法中使用 numpy 代碼

numpy 代碼在 CPU 上運(yùn)行的速度比 torch 代碼慢。由于 torch 的開(kāi)發(fā)理念和 numpy 類(lèi)似,所以 pytorch 支持大多數(shù) numpy 函數(shù)。

將數(shù)據(jù)加載器與主代碼分離

數(shù)據(jù)加載管道應(yīng)該獨(dú)立于你的主要訓(xùn)練代碼。PyTorch 使后臺(tái)工作人員可以更高效地加載數(shù)據(jù),但不會(huì)干擾主要的訓(xùn)練過(guò)程。

不要每個(gè)步驟都輸出結(jié)果日志

通常,我們對(duì)模型進(jìn)行數(shù)千步的訓(xùn)練。因此,不要在每一步記錄結(jié)果就足以減少開(kāi)銷(xiāo)。尤其是,在訓(xùn)練過(guò)程中將中間結(jié)果保存為圖像成本高昂。

使用命令行參數(shù)

在代碼執(zhí)行期間使用命令行參數(shù)設(shè)置參數(shù)(批大小、學(xué)習(xí)速率等)非常方便。跟蹤實(shí)驗(yàn)參數(shù)的一個(gè)簡(jiǎn)單方法是只打印從 parse_args 接收到的字典:

...

# saves arguments to config.txt file

opt = parser.parse_args()

with open("config.txt", "w") as f:
   f.write(opt.__str__())...

如果可能,使用 .detach()從圖表中釋放張量

pytorch跟蹤所有涉及張量的自動(dòng)微分操作。使用 .detach()防止記錄不必要的操作。

使用 .item()打印標(biāo)量張量

你可以直接打印變量,但是建議使用 variable.detach()或 variable.item()。在早期的 pytorch 版本中,必須使用 .data 來(lái)訪(fǎng)問(wèn)變量的張量。

在 nn.Module 上使用 call 方法而不是 forward

這兩種方法不完全相同,下面的例子就可以看出這一點(diǎn):

output = self.net.forward(input)
# they are not equal!
output = self.net(input)

另外,原文中還有關(guān)于常見(jiàn)問(wèn)題的解答,感興趣的可以移步這里。

via:https://github.com/IgorSusmelj/pytorch-styleguide

雷鋒網(wǎng)雷鋒網(wǎng)

雷峰網(wǎng)版權(quán)文章,未經(jīng)授權(quán)禁止轉(zhuǎn)載。詳情見(jiàn)轉(zhuǎn)載須知。

PyTorch代碼規(guī)范最佳實(shí)踐和樣式指南

分享:
相關(guān)文章
當(dāng)月熱門(mén)文章
最新文章
請(qǐng)?zhí)顚?xiě)申請(qǐng)人資料
姓名
電話(huà)
郵箱
微信號(hào)
作品鏈接
個(gè)人簡(jiǎn)介
為了您的賬戶(hù)安全,請(qǐng)驗(yàn)證郵箱
您的郵箱還未驗(yàn)證,完成可獲20積分喲!
請(qǐng)驗(yàn)證您的郵箱
立即驗(yàn)證
完善賬號(hào)信息
您的賬號(hào)已經(jīng)綁定,現(xiàn)在您可以設(shè)置密碼以方便用郵箱登錄
立即設(shè)置 以后再說(shuō)