常見問題#
建立日期:2018 年 2 月 15 日 | 最後更新日期:2021 年 8 月 5 日
我的模型報告“cuda runtime error(2): out of memory”#
正如錯誤訊息所示,您的 GPU 記憶體已滿。由於我們在 PyTorch 中經常處理大量資料,小的失誤可能很快導致您的程式耗盡所有 GPU 記憶體;幸運的是,這些情況下的修復通常很簡單。以下是一些常見需要檢查的地方:
在訓練迴圈中不要累積歷史記錄。 預設情況下,涉及需要梯度的變數的計算會保留歷史記錄。這意味著您應該避免在超出訓練迴圈的計算中使用此類變數,例如在跟蹤統計資訊時。相反,您應該分離變數或訪問其底層資料。
有時,區分變數何時可能出現可能不明顯。請考慮以下訓練迴圈(節選自 來源)
total_loss = 0
for i in range(10000):
optimizer.zero_grad()
output = model(input)
loss = criterion(output)
loss.backward()
optimizer.step()
total_loss += loss
在此,由於 loss 是一個具有 autograd 歷史的可微分變數,total_loss 會在您的訓練迴圈中累積歷史記錄。您可以透過將 total_loss += float(loss) 來解決此問題。
此問題的其他示例: 1。
不要保留您不需要的張量和變數。 如果您將張量或變數分配給區域性變數,Python 將在區域性變數超出作用域之前不會將其解除分配。您可以使用 del x 來釋放此引用。同樣,如果您將張量或變數分配給物件的成員變數,它將不會被解除分配,直到物件超出作用域。如果您不保留您不需要的臨時變數,您將獲得最佳的記憶體使用率。
區域性變數的作用域可能比您預期的要大。例如
for i in range(5):
intermediate = f(input[i])
result += g(intermediate)
output = h(result)
return output
在這裡,即使在 h 執行時,intermediate 仍然存活,因為它的作用域超出了迴圈的結尾。要更早地釋放它,您應該在使用完它之後執行 del intermediate。
避免在過大的序列上執行 RNN。 反向傳播透過 RNN 所需的記憶體量與 RNN 輸入的長度成線性關係;因此,如果您嘗試向 RNN 輸入過長的序列,您將耗盡記憶體。
這個現象的技術術語是 時間反向傳播,並且有很多關於如何實現截斷 BPTT 的參考資料,包括在 詞語言模型 示例中;截斷由 repackage 函式處理,具體請參閱 這篇論壇帖子。
不要使用過大的線性層。 線性層 nn.Linear(m, n) 使用 記憶體:也就是說,權重的記憶體需求隨著特徵數量的增加而平方增長。很容易 耗盡您的記憶體(請記住,您至少需要權重大小的兩倍,因為您還需要儲存梯度。)
考慮檢查點。 您可以使用 檢查點 來權衡記憶體和計算。
我的 GPU 記憶體沒有被正確釋放#
PyTorch 使用快取記憶體分配器來加速記憶體分配。因此,nvidia-smi 中顯示的值通常不能反映實際記憶體使用情況。有關 GPU 記憶體管理的更多詳細資訊,請參閱 記憶體管理。
如果即使在 Python 退出後 GPU 記憶體仍未釋放,那很可能是因為一些 Python 子程序仍然存活。您可以透過 ps -elf | grep python 找到它們,並使用 kill -9 [pid] 手動終止它們。
我的 out of memory 異常處理程式無法分配記憶體#
您可能有一些程式碼試圖從 out of memory 錯誤中恢復。
try:
run_model(batch_size)
except RuntimeError: # Out of memory
for _ in range(batch_size):
run_model(1)
但發現當您確實遇到 out of memory 時,您的恢復程式碼也無法分配。這是因為 Python 異常物件持有對引發錯誤的堆疊幀的引用。這阻止了原始張量物件的釋放。解決方案是將您的 OOM 恢復程式碼移到 except 子句之外。
oom = False
try:
run_model(batch_size)
except RuntimeError: # Out of memory
oom = True
if oom:
for _ in range(batch_size):
run_model(1)
我的資料載入器工作程序返回相同的隨機數#
您很可能在資料集和透過 fork 啟動的工作程序中使用了其他庫來生成隨機數。請參閱 torch.utils.data.DataLoader 的文件,瞭解如何使用其 worker_init_fn 選項在工作程序中正確設定隨機種子。
我的迴圈神經網路與資料並行不相容#
在使用 pack sequence -> recurrent network -> unpack sequence 模式並在 Module 中使用 DataParallel 或 data_parallel() 時存在一個細微之處。每個裝置上的 forward() 的輸入僅為整個輸入的一部分。因為 unpack 操作 torch.nn.utils.rnn.pad_packed_sequence() 預設僅填充到它看到的 longest input(即該特定裝置上的 longest input),當結果被收集在一起時會發生大小不匹配。因此,您可以利用 pad_packed_sequence() 的 total_length 引數來確保 forward() 呼叫返回相同長度的序列。例如,您可以編寫
from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_sequence
class MyModule(nn.Module):
# ... __init__, other methods, etc.
# padded_input is of shape [B x T x *] (batch_first mode) and contains
# the sequences sorted by lengths
# B is the batch size
# T is max sequence length
def forward(self, padded_input, input_lengths):
total_length = padded_input.size(1) # get the max sequence length
packed_input = pack_padded_sequence(padded_input, input_lengths,
batch_first=True)
packed_output, _ = self.my_lstm(packed_input)
output, _ = pad_packed_sequence(packed_output, batch_first=True,
total_length=total_length)
return output
m = MyModule().cuda()
dp_m = nn.DataParallel(m)
此外,當批次維度為 dim 1(即 batch_first=False)並使用資料並行時,需要格外小心。在這種情況下,pack_padded_sequence 的第一個引數 padding_input 的形狀將是 [T x B x *],並且應該沿著 dim 1 分散,而第二個引數 input_lengths 的形狀將是 [B],並且應該沿著 dim 0 分散。需要額外的程式碼來操作張量形狀。