Chapter 1 TENSORS
Tensor 是一种特殊的数据结构,与数组和矩阵非常相似。在Pytorch中,我们使用Tensor对模型的输入和输出以及模型的参数进行编码
Tensor类似于Numpy的ndarray,不同之处在于Tensor可以在GPU或其他专用硬件上运行以加速计算。事实上,张量和Numpy数组通常可以共享相同的底层内存,从而消除了复制数据的需要(详见Numpy中桥的使用)。张量也对自动求导进行了优化(将在后面的Autograd部分看到更多)。
import torch
import numpy as np
1. Tensor的初始化
Tensor可以以多种方式初始化,下面是一些样例。
- 直接由数据初始化Tensor,自动推断数据类型
data = [[1, 2], [3, 4]]
x_data = torch.tensor(data)
print(x_data)
"""
Output:
tensor([[1, 2],
[3, 4]])
"""
- 由Numpy array初始化Tensor
np_array = np.array(data)
x_np = torch.from_numpy(np_array)
print(x_np)
"""
Output:
tensor([[1, 2],
[3, 4]], dtype=torch.int32)
"""
- 由其他Tensor初始化Tensor(除非明确覆盖,否则新Tensor保留参数Tensor的属性(形状、数据类型))
x_ones = torch.ones_like(x_data) # 保留Tensor x_data的属性
print(f"Ones Tensors: \n {x_ones} \n")
x_rand = torch.rand_like(x_data, dtype=torch.float) # 覆盖x_data的数据类型
print(f"Random Tensor \n {x_rand} \n")
"""
Output:
Ones Tensor:
tensor([[1, 1],
[1, 1]])
Random Tensor
tensor([[0.8968, 0.3156],
[0.9684, 0.7991]])
"""
- 使用随机值或常量值初始化Tensor
shape = (2, 3, ) # shape是Tensor维度的元组,制定了输出Tensor的维度
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)
print(f"Random Tensor: \n {rand_tensor} \n")
print(f"Ones Tensor: \n {ones_tensor} \n")
print(f"Zeros Tensor: \n {zeros_tensor} \n")
"""
Output:
Random Tensor:
tensor([[0.1472, 0.9896, 0.5752],
[0.5470, 0.5682, 0.5639]])
Ones Tensor:
tensor([[1., 1., 1.],
[1., 1., 1.]])
Zeros Tensor:
tensor([[0., 0., 0.],
[0., 0., 0.]])
"""
2 Tensor属性
Tensor属性描述了Tensor的形状、数据类型以及存储他们的设备(CPU/GPU)
tensor = torch.rand(2, 4)
print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stroed on: {tensor.device}")
"""
Shape of tensor: torch.Size([2, 4])
Datatype of tensor: torch.float32
Device tensor is stroed on: cpu
"""
3 Tensor操作
Tensor拥有100多种操作,包括转置、索引、切片、数学运算、线性代数、随机采样等。这些运算在GPU上的运算速度远超CPU。
如果GPU可用,则将Tensor运算置于GPU
if torch.cuda.is_avaiable():
tensor = tensor.to('cuda')
4 下面是一些Tensor运算样例
- 标准的类Numpy的索引和切片
"""
tensor[i:] = 0 将Tensor的从第i行到最后一行全部置为零
tensor[i,:] = 0 将tensor的第i行置为零
tensor[:i] = 0 将tensor的前i行置为零
tensor[:i,] = 0 将tensor的前i行置为零
tensor[:,i] = 0 将tensor的第i列置为零
"""
tensor[:, 2] = 0
print(tensor)
"""
Output:
tensor([[1., 1., 0., 1.],
[1., 1., 0., 1.],
[1., 1., 0., 1.],
[1., 1., 0., 1.]])
"""
- 连接Tensor
# 计算元素的乘积(对应元素相乘)
print(f"tensor.mul(tensor) \n {tensor.mul(tensor)} \n")
# 第二种方式
print(f"tensor * tensor \n {tensor * tensor}")
"""
Output:
tensor.mul(tensor)
tensor([[1., 1., 0., 1.],
[1., 1., 0., 1.],
[1., 1., 0., 1.],
[1., 1., 0., 1.]])
tensor * tensor
tensor([[1., 1., 0., 1.],
[1., 1., 0., 1.],
[1., 1., 0., 1.],
[1., 1., 0., 1.]])
"""
- 计算两个Tensor的矩阵乘积
print(f"tensor.matmul(tensor.T) \n {tensor.matmul(tensor.T)} \n")
# 第二种方式
print(f"tensor @ tensor.T \n {tensor @ tensor.T}")
"""
Output:
tensor.matmul(tensor.T)
tensor([[3., 3., 3., 3.],
[3., 3., 3., 3.],
[3., 3., 3., 3.],
[3., 3., 3., 3.]])
tensor @ tensor.T
tensor([[3., 3., 3., 3.],
[3., 3., 3., 3.],
[3., 3., 3., 3.],
[3., 3., 3., 3.]])
"""
- 就地操作:具有_后缀的操作是就地操作。例如:x.copy_(y),x.t_(),将改变x自身。就地操作节省了一些内存,但在计算导数时可能会出现问题,因为会立即丢失历史记录。因此,不推荐使用。
print(tensor, "\n")
tensor.add_(5) # 按元素加5
print(tensor)
"""
Output:
tensor([[1., 1., 0., 1.],
[1., 1., 0., 1.],
[1., 1., 0., 1.],
[1., 1., 0., 1.]])
tensor([[6., 6., 5., 6.],
[6., 6., 5., 6.],
[6., 6., 5., 6.],
[6., 6., 5., 6.]])
"""
- Numpy桥接
CPU上的Tensors和Numpy数组可以共享他们的底层内存地址,改变一个就会改变另一个。
6.1 Tensor to Numpy array
t = torch.ones(5)
print(f"t: {t}")
n = t.numpy() # Tensor转Numpy array
print(f"n: {n}")
"""
Output:
t: tensor([1., 1., 1., 1., 1.])
n: [1. 1. 1. 1. 1.]
"""
此时,如果改变tensor t,那么Numpy array也将随之改变
t.add_(1)
print(f"t: {t}")
print(f"n: {n}")
"""
Output:
t: tensor([1., 1., 1., 1., 1.])
n: [1. 1. 1. 1. 1.]
"""
6.2 Numpy array to Tensor
n = np.ones(5)
t = torch.from_numpy(n)
此时,如果改变Numpy array,那么tensor也将随之改变
np.add(n, 1, out=n)
print(f"t: {t}")
print(f"n: {n}")
"""
Output:
t: tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
n: [2. 2. 2. 2. 2.]
"""