PyTorch-常用代码
PyTorch常用代碼
簡介
之前提到過PyTorch進行深度訓練是一套總的來說較為固定的流程,因此學習一些常用的代碼段是很有必要的,本文整理了一些常用的代碼段,當然最合適的參考資料依然是官方文檔。
框架配置
包導入與信息查看
這部分主要是PyTorch的版本查看、本機是否可以使用GPU訓練、可用的GPU數目等,具體如下代碼,已經備注詳細。
import torch# PyTorch版本 print(torch.__version__) # 是否可用GPU print(torch.cuda.is_available()) # 可用的GPU數目 print(torch.cuda.device_count()) # 可用的第一個GPU(默認從0編號開始) print(torch.cuda.get_device_name(0) if torch.cuda.device_count() > 0 else "no gpu")固定隨機數種子
在硬件設備不同的情況下,完整的復現是比較困難的,但是同一個設備上應該盡可能保證可復現性,主要做法就是在程序開始之前固定隨機數種子,包括Numpy、Torch的隨機數種子。
import numpy as np import torchnp.random.seed(0) torch.manual_seed(0) torch.cuda.manual_seed_all(0)顯卡設置
單顯卡設置如下。
import torch device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')指定多張顯卡設置(下面代碼示例指定0和1號顯卡)。
import os os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'張量運算
PyTorch總共提供了9種CPU張量類型以及對應的GPU張量類型,這部分主要是張量的一些常用操作,不包括基本的張量創建之類的API。
張量信息
對于一個張量,有時代碼調試時關注其基本信息如維度、數據類型。
import torcht = torch.randn(2, 3, 4, 4) # 張量的數據類型 print(t.type()) # 張量的維度信息 print(t.size()) # 張量的維度數量 print(t.dim())數據類型轉換
PyTorch經常涉及到不同數據類型之間的轉換,尤其是CPU數據轉為GPU數據,有時候為了方便運行也需要設置默認的數據類型(在PyTorch中Float類型遠超Double類型的速度)。
import torch# 設置默認數據類型 torch.set_default_tensor_type(torch.FloatTensor)t = torch.randn(1) print(t.type()) # 轉為GPU數據類型 t = t.cuda() # 轉為CPU數據類型 t = t.cpu() t = t.float() t = t.long()Numpy轉換
除了字符類型,其他類型的張量都支持轉換為Numpy的數組,也支持Numpy的數組轉為PyTorch的張量。
import torcht = torch.randn(1, 2, 3) # Tensor轉ndarray t = t.cpu().numpy() # ndarray轉tensor t = torch.from_numpy(t).float()PIL轉換
PyTorch中圖片采用[N,C,H,W]的順序存放,且數值在[0,1]之間,其他任何圖像庫讀取的圖片要在PyTorch中使用必須規范為該格式。這個轉換可以自己通過張量變換完成,也可以直接調用torchvision封裝好的函數。
import torch import torchvision import numpy as np from PIL import Image# tensor轉pil image t = torch.randn(32, 3, 224, 224) image = Image.fromarray(torch.clamp(t*255, min=0, max=255).byte().permute(1, 2, 0).cpu().numpy()) image = torchvision.transforms.functional.to_pil_image(t)# pil image轉tensor path = 'test.jpg' tensor = torch.from_numpy(np.asarray(Image.open(path).convert('RGB'))).permute(2, 0, 1).float() / 255 tensor = torchvision.transforms.functional.to_tensor(Image.open(path))單元素張量值獲取
有時候對于loss這些值雖然結果是一個張量,但是這個張量其實就只含有一個值,這種張量通過item方法取出這個值。
import torcha = torch.randn(1) print(a) print(a.item())模型操作
模型定義
主要的注意點就是繼承自nn.Module類且需要定義前向傳播運算,下面是個簡單的示例,具體可以查看我之前關于模型的文章。
import torch import torch.nn as nn import torch.nn.functional as Fclass Net(nn.Module):def __init__(self):super(Net, self).__init__()self.conv1 = nn.Conv2d(in_channels=3, out_channels=32, kernel_size=(3, 3))self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)self.conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3)self.pool2 = nn.MaxPool2d(2, 2)self.fc1 = nn.Linear(64*54*54, 256)self.fc2 = nn.Linear(256, 128)self.fc3 = nn.Linear(128, 101)def forward(self, x):x = self.pool1(F.relu(self.conv1(x)))x = self.pool2(F.relu(self.conv2(x)))x = x.view(-1, 64*54*54)x = F.relu(self.fc1(x))x = F.relu(self.fc2(x))x = self.fc3(x)return x模型總參數量
對所有層參數數目求和即可。
model = Net() num_parameters = sum(torch.numel(parameter) for parameter in model.parameters()) print(num_parameters)模型參數查看
通過model.state_dict()或者model.named_parameters()查看所有可訓練參數,包括父類參數。
params = list(model.named_parameters()) (name, param) = params[0] print(name) print(param.grad)模型可視化
通過pytorchviz或者pytorch-summary兩個工具進行結構可視化。
模型權重初始化
這方面我在之前的博客中提到過,一般區分不同的層進行不同的初始化,例如模型中定義如下的成員函數。尤其注意,model.modules()和model.children()都會返回模型的成員層,但是model.modules()會迭代地遍歷模型的所有子層,而model.children()只會遍歷模型下的一層。
def init_weights(self):for m in self.modules():if isinstance(m, nn.Conv2d):nn.init.xavier_normal_(m.weight.data)if m.bias is not None:m.bias.data.zero_()elif isinstance(m, nn.BatchNorm2d):m.weight.data.fill_(1)m.bias.data.zero_()elif isinstance(m, nn.Linear):nn.init.normal_(m.weight.data, 0, 0.01)m.bias.data.zero_()提取模型某層
model.modules()會返回模型中所有模塊的迭代器,它能夠訪問到最內層。
new_model = nn.Sequential() for layer in model.named_modules():if isinstance(layer[1],nn.Conv2d):new_model.add_module(layer[0],layer[1])使用預訓練模型參數
使用預訓練模型一般是加載模型訓練好的參數,大多時候訓練到部署還會涉及到GPU模型參數加載到CPU模型上。
model.load_state_dict(torch.load('model.pkl'), strict=False) model.load_state_dict(torch.load('model.pth', map_location='cpu'))數據處理
視頻基本信息
通過opencv-python得到視頻的信息。
import cv2 video = cv2.VideoCapture('v.mp4) 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()圖像常用預處理
各種處理方法的含義我在數據準備的博客中介紹過。
import torchvision train_transform = torchvision.transforms.Compose([torchvision.transforms.RandomResizedCrop(size=224,scale=(0.08, 1.0)),torchvision.transforms.RandomHorizontalFlip(),torchvision.transforms.ToTensor(),torchvision.transforms.Normalize(mean=(0.485, 0.456, 0.406),std=(0.229, 0.224, 0.225)), ]) val_transform = torchvision.transforms.Compose([torchvision.transforms.Resize(256),torchvision.transforms.CenterCrop(224),torchvision.transforms.ToTensor(),torchvision.transforms.Normalize(mean=(0.485, 0.456, 0.406),std=(0.229, 0.224, 0.225)), ])分類模型訓練及驗證
訓練的大致流程代碼。
for epoch in range(epochs):# 訓練集訓練train_loss = 0.0correct = 0.0total = 0.0for step, data in enumerate(train_loader):x, y = dataout = model(x)loss = criterion(out, y)optimizer.zero_grad()loss.backward()optimizer.step()_, pred = torch.max(out.data, 1)total += y.size(0)correct += (pred == y).squeeze().sum().numpy()train_loss += loss.item()if step % 100 == 0:print("epoch", epoch, "step", step, "loss", loss.item())train_acc = correct / total# 驗證集驗證valid_loss = 0.0correct = 0.0total = 0.0for step, data in enumerate(valid_loader):model.eval()x, y = dataout = model(x)out.detach_()loss = criterion(out, y)_, pred = torch.max(out.data, 1)valid_loss += loss.item()total += y.size(0)correct += (pred == y).squeeze().sum().numpy()valid_acc = correct / totalscheduler.step(valid_loss)writer.add_scalars('loss', {'train_loss': train_loss, 'valid_loss': valid_loss}, epoch)writer.add_scalars('accuracy', {'train_acc': train_acc, 'valid_acc': valid_acc}, epoch)自定義損失
PyTorch中損失也是繼承自nn.Module類,通過forward方法計算損失值。
from torch import nn class MyLoss(nn.Moudle):def __init__(self):super(MyLoss, self).__init__()def forward(self, x, y):loss = torch.mean((x - y) ** 2)return loss梯度裁減
這是一個防止梯度爆炸的有效手段,但也會帶來一些問題。
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=20)其他
模型狀態
訓練和驗證是對模型的要求是不一樣的,通過模型前向計算輸出前調整模型狀態。(通過model.train()和model.eval()進行調整。)
交叉熵損失
PyTorch中的交叉熵損失不需要經過Softmax,因為其內置了softmax計算。同時,也不需要進行onehot編碼,內置了。
梯度置零
model.zero_grad()會把整個模型的參數的梯度都歸零, 而optimizer.zero_grad()只會把傳入其中的參數的梯度歸零。
反向傳播
loss.backward()進行反向傳播之前要用用optimizer.zero_grad()清除累積的梯度。
GPU數據IO
盡量減少CPU和GPU之間的數據交互,因為這種交互很費時間。如要獲得每個batch的loss 和acc,可以將它們累積在GPU中等整個epoch結束后一起傳輸到CPU(使用loss.cpu()方法)會比每個batch 都進行一次GPU到CPU的IO快很多。
補充說明
本文主要介紹PyTorch中常用的一些代碼段,都是比較實用的代碼,部分參考PyTorch CookBook。本文涉及到的所有代碼都可以在我的Github找到,歡迎star或者fork。
總結
以上是生活随笔為你收集整理的PyTorch-常用代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PyTorch-运算加速
- 下一篇: Docker教程-简介