一. 概念:张量、算子
张量:在深度学习的实践中,我们通常使用向量或矩阵运算来提高计算效率。比如w1x1+w2x2+⋯+wNxNw 的计算可以用w⊤x
来代替(其中w=[w1w2⋯wN]⊤,x=[x1x2⋯xN]⊤,这样可以充分利用计算机的并行计算能力,特别是利用GPU来实现高效矩阵运算。
在深度学习框架中,数据经常用张量(Tensor)的形式来存储。张量是矩阵的扩展与延伸,可以认为是高阶的矩阵。1阶张量为向量,2阶张量为矩阵。如果你对Numpy熟悉,那么张量是类似于Numpy的多维数组(ndarray)的概念,可以具有任意多的维度。
张量的大小可以用形状(shape)来描述。比如一个三维张量的形状是 [2,2,5][2, 2, 5][2,2,5],表示每一维(也称为轴(axis))的元素的数量,即第0轴上元素数量是2,第1轴上元素数量是2,第2轴上的元素数量为5。
注意:这里的“维度"是”阶“的概念,和线性代数中向量的维度不同
算子:深度学习算法由一个个计算单元组成,我们称这些计算单元为算子(Operator,简称OP)。在网络模型中,算子对应层中的计算逻辑,例如:卷积层(Convolution Layer)是一个算子;全连接层(Fully-connected Layer, FC layer)中的权值求和过程,是一个算子。再例如:tanh、ReLU等,为在网络模型中被用做激活函数的算子。
二. 使用pytorch实现张量运算
1.2 张量
1.2.1 创建张量
1.2.1.1 指定数据创建张量
通过给定Python列表数据,可以创建任意维度的张量。
(1)通过指定的Python列表数据[2.0, 3.0, 4.0],创建一个一维张量。
# 导入torch
import torch
# 创建一维Tensor
ndim_1 = torch.tensor([2.0, 3.0, 4.0])
print(ndim_1)
结果
tensor([2., 3., 4.])
(2)通过指定的Python列表数据来创建类似矩阵(matrix)的二维张量。
# 导入torch
import torch
# 创建二维Tensor
ndim_2_Tensor = torch.tensor([[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0]])
print(ndim_2_Tensor)
结果:
tensor([[1., 2., 3.],
[4., 5., 6.]])
(3)同样地,还可以创建维度为3、4…N等更复杂的多维张量。
# 导入torch
import torch
# 创建多维Tensor
ndim_3_Tensor = torch.tensor([[[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10]],
[[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20]]])
print(ndim_3_Tensor)
结果:
tensor([[[ 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10]],
[[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20]]])
需要注意的是,张量在任何一个维度上的元素数量必须相等。下面尝试定义一个在同一维度上元素数量不等的张量。
# 导入torch
import torch
# 尝试定义在不同维度上元素数量不等的Tensor
ndim_2_Tensor = torch.tensor([[1.0, 2.0],
[4.0, 5.0, 6.0]])
print(ndim_2_Tensor )
输出结果:
从输出结果看,这种定义情况会抛出异常,提示在任何维度上的元素数量必须相等。
1.2.1.2 指定形状创建
如果要创建一个指定形状、元素数据相同的张量,可以使用torch.zeros、torch.ones、torch.full等API。
# 导入torch
import torch
m, n = 2, 3
# 使用torch.zeros创建数据全为0,形状为[m, n]的Tensor
zeros_Tensor = torch.zeros([m, n])
# 使用torch.ones创建数据全为1,形状为[m, n]的Tensor
ones_Tensor = torch.ones([m, n])
# 使用torch.full创建数据全为指定值,形状为[m, n]的Tensor,这里我们指定数据为10
full_Tensor = torch.full([m, n], 10)
print('zeros Tensor: ', zeros_Tensor)
print('ones Tensor: ', ones_Tensor)
print('full Tensor: ', full_Tensor)
输出结果:
zeros Tensor: tensor([[0., 0., 0.],
[0., 0., 0.]])
ones Tensor: tensor([[1., 1., 1.],
[1., 1., 1.]])
full Tensor: tensor([[10, 10, 10],
[10, 10, 10]])
1.2.1.3 指定区间创建
如果要在指定区间内创建张量,可以使用torch.arange、torch.linspace等API。
# 导入torch
import torch
# 使用torch.arange创建以步长step均匀分隔数值区间[start, end)的一维Tensor
arange_Tensor = torch.arange(1, 5, 1)
# 使用torch.linspace创建以元素个数num均匀分隔数值区间[start, stop]的Tensor
linspace_Tensor = torch.linspace(1, 5, 5)
print('arange Tensor: ', arange_Tensor)
print('linspace Tensor: ', linspace_Tensor)
输出结果:
arange Tensor: tensor([1, 2, 3, 4])
linspace Tensor: tensor([1., 2., 3., 4., 5.])
1.2.2 张量的属性
1.2.2.1 张量的形状
Tensor.ndim:张量的维度,例如向量的维度为1,矩阵的维度为2。
Tensor.shape: 张量每个维度上元素的数量。
Tensor.shape[n]:张量第nnn维的大小。第nnn维也称为轴(axis)。
Tensor.size:张量中全部元素的个数。
为了更好地理解ndim、shape、axis、size四种属性间的区别,创建一个如图1.6所示的四维张量。
创建一个四维张量,并打印出shape、ndim、shape[n]、size属性。
# 导入torch
import torch
ndim_4_Tensor = torch.ones([2, 3, 4, 5])
print("Number of dimensions:", ndim_4_Tensor.ndim)
print("Shape of Tensor:", ndim_4_Tensor.shape)
print("Elements number along axis 0 of Tensor:", ndim_4_Tensor.shape[0])
print("Elements number along the last axis of Tensor:", ndim_4_Tensor.shape[-1])
print('Number of elements in Tensor: ', ndim_4_Tensor.size)
输出结果:
Number of dimensions: 4
Shape of Tensor: torch.Size([2, 3, 4, 5])
Elements number along axis 0 of Tensor: 2
Elements number along the last axis of Tensor: 5
Number of elements in Tensor:
1.2.2.2 形状的改变
除了查看张量的形状外,重新设置张量的在实际编程中也具有重要意义,飞桨提供了torch.reshape接口来改变张量的形状。
# 导入torch
import torch
# 定义一个shape为[3,2,5]的三维Tensor
ndim_3_Tensor = torch.tensor([[[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10]],
[[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20]],
[[21, 22, 23, 24, 25],
[26, 27, 28, 29, 30]]])
print("the shape of ndim_3_Tensor:", ndim_3_Tensor.shape)
# torch.reshape 可以保持在输入数据不变的情况下,改变数据形状。这里我们设置reshape为[2,5,3]
reshape_Tensor = torch.reshape(ndim_3_Tensor, [2, 5, 3])
print("After reshape:", reshape_Tensor)
输出结果:
the shape of ndim_3_Tensor: torch.Size([3, 2, 5])
After reshape: tensor([[[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12],
[13, 14, 15]],
[[16, 17, 18],
[19, 20, 21],
[22, 23, 24],
[25, 26, 27],
[28, 29, 30]]])
从输出结果看,将张量从[3, 2, 5]的形状reshape为[2, 5, 3]的形状时,张量内的数据不会发生改变,元素顺序也没有发生改变,只有数据形状发生了改变。
1.2.2.3 张量的数据类型
可以通过Tensor.dtype来查看张量的数据类型,类型支持bool、float16、float32、float64、uint8、int8、int16、int32、int64和复数类型数据。
# 导入torch
import torch
# 使用torch.tensor通过已知数据来创建一个Tensor
print("Tensor dtype from Python integers:", torch.tensor(1).dtype)
print("Tensor dtype from Python floating point:", torch.tensor(1.0).dtype)
输出结果:
Tensor dtype from Python integers: torch.int64
Tensor dtype from Python floating point: torch.float32
1.2.2.4 张量的设备位置
初始化张量时可以通过place来指定其分配的设备位置,可支持的设备位置有三种:CPU、GPU和固定内存。
除非额外指定,新的张量将存储在内存中,并采用基于CPU的计算。
print(x.device)
cpu
1.2.3 张量与Numpy数组转换
两者可以相互转换
(1)numpy转为tensor
import numpy as np
n = np.array([[5, 6], [7, 8]])
t = torch.from_numpy(n)
print(t.dtype)
torch.int32
(2)tensor转为numpy
import numpy as np
import torch
n = np.array([[5, 6], [7, 8]])
t = torch.from_numpy(n)
x = t.numpy()
print(x)
输出结果:
[[5 6]
[7 8]]
1.2.4 张量的访问
1.2.4.1 索引和切片
索引:
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(x[0])
输出结果:
tensor([1, 2, 3])
切片:
import numpy as np
import torch
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(x[1:2])
输出结果:
tensor([[4, 5, 6]])
1.2.4.2 访问张量
import torch
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(x[0:2, 1:3])
输出结果:
tensor([[2, 3],
[5, 6]])
1.2.4.3 修改张量
import torch
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
x[1, 1] = 2
print(x)
输出结果:
tensor([[1, 2, 3],
[4, 2, 6]])
1.2.5 张量的运算
张量支持包括基础数学运算、逻辑运算、矩阵运算等100余种运算操作。
1.2.5.1 数学运算
(1)加法运算
import torch
a = torch.tensor([[1., 2.], [3., 4.]])
b = torch.tensor([[5., 6.], [7., 8.]])
print(a + b)
print(a.add(b))
print(torch.add(a, b))
print(a)
print(a.add_(b))
print(a)
输出结果:
tensor([[ 6., 8.],
[10., 12.]])
tensor([[ 6., 8.],
[10., 12.]])
tensor([[ 6., 8.],
[10., 12.]])
tensor([[1., 2.],
[3., 4.]])
tensor([[ 6., 8.],
[10., 12.]])
tensor([[ 6., 8.],
[10., 12.]])
(2)减法运算
import torch
a = torch.tensor([[1., 2.], [3., 4.]])
b = torch.tensor([[5., 6.], [7., 8.]])
print(a - b)
print(torch.sub(a, b))
print(a.sub(b))
print(a.sub_(b))
print(a)
输出结果:
tensor([[-4., -4.],
[-4., -4.]])
tensor([[-4., -4.],
[-4., -4.]])
tensor([[-4., -4.],
[-4., -4.]])
tensor([[-4., -4.],
[-4., -4.]])
tensor([[-4., -4.],
[-4., -4.]])
(3)乘法运算
import torch
a = torch.tensor([[1., 2.], [3., 4.]])
b = torch.tensor([[5., 6.], [7., 8.]])
print(a * b)
print(torch.mul(a, b))
print(a.mul(b))
print(a)
print(a.mul_(b))
print(a)
输出结果:
tensor([[ 5., 12.],
[21., 32.]])
tensor([[ 5., 12.],
[21., 32.]])
tensor([[ 5., 12.],
[21., 32.]])
tensor([[1., 2.],
[3., 4.]])
tensor([[ 5., 12.],
[21., 32.]])
tensor([[ 5., 12.],
[21., 32.]])
(4)除法运算
import torch
a = torch.tensor([[1., 2.], [3., 4.]])
b = torch.tensor([[5., 6.], [7., 8.]])
print(a/b)
print(torch.div(a, b))
print(a.div(b))
print(a.div_(b))
print(a)
输出结果:
tensor([[0.2000, 0.3333],
[0.4286, 0.5000]]) tensor([[0.2000, 0.3333],
[0.4286, 0.5000]]) tensor([[0.2000, 0.3333],
[0.4286, 0.5000]]) tensor([[0.2000, 0.3333],
[0.4286, 0.5000]]) tensor([[0.2000, 0.3333],
[0.4286, 0.5000]])
1.2.5.2 逻辑运算
import torch
a = torch.tensor([[True, False], [False, True]])
b = torch.tensor([[False, False], [True, True]])
print(a & b) # 与运算
print(a | b) # 或运算
print(~a) # 取反
print(a ^ b) # 异或运算
print(torch.eq(a, b))
print(torch.equal(a, b))
输出结果:
tensor([[False, False],
[False, True]])
tensor([[ True, False],
[ True, True]])
tensor([[False, True],
[ True, False]])
tensor([[ True, False],
[ True, False]])
tensor([[False, True],
[False, True]])
False
1.2.5.3 矩阵运算
二维矩阵乘法运算操作包括torch.mm()、torch.matmul()、@
(1)矩阵的乘法运算
import torch
a = torch.ones(3, 1)
b = torch.ones(1, 3)
print(a @ b)
print(a.matmul(b))
print(torch.matmul(a, b))
print(torch.mm(a, b))
print(a.mm(b))
print(torch.equal(a, b))
输出结果:
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
False
(2)矩阵的转置
import torch
a = torch.ones(2, 1)
a = torch.ones(2, 1)
print(a)
print(a.t())
输出结果:
tensor([[1.],
[1.]])
tensor([[1., 1.]])
1.2.5.4 广播机制
广播机制需要满足两个条件:
1.每个张量至少有一个维度
2.从后往前比较张量的形状,当前维度的大小要么相等,要么其中一个等于1,要么其中一个不存在。
import torch
a = torch.rand(2, 3)
b = torch.rand(2,3)
c = a + b
print(a)
print(b)
print(c)
print(c.shape)
输出结果:
tensor([[0.5201, 0.0269, 0.6900],
[0.5837, 0.0894, 0.4005]])
tensor([[0.6050, 0.0324, 0.4918],
[0.3073, 0.9762, 0.2903]])
tensor([[1.1251, 0.0593, 1.1818],
[0.8910, 1.0656, 0.6908]])
torch.Size([2, 3])
三. 数据预处理
house_tiny_path = './house_tiny.csv'
boston_house_prices_path = './boston_house_prices.csv'
Iris_path = './Iris.csv'
house_tiny_data = pd.read_csv(house_tiny_path)
boston_house_prices_data = pd.read_csv(boston_house_prices_path)
Iris_data = pd.read_csv(Iris_path)
print(house_tiny_data.head())
输出结果:
NumRooms Alley Price
0 NaN Pave 127500
1 2.0 NaN 106000
2 4.0 NaN 178100
3 NaN NaN 140000
X = X.fillna(X.mean())
print(X)
输出结果:
NumRooms Alley
0 3.0 Pave
1 2.0 NaN
2 4.0 NaN
3 3.0 NaN
# X_tensor, y_tensor = torch.Tensor(X.values), torch.Tensor(y.values)
X_tensor = torch.from_numpy(X.to_numpy())
y_tensor = torch.from_numpy(y.to_numpy())
print(X_tensor, y_tensor)
输出结果:
tensor([[nan, 1., 0.],
[2., 0., 1.],
[4., 0., 1.],
[nan, 0., 1.]]) tensor([127500., 106000., 178100., 140000.])
注意:必须保证要转换的数据类型为数值类型,要不然会报错。
四、心得体会
本篇文章主要讲述了张量和算子的概念,如何使用pytorch进行张量的运算,知道如何创建张量,了解张量的属性,熟悉张量和numpy的转换,以及如何访问张量,张量的四则运算,逻辑运算,矩阵运算等,并且使用pytorch进行数据读取,数据预处理等操作,学会了如何将paddle转为pytorch并且变得熟练。