<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          PyTorch Cookbook(常用代碼全集)

          共 32577字,需瀏覽 66分鐘

           ·

          2021-03-25 21:21

          來(lái)源:機(jī)器學(xué)習(xí)實(shí)驗(yàn)室

          眾所周知,程序猿在寫(xiě)代碼時(shí)通常會(huì)在網(wǎng)上搜索大量資料,其中大部分是代碼段。然而,這項(xiàng)工作常常令人心累身疲,耗費(fèi)大量時(shí)間。所以,今天小編轉(zhuǎn)載了知乎上的一篇文章,介紹了一些常用PyTorch代碼段,希望能夠?yàn)閵^戰(zhàn)在電腦桌前的眾多程序猿們提供幫助!

          本文代碼基于 PyTorch 1.x 版本,需要用到以下包:


          import collections
          import os
          import shutil
          import tqdm

          import numpy as np
          import PIL.Image
          import torch
          import torchvision


          基礎(chǔ)配置


          檢查 PyTorch 版本


          torch.__version__               # PyTorch version
          torch.version.cuda              # Corresponding CUDA version
          torch.backends.cudnn.version()  # Corresponding cuDNN version
          torch.cuda.get_device_name(0)   # GPU type


          更新 PyTorch


          PyTorch 將被安裝在 anaconda3/lib/python3.7/site-packages/torch/目錄下。


          conda update pytorch torchvision -c pytorch


          固定隨機(jī)種子


          torch.manual_seed(0)
          torch.cuda.manual_seed_all(0)


          指定程序運(yùn)行在特定 GPU 卡上


          在命令行指定環(huán)境變量


          CUDA_VISIBLE_DEVICES=0,1 python train.py


          或在代碼中指定


          os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'


          判斷是否有 CUDA 支持


          torch.cuda.is_available()


          設(shè)置為 cuDNN benchmark 模式


          Benchmark 模式會(huì)提升計(jì)算速度,但是由于計(jì)算中有隨機(jī)性,每次網(wǎng)絡(luò)前饋結(jié)果略有差異。


          torch.backends.cudnn.benchmark = True


          如果想要避免這種結(jié)果波動(dòng),設(shè)置


          torch.backends.cudnn.deterministic = True


          清除 GPU 存儲(chǔ)


          有時(shí) Control-C 中止運(yùn)行后 GPU 存儲(chǔ)沒(méi)有及時(shí)釋放,需要手動(dòng)清空。在 PyTorch 內(nèi)部可以


          torch.cuda.empty_cache()


          或在命令行可以先使用 ps 找到程序的 PID,再使用 kill 結(jié)束該進(jìn)程


          ps aux | grep pythonkill -9 [pid]


          或者直接重置沒(méi)有被清空的 GPU


          nvidia-smi --gpu-reset -i [gpu_id]


          張量處理


          張量基本信息


          tensor.type()   # Data type
          tensor.size()   # Shape of the tensor. It is a subclass of Python tuple
          tensor.dim()    # Number of dimensions.


          數(shù)據(jù)類(lèi)型轉(zhuǎn)換


          # Set default tensor type. Float in PyTorch is much faster than double.
          torch.set_default_tensor_type(torch.FloatTensor)

          # Type convertions.
          tensor = tensor.cuda()
          tensor = tensor.cpu()
          tensor = tensor.float()
          tensor = tensor.long()

          torch.Tensor 與 np.ndarray 轉(zhuǎn)換

          # torch.Tensor -> np.ndarray.
          ndarray = tensor.cpu().numpy()

          # np.ndarray -> torch.Tensor.
          tensor = torch.from_numpy(ndarray).float()
          tensor = torch.from_numpy(ndarray.copy()).float()  # If ndarray has negative stride


          torch.Tensor 與 PIL.Image 轉(zhuǎn)換


          PyTorch 中的張量默認(rèn)采用 N×D×H×W 的順序,并且數(shù)據(jù)范圍在 [0, 1],需要進(jìn)行轉(zhuǎn)置和規(guī)范化。


          # torch.Tensor -> PIL.Image.
          image = PIL.Image.fromarray(torch.clamp(tensor * 255, min=0, max=255
              ).byte().permute(1, 2, 0).cpu().numpy())
          image = torchvision.transforms.functional.to_pil_image(tensor)  # Equivalently way

          # PIL.Image -> torch.Tensor.
          tensor = torch.from_numpy(np.asarray(PIL.Image.open(path))
              ).permute(2, 0, 1).float() / 255
          tensor = torchvision.transforms.functional.to_tensor(PIL.Image.open(path))  # Equivalently way


          np.ndarray 與 PIL.Image 轉(zhuǎn)換


          # np.ndarray -> PIL.Image.
          image = PIL.Image.fromarray(ndarray.astypde(np.uint8))

          # PIL.Image -> np.ndarray.
          ndarray = np.asarray(PIL.Image.open(path))


          從只包含一個(gè)元素的張量中提取值


          這在訓(xùn)練時(shí)統(tǒng)計(jì) loss 的變化過(guò)程中特別有用。否則這將累積計(jì)算圖,使 GPU 存儲(chǔ)占用量越來(lái)越大。


          value = tensor.item()


          張量形變


          張量形變常常需要用于將卷積層特征輸入全連接層的情形。相比 torch.view,torch.reshape 可以自動(dòng)處理輸入張量不連續(xù)的情況。


          tensor = torch.reshape(tensor, shape)


          打亂順序


          tensor = tensor[torch.randperm(tensor.size(0))]  # Shuffle the first dimension

          水平翻轉(zhuǎn)

          PyTorch 不支持 tensor[::-1] 這樣的負(fù)步長(zhǎng)操作,水平翻轉(zhuǎn)可以用張量索引實(shí)現(xiàn)。


          # Assume tensor has shape N*D*H*W.tensor = tensor[:, :, :, torch.arange(tensor.size(3) - 1, -1, -1).long()]

          復(fù)制張量

          有三種復(fù)制的方式,對(duì)應(yīng)不同的需求。


          # Operation                 |  New/Shared memory | Still in computation graph |
          tensor.clone()            # |        New         |          Yes               |
          tensor.detach()           # |      Shared        |          No                |
          tensor.detach.clone()()   # |        New         |          No                |


          拼接張量

          注意 torch.cat 和 torch.stack 的區(qū)別在于 torch.cat 沿著給定的維度拼接,而 torch.stack 會(huì)新增一維。例如當(dāng)參數(shù)是 3 個(gè) 10×5 的張量,torch.cat 的結(jié)果是 30×5 的張量,而 torch.stack 的結(jié)果是 3×10×5 的張量。


          tensor = torch.cat(list_of_tensors, dim=0)
          tensor = torch.stack(list_of_tensors, dim=0)


          將整數(shù)標(biāo)記轉(zhuǎn)換成獨(dú)熱(one-hot)編碼

          PyTorch 中的標(biāo)記默認(rèn)從 0 開(kāi)始。


          N = tensor.size(0)
          one_hot = torch.zeros(N, num_classes).long()
          one_hot.scatter_(dim=1, index=torch.unsqueeze(tensor, dim=1), src=torch.ones(N, num_classes).long())

          得到非零/零元素

          torch.nonzero(tensor)               # Index of non-zero elements
          torch.nonzero(tensor == 0)          # Index of zero elements
          torch.nonzero(tensor).size(0)       # Number of non-zero elements
          torch.nonzero(tensor == 0).size(0)  # Number of zero elements


          張量擴(kuò)展


          Expand tensor of shape 64*512 to shape 64*512*7*7.
          torch.reshape(tensor, (64, 512, 1, 1)).expand(64, 512, 7, 7)


          矩陣乘法


          # Matrix multiplication: (m*n) * (n*p) -> (m*p).
          result = torch.mm(tensor1, tensor2)

          # Batch matrix multiplication: (b*m*n) * (b*n*p) -> (b*m*p).
          result = torch.bmm(tensor1, tensor2)

          # Element-wise multiplication.
          result = tensor1 * tensor2


          計(jì)算兩組數(shù)據(jù)之間的兩兩歐式距離


          # X1 is of shape m*d.
          X1 = torch.unsqueeze(X1, dim=1).expand(m, n, d)
          # X2 is of shape n*d.
          X2 = torch.unsqueeze(X2, dim=0).expand(m, n, d)
          # dist is of shape m*n, where dist[i][j] = sqrt(|X1[i, :] - X[j, :]|^2)
          dist = torch.sqrt(torch.sum((X1 - X2) ** 2, dim=2))


          模型定義


          卷積層


          最常用的卷積層配置是


          conv = torch.nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=True)conv = torch.nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, padding=0, bias=True)


          如果卷積層配置比較復(fù)雜,不方便計(jì)算輸出大小時(shí),可以利用如下可視化工具輔助

          鏈接:https://ezyang.github.io/convolution-visualizer/index.html


          0GAP(Global average pooling)層


          gap = torch.nn.AdaptiveAvgPool2d(output_size=1)


          雙線(xiàn)性匯合(bilinear pooling)


          X = torch.reshape(N, D, H * W)                        # Assume X has shape N*D*H*W
          X = torch.bmm(X, torch.transpose(X, 1, 2)) / (H * W)  # Bilinear pooling
          assert X.size() == (N, D, D)
          X = torch.reshape(X, (N, D * D))
          X = torch.sign(X) * torch.sqrt(torch.abs(X) + 1e-5)   # Signed-sqrt normalization
          X = torch.nn.functional.normalize(X)                  # L2 normalization


          多卡同步 BN(Batch normalization)


          當(dāng)使用 torch.nn.DataParallel 將代碼運(yùn)行在多張 GPU 卡上時(shí),PyTorch 的 BN 層默認(rèn)操作是各卡上數(shù)據(jù)獨(dú)立地計(jì)算均值和標(biāo)準(zhǔn)差,同步 BN 使用所有卡上的數(shù)據(jù)一起計(jì)算 BN 層的均值和標(biāo)準(zhǔn)差,緩解了當(dāng)批量大?。╞atch size)比較小時(shí)對(duì)均值和標(biāo)準(zhǔn)差估計(jì)不準(zhǔn)的情況,是在目標(biāo)檢測(cè)等任務(wù)中一個(gè)有效的提升性能的技巧。


          鏈接:https://github.com/vacancy/Synchronized-BatchNorm-PyTorch


          類(lèi)似 BN 滑動(dòng)平均


          如果要實(shí)現(xiàn)類(lèi)似 BN 滑動(dòng)平均的操作,在 forward 函數(shù)中要使用原地(inplace)操作給滑動(dòng)平均賦值。


          class BN(torch.nn.Module)
              def __init__(self):
                  ...
                  self.register_buffer('running_mean', torch.zeros(num_features))

              def forward(self, X):
                  ...
                  self.running_mean += momentum * (current - self.running_mean)


          計(jì)算模型整體參數(shù)量


          num_parameters = sum(torch.numel(parameter) for parameter in model.parameters())


          類(lèi)似 Keras 的 model.summary() 輸出模型信息


          鏈接:https://github.com/sksq96/pytorch-summary


          模型權(quán)值初始化


          注意 model.modules() 和 model.children() 的區(qū)別:model.modules() 會(huì)迭代地遍歷模型的所有子層,而 model.children() 只會(huì)遍歷模型下的一層。


          # Common practise for initialization.
          for layer in model.modules():
              if isinstance(layer, torch.nn.Conv2d):
                  torch.nn.init.kaiming_normal_(layer.weight, mode='fan_out',
                                                nonlinearity='relu')
                  if layer.bias is not None:
                      torch.nn.init.constant_(layer.bias, val=0.0)
              elif isinstance(layer, torch.nn.BatchNorm2d):
                  torch.nn.init.constant_(layer.weight, val=1.0)
                  torch.nn.init.constant_(layer.bias, val=0.0)
              elif isinstance(layer, torch.nn.Linear):
                  torch.nn.init.xavier_normal_(layer.weight)
                  if layer.bias is not None:
                      torch.nn.init.constant_(layer.bias, val=0.0)

          # Initialization with given tensor.
          layer.weight = torch.nn.Parameter(tensor)


          部分層使用預(yù)訓(xùn)練模型


          注意如果保存的模型是 torch.nn.DataParallel,則當(dāng)前的模型也需要是


          model.load_state_dict(torch.load('model,pth'), strict=False)


          將在 GPU 保存的模型加載到 CPU


          model.load_state_dict(torch.load('model,pth', map_location='cpu'))



          數(shù)據(jù)準(zhǔn)備、特征提取與微調(diào)


          得到視頻數(shù)據(jù)基本信息


          import cv2
          video = cv2.VideoCapture(mp4_path)
          height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
          width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
          num_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
          fps = int(video.get(cv2.CAP_PROP_FPS))
          video.release()


          TSN 每段(segment)采樣一幀視頻


          K = self._num_segments
          if is_train:
              if num_frames > K:
                  # Random index for each segment.
                  frame_indices = torch.randint(
                      high=num_frames // K, size=(K,), dtype=torch.long)
                  frame_indices += num_frames // K * torch.arange(K)
              else:
                  frame_indices = torch.randint(
                      high=num_frames, size=(K - num_frames,), dtype=torch.long)
                  frame_indices = torch.sort(torch.cat((
                      torch.arange(num_frames), frame_indices)))[0]
          else:
              if num_frames > K:
                  # Middle index for each segment.
                  frame_indices = num_frames / K // 2
                  frame_indices += num_frames // K * torch.arange(K)
              else:
                  frame_indices = torch.sort(torch.cat((                              
                      torch.arange(num_frames), torch.arange(K - num_frames))))[0]
          assert frame_indices.size() == (K,)
          return [frame_indices[i] for i in range(K)]


          提取 ImageNet 預(yù)訓(xùn)練模型某層的卷積特征


          # VGG-16 relu5-3 feature.
          model = torchvision.models.vgg16(pretrained=True).features[:-1]
          # VGG-16 pool5 feature.
          model = torchvision.models.vgg16(pretrained=True).features
          # VGG-16 fc7 feature.
          model = torchvision.models.vgg16(pretrained=True)
          model.classifier = torch.nn.Sequential(*list(model.classifier.children())[:-3])
          # ResNet GAP feature.
          model = torchvision.models.resnet18(pretrained=True)
          model = torch.nn.Sequential(collections.OrderedDict(
              list(model.named_children())[:-1]))

          with torch.no_grad():
              model.eval()
              conv_representation = model(image)


          提取 ImageNet 預(yù)訓(xùn)練模型多層的卷積特征


          class FeatureExtractor(torch.nn.Module):
              """Helper class to extract several convolution features from the given
              pre-trained model.

              Attributes:
                  _model, torch.nn.Module.
                  _layers_to_extract, list<str> or set<str>

              Example:
                  >>> model = torchvision.models.resnet152(pretrained=True)
                  >>> model = torch.nn.Sequential(collections.OrderedDict(
                          list(model.named_children())[:-1]))
                  >>> conv_representation = FeatureExtractor(
                          pretrained_model=model,
                          layers_to_extract={'layer1', 'layer2', 'layer3', 'layer4'})(image)
              """

              def __init__(self, pretrained_model, layers_to_extract):
                  torch.nn.Module.__init__(self)
                  self._model = pretrained_model
                  self._model.eval()
                  self._layers_to_extract = set(layers_to_extract)

              def forward(self, x):
                  with torch.no_grad():
                      conv_representation = []
                      for name, layer in self._model.named_children():
                          x = layer(x)
                          if name in self._layers_to_extract:
                              conv_representation.append(x)
                      return conv_representation


          其他預(yù)訓(xùn)練模型

          鏈接:https://github.com/Cadene/pretrained-models.pytorch


          微調(diào)全連接層


          model = torchvision.models.resnet18(pretrained=True)
          for param in model.parameters():
              param.requires_grad = False
          model.fc = nn.Linear(512, 100)  # Replace the last fc layer
          optimizer = torch.optim.SGD(model.fc.parameters(), lr=1e-2, momentum=0.9, weight_decay=1e-4)


          以較大學(xué)習(xí)率微調(diào)全連接層,較小學(xué)習(xí)率微調(diào)卷積層


          model = torchvision.models.resnet18(pretrained=True)
          finetuned_parameters = list(map(id, model.fc.parameters()))
          conv_parameters = (p for p in model.parameters() if id(p) not in finetuned_parameters)
          parameters = [{'params': conv_parameters, 'lr': 1e-3}, 
                        {'params': model.fc.parameters()}]
          optimizer = torch.optim.SGD(parameters, lr=1e-2, momentum=0.9, weight_decay=1e-4)



          模型訓(xùn)練


          常用訓(xùn)練和驗(yàn)證數(shù)據(jù)預(yù)處理


          其中 ToTensor 操作會(huì)將 PIL.Image 或形狀為 H×W×D,數(shù)值范圍為 [0, 255] 的 np.ndarray 轉(zhuǎn)換為形狀為 D×H×W,數(shù)值范圍為 [0.0, 1.0] 的 torch.Tensor。


          train_transform = torchvision.transforms.Compose([
              torchvision.transforms.RandomResizedCrop(size=224,
                                                       scale=(0.081.0)),
              torchvision.transforms.RandomHorizontalFlip(),
              torchvision.transforms.ToTensor(),
              torchvision.transforms.Normalize(mean=(0.4850.4560.406),
                                               std=(0.2290.2240.225)),
           ])
           val_transform = torchvision.transforms.Compose([
              torchvision.transforms.Resize(224),
              torchvision.transforms.CenterCrop(224),
              torchvision.transforms.ToTensor(),
              torchvision.transforms.Normalize(mean=(0.4850.4560.406),
                                               std=(0.2290.2240.225)),
          ])


          訓(xùn)練基本代碼框架


          for t in epoch(80):
              for images, labels in tqdm.tqdm(train_loader, desc='Epoch %3d' % (t + 1)):
                  images, labels 
          = images.cuda(), labels.cuda()
                  scores = model(images)
                  loss = loss_function(scores, labels)
                  optimizer.zero_grad()
                  loss.backward()
                  optimizer.step()


          標(biāo)記平滑(label smoothing)


          for images, labels in train_loader:
              images, labels = images.cuda(), labels.cuda()
              N = labels.size(0)
              # C is the number of classes.
              smoothed_labels = torch.full(size=(N, C), fill_value=0.1 / (C - 1)).cuda()
              smoothed_labels.scatter_(dim=1, index=torch.unsqueeze(labels, dim=1), value=0.9)

              score = model(images)
              log_prob = torch.nn.functional.log_softmax(score, dim=1)
              loss = -torch.sum(log_prob * smoothed_labels) / N
              optimizer.zero_grad()
              loss.backward()
              optimizer.step()


          Mixup


          beta_distribution = torch.distributions.beta.Beta(alpha, alpha)
          for images, labels in train_loader:
              images, labels = images.cuda(), labels.cuda()

              # Mixup images.
              lambda_ = beta_distribution.sample([]).item()
              index = torch.randperm(images.size(0)).cuda()
              mixed_images = lambda_ * images + (1 - lambda_) * images[index, :]

              # Mixup loss.    
              scores = model(mixed_images)
              loss = (lambda_ * loss_function(scores, labels) 
                      + (1 - lambda_) * loss_function(scores, labels[index]))

              optimizer.zero_grad()
              loss.backward()
              optimizer.step()


          L1 正則化


          l1_regularization = torch.nn.L1Loss(reduction='sum')
          loss = ...  # Standard cross-entropy loss
          for param in model.parameters():
              loss += torch.sum(torch.abs(param))
          loss.backward()


          不對(duì)偏置項(xiàng)進(jìn)行 L2 正則化/權(quán)值衰減(weight decay)


          bias_list = (param for name, param in model.named_parameters() if name[-4:] == 'bias')
          others_list = (param for name, param in model.named_parameters() if name[-4:] != 'bias')
          parameters = [{'parameters': bias_list, 'weight_decay': 0},                
                        {'parameters': others_list}]
          optimizer = torch.optim.SGD(parameters, lr=1e-2, momentum=0.9, weight_decay=1e-4)


          梯度裁剪(gradient clipping)


          torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=20)


          計(jì)算 Softmax 輸出的準(zhǔn)確率


          score = model(images)
          prediction = torch.argmax(score, dim=1)
          num_correct = torch.sum(prediction == labels).item()
          accuruacy = num_correct / labels.size(0)


          可視化模型前饋的計(jì)算圖

          鏈接:https://github.com/szagoruyko/pytorchviz


          可視化學(xué)習(xí)曲線(xiàn)


          有 Facebook 自己開(kāi)發(fā)的 Visdom 和 Tensorboard 兩個(gè)選擇。


          https://github.com/facebookresearch/visdom


          https://github.com/lanpa/tensorboardX


          # Example using Visdom.
          vis = visdom.Visdom(env='Learning curve', use_incoming_socket=False)
          assert self._visdom.check_connection()
          self._visdom.close()
          options = collections.namedtuple('Options', ['loss''acc''lr'])(
              loss={'xlabel''Epoch''ylabel''Loss''showlegend'True},
              acc={'xlabel''Epoch''ylabel''Accuracy''showlegend'True},
              lr={'xlabel''Epoch''ylabel''Learning rate''showlegend'True})

          for t in epoch(80):
              tran(...)
              val(...)
              vis.line(X=torch.Tensor([t + 1]), Y=torch.Tensor([train_loss]),
                       name='train', win='Loss', update='append', opts=options.loss)
              vis.line(X=torch.Tensor([t + 1]), Y=torch.Tensor([val_loss]),
                       name='val', win='Loss', update='append', opts=options.loss)
              vis.line(X=torch.Tensor([t + 1]), Y=torch.Tensor([train_acc]),
                       name='train', win='Accuracy', update='append', opts=options.acc)
              vis.line(X=torch.Tensor([t + 1]), Y=torch.Tensor([val_acc]),
                       name='val', win='Accuracy', update='append', opts=options.acc)
              vis.line(X=torch.Tensor([t + 1]), Y=torch.Tensor([lr]),
                       win='Learning rate', update='append', opts=options.lr)


          得到當(dāng)前學(xué)習(xí)率


          If there is one global learning rate (which is the common case).
          lr = next(iter(optimizer.param_groups))['lr']

          If there are multiple learning rates for different layers.
          all_lr = []
          for param_group in optimizer.param_groups:
              all_lr.append(param_group['lr'])


          學(xué)習(xí)率衰減


          # Reduce learning rate when validation accuarcy plateau.
          scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='max', patience=5, verbose=True)
          for t in range(080):
              train(...); val(...)
              scheduler.step(val_acc)

          # Cosine annealing learning rate.
          scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=80)
          # Reduce learning rate by 10 at given epochs.
          scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[5070], gamma=0.1)
          for t in range(080):
              scheduler.step()    
              train(...); val(...)

          # Learning rate warmup by 10 epochs.
          scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda t: t / 10)
          for t in range(010):
              scheduler.step()
              train(...); val(...)


          保存與加載斷點(diǎn)


          注意為了能夠恢復(fù)訓(xùn)練,我們需要同時(shí)保存模型和優(yōu)化器的狀態(tài),以及當(dāng)前的訓(xùn)練輪數(shù)。


          # Save checkpoint.
          is_best = current_acc > best_acc
          best_acc = max(best_acc, current_acc)
          checkpoint = {
              'best_acc': best_acc,    
              'epoch': t + 1,
              'model': model.state_dict(),
              'optimizer': optimizer.state_dict(),
          }
          model_path = os.path.join('model''checkpoint.pth.tar')
          torch.save(checkpoint, model_path)
          if is_best:
              shutil.copy('checkpoint.pth.tar', model_path)

          # Load checkpoint.
          if resume:
              model_path = os.path.join('model''checkpoint.pth.tar')
              assert os.path.isfile(model_path)
              checkpoint = torch.load(model_path)
              best_acc = checkpoint['best_acc']
              start_epoch = checkpoint['epoch']
              model.load_state_dict(checkpoint['model'])
              optimizer.load_state_dict(checkpoint['optimizer'])
              print('Load checkpoint at epoch %d.' % start_epoch)


          計(jì)算準(zhǔn)確率、查準(zhǔn)率(precision)、查全率(recall)


          data['label'] and data['prediction'] are groundtruth label and prediction 
          for each image, respectively.
          accuracy = np.mean(data['label'] == data['prediction']) * 100

          # Compute recision and recall for each class.
          for c in range(len(num_classes)):
              tp = np.dot((data['label'] == c).astype(int),
                          (data['prediction'] == c).astype(int))
              tp_fp = np.sum(data['prediction'] == c)
              tp_fn = np.sum(data['label'] == c)
              precision = tp / tp_fp * 100
              recall = tp / tp_fn * 100


          PyTorch 其他注意事項(xiàng)


          模型定義


          • 建議有參數(shù)的層和匯合(pooling)層使用 torch.nn 模塊定義,激活函數(shù)直接使用 torch.nn.functional。torch.nn 模塊和 torch.nn.functional 的區(qū)別在于,torch.nn 模塊在計(jì)算時(shí)底層調(diào)用了 torch.nn.functional,但 torch.nn 模塊包括該層參數(shù),還可以應(yīng)對(duì)訓(xùn)練和測(cè)試兩種網(wǎng)絡(luò)狀態(tài)。使用 torch.nn.functional 時(shí)要注意網(wǎng)絡(luò)狀態(tài),如


          def forward(self, x):
              ...
              x = torch.nn.functional.dropout(x, p=0.5, training=self.training)


          • model(x) 前用 model.train() 和 model.eval() 切換網(wǎng)絡(luò)狀態(tài)。

          • 不需要計(jì)算梯度的代碼塊用 with torch.no_grad() 包含起來(lái)。model.eval() 和 torch.no_grad() 的區(qū)別在于,model.eval() 是將網(wǎng)絡(luò)切換為測(cè)試狀態(tài),例如 BN 和隨機(jī)失活(dropout)在訓(xùn)練和測(cè)試階段使用不同的計(jì)算方法。torch.no_grad() 是關(guān)閉 PyTorch 張量的自動(dòng)求導(dǎo)機(jī)制,以減少存儲(chǔ)使用和加速計(jì)算,得到的結(jié)果無(wú)法進(jìn)行 loss.backward()。

          • torch.nn.CrossEntropyLoss 的輸入不需要經(jīng)過(guò) Softmax。torch.nn.CrossEntropyLoss 等價(jià)于 torch.nn.functional.log_softmax + torch.nn.NLLLoss。

          • loss.backward() 前用 optimizer.zero_grad() 清除累積梯度。optimizer.zero_grad() 和 model.zero_grad() 效果一樣。


          PyTorch 性能與調(diào)試


          • torch.utils.data.DataLoader 中盡量設(shè)置 pin_memory=True,對(duì)特別小的數(shù)據(jù)集如 MNIST 設(shè)置 pin_memory=False 反而更快一些。num_workers 的設(shè)置需要在實(shí)驗(yàn)中找到最快的取值。

          • 用 del 及時(shí)刪除不用的中間變量,節(jié)約 GPU 存儲(chǔ)。

          • 使用 inplace 操作可節(jié)約 GPU 存儲(chǔ),如


          x = torch.nn.functional.relu(x, inplace=True)


          • 減少 CPU 和 GPU 之間的數(shù)據(jù)傳輸。例如如果你想知道一個(gè) epoch 中每個(gè) mini-batch 的 loss 和準(zhǔn)確率,先將它們累積在 GPU 中等一個(gè) epoch 結(jié)束之后一起傳輸回 CPU 會(huì)比每個(gè) mini-batch 都進(jìn)行一次 GPU 到 CPU 的傳輸更快。

          • 使用半精度浮點(diǎn)數(shù) half() 會(huì)有一定的速度提升,具體效率依賴(lài)于 GPU 型號(hào)。需要小心數(shù)值精度過(guò)低帶來(lái)的穩(wěn)定性問(wèn)題。

          • 時(shí)常使用 assert tensor.size() == (N, D, H, W) 作為調(diào)試手段,確保張量維度和你設(shè)想中一致。

          • 除了標(biāo)記 y 外,盡量少使用一維張量,使用 n*1 的二維張量代替,可以避免一些意想不到的一維張量計(jì)算結(jié)果。

          • 統(tǒng)計(jì)代碼各部分耗時(shí)


          with torch.autograd.profiler.profile(enabled=True, use_cuda=Falseas profile:
              ...
          print(profile)


          或者在命令行運(yùn)行


          python -m torch.utils.bottleneck main.py


          致謝


          感謝 @些許流年和@El tnoto的勘誤。由于作者才疏學(xué)淺,更兼時(shí)間和精力所限,代碼中錯(cuò)誤之處在所難免,敬請(qǐng)讀者批評(píng)指正。


          參考資料


          • PyTorch 官方代碼:pytorch/examples (https://link.zhihu.com/?target=https%3A//github.com/pytorch/examples)

          • PyTorch 論壇:PyTorch Forums (https://link.zhihu.com/?target=https%3A//discuss.pytorch.org/latest%3Forder%3Dviews)

          • PyTorch 文檔:http://pytorch.org/docs/stable/index.html (https://link.zhihu.com/?target=http%3A//pytorch.org/docs/stable/index.html)

          • 其他基于 PyTorch 的公開(kāi)實(shí)現(xiàn)代碼,無(wú)法一一列舉 

          張皓:南京大學(xué)計(jì)算機(jī)系機(jī)器學(xué)習(xí)與數(shù)據(jù)挖掘所(LAMDA)碩士生,研究方向?yàn)橛?jì)算機(jī)視覺(jué)和機(jī)器學(xué)習(xí),特別是視覺(jué)識(shí)別和深度學(xué)習(xí)。

          個(gè)人主頁(yè):http://lamda.nju.edu.cn/zhangh/

          知乎鏈接:https://zhuanlan.zhihu.com/p/59205847?




          · 推薦閱讀 ·

          炫酷!純Python開(kāi)發(fā)LOL英雄信息查詢(xún)平臺(tái)


          瀏覽 26
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  少妇呻吟视频 | 蜜桃臀久久久蜜桃臀久久 | 国产三级网站在线观看 | 亚洲首页AV | 精品人伦一区二区三电影 - 百度 麻豆网站-麻豆午夜在线-成人AV |