文章目录
.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()作用。
- eg:
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()不同点
- .data 是一个属性,二.detach()是一个方法;
- .data 是不安全的此篇文章解析.data为何是不安全的,.detach()是安全的。
参考博客
pytorch中.numpy()、.item()、.cpu()、.detach()及.data的使用
ValueError:only one element tensors can be converted to Python scalars解决办法
Tensor类型的转换:
-
CPU和GPU的Tensor之间转换 : 多用于将数据从gpu()转到cpu(),因为gpu上的数据不能直接操作
- data.cuda():cpu –> gpu
- data.cpu():gpu –> cpu
-
Tensor与Numpy Array之间的转换
-
data.numpy():Tensor –> Numpy.ndarray
- 但是要注意此时需要转换的
data是否有梯度
- data有梯度时候,如果直接numpy(),会报错,正确做法是先.detach()
a = torch.tensor([[1,2,3],[4,5,6]],requires_grad=True,dtype=torch.float64) b = a.numpy() print(b) # RuntimeError: Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead. # 正确做法 b = a.detach().numpy() b 输出: array([[1., 2., 3.], [4., 5., 6.]])
- data没有梯度的时候,可以直接numpy()
a = torch.tensor([[1,2,3],[4,5,6]],dtype=torch.float64) b = a.numpy() print(b) # array([[1., 2., 3.],[4., 5., 6.]])
- 若data在gpu上,需要先将数据移动到cpu上,具体操作如下:
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.]])
- 但是要注意此时需要转换的
-
torch.from_numpy(data):Numpy.ndarray –> Tensor
- Numpy桥,将
numpy.ndarray
转换为pytorch的Tensor
。 返回的张量tensor和numpy的ndarray共享同一内存空间。修改一个会导致另外一个也被修改。返回的张量不能改变大小。
a = np.array([1, 2, 3]) t = torch.from_numpy(a) t # torch.LongTensor([1, 2, 3]) t[0] = -1 a # array([-1, 2, 3]
- Numpy桥,将
-
-
Tensor的基本类型转换
- tensor.long():
- tensor.half():将tensor投射为半精度浮点(16位浮点)类型
- tensor.int():
- tensor.double():
- tensor.float():
- tensor.char():
- tensor.byte():
- tensor.short():
-
Tensor的基本数据类型转换
- type(dtype=None, non_blocking=False, **kwargs):指定类型改变。例如data = data.type(torch.float32)
- type_as(tensor):按照给定的tensor的类型转换类型。