PyTorch浅尝辄止(2)
Tensor谜题
力学中的张量
在数学上,一个张量是一个多维数组。在流体力学中,张量与场相关。场描述三维空间中的标量或者适量的分布。在三维空间中,一个标量场通常用等值面来表示。
在空间区域 Ω \Omega Ω中的函数或者数量场 u = u ( x , y , z ) u = u(x,y,z) u=u(x,y,z)在 Ω \Omega Ω中有连续的一阶偏导数,则曲面 u ( x , y , z ) = C u(x,y,z)=C u(x,y,z)=C称为等值面。常见的如等温面、等压面。等值面的特点是:
- 等值面彼此不相交;
- 等值面的疏密表达标量函数的变化情况。
标量场的梯度可以表达为适量的形式:
d u ( x , y , z ) = [ ∂ u ∂ x ∂ u ∂ y ∂ u ∂ z ] \mathbf{du}(x,y,z) = \left[\begin{array}{c} \frac{\partial u}{\partial x} \\ \frac{\partial u}{\partial y} \\ \frac{\partial u}{\partial z} \end{array}\right] du(x,y,z)=⎣⎡∂x∂u∂y∂u∂z∂u⎦⎤
这就构成一个矢量场。如果函数 u = u ( x , y , z ) u=u(x,y,z) u=u(x,y,z)是二阶连续的,那么上述矢量场的梯度可以表达为2阶张量的形式:
d 2 u ( x , y , z ) = [ ∂ 2 u ∂ x 2 ∂ 2 u ∂ x ∂ y ∂ 2 u ∂ x ∂ z ∂ 2 u ∂ x ∂ y ∂ 2 u ∂ y 2 ∂ 2 u ∂ y ∂ z ∂ 2 u ∂ x ∂ z ∂ 2 u ∂ y ∂ z ∂ 2 u ∂ z 2 ] \mathbf{d^2u}(x,y,z) = \left[\begin{array}{ccc} \frac{\partial^2 u}{\partial x^2} & \frac{\partial^2 u}{\partial x \partial y} & \frac{\partial^2 u}{\partial x \partial z}\\ \frac{\partial^2 u}{\partial x \partial y} & \frac{\partial^2 u}{\partial y^2} & \frac{\partial^2 u}{\partial y \partial z} \\ \frac{\partial^2 u}{\partial x \partial z} & \frac{\partial^2 u}{\partial y \partial z} & \frac{\partial^2 u}{\partial z^2} \end{array}\right] d2u(x,y,z)=⎣⎢⎡∂x2∂2u∂x∂y∂2u∂x∂z∂2u∂x∂y∂2u∂y2∂2u∂y∂z∂2u∂x∂z∂2u∂y∂z∂2u∂z2∂2u⎦⎥⎤
当然还可以继续下去,每次求导都增加一个维度,从标量场到矢量场,到2阶张量场,到3阶张量场,这就是张量给人的感觉……
说了这一堆,我也不知道为什么要这么复杂。我只是想说张量是一个多维数组而已。
机器学习中的张量
在机器学习中,张量通常用各种形状的矩阵表示,矩阵的维数就是张量的阶数。
在深度学习中,张量通常用于表示神经网络的权重和激活。
在PyTorch中,张量是一个类似于NumPy数组的对象,它可以在GPU上使用以加速计算。张量与NumPy数组之间的主要区别是张量可以在GPU上使用以加速计算。
简单的张量
上面是瞎扯的部分。以下就开始搬砖。一件事情只要落到搬砖或者焊钢板上,我就会很开心。我就是一个搬砖的。
搬砖,哦不,张量的创建
在PyTorch中,张量可以通过以下方式创建:
import numpy as np
import torch
data = [[1, 2], [3, 4]]
x_data = torch.tensor(data)
def show_tensor(x_t):
print(f"Tensor的数据类型: {x_t.dtype}")
print(f"Tensor的设备: {x_t.device}")
print(f"Tensor的维度: {x_t.dim()}")
print(f"Tensor的形状: {x_t.shape}")
print(f"Tensor的元素个数: {x_t.numel()}")
print(f"Tensor的字符串表示:\n{x_t}")
print("Tensor的遍历:")
for i, xi in enumerate(x_t):
print(f"\t{i}:{xi}")
print(f"Tensor的索引: x_t[0, 0]={x_t[0, 0]}")
show_tensor(x_data)
Tensor的数据类型: torch.int64
Tensor的设备: cpu
Tensor的维度: 2
Tensor的形状: torch.Size([2, 2])
Tensor的元素个数: 4
Tensor的字符串表示:
tensor([[1, 2],
[3, 4]])
Tensor的遍历:
0:tensor([1, 2])
1:tensor([3, 4])
Tensor的索引: x_t[0, 0]=1
还可以从NumPy数组创建张量,以及从其它张量来创建张量。
print("-------------------------\n从NumPy数组创建张量")
np_array = np.array(data)
x_np = torch.from_numpy(np_array)
show_tensor(x_np)
print("\n-------------------------\n从其它张量来创建张量")
x_ones = torch.ones_like(x_data)
show_tensor(x_ones)
print("\n-------------------------\n随机初始化张量")
x_rand = torch.rand_like(x_data, dtype=torch.float)
show_tensor(x_rand)
-------------------------
从NumPy数组创建张量
Tensor的数据类型: torch.int32
Tensor的维度: 2
Tensor的形状: torch.Size([2, 2])
Tensor的元素个数: 4
Tensor的字符串表示:
tensor([[1, 2],
[3, 4]], dtype=torch.int32)
Tensor的遍历:
0:tensor([1, 2], dtype=torch.int32)
1:tensor([3, 4], dtype=torch.int32)
Tensor的索引: x_t[0, 0]=1
-------------------------
从其它张量来创建张量
Tensor的数据类型: torch.int64
Tensor的维度: 2
Tensor的形状: torch.Size([2, 2])
Tensor的元素个数: 4
Tensor的字符串表示:
tensor([[1, 1],
[1, 1]])
Tensor的遍历:
0:tensor([1, 1])
1:tensor([1, 1])
Tensor的索引: x_t[0, 0]=1
-------------------------
随机初始化张量
Tensor的数据类型: torch.float32
Tensor的维度: 2
Tensor的形状: torch.Size([2, 2])
Tensor的元素个数: 4
Tensor的字符串表示:
tensor([[0.0646, 0.8117],
[0.3862, 0.2779]])
Tensor的遍历:
0:tensor([0.0646, 0.8117])
1:tensor([0.3862, 0.2779])
Tensor的索引: x_t[0, 0]=0.06455862522125244
更多的创建函数可以参考官方文档。例如可以从其形状来创建张量,并用各种方式来填充。
shape = (2, 3,)
print("\n-------------------------\n从均匀分布的随机变量填充张量")
x_rand = torch.rand(shape)
show_tensor(x_rand)
print("\n-------------------------\n用高斯分布的随机量填充")
x_randn = torch.randn(shape)
show_tensor(x_randn)
print("\n-------------------------\n用0填充")
x_zeros = torch.zeros(shape)
show_tensor(x_zeros)
print("\n-------------------------\n用1填充")
x_ones = torch.ones(shape)
show_tensor(x_ones)
print("\n-------------------------\n用指定值填充")
x_fill = torch.full(shape, 3.14)
show_tensor(x_fill)
-------------------------
从均匀分布的随机变量填充张量
Tensor的数据类型: torch.float32
Tensor的设备: cpu
Tensor的维度: 2
Tensor的形状: torch.Size([2, 3])
Tensor的元素个数: 6
Tensor的字符串表示:
tensor([[0.8780, 0.6692, 0.2388],
[0.3589, 0.1243, 0.3470]])
Tensor的遍历:
0:tensor([0.8780, 0.6692, 0.2388])
1:tensor([0.3589, 0.1243, 0.3470])
Tensor的索引: x_t[0, 0]=0.8779925107955933
-------------------------
用高斯分布的随机量填充
Tensor的数据类型: torch.float32
Tensor的设备: cpu
Tensor的维度: 2
Tensor的形状: torch.Size([2, 3])
Tensor的元素个数: 6
Tensor的字符串表示:
tensor([[-0.1044, 0.6825, 0.3588],
[-0.7004, 1.5626, 0.8914]])
Tensor的遍历:
0:tensor([-0.1044, 0.6825, 0.3588])
1:tensor([-0.7004, 1.5626, 0.8914])
Tensor的索引: x_t[0, 0]=-0.1043904721736908
-------------------------
用0填充
Tensor的数据类型: torch.float32
Tensor的设备: cpu
Tensor的维度: 2
Tensor的形状: torch.Size([2, 3])
Tensor的元素个数: 6
Tensor的字符串表示:
tensor([[0., 0., 0.],
[0., 0., 0.]])
Tensor的遍历:
0:tensor([0., 0., 0.])
1:tensor([0., 0., 0.])
Tensor的索引: x_t[0, 0]=0.0
-------------------------
用1填充
Tensor的数据类型: torch.float32
Tensor的设备: cpu
Tensor的维度: 2
Tensor的形状: torch.Size([2, 3])
Tensor的元素个数: 6
Tensor的字符串表示:
tensor([[1., 1., 1.],
[1., 1., 1.]])
Tensor的遍历:
0:tensor([1., 1., 1.])
1:tensor([1., 1., 1.])
Tensor的索引: x_t[0, 0]=1.0
-------------------------
用指定值填充
Tensor的数据类型: torch.float32
Tensor的设备: cpu
Tensor的维度: 2
Tensor的形状: torch.Size([2, 3])
Tensor的元素个数: 6
Tensor的字符串表示:
tensor([[3.1400, 3.1400, 3.1400],
[3.1400, 3.1400, 3.1400]])
Tensor的遍历:
0:tensor([3.1400, 3.1400, 3.1400])
1:tensor([3.1400, 3.1400, 3.1400])
Tensor的索引: x_t[0, 0]=3.140000104904175
张量支持的运算
张量支持的运算有很多,这里只列举一些常用的。官方文档中有超过100个运算函数,可以参考官方文档。所有这些操作都可以在GPU上执行,只需要将张量移动到GPU上即可。默认情况下,张量在CPU上创建。需要调用to
方法将张量移动到GPU上。
# 如果有GPU,就将张量移动到GPU上
if torch.cuda.is_available():
device = torch.device("cuda")
x_data = x_data.to(device)
x_np = x_np.to(device)
x_ones = x_ones.to(device)
x_rand = x_rand.to(device)
print("张量已经移动到GPU上")
else:
device = torch.device("cpu")
print("没有GPU,张量仍然在CPU上")
没有GPU,张量仍然在CPU上
张量的运算和操作大概可以分为以下几类:
- 形状操作
- 索引操作
- 数学运算
- 线性代数运算
- 随机数
- 序列操作
- 并行操作
- 其它操作
shape = (2, 2,)
x_data = torch.rand(shape)
print("张量的形状操作")
print(f"张量的形状: {x_data.shape}")
print(f"张量的元素个数: {x_data.numel()}")
print(f"张量的维度: {x_data.dim()}")
print(f"随机张量: \n{x_data}")
print(f"张量的转置: \n{x_data.t()}")
print(f"张量的展平(-1,):\n{x_data.view(-1)}")
print(f"张量的展平(4,):\n{x_data.view(4)}")
print(f"张量的展平(2,2):\n{x_data.view(2, 2)}")
print(f"张量的展平(1,4):\n{x_data.view(1, 4)}")
print(f"张量的展平(1,-1):\n{x_data.view(1, -1)}")
print(f"张量的展平(-1,1):\n{x_data.view(-1, 1)}")
print(f"张量的展平(2,-1):\n{x_data.view(2, -1)}")
print(f"张量的展平(-1,2):\n{x_data.view(-1, 2)}")
张量的形状操作
张量的形状: torch.Size([2, 2])
张量的元素个数: 4
张量的维度: 2
随机张量:
tensor([[0.0734, 0.9233],
[0.8115, 0.4401]])
张量的转置:
tensor([[0.0734, 0.8115],
[0.9233, 0.4401]])
张量的展平(-1,):
tensor([0.0734, 0.9233, 0.8115, 0.4401])
张量的展平(4,):
tensor([0.0734, 0.9233, 0.8115, 0.4401])
张量的展平(2,2):
tensor([[0.0734, 0.9233],
[0.8115, 0.4401]])
张量的展平(1,4):
tensor([[0.0734, 0.9233, 0.8115, 0.4401]])
张量的展平(1,-1):
tensor([[0.0734, 0.9233, 0.8115, 0.4401]])
张量的展平(-1,1):
tensor([[0.0734],
[0.9233],
[0.8115],
[0.4401]])
张量的展平(2,-1):
tensor([[0.0734, 0.9233],
[0.8115, 0.4401]])
张量的展平(-1,2):
tensor([[0.0734, 0.9233],
[0.8115, 0.4401]])
shape = (2, 2,)
x_data = torch.rand(shape)
print("张量的索引操作")
print(f"张量的第0行: {x_data[0]}")
print(f"张量的第0行第0列: {x_data[0, 0]}")
print(f"张量的第0行第1列: {x_data[0, 1]}")
print(f"张量的第1行第0列: {x_data[1, 0]}")
print(f"张量的第1行第1列: {x_data[1, 1]}")
print(f"张量的第0行第0列: {x_data[0][0]}")
print(f"张量的第0行第1列: {x_data[0][1]}")
print(f"张量的第1行第0列: {x_data[1][0]}")
print(f"张量的第1行第1列: {x_data[1][1]}")
张量的索引操作
张量的第0行: tensor([0.0734, 0.9233])
张量的第0行第0列: 0.0734146237373352
张量的第0行第1列: 0.9233333468437195
张量的第1行第0列: 0.8114538192749023
张量的第1行第1列: 0.440138041973114
张量的第0行第0列: 0.0734146237373352
张量的第0行第1列: 0.9233333468437195
张量的第1行第0列: 0.8114538192749023
张量的第1行第1列: 0.440138041973114
shape = (2, 2,)
x_data = torch.rand(shape)
print("张量的数学运算")
print(f"张量的加法: \n{x_data + x_data}")
print(f"张量的减法: \n{x_data - x_data}")
print(f"张量的乘法: \n{x_data * x_data}")
print(f"张量的除法: \n{x_data / x_data}")
print(f"张量的幂运算: \n{x_data ** 2}")
print(f"张量的平方根: \n{x_data ** 0.5}")
print(f"张量的指数运算: \n{x_data.exp()}")
print(f"张量的对数运算: \n{x_data.log10()}")
print(f"张量的三角函数运算: \n{x_data.sin()}")
print(f"张量的比较运算: \n{x_data > 0.5}")
print(f"张量的求和: \n{x_data.sum()}")
print(f"张量的求均值: \n{x_data.mean()}")
print(f"张量的求最大值: \n{x_data.max()}")
print(f"张量的求最小值: \n{x_data.min()}")
print(f"张量的求标准差: \n{x_data.std()}")
print(f"张量的求方差: \n{x_data.var()}")
张量的数学运算
张量的加法:
tensor([[0.1468, 1.8467],
[1.6229, 0.8803]])
张量的减法:
tensor([[0., 0.],
[0., 0.]])
张量的乘法:
tensor([[0.0054, 0.8525],
[0.6585, 0.1937]])
张量的除法:
tensor([[1., 1.],
[1., 1.]])
张量的幂运算:
tensor([[0.0054, 0.8525],
[0.6585, 0.1937]])
张量的平方根:
tensor([[0.2710, 0.9609],
[0.9008, 0.6634]])
张量的指数运算:
tensor([[1.0762, 2.5177],
[2.2512, 1.5529]])
张量的对数运算:
tensor([[-1.1342, -0.0346],
[-0.0907, -0.3564]])
张量的三角函数运算:
tensor([[0.0733, 0.7976],
[0.7253, 0.4261]])
张量的比较运算:
tensor([[False, True],
[ True, False]])
张量的求和:
2.248339891433716
张量的求均值:
0.562084972858429
张量的求最大值:
0.9233333468437195
张量的求最小值:
0.0734146237373352
张量的求标准差:
0.38572657108306885
张量的求方差:
0.14878499507904053
shape = (2, 2,)
x_data = torch.rand(shape)
print("张量的线性代数运算")
print(f"张量的转置: \n{x_data.t()}")
print(f"张量的矩阵乘法: \n{x_data.mm(x_data.t())}")
print(f"张量的矩阵乘法: \n{x_data @ x_data.t()}")
print(f"张量的矩阵乘法: \n{x_data.matmul(x_data.t())}")
print(f"张量的矩阵乘法: \n{torch.mm(x_data, x_data.t())}")
print(f"张量的矩阵乘法: \n{torch.matmul(x_data, x_data.t())}")
x_data = torch.tensor([1, 2, 3, 4], dtype=torch.float32)
print(f"张量的点乘: \n{torch.dot(x_data, x_data.t())}")
print(f"张量的内积: \n{torch.inner(x_data, x_data.t())}")
print(f"张量的外积: \n{torch.outer(x_data, x_data.t())}")
print(f"张量的kron: \n{torch.kron(x_data, x_data.t())}")
张量的线性代数运算
张量的转置:
tensor([[0.2352, 0.4036],
[0.5942, 0.5712]])
张量的矩阵乘法:
tensor([[0.4084, 0.4343],
[0.4343, 0.4892]])
张量的矩阵乘法:
tensor([[0.4084, 0.4343],
[0.4343, 0.4892]])
张量的矩阵乘法:
tensor([[0.4084, 0.4343],
[0.4343, 0.4892]])
张量的矩阵乘法:
tensor([[0.4084, 0.4343],
[0.4343, 0.4892]])
张量的矩阵乘法:
tensor([[0.4084, 0.4343],
[0.4343, 0.4892]])
张量的点乘:
30.0
张量的内积:
30.0
张量的外积:
tensor([[ 1., 2., 3., 4.],
[ 2., 4., 6., 8.],
[ 3., 6., 9., 12.],
[ 4., 8., 12., 16.]])
张量的kron:
tensor([ 1., 2., 3., 4., 2., 4., 6., 8., 3., 6., 9., 12., 4., 8.,
12., 16.])
shape = (2, 2,)
print("张量的随机生成")
print(f"张量的均匀分布随机数: \n{torch.rand(shape)}")
print(f"张量的标准正态分布随机数: \n{torch.randn(shape)}")
print(f"张量的正态分布随机数: \n{torch.normal(mean=torch.full(shape, 0.5), std=torch.full(shape, 0.1))}")
print(f"张量的随机整数: \n{torch.randint(low=0, high=10, size=shape)}")
张量的随机生成
张量的均匀分布随机数:
tensor([[0.7879, 0.6105],
[0.1430, 0.3645]])
张量的标准正态分布随机数:
tensor([[ 0.3791, 0.7605],
[-0.2999, 1.1909]])
张量的正态分布随机数:
tensor([[0.4994, 0.4389],
[0.6507, 0.7285]])
张量的随机整数:
tensor([[1, 0],
[0, 8]])
值得注意的一点是,张量可以通过计算得到一个新的张量,也有本地计算模式,也就是把运算的结果直接存储在原来的张量中,这样可以节省内存空间,但是需要注意的是,这种模式下,如果原来的张量被其他变量引用,那么这个变量的值也会发生改变,这是因为这两个变量指向的是同一个内存地址,所以会发生改变,这种情况下,我们可以使用clone()方法来复制一个张量,这样就不会影响到原来的张量了。一般的规则就是在运算函数后面增加一个下划线,就是本地计算模式,比如add_()
、t_()
等。
shape = (2, 2,)
x_data = torch.randint(low=0, high=10, size=shape)
y_data = torch.randint(low=0, high=10, size=shape)
print("张量的本地计算模式")
print(f"张量x_data: \n{x_data}")
print(f"张量y_data: \n{y_data}")
print(f"张量的加法add: \n{x_data.add(y_data)}")
print(f"张量x_data: \n{x_data}")
print(f"张量的加法add_: \n{x_data.add_(y_data)}")
print(f"张量x_data: \n{x_data}")
张量的本地计算模式
张量x_data:
tensor([[7, 7],
[3, 3]])
张量y_data:
tensor([[6, 8],
[5, 0]])
张量的加法add:
tensor([[13, 15],
[ 8, 3]])
张量x_data:
tensor([[7, 7],
[3, 3]])
张量的加法add_:
tensor([[13, 15],
[ 8, 3]])
张量x_data:
tensor([[13, 15],
[ 8, 3]])
张量与Numpy数组的转换
张量和Numpy数组之间可以相互转换,这样就可以方便的使用Numpy的各种函数了,但是需要注意的是,张量和Numpy数组共享内存,也就是说,如果张量和Numpy数组中的一个发生改变,另一个也会发生改变,这是因为这两个变量指向的是同一个内存地址,所以会发生改变,这种情况下,我们可以使用clone()
方法来复制一个张量,这样就不会影响到原来的张量了。
这一点在后面开始自动微分的时候就会变得很关键,所以需要注意一下。
shape = (2, 2,)
x_data = torch.randint(low=0, high=10, size=shape)
x_np = x_data.numpy()
print(f"张量x_data: \n{x_data}")
x_np[0, 0] = 100
print(f"张量x_data: \n{x_data}")
x_np = np.random.rand(2, 2)
x_data = torch.from_numpy(x_np)
print(f"x_np: \n{x_np}")
x_data[0, 0] = 100
print(f"x_np: \n{x_np}")
张量x_data:
tensor([[1, 5],
[6, 7]])
张量x_data:
tensor([[100, 5],
[ 6, 7]])
x_np:
[[0.92873644 0.20536021]
[0.27923725 0.74282402]]
x_np:
[[100. 0.20536021]
[ 0.27923725 0.74282402]]
总结
- 张量是PyTorch中最基本的数据结构,可以看作是一个多维数组,可以通过
torch.tensor()
方法来创建一个张量,也可以通过torch.rand()
、torch.randn()
、torch.normal()
、torch.randint()
等方法来创建一个张量。 - 张量可以通过计算得到一个新的张量,也有本地计算模式,也就是把运算的结果直接存储在原来的张量中。
- 张量和Numpy数组之间可以相互转换,这样就可以方便的使用Numpy的各种函数了,但是需要注意的是,张量和Numpy数组共享内存,也就是说,如果张量和Numpy数组中的一个发生改变,另一个也会发生改变,这是因为这两个变量指向的是同一个内存地址。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)