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!")

8. pytorch 内存泄漏

torch.as_tensor(data, dtype=None,device=None)->Tensor : 为data生成tensor。

如果data已经是tensor,且dtype和device与参数相同,则生成的tensor会和data共享内存。如果data是ndarray,且dtype对应,devices为cpu,则同样共享内存。其他情况则不共享内存。

import torch
import numpy
a = numpy.array([1, 2, 3])
t = torch.as_tensor(a)
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值