# 常见问题

### 我的模型报告 "cuda runtime error(2): out of memory"

如错误消息所示，您的`GPU`上的内存不足。由于我们经常在`PyTorch`中处理大量数据，因此小错误可能会迅速导致程序耗尽所有 GPU；幸运的是，这些情况下的修复通常很简单。这里有几个常见的事情要检查：

**不要在训练循环中累积历史记录**。默认情况下，当计算涉及到有需要梯度的变量时，此计算过程将保留运算的历史记录。这意味着您应该避免在计算中使用这些变量，这些变量的生存期将超出您的训练循环（例如在跟踪统计数据时）。您应该分离该变量或访问其底层数据。

有时，当可微分变量可能发生时，它可能并不明显。考虑以下训练循环（从[源代码](https://discuss.pytorch.org/t/high-memory-usage-while-training/162)节选）：

```py
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](https://discuss.pytorch.org/t/resolved-gpu-out-of-memory-error-with-batch-size-1/3719)

**删除你不需要的张量和变量**。如果将一个张量或变量分配到一个局部栈，在局部栈超出作用域之前，Python 都不会将其释放。您可以使用 `del x` 释放该引用。同样，如果将一个张量或变量赋值给对象的成员变量，直到该对象超出作用域之前，它将不会释放。如果你及时删除你不需要的临时变量，你将获得最佳的内存使用率。

作用域的范围可能比你想象的要大。例如：

```py
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](https://en.wikipedia.org/wiki/Backpropagation_through_time)，关于如何实现截断的 BPTT 有很多参考资料，包括在单词语言模型 [word language model](https://github.com/pytorch/examples/tree/master/word_language_model) 中;截断由[这个论坛帖子](https://discuss.pytorch.org/t/help-clarifying-repackage-hidden-in-word-language-model/226)中描述的 `repackage` 函数处理。

**不要使用太大的线性图层。** 线性层 `nn.Linear(m，n)` 使用 `O(nm)` 存储器：也就是说，权值的存储器需求随着要素的数量按比例缩放。这种方式会轻易的占用你的内存（并且记住，你将至少需要两倍存储权值的内存量，因为你还需要存储梯度。）

### 我的 GPU 内存没有正确释放

PyTorch 使用缓存内存分配器来加速内存分配。因此，`nvidia-smi` 中显示的值通常不会反映真实的内存使用情况。有关 GPU 内存管理的更多细节，请参阅 [内存管理](http://pytorch.org/docs/stable/notes/cuda.html#cuda-memory-management) 。

如果您的 GPU 内存在 Python 退出后仍未释放，那么很可能某些 Python 子进程仍然存在。你可以通过 `ps -elf | grep python` 找到它们，并用 `kill -9 [pid]` 手动杀死它们。

### 我的多个数据加载器返回相同的随机数

您可能正使用其他库生成数据集中的随机数。例如，当通过 `fork` 启动工作子进程时，NumPy 的 RNG 会被复制。请参阅 [`torch.utils.data.DataLoader`](http://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader) 的文档，了解如何使用其 `worker_init_fn` 选项正确设置工作进程中的随机种子。

### 我的回归网络不能使用数据并行

在具有 `DataParallel` 或 `data_parallel()` 的模块中使用 `pack sequence -&gt; recurrent network -&gt; unpack sequence` 模式时有一个非常微妙的地方。每个设备上的 `forward()` 的输入只会是整个输入的一部分。由于默认情况下，解包操作 `torch.nn.utils.rnn.pad_packed_sequence()` 仅填充到其所见的最长输入，即该特定设备上的最长输入，所以在将结果收集在一起时会发生尺寸的不匹配。因此，您可以利用 `pad_packed_sequence()` 的 `total_length` 参数来确保 `forward()` 调用返回相同长度

的序列。例如，你可以写：

```py
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 轴）分散。需要额外的代码来操纵张量的维度。

### 译者署名

| 用户名 | 头像 | 职能 | 签名 |
| --- | --- | --- | --- |
| 风中劲草 | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 |