.numpy()、.item()、.cpu()、.clone()、.detach()及.data的使用
本文主要介绍 .numpy()、.item()、.cpu()、.clone()、.detach()及.data这些常用的类型转换的函数或者方法的区分,以及在实际训练中遇到的实际问题。
.item()
-
item() 将一个Tensor变量转换为python标量(
int float
等单个的数值,但是不能是数组)常用于用于深度学习训练时,将loss值转换为标量并加,以及进行分类任务,计算准确值值时需要(如例一)。 item()是可以直接从gpu上转换为标量,看下面例一的loss.item() && 例二 运行结果。
# 例一 在gpu上运行 计算loss值和acc值
optimizer.zero_grad()
outputs = model(data)
loss = F.cross_entropy(outputs, label)
acc = (outputs.argmax(dim=1) == label).sum().cpu().item() / len(labels) #这里也用到了.item(),可以先转换到cpu
loss.backward()
optimizer.step()
train_loss += loss.item() #这里用到了.item(),也可以直接item()
train_acc += acc
# 例二
a = torch.tensor([5],requires_grad=True,dtype=torch.float64,device='cuda')
print(a)
b = a.item()
print(b)
#输出:
# tensor([5.], device='cuda:0', dtype=torch.float64, requires_grad=True)
# 5.0
.cpu()
-
.cpu() 将数据的处理设备从其他设备(如.cuda()拿到cpu上),不会改变变量类型,转换后仍然是Tensor变量。 为什么需要这一步? 因为gpu上的数组不能直接进行转换类型的操作。
a = torch.tensor([[1,2,3],[4,5,6]],requires_grad=True,dtype=torch.float64).cuda()
# 正确做法
b = a.cpu().detach().numpy()
b
输出: array([[1., 2., 3.],
[4., 5., 6.]])
.numpy()
-
numpy()
Tensor.numpy()
将Tensor转化为ndarray,这里的Tensor可以是标量(即item()的作用)或者 向量(与item()不同,一般是矩阵),转换前后的dtype不会改变,但是要注意能直接numpy()的tensor的前提是没有梯度·(requires_grad=False
)的。若有梯度(比如requires_grad=True
,或者在神经网络的前向传播过程中),则需要 先.detach()再进行numpy()。 在后面还会讲解.detach()作用。
a = torch.tensor([[1.,2.]])
a_numpy = a.numpy() #[[1., 2.]]
a = torch.tensor(1.5)
a_numpy = a.numpy() #1.5
.clone()
-
.clone()函数可以返回一个完全相同的tensor,新的tensor开辟新的内存,但是仍然留在计算图中。**所以它复制完会保留原来的梯度,原来有梯度那么clone完也是有梯度的。**而 没有梯度 又可以直接用.numpy()转换为数组,用.clone()也不行,这个暂时感觉用的很少。
- 下列是有梯度不能用numpy() 的一个错误案例。在前向传播过程中.clone()依旧有梯度,所以不可numpy()
.detach()
-
.detach() 函数可以返回一个完全相同的tensor,新的tensor开辟与旧的tensor共享内存,新的tensor会脱离计算图,不会牵扯梯度计算。也就是
requires_grad=False
, 因此可以 接着进行numpy() 的操作,解决了numpy()需要建立在无梯度的tensor的基础上的问题。
举例:前向传播过程中,如果在传播过程中用.detach()
生成了新的变量,然后用这个新的变量继续往下传播,这样会导致梯度反向传播到这里就不能继续,前面的参数也不会发生改变了。
# enc_outputs : 前向传播产生的变量
# enc_outputs1 : 过程中自己生成的变量
for i in enc_outputs:
a = i[0].cpu().detach().numpy()
enc_outputs1.append(a)
return torch.tensor(enc_outputs1).to(device), enc_self_attns
# 进行训练的时候enc_outputs1脱离了计算,不进行反向传播了
- .detach()就是返回一个新的tensor,并且这个tensor是从当前的计算图中分离出来的。但是返回的tensor和原来的tensor是共享内存空间的。当model不希望更新某部分的参数的时候,就可以用
.detach()
一下,如下例子:
如果A网络的输出被喂给B网络作为输入, 如果我们希望在梯度反传的时候只更新B中参数的值,而不更新A中的参数值,这时候就可以使用.detach()
a = A(input)
a = a.deatch() # 或者a.detach_()进行in_place操作
out = B(a)
loss = criterion(out, labels)
loss.backward()
.data
-
.data — tensor .data 返回和 x 的相同数据 tensor,而且这个新的tensor和原来的tensor是共用数据的,一者改变,另一者也会跟着改变,而且新分离得到的tensor的require s_grad = False, 即不可求导的。(这一点其实detach是一样的)
a = torch.tensor([1.0], requires_grad=True)
b = a.data
print(b, b.requires_grad)
## 输出为: tensor([1.]) False
.data和.detach()不同点
参考博客
pytorch中.numpy()、.item()、.cpu()、.detach()及.data的使用
ValueError:only one element tensors can be converted to Python scalars解决办法
torch.Tensor
Tensor类型的转换:
torch.Tensor