doc22_016.md 6.7 KB
Newer Older
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
1 2
# 常见问题

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
3 4 5 6 7 8
> 原文:[`pytorch.org/docs/stable/notes/faq.html`](https://pytorch.org/docs/stable/notes/faq.html)> 
>
> 译者:[飞龙](https://github.com/wizardforcel)
>
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
9

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
10
## 我的模型报告“cuda 运行时错误(2):内存不足”
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
11

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
12
正如错误消息所示,您的 GPU 上的内存已耗尽。由于我们在 PyTorch 中经常处理大量数据,小错误可能迅速导致程序使用完所有 GPU 的内存;幸运的是,在这些情况下修复通常很简单。以下是一些常见的检查事项:
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

**不要在整个训练循环中积累历史。**默认情况下,涉及需要梯度的变量的计算将保留历史记录。这意味着您应该避免在超出训练循环的计算中使用这些变量,例如跟踪统计数据。相反,您应该分离变量或访问其基础数据。

有时,当可微变量出现时,可能并不明显。考虑以下训练循环(摘自[source](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 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
29
在这里,`total_loss`在整个训练循环中积累历史,因为`loss`是一个具有自动求导历史的可微变量。您可以通过编写 total_loss += float(loss)来修复这个问题。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
30 31 32

此问题的其他实例:[1](https://discuss.pytorch.org/t/resolved-gpu-out-of-memory-error-with-batch-size-1/3719)

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
33
**不要保留不需要的张量和变量。**如果将 Tensor 或 Variable 分配给本地变量,Python 将不会释放,直到本地变量超出作用域。您可以通过使用`del x`来释放此引用。同样,如果将 Tensor 或 Variable 分配给对象的成员变量,直到对象超出作用域,它将不会释放。如果不保留不需要的临时变量,您将获得最佳的内存使用情况。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
34 35 36 37 38 39 40 41 42 43 44 45 46

本地变量的作用域可能比您预期的要大。例如:

```py
for i in range(5):
    intermediate = f(input[i])
    result += g(intermediate)
output = h(result)
return output 
```

在这里,`intermediate`即使在`h`执行时仍然存在,因为其作用域延伸到循环结束之后。为了更早释放它,您应该在完成后`del intermediate`

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
47
**避免在太大的序列上运行 RNN。**通过 RNN 进行反向传播所需的内存量与 RNN 输入的长度成线性关系;因此,如果尝试向 RNN 提供太长的序列,将会耗尽内存。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
48

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
49
这种现象的技术术语是[时间反向传播](https://en.wikipedia.org/wiki/Backpropagation_through_time),有很多关于如何实现截断 BPTT 的参考资料,包括在[word 语言模型](https://github.com/pytorch/examples/tree/master/word_language_model)示例中;截断由`repackage`函数处理,如[this forum post](https://discuss.pytorch.org/t/help-clarifying-repackage-hidden-in-word-language-model/226)中所述。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
50

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
51
**不要使用太大的线性层。**线性层`nn.Linear(m, n)`使用$O(nm)$内存:也就是说,权重的内存需求与特征数量呈二次比例。通过这种方式很容易[耗尽内存](https://github.com/pytorch/pytorch/issues/958)(请记住,您至少需要两倍于权重大小的内存,因为您还需要存储梯度)。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
52 53 54

**考虑使用检查点。**您可以通过使用[checkpoint](https://pytorch.org/docs/stable/checkpoint.html)来在内存和计算之间进行权衡。

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
55
## 我的 GPU 内存没有正确释放
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
56

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
57
PyTorch 使用缓存内存分配器来加速内存分配。因此,`nvidia-smi`中显示的值通常不反映真实的内存使用情况。有关 GPU 内存管理的更多详细信息,请参见内存管理。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
58

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
59
如果即使在 Python 退出后 GPU 内存仍未释放,很可能仍有一些 Python 子进程在运行。您可以通过`ps -elf | grep python`找到它们,并使用`kill -9 [pid]`手动杀死它们。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
60

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
61
## 我的内存不足异常处理程序无法分配内存
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
62 63 64 65 66 67 68 69 70 71 72

您可能有一些代码尝试从内存不足错误中恢复。

```py
try:
    run_model(batch_size)
except RuntimeError: # Out of memory
    for _ in range(batch_size):
        run_model(1) 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
73
但是发现当内存不足时,您的恢复代码也无法分配。这是因为 Python 异常对象保留了引用到引发错误的堆栈帧。这会阻止原始张量对象被释放。解决方案是将 OOM 恢复代码移出`except`子句。
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
74 75 76 77 78 79 80 81 82 83 84 85 86

```py
oom = False
try:
    run_model(batch_size)
except RuntimeError: # Out of memory
    oom = True

if oom:
    for _ in range(batch_size):
        run_model(1) 
```

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
87
## 我的数据加载器工作程序返回相同的随机数
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
88

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
89
您可能正在使用其他库在数据集中生成随机数,并且工作程序子进程是通过`fork`启动的。请查看`torch.utils.data.DataLoader`的文档,了解如何使用其`worker_init_fn`选项正确设置工作程序中的随机种子。## 我的循环网络无法与数据并行性一起工作
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
90

绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
91
在使用`Module``DataParallel``data_parallel()`时,使用`pack sequence -> recurrent network -> unpack sequence`模式存在一个微妙之处。每个设备上的`forward()`的输入只会是整个输入的一部分。因为解包操作`torch.nn.utils.rnn.pad_packed_sequence()`默认只填充到它看到的最长输入,即该特定设备上的最长输入,当结果被收集在一起时会发生大小不匹配。因此,您可以利用`pad_packed_sequence()``total_length`参数,以确保`forward()`调用返回相同长度的序列。例如,您可以编写:
绝不原创的飞龙's avatar
绝不原创的飞龙 已提交
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116

```py
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) 
```

此外,在批处理维度为`1`(即`batch_first=False`)时,需要特别小心处理数据并行性。在这种情况下,`pack_padded_sequence`的第一个参数`padding_input`的形状将为`[T x B x *]`,应该沿着维度`1`进行分散,但第二个参数`input_lengths`的形状将为`[B]`,应该沿着维度`0`进行分散。需要额外的代码来操作张量形状。