Pytorch學習筆記

pan_jinquan 2021-08-15 21:12:25 阅读数:293

本文一共[544]字,预计阅读时长:1分钟~
pytorch

Pytorch學習筆記

目錄

Pytorch學習筆記

1. nn.moduleList 和Sequential用法和實例

1.1、nn.Sequential():模型建立方式

2. Pytorch基本操作

expand()擴展維度的

contiguous()

torch.ge,torch.gt,torch.le逐元素比較

 3. Pytorch常用工具

Pytorch可視化工具

統計模型參數量與FLOPs

4.PyTorch Trick 集錦

1、指定GPU編號

2、查看模型每層輸出詳情

3、梯度裁剪(Gradient Clipping)

4、擴展單張圖片維度

5、獨熱編碼

6、防止驗證模型時爆顯存

7、學習率衰减

8、凍結某些層的參數

9、對不同層使用不同學習率

10、模型相關操作

11、Pytorch內置one_hot函數

 12. 加載多GPU模型

13.設置隨機數種子


1. nn.moduleList 和Sequential用法和實例

    https://blog.csdn.net/e01528/article/details/84397174

建立nn.Sequential()對象,必須小心確保一個塊的輸出大小與下一個塊的輸入大小匹配。基本上,它的行為就像一個nn.Module。

1.1、nn.Sequential():模型建立方式

第一種寫法:
nn.Sequential()對象.add_module(層名,層class的實例)

net1 = nn.Sequential()
net1.add_module('conv', nn.Conv2d(3, 3, 3))
net1.add_module('batchnorm', nn.BatchNorm2d(3))
net1.add_module('activation_layer', nn.ReLU())

第二種寫法:
nn.Sequential(*多個層class的實例)

net2 = nn.Sequential(
        nn.Conv2d(3, 3, 3),
        nn.BatchNorm2d(3),
        nn.ReLU()
        )

第三種寫法:
nn.Sequential(OrderedDict([*多個(層名,層class的實例)]))

from collections import OrderedDict
net3= nn.Sequential(OrderedDict([
          ('conv', nn.Conv2d(3, 3, 3)),
          ('batchnorm', nn.BatchNorm2d(3)),
          ('activation_layer', nn.ReLU())
        ]))

2. Pytorch基本操作


expand()擴展維度的

擴展某個相同元素維度,將如x.shape=(2,2,1)的第3維度擴展為x.shape=(2,2,3),一般用在Tensor維度不匹配時,可使用該方法進行擴展.

import torch
x=torch.randn(2,2,1)
print(x)
y=x.expand(2,2,3)
print(y)

輸出:

tensor([[[ 0.0608],
         [ 2.2106]],
 
        [[-1.9287],
         [ 0.8748]]])
tensor([[[ 0.0608,  0.0608,  0.0608],
         [ 2.2106,  2.2106,  2.2106]],
 
        [[-1.9287, -1.9287, -1.9287],
         [ 0.8748,  0.8748,  0.8748]]])

contiguous()

返回一個內存為連續的張量,如本身就是連續的,返回它自己。一般用在view()函數之前,因為view()要求調用張量是連續的。可以通過is_contiguous查看張量內存是否連續。調用view之前最好先contiguous

x.contiguous().view() 

返回一個內存為連續的張量,如本身就是連續的,返回它自己。一般用在view()函數之前,因為view()要求調用張量是連續的。可以通過is_contiguous查看張量內存是否連續。

import torch
a=torch.tensor([[[1,2,3],[4,8,12]],[[1,2,3],[4,8,12]]])
print(a.is_contiguous)
print(a.contiguous().view(4,3))

輸出:

<built-in method is_contiguous of Tensor object at 0x7f4b5e35afa0>
tensor([[  1,   2,   3],
        [  4,   8,  12],
        [  1,   2,   3],
        [  4,   8,  12]])

torch.ge,torch.gt,torch.le逐元素比較

https://blog.csdn.net/jacke121/article/details/86360959


 3. Pytorch常用工具

Pytorch可視化工具

https://www.jianshu.com/p/46eb3004beca

https://github.com/miaoshuyu/pytorch-tensorboardx-visualization

統計模型參數量與FLOPs

https://mp.weixin.qq.com/s/2HFulqqNdqp3b6XXa7qPgA

PyTorch-OpCounter GitHub 地址: https://github.com/Lyken17/pytorch-OpCounte 

對於 torchvision 中自帶的模型,Flops 統計通過以下幾行代碼就能完成:

from torchvision.models import resnet50
from thop import profile
model = resnet50()
input = torch.randn(1, 3, 224, 224)
flops, params = profile(model, inputs=(input, ))

4.PyTorch Trick 集錦

https://github.com/lartpang/PyTorchTricks

1、指定GPU編號

  • 設置當前使用的GPU設備僅為0號設備,設備名稱為 /gpu:0os.environ["CUDA_VISIBLE_DEVICES"] = "0"
  • 設置當前使用的GPU設備為0,1號兩個設備,名稱依次為 /gpu:0/gpu:1: os.environ["CUDA_VISIBLE_DEVICES"] = "0,1" ,根據順序錶示優先使用0號設備,然後使用1號設備。

指定GPU的命令需要放在和神經網絡相關的一系列操作的前面。

2、查看模型每層輸出詳情

Keras有一個簡潔的API來查看模型的每一層輸出尺寸,這在調試網絡時非常有用。現在在PyTorch中也可以實現這個功能。

使用很簡單,如下用法:

from torchsummary import summary
summary(your_model, input_size=(channels, H, W))

input_size 是根據你自己的網絡模型的輸入尺寸進行設置。

pytorch-summary

3、梯度裁剪(Gradient Clipping)

import torch.nn as nn
outputs = model(data)
loss= loss_fn(outputs, target)
optimizer.zero_grad()
loss.backward()
nn.utils.clip_grad_norm_(model.parameters(), max_norm=20, norm_type=2)
optimizer.step()

nn.utils.clip_grad_norm_ 的參數:

  • parameters – 一個基於變量的迭代器,會進行梯度歸一化
  • max_norm – 梯度的最大範數
  • norm_type – 規定範數的類型,默認為L2

知乎用戶 不橢的橢圓 提出:梯度裁剪在某些任務上會額外消耗大量的計算時間,可移步評論區查看詳情。

4、擴展單張圖片維度

因為在訓練時的數據維度一般都是 (batch_size, c, h, w),而在測試時只輸入一張圖片,所以需要擴展維度,擴展維度有多個方法:

import cv2
import torch
image = cv2.imread(img_path)
image = torch.tensor(image)
print(image.size())
img = image.view(1, *image.size())
print(img.size())
# output:
# torch.Size([h, w, c])
# torch.Size([1, h, w, c])

import cv2
import numpy as np
image = cv2.imread(img_path)
print(image.shape)
img = image[np.newaxis, :, :, :]
print(img.shape)
# output:
# (h, w, c)
# (1, h, w, c)

或(感謝知乎用戶 coldleaf 的補充)

import cv2
import torch
image = cv2.imread(img_path)
image = torch.tensor(image)
print(image.size())
img = image.unsqueeze(dim=0)
print(img.size())
img = img.squeeze(dim=0)
print(img.size())
# output:
# torch.Size([(h, w, c)])
# torch.Size([1, h, w, c])
# torch.Size([h, w, c])

tensor.unsqueeze(dim):擴展維度,dim指定擴展哪個維度。

tensor.squeeze(dim):去除dim指定的且size為1的維度,維度大於1時,squeeze()不起作用,不指定dim時,去除所有size為1的維度。

5、獨熱編碼

在PyTorch中使用交叉熵損失函數的時候會自動把label轉化成onehot,所以不用手動轉化,而使用MSE需要手動轉化成onehot編碼。

import torch
class_num = 8
batch_size = 4
def one_hot(label):
"""
將一維列錶轉換為獨熱編碼
"""
label = label.resize_(batch_size, 1)
m_zeros = torch.zeros(batch_size, class_num)
# 從 value 中取值,然後根據 dim 和 index 給相應比特置賦值
onehot = m_zeros.scatter_(1, label, 1) # (dim,index,value)
return onehot.numpy() # Tensor -> Numpy
label = torch.LongTensor(batch_size).random_() % class_num # 對隨機數取餘
print(one_hot(label))
# output:
[[0. 0. 0. 1. 0. 0. 0. 0.]
[0. 0. 0. 0. 1. 0. 0. 0.]
[0. 0. 1. 0. 0. 0. 0. 0.]
[0. 1. 0. 0. 0. 0. 0. 0.]]

Convert int into one-hot format

注:第11條有更簡單的方法。

6、防止驗證模型時爆顯存

驗證模型時不需要求導,即不需要梯度計算,關閉autograd,可以提高速度,節約內存。如果不關閉可能會爆顯存。

with torch.no_grad():
# 使用model進行預測的代碼
pass

感謝知乎用戶zhaz 的提醒,我把 torch.cuda.empty_cache() 的使用原因更新一下。

這是原回答:

Pytorch 訓練時無用的臨時變量可能會越來越多,導致 out of memory ,可以使用下面語句來清理這些不需要的變量。

官網 上的解釋為:

Releases all unoccupied cached memory currently held by the caching allocator so that those can be used in other GPU application and visible innvidia-smi.torch.cuda.empty_cache()

意思就是PyTorch的緩存分配器會事先分配一些固定的顯存,即使實際上tensors並沒有使用完這些顯存,這些顯存也不能被其他應用使用。這個分配過程由第一次CUDA內存訪問觸發的。

而 torch.cuda.empty_cache() 的作用就是釋放緩存分配器當前持有的且未占用的緩存顯存,以便這些顯存可以被其他GPU應用程序中使用,並且通過 nvidia-smi命令可見。注意使用此命令不會釋放tensors占用的顯存。

對於不用的數據變量,Pytorch 可以自動進行回收從而釋放相應的顯存。

更詳細的優化可以查看 優化顯存使用 和 顯存利用問題

7、學習率衰减

import torch.optim as optim
from torch.optim import lr_scheduler
# 訓練前的初始化
optimizer = optim.Adam(net.parameters(), lr=0.001)
scheduler = lr_scheduler.StepLR(optimizer, 10, 0.1)
# 訓練過程中
for n in n_epoch:
scheduler.step()
...

關鍵語句為lr_scheduler.StepLR(optimizer, 10, 0.1),錶示每過10個epoch,學習率乘以0.1。

8、凍結某些層的參數

參考:Pytorch 凍結預訓練模型的某一層

在加載預訓練模型的時候,我們有時想凍結前面幾層,使其參數在訓練過程中不發生變化。

我們需要先知道每一層的名字,通過如下代碼打印:

net = Network() # 獲取自定義網絡結構
for name, value in net.named_parameters():
print('name: {0},\t grad: {1}'.format(name, value.requires_grad))

假設前幾層信息如下:

name: cnn.VGG_16.convolution1_1.weight, grad: True
name: cnn.VGG_16.convolution1_1.bias, grad: True
name: cnn.VGG_16.convolution1_2.weight, grad: True
name: cnn.VGG_16.convolution1_2.bias, grad: True
name: cnn.VGG_16.convolution2_1.weight, grad: True
name: cnn.VGG_16.convolution2_1.bias, grad: True
name: cnn.VGG_16.convolution2_2.weight, grad: True
name: cnn.VGG_16.convolution2_2.bias, grad: True

後面的True錶示該層的參數可訓練,然後我們定義一個要凍結的層的列錶:

no_grad = [
'cnn.VGG_16.convolution1_1.weight',
'cnn.VGG_16.convolution1_1.bias',
'cnn.VGG_16.convolution1_2.weight',
'cnn.VGG_16.convolution1_2.bias'
]

凍結方法如下:

net = Net.CTPN() # 獲取網絡結構
for name, value in net.named_parameters():
if name in no_grad:
value.requires_grad = False
else:
value.requires_grad = True

凍結後我們再打印每層的信息:

name: cnn.VGG_16.convolution1_1.weight, grad: False
name: cnn.VGG_16.convolution1_1.bias, grad: False
name: cnn.VGG_16.convolution1_2.weight, grad: False
name: cnn.VGG_16.convolution1_2.bias, grad: False
name: cnn.VGG_16.convolution2_1.weight, grad: True
name: cnn.VGG_16.convolution2_1.bias, grad: True
name: cnn.VGG_16.convolution2_2.weight, grad: True
name: cnn.VGG_16.convolution2_2.bias, grad: True

可以看到前兩層的weight和bias的requires_grad都為False,錶示它們不可訓練。

最後在定義優化器時,只對requires_grad為True的層的參數進行更新。

optimizer = optim.Adam(filter(lambda p: p.requires_grad, net.parameters()), lr=0.01)

9、對不同層使用不同學習率

我們對模型的不同層使用不同的學習率。

還是使用這個模型作為例子:

net = Network() # 獲取自定義網絡結構
for name, value in net.named_parameters():
print('name: {}'.format(name))
# 輸出:
# name: cnn.VGG_16.convolution1_1.weight
# name: cnn.VGG_16.convolution1_1.bias
# name: cnn.VGG_16.convolution1_2.weight
# name: cnn.VGG_16.convolution1_2.bias
# name: cnn.VGG_16.convolution2_1.weight
# name: cnn.VGG_16.convolution2_1.bias
# name: cnn.VGG_16.convolution2_2.weight
# name: cnn.VGG_16.convolution2_2.bias

對 convolution1 和 convolution2 設置不同的學習率,首先將它們分開,即放到不同的列錶裏:

conv1_params = []
conv2_params = []
for name, parms in net.named_parameters():
if "convolution1" in name:
conv1_params += [parms]
else:
conv2_params += [parms]
# 然後在優化器中進行如下操作:
optimizer = optim.Adam(
[
{"params": conv1_params, 'lr': 0.01},
{"params": conv2_params, 'lr': 0.001},
],
weight_decay=1e-3,
)

我們將模型劃分為兩部分,存放到一個列錶裏,每部分就對應上面的一個字典,在字典裏設置不同的學習率。當這兩部分有相同的其他參數時,就將該參數放到列錶外面作為全局參數,如上面的weight_decay

也可以在列錶外設置一個全局學習率,當各部分字典裏設置了局部學習率時,就使用該學習率,否則就使用列錶外的全局學習率。

10、模型相關操作

這個內容比較多,我就寫成了一篇文章。

PyTorch 中模型的使用

11、Pytorch內置one_hot函數

感謝 yangyangyang 補充:Pytorch 1.1後,one_hot可以直接用 torch.nn.functional.one_hot。 然後我將Pytorch昇級到1.2版本,試用了下 one_hot 函數,確實很方便。
具體用法如下:

import torch.nn.functional as F
import torch
tensor = torch.arange(0, 5) % 3 # tensor([0, 1, 2, 0, 1])
one_hot = F.one_hot(tensor)
# 輸出:
# tensor([[1, 0, 0],
# [0, 1, 0],
# [0, 0, 1],
# [1, 0, 0],
# [0, 1, 0]])

F.one_hot 會自己檢測不同類別個數,生成對應獨熱編碼。我們也可以自己指定類別數:

tensor = torch.arange(0, 5) % 3 # tensor([0, 1, 2, 0, 1])
one_hot = F.one_hot(tensor, num_classes=5)
# 輸出:
# tensor([[1, 0, 0, 0, 0],
# [0, 1, 0, 0, 0],
# [0, 0, 1, 0, 0],
# [1, 0, 0, 0, 0],
# [0, 1, 0, 0, 0]])
昇級 Pytorch (cpu版本)的命令:
conda install pytorch torchvision -c pytorch
 (希望Pytorch昇級不會影響項目代碼)

 12. 加載多GPU模型

model.load_state_dict(load_torch_state_dict(model_path))

def load_torch_state_dict(model_path, device, module=True):
"""
load pytorch model
:param model_path:
:param module:
:return:
"""
state_dict = None
if model_path:
print('=> loading model from {}'.format(model_path))
state_dict = torch.load(model_path,
map_location=lambda storage, loc: storage.cuda(device))
if module:
state_dict = load_torch_module_state_dict(state_dict)
else:
print("Error:no model file:{}".format(model_path))
exit(0)
return state_dict
def load_torch_module_state_dict(state_dict):
"""
load pytorch model-module,for multi-GPU
:param state_dict:
:return:
"""
# 初始化一個空 dict
new_state_dict = OrderedDict()
# 修改 key,沒有module字段則需要不上,如果有,則需要修改為 module.features
for k, v in state_dict.items():
if 'module' in k:
k = k.replace('module.', '')
new_state_dict[k] = v
return new_state_dict

13.設置隨機數種子

def seed_torch(seed=1029):
random.seed(seed)
os.environ['PYTHONHASHSEED'] = str(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)
torch.cuda.manual_seed_all(seed) # if you are using multi-GPU.
torch.backends.cudnn.benchmark = False
torch.backends.cudnn.deterministic = True
seed_torch()

 

版权声明:本文为[pan_jinquan]所创,转载请带上原文链接,感谢。 https://gsmany.com/2021/08/20210815211145800h.html