PyTorch 01 - Introduction_Tensor

221 阅读6分钟

什么是Tensor?

  • 定义 : Tensor就是专用于处理与数组和矩阵极其相似的数据的元件。在PyTorch里面,我们使用Tensor来为模型的输入和输出、模型参数等进行编码。

  • 其实,Tensor和Numpy的n维阵列极其相似,但是Tensor能在GPU或其他硬件加速器上面运行。实际上,Tensor和Numpy通常都能共享同一底层(物理)地址。为了摒除了拷贝数据的需求,需要一种称为bridge-to-np-label的能力。传感器同样可以对辨别进行自动优化。熟悉Numpy会让你对Tensor学习驾轻就熟。

2-tensor-1.png

%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 卡。

2-tensor-2.png

默认情况下,在 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是和传入数据的阵列共享存储空间的