常见问题
我的模型报告 “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):
intermdeiate = f(input[i])
result += g(intermediate)
output = h(result)
return output
在本段代码中,即使当 h
在执行时,intermediate
仍然存在,因为它的作用域延伸出了循环的末尾。为了尽早释放它,当你不需要它时,你应该用 del intermediate
删除这个中间值。
不要在太大的序列上运行 RNN。 因为 RNN 反向传播所需的内存量与 RNN 的长度成线性关系;因此,如果尝试向 RNN 提供一个太长的序列时,会耗尽内存。
这一现象的技术术语是时间反向传播Backpropagation through time,关于如何实现截断的 BPTT 有很多参考资料,包括在单词语言模型 word language model 中;截断由这个论坛帖子中描述的 repackage
函数处理。
不要使用太大的线性图层。 线性层 nn.Linear(m,n)
使用 O(nm)
存储器:也就是说,权值的存储器需求随着要素的数量按比例缩放。这种方式会轻易的占用你的内存(并且记住,你将至少需要两倍存储权值的内存量,因为你还需要存储梯度。)
我的 GPU 内存没有正确释放
PyTorch 使用缓存内存分配器来加速内存分配。因此,nvidia-smi
中显示的值通常不会反映真实的内存使用情况。有关 GPU 内存管理的更多细节,请参阅 内存管理 。
如果您的 GPU 内存在 Python 退出后仍未释放,那么很可能某些 Python 子进程仍然存在。你可以通过 ps -elf | grep python
找到它们,并用 kill -9 [pid]
手动杀死它们。
我的多个数据加载器返回相同的随机数
您可能正使用其他库生成数据集中的随机数。例如,当通过 fork
启动工作子进程时,NumPy 的 RNG 会被复制。请参阅 torch.utils.data.DataLoader
的文档,了解如何使用其 worker_init_fn
选项正确设置工作进程中的随机种子。
我的回归网络不能使用数据并行
在具有 DataParallel
或 data_parallel()
的模块中使用 pack sequence -> recurrent network -> unpack sequence
模式时有一个非常微妙的地方。每个设备上的 forward()
的输入只会是整个输入的一部分。由于默认情况下,解包操作 torch.nn.utils.rnn.pad_packed_sequence()
仅填充到其所见的最长输入,即该特定设备上的最长输入,所以在将结果收集在一起时会发生尺寸的不匹配。因此,您可以利用 pad_packed_sequence()
的 total_length
参数来确保 forward()
调用返回相同长度
的序列。例如,你可以写:
from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_squence
class MyModule(nn.Module):
# ... __init__, 以及其他访求
# padding_input 的形状是[B x T x *](batch_first 模式),包含按长度排序的序列
# B 是批量大小
# T 是最大序列长度
def forward(self, padded_input, input_lengths):
total_length = padded_input.size(1) # 获取最大序列长度
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
(第1轴)(即 batch_first = False
)时需要额外注意数据的并行性。在这种情况下,pack_padded_sequence
函数的的第一个参数 padding_input
维度将是 [T x B x *]
,并且应该沿dim 1
(第1轴)分散,但第二个参数 input_lengths
的维度为 [B]
,应该沿dim 0
(第0轴)分散。需要额外的代码来操纵张量的维度。
译者署名
用户名 | 头像 | 职能 | 签名 |
---|---|---|---|
风中劲草 | 翻译 | 人生总要追求点什么 |