1. 程式人生 > 其它 >PyTorch 寫程式碼時的一些坑(持續更新)

PyTorch 寫程式碼時的一些坑(持續更新)

1. nn.Module.cuda() 和 Tensor.cuda()

無論是對於模型還是資料,cuda() 都能實現從CPU到GPU的記憶體遷移,但是他們的作用效果有所不同。

Model:

model = model.cuda()
model.cuda()

上面兩句能夠達到一樣的效果,即對model自身進行的記憶體遷移

Tensor:

model = Model()
tensor = torch.zeros([2, 3, 10, 10])
model.cuda()
tensor.cuda()
tensor_cuda = tensor.cuda()
model(tensor)	# 會報錯
model(
tensor_cuda) # 正常執行

和 nn.Module 不同,呼叫 tensor.cuda 只是返回這個 tensor 物件在 GPU 記憶體上的拷貝,而不會對自身進行改變。因此必須對 tensor 進行重新賦值,即 tensor = tensor.cuda()

2. PyTorch 0.4 計算累積損失的不同

以廣泛使用的模式 total_loss += loss.data[0] 為例。Python0.4.0 之前,loss 是一個封裝了 (1,) 張量的 Variable,但 Python0.4.0 的 loss 現在是一個零維的標量。對標量進行 索引是沒有意義的(似乎會報 invalid index to scalar variable 的錯誤)。使用 loss.item() 可以從標量中獲取 Python 數字。所以改為:

total_loss = total_loss + loss.item()

如果在累加損失時未將其轉換為 Python 數字,則可能出現程式記憶體使用量增加的情況。這是因為上面表示式的右側原本是一個 Python 浮點數,而它現在是一個零維張量。因此,總損失累加了張量和它們的梯度歷史,這可能會產生很大的 autograd 圖,耗費記憶體和計算資源。

3. 自適應 CPU 和 GPU裝置的 trick

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model =
Model().to(device) total_loss = 0 for input, target in train_loader: input, target = input.to(device), target.to(device) ... total_loss = total_loss + loss.item() with torch.no_grad(): for input, target in test_loader: ...

4. torch.Tensor.detach的使用

官方說明:Returns a new Tensor, detached from the current graph,
The result will never require gradient

假設有模型 A 和模型 B,我們需要將 A 的輸出作為 B 的輸入,但訓練時我們只訓練模型 B. 那麼可以這樣做:

input_B = output_A.detach

它可以使兩個計算圖的梯度傳遞斷開,從而實現我們所需的功能。

5. pytorch中loss函式的引數設定

以CrossEntropyLoss為例:

CrossEntropyLoss(self, weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='elementwise_mean')
  • 若 reduce = False,那麼 size_average 引數失效,直接返回向量形式的 loss,即batch中每個元素對應的loss.
  • 若 reduce = True,那麼 loss 返回的是標量:
  • 如果 size_average = True,返回 loss.mean.
  • 如果 size_average = False,返回 loss.sum.
  • weight : 輸入一個1D的權值向量,為各個類別的loss加權,如下公式所示:
    在這裡插入圖片描述
  • ignore_index : 選擇要忽視的目標值,使其對輸入梯度不作貢獻。如果 size_average = True,那麼只計算不被忽視的目標的loss的均值。
  • reduction : 可選的引數有:‘none’ | ‘elementwise_mean’ | ‘sum’, 正如引數的字面意思。

6. 多GPU的處理機制

使用多GPU時,應該記住 PyTorch 的處理邏輯是:

  1. 在各個GPU上初始化模型。

  2. 前向傳播時,把batch分配到各個GPU上進行計算。

  3. 得到的輸出在主GPU上進行彙總,計算loss並反向傳播,更新主GPU上的權值。

  4. 把主GPU上的模型複製到其它GPU上。

7. 訓練時損失出現nan的問題

訓練模型時出現損失為 nan 的情況

可能導致梯度出現 nan 的三個原因:

  1. 梯度爆炸。也就是說梯度數值超出範圍變成 nan. 通常可以調小學習率、加 BN 層或者做梯度裁剪來試試看有沒有解決。

  2. 損失函式或者網路設計。比方說,出現了除 0,或者出現一些邊界情況導致函式不可導,比方說log(0)、sqrt(0).

  3. 髒資料。可以事先對輸入資料進行判斷看看是否存在 nan.

補充一下nan資料的判斷方法:

注意!像 nan 或者 inf 這樣的數值不能使用 == 或者 is 來判斷!為了安全起見統一使用 math.isnan 或者 numpy.isnan 吧。

import numpy as np
if np.any(np.isnan(input.cpu().numpy())):
	print("Input data has NaN!")
if(np.isnan(loss.item())):
	print("Loss value is NaN!")