Tensor
索引
Torch可以利用索引来访问Tensor的一部分,但索引出来的部分是与原Tensor共享内存的,即:修改一个,另一个也会随之改变。
import torch
x = torch.rand(5,3)
print(x)
y = x[0, :]
y += 1
print(x) # x也会随共享内存的变化而变化
print(y)
print(x[0, :])
tensor([[0.2241, 0.1542, 0.2040],
[0.5804, 0.4996, 0.2491],
[0.6062, 0.9463, 0.7360],
[0.3936, 0.3053, 0.3879],
[0.0753, 0.7341, 0.0423]])
tensor([[1.2241, 1.1542, 1.2040],
[0.5804, 0.4996, 0.2491],
[0.6062, 0.9463, 0.7360],
[0.3936, 0.3053, 0.3879],
[0.0753, 0.7341, 0.0423]])
tensor([1.2241, 1.1542, 1.2040])
tensor([1.2241, 1.1542, 1.2040])
改变形状
用 view() 来改变Tensor的形状:
注:view()返回的tensor与源tensor共享内存,即:改变其中一个,另一个也会随之改变。
(其实是同一个tensor,view只是改变了对这个tensor的观察角度)
import torch
x = torch.rand(5,3)
y = x.view(15)
z = x.view(-1, 5) # -1所指的纬度可以通过其他纬度值推出
print(x.size(), y.size(), z.size())
x += 1
print(x) # x,y,z都会随之改变
print(y)
print(z)
torch.Size([5, 3]) torch.Size([15]) torch.Size([3, 5])
tensor([[1.0032, 1.2100, 1.1447],
[1.0054, 1.6816, 1.6480],
[1.7444, 1.9329, 1.8243],
[1.5565, 1.8191, 1.6048],
[1.8856, 1.9817, 1.3463]])
tensor([1.0032, 1.2100, 1.1447, 1.0054, 1.6816, 1.6480, 1.7444, 1.9329, 1.8243,
1.5565, 1.8191, 1.6048, 1.8856, 1.9817, 1.3463])
tensor([[1.0032, 1.2100, 1.1447, 1.0054, 1.6816],
[1.6480, 1.7444, 1.9329, 1.8243, 1.5565],
[1.8191, 1.6048, 1.8856, 1.9817, 1.3463]])
如果我们想返回一个真正新的副本(不共享内存),推荐先用 clone() 创造一个副本,然后再使用 view()。Pytorch还提供了一个 reshape() 可以改变形状,但此函数并不能保证返回的是其拷贝,故而不推荐使用。
使用 clone() 会被记录到计算图中,即:梯度回传到副本时也会传到源Tensor。
import torch
x = torch.rand(5,3)
x_cp = x.clone().view(3,5)
x += 1
print(x)
print(x_cp)
tensor([[1.7448, 1.9305, 1.3424],
[1.7032, 1.4505, 1.3283],
[1.6731, 1.5162, 1.2067],
[1.0565, 1.1451, 1.9345],
[1.5496, 1.5229, 1.7395]])
tensor([[0.7448, 0.9305, 0.3424, 0.7032, 0.4505],
[0.3283, 0.6731, 0.5162, 0.2067, 0.0565],
[0.1451, 0.9345, 0.5496, 0.5229, 0.7395]])
还有一个常用函数 item(),它可以将一个标量Tensor转换成一个数字。
x = torch.randn(1)
print(x)
print(x.item())
tensor([1.1236])
1.1236071586608887
总结:索引、view操作是不会开辟新内存的,而y=x+y这样的运算是会新开内存的,然后将y指向新内存。
Tensor与Numpy互相转换
Tensor -> Numpy : numpy()
Numpy -> Tensor : from_numpy() or torch.tensor()
numpy()和from_numpy() 这两个函数产生的tensor和numpy是共享内存的,改变其中一个时,另一个也会随之改变。
而 torch.tensor() 总是会进行数据拷贝,所以返回的tensor与原来的数据不再共享内存。
Tensor -> Numpy : numpy()
import torch
import numpy as np
a = torch.ones(5)
b = a.numpy()
print(a, b)
a += 1
print(a, b)
b += 1
print(a, b)
tensor([1., 1., 1., 1., 1.]) [1. 1. 1. 1. 1.]
tensor([2., 2., 2., 2., 2.]) [2. 2. 2. 2. 2.]
tensor([3., 3., 3., 3., 3.]) [3. 3. 3. 3. 3.]
Numpy -> Tensor : from_numpy()
import torch
import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
print(a, b)
a += 1
print(a, b)
b += 1
print(a, b)
[1. 1. 1. 1. 1.] tensor([1., 1., 1., 1., 1.], dtype=torch.float64)
[2. 2. 2. 2. 2.] tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
[3. 3. 3. 3. 3.] tensor([3., 3., 3., 3., 3.], dtype=torch.float64)
Numpy -> Tensor : from_numpy()
import torch
import numpy as np
a = np.ones(5)
c = torch.tensor(a)
a += 1
print(a)
print(c)
[2. 2. 2. 2. 2.]
tensor([1., 1., 1., 1., 1.], dtype=torch.float64)
Tensor on CPU/GPU
用方法 to() 可以将Tensor在CPU/GPU之间相互移动。
# 以下代码只有在Pytorch GPU版本上才会执行
import torch
x = torch.rand(5,3) # 在CPU上
if torch.cuda.is_available():
device = torch.device("cuda")
y = torch.ones_like(x, device=device) # 直接创建一个在GPU上的Tensor
x = x.to(device) # 将变量移到GPU上
z = x + y
print(z)
print(z.to("cpu", torch.double)) # to()还可以更改数据类型