什么是Tensor?
-
定义 : Tensor就是专用于处理与数组和矩阵极其相似的数据的元件。在PyTorch里面,我们使用Tensor来为模型的输入和输出、模型参数等进行编码。
-
其实,Tensor和Numpy的n维阵列极其相似,但是Tensor能在GPU或其他硬件加速器上面运行。实际上,Tensor和Numpy通常都能共享同一底层(物理)地址。为了摒除了拷贝数据的需求,需要一种称为bridge-to-np-label的能力。传感器同样可以对辨别进行自动优化。熟悉Numpy会让你对Tensor学习驾轻就熟。
%matplotlib inline
import torch as th
import numpy as np
一、初始化一个Tensor
Tensor有多重初始化方式,可以看以下例子:
1. 直接导入数据
Tensor可以直接从数据导入,数据类型是自动继承传入数据的类型
data = [[1, 2],[3, 4]]
x_data = th.tensor(data)
x_data
tensor([[1, 2],
[3, 4]])
2. 从Numpy阵列中导入数据
可以从 NumPy 数组创建 Tensors,反之亦然。因为 numpy‘ np _ array’和Tensor‘ x _ np’在这里共享相同的内存位置,改变其中一个的值会改变另一个。
np_array = np.array(data)
x_np = th.from_numpy(np_array)
print(f"Numpy np_array value: \n {np_array} \n")
print(f"Tensor x_np value: \n {x_np} \n")
np.multiply(np_array, 2, out=np_array) # 传入的三个参数依次为 输入阵列 标量 输出阵列
print(f"Numpy np_array after * 2 operation: \n {np_array} \n")
print(f"Tensor x_np value after modifying numpy array: \n {x_np} \n") # 从numpy多维阵列中导入数据的Tensor确实共享数据
Numpy np_array value:
[[1 2]
[3 4]]
Tensor x_np value:
tensor([[1, 2],
[3, 4]], dtype=torch.int32)
Numpy np_array after * 2 operation:
[[2 4]
[6 8]]
Tensor x_np value after modifying numpy array:
tensor([[2, 4],
[6, 8]], dtype=torch.int32)
3. 从另一个Tensor中导入数据
除非显式重写,否则新张量保留参数张量的属性(形状、数据类型)。
a = np.arange(4).reshape((2, 2))
T_a = th.tensor(a)
T_a
tensor([[0, 1],
[2, 3]], dtype=torch.int32)
T_b = T_a # 由此可知,
T_b
tensor([[0, 1],
[2, 3]], dtype=torch.int32)
x_ones = torch.ones_like(x_data) # 继承属性但不继承数据
print(f"Ones Tensor: \n {x_ones} \n")
x_rand = torch.rand_like(x_data, dtype=torch.float) # 显示重写,重载参数Tensor的数据类型
print(f"Random Tensor: \n {x_rand} \n")
Ones Tensor:
tensor([[1, 1],
[1, 1]])
Random Tensor:
tensor([[0.9755, 0.5511],
[0.2797, 0.3115]])
4. 使用随机或常数阵列
shape是Tensor维数的一个元组。在下面的函数中,它确定了输出张量的维数。shape表示张量中的行和列的数目。例如 shape = (# of rows,# of columns)。
shape = (2,3) # 定义Tensor的结构
# 下列方法和numpy类似
rand_tensor = torch.rand(shape) # 随机数范围为[0, 1)
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}")
Random Tensor:
tensor([[0.4092, 0.6632, 0.8902],
[0.3870, 0.7510, 0.8973]])
Ones Tensor:
tensor([[1., 1., 1.],
[1., 1., 1.]])
Zeros Tensor:
tensor([[0., 0., 0.],
[0., 0., 0.]])
二、Tensor属性
Tensor属性描述它们的shape、数据类型和存储它们的设备。
tensor = torch.rand(3,4)
print(f"Shape of tensor: {tensor.shape}")
# Tensor的默认数据类型为torch.float32
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stored on: {tensor.device}")
Shape of tensor: torch.Size([3, 4])
Datatype of tensor: torch.float32
Device tensor is stored on: cpu
三、Tensor选项
Tensor操作数量超过100,包括算术,线性代数,矩阵操作(转置,索引,切片)。在此pytorch.org/docs/stable… 查阅详尽的说明,以便抽样及复检。
每一个操作都可以在 GPU 上运行(通常比在 CPU 上运行速度更快)。
- Cpu 最多有16个核。核心是执行实际计算的单位。每个核心按顺序处理任务(一次处理一个任务)。
- GPU 有1000个内核。GPU 核心处理并行处理计算。任务在不同的核之间进行划分和处理。这使得 gpu 在大多数情况下比 cpu 更快。大数据比小数据具有更好的 gpu 预形式。GPU 通常用于图形或神经网络的高强度计算(我们将在后面的神经网络单元中看到更多)。
- PyTorch可以使用 Nvidia CUDA 库来利用他们的 GPU 卡。
默认情况下,在 CPU 上创建张量。也可以将张量在 gpu 上计算, 为此,需要使用.to方法(在检查 GPU 的可用性之后)。请记住,跨设备复制大张量可能会耗费时间和内存方面!
# We move our tensor to the GPU if available
if torch.cuda.is_available():
tensor = tensor.to('cuda')
尝试列表中的一些操作。如果您熟悉 numpy-api,您会发现使用 Tensor API 是轻而易举的事情。
四、类似Numpy的标准索引和切片操作
tensor = torch.ones(4, 4)
print('First row: ',tensor[0])
print('First column: ', tensor[:, 0]) # 此处的,使用来分隔维度的
print('Last column:', tensor[..., -1]) # 一定要使用...才能自动补齐其他维,不能省略为空格
tensor[:,1] = 0 # 将第二列赋值为 0
print(tensor)
First row: tensor([1., 1., 1., 1.])
First column: tensor([1., 1., 1., 1.])
Last column: tensor([1., 1., 1., 1.])
tensor([[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.]])
a = th.zeros([4, 4])
a[::2,::2] = 1 # 切片操作是和python一致的,单个参数分别为:起始点、终点前一个、步长
a
tensor([[1., 0., 1., 0.],
[0., 0., 0., 0.],
[1., 0., 1., 0.],
[0., 0., 0., 0.]])
1. 拼接Tensor
可以使用 torchi.cat 将一个张量序列连接到给定维度上。Stack 是另一个与 torch.cat 略有不同的张量引入方式。
tensor = th.ones((4, 1))
t1 = torch.cat([tensor, tensor, tensor], dim=1) #dim = 1,表示拼接第二维就是扩充第二维
print(t1)
t1.shape
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
torch.Size([4, 3])
tensor = th.ones((4, 1))
t1 = torch.cat([tensor, tensor, tensor], dim=0) #dim = 0,表示扩充第一维
print(t1)
t1.shape
tensor([[1.],
[1.],
[1.],
[1.],
[1.],
[1.],
[1.],
[1.],
[1.],
[1.],
[1.],
[1.]])
torch.Size([12, 1])
2. 算术运算
tensor = th.ones((4, 4))
tensor[::2,::2] = 0
# This computes the matrix multiplication between two tensors. y1, y2, y3 will have the same value
# 矩阵乘法,下面的 y1、y2、y3三者值相同
y1 = tensor @ tensor.T # 矩阵乘法,和阵列乘法区别开来
y2 = tensor.matmul(tensor.T) # matmul(tensor.T)等价于 tensor @ tensor.T,都是矩阵乘法
y3 = torch.rand_like(tensor) # 以tensor的属性初始化 y3,但填入数据为[0, 1)
torch.matmul(tensor, tensor.T, out=y3)
# This computes the element-wise product. z1, z2, z3 will have the same value
# 下面是阵列乘法,z1、z2、z3三值相同
z1 = tensor * tensor
z2 = tensor.mul(tensor)
z3 = torch.rand_like(tensor)
torch.mul(tensor, tensor, out=z3)
tensor([[0., 1., 0., 1.],
[1., 1., 1., 1.],
[0., 1., 0., 1.],
[1., 1., 1., 1.]])
3. 单元张量
如果有一个单元素张量,例如通过将张量的所有值聚合成一个值,则可以使用 item ()将其转换为 Python 数值
agg = tensor.sum()
agg_item = agg.item() # .item()转化为python的数据类型
print(agg_item, type(agg_item))
12.0 <class 'float'>
type(agg) # torch.Tensor类
torch.Tensor
4. 原地操作
将结果存储到操作数中的操作就地调用。它们由一个 _ 后缀表示。例如: x.copy _ (y) ,x.t _ () ,将更改 x。
- 注意: 原地操作可以节省一些内存,但在计算导数时可能会出现问题,因为会立即丢失历史记录。因此,不建议使用原地操作。
print(tensor, "\n")
tensor.add_(5)
print(tensor)
tensor([[0., 1., 0., 1.],
[1., 1., 1., 1.],
[0., 1., 0., 1.],
[1., 1., 1., 1.]])
tensor([[5., 6., 5., 6.],
[6., 6., 6., 6.],
[5., 6., 5., 6.],
[6., 6., 6., 6.]])
五、桥接 NumPy
CPU 和 NumPy 数组上的张量可以共享它们的底层内存位置,更改其中一个将更改另一个。
1. 张量到 NumPy 数组
t = torch.ones(5)
print(f"t: {t}")
n = t.numpy()
print(f"n: {n}")
t: tensor([1., 1., 1., 1., 1.])
n: [1. 1. 1. 1. 1.]
张量的变化能从Numpy阵列上反映
t[1::3] = 159 # 注意 [:-1] 和 [:] 的区别,前者不能取到最后一个元素
print(f"t: {t}")
print(f"n: {n}")
t: tensor([ 1., 159., 1., 1., 159.])
n: [ 1. 159. 1. 1. 159.]
2. NumPy array to Tensor
n = np.ones(5)
t = torch.from_numpy(n)
NumPy 数组中的变化反映在张量中。
np.add(n, 1, out=n)
print(f"t: {t}")
print(f"n: {n}")
t: tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
n: [2. 2. 2. 2. 2.]
上面的两个例子都说明了:利用numpy阵列生成的tensor是和传入数据的阵列共享存储空间的