在前面的例子中,我们已经讨论了标量的概念,并展示了如何使用代码对标量进行基本的算术运算。接下来,我将进一步说明该过程,并解释每一步的实现。
标量是只有一个元素的数值。它可以是整数、浮点数等。通过下面的 Python 代码,我们可以很容易地进行标量的加法、乘法、除法和指数运算。
代码实现:
import torch
# 定义两个标量
x = torch.tensor(3.0) # 标量x,值为3.0
y = torch.tensor(2.0) # 标量y,值为2.0
# 执行加法、乘法、除法、指数运算
x + y, x * y, x / y, x**y
输出解释:
x + y
:标量的加法,3.0 + 2.0 = 5.0x * y
:标量的乘法,3.0 * 2.0 = 6.0x / y
:标量的除法,3.0 / 2.0 = 1.5x**y
:标量的指数运算,3.0 的 2 次方,即 3.0^2 = 9.0输出:
(tensor(5.), tensor(6.), tensor(1.5000), tensor(9.))
在这里,tensor(5.)
表示存储标量值 5.0 的张量。通过这种方式,我们可以将基本的标量运算转化为机器可执行的代码。
在这个小节中,我们将介绍 向量 的概念以及如何使用代码表示和操作向量。
向量是由标量组成的列表,可以看作是多维数据的表示。向量的每个分量都有其特定的含义,比如在某些机器学习应用中,向量的分量可能代表特定样本的特征,如收入、年龄、健康指标等。
在数学符号中,向量通常记作粗体的小写字母,例如 ( \mathbf{x}, \mathbf{y}, \mathbf{z} )。如果一个向量包含 (n) 个元素,可以表示为:
[
\mathbf{x} = \begin{bmatrix} x_1 \ x_2 \ \vdots \ x_n \end{bmatrix}
]
其中,( x_i ) 是向量的第 (i) 个元素。
在深度学习框架中,向量可以用 一维张量 来表示。我们可以轻松创建一个包含多个元素的向量,并访问其任意元素。
代码示例:
import torch
# 创建一个向量(长度为4)
x = torch.arange(4) # 创建包含 0, 1, 2, 3 的向量
print(x) # 输出整个向量
输出:
tensor([0, 1, 2, 3])
在这个例子中,torch.arange(4)
创建了一个向量,包含从 0 到 3 的 4 个元素。
我们可以通过索引访问向量中的任意元素。请注意,索引是从 0 开始的。
代码示例:
# 访问向量的第 4 个元素(索引为 3)
print(x[3]) # 输出 tensor(3)
输出:
tensor(3)
在这个例子中,我们通过 x[3]
访问向量中的第四个元素(索引从 0 开始),即 3。
向量:向量可以看作一个数字数组。每个向量都有一个长度,在数学上我们表示一个向量由若干个实值标量组成,向量的长度通常称为维度(dimension)。
长度获取:与普通的 Python 数组一样,我们可以通过调用 Python 的内置 len()
函数来获取向量的长度。例如,在深度学习框架(如 MXNet、PyTorch、TensorFlow 和 Paddle)中,len(x)
可以返回向量的长度。
print(len(x)) # 返回 4,表示向量的长度
.shape
属性访问该向量的长度。形状(shape)是一个列出张量每个轴长度的元素组。对于一维张量,其形状为 (n,)
,例如:print(x.shape) # 返回 torch.Size([4]),表示张量有一个长度为 4 的轴
矩阵:将向量扩展到二阶,通常用粗体大写字母表示,如 ( A )、( B ) 等。矩阵在代码中表示为具有两个轴的张量。矩阵 ( A ) 的形状为 ( (m, n) ),其中 ( m ) 是行数,( n ) 是列数。若行列相等,称为方阵。
创建矩阵:可以通过函数指定行数和列数来创建矩阵。例如:
A = torch.arange(20).reshape(5, 4)
访问元素:通过行索引和列索引访问矩阵中的元素,如 ( A_{i,j} )。在表达式中,通常使用小写字母和下标来表示矩阵的某个元素。
矩阵转置:交换矩阵的行和列称为转置,用 ( A^T ) 表示。代码中访问转置矩阵:
A.T
对称矩阵:若矩阵等于其转置,则称为对称矩阵。可以通过代码比较矩阵与其转置是否相等,例如:
B = torch.tensor([[1, 2, 3], [2, 0, 4], [3, 4, 5]])
B == B.T # 返回一个布尔矩阵,表示是否对称
应用场景:矩阵常用于组织和处理具有不同模式的数据,例如,行对应不同的数据样本,列对应不同的属性。在深度学习中,这种方式很常见,支持小批量数据的处理。
import torch
# 矩阵是将向量从一阶推广到二阶,通常用粗体大写字母表示,例如 A 和 B。
# 矩阵在代码中表示为具有两个轴的张量。
# 创建一个 5 行 4 列的矩阵 A
A = torch.arange(20).reshape(5, 4)
print("矩阵 A:")
print(A)
# 可以通过行索引和列索引访问矩阵中的元素。
# 例如访问 A 的第 1 行,第 2 列的元素 A[1-1, 2-1]
element = A[1-1, 2-1]
print(f"A[0, 1] 的元素值为: {element}")
# 交换矩阵的行和列称为转置,用 A.T 表示
A_T = A.T
print("矩阵 A 的转置:")
print(A_T)
# 对称矩阵是一个特殊的矩阵,等于其转置
# 例如,创建一个 3x3 的对称矩阵 B
B = torch.tensor([[1, 2, 3], [2, 0, 4], [3, 4, 5]])
print("对称矩阵 B:")
print(B)
# 比较 B 与 B.T,判断是否为对称矩阵
is_symmetric = B == B.T
print("B 是否对称 (B == B.T):")
print(is_symmetric)
# 矩阵可以用于组织不同模式的数据,例如行可以对应不同的数据样本,列对应不同的属性。
# 这种方式在深度学习中很常见。
张量的定义:张量是具有任意数量轴的高维数组。向量是一阶张量,矩阵是二阶张量,张量的维度可以更高,用于表示更复杂的数据结构。张量的索引机制与矩阵类似,通常用特殊字体的大写字母表示(如 ( X )、( Y ) 等)。
图像与张量:当处理图像时,张量非常重要。图像可以表示为一个三维数组,其中三个轴对应图像的高度、宽度,以及通道(channel),例如红、绿、蓝颜色通道。
张量示例:下面的代码展示了一个形状为 ( (2, 3, 4) ) 的三阶张量,其中有 2 个矩阵,每个矩阵包含 3 行 4 列的元素。
import torch
# 创建一个形状为 (2, 3, 4) 的张量 X
X = torch.arange(24).reshape(2, 3, 4)
print("三阶张量 X:")
print(X)
三阶张量 X:
tensor([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
在这个例子中,张量 X
的形状是 (2, 3, 4)
,它包含 2 个 3x4 的矩阵。每个矩阵包含 3 行和 4 列的数据。这展示了张量的基本结构,未来可以进一步扩展到更复杂的多维数据,例如处理图像时的三维张量。
按元素操作:
Hadamard积:
张量与标量运算:
import torch
# 创建两个相同形状的矩阵 A 和 B
A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
B = A.clone() # 复制 A 到 B
print("矩阵 A:")
print(A)
print("矩阵 B:")
print(B)
# 对两个矩阵执行按元素加法
print("A + B 的结果:")
print(A + B)
# Hadamard 积 (按元素乘法)
print("A 和 B 的 Hadamard 积 (A * B):")
print(A * B)
# 张量与标量的运算
a = 2
X = torch.arange(24).reshape(2, 3, 4)
print("标量 a 与张量 X 的加法结果:")
print(a + X)
print("标量 a 与张量 X 的乘法结果的形状:")
print((a * X).shape)
矩阵 A:
tensor([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[12., 13., 14., 15.],
[16., 17., 18., 19.]])
矩阵 B:
tensor([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[12., 13., 14., 15.],
[16., 17., 18., 19.]])
A + B 的结果:
tensor([[ 0., 2., 4., 6.],
[ 8., 10., 12., 14.],
[16., 18., 20., 22.],
[24., 26., 28., 30.],
[32., 34., 36., 38.]])
A 和 B 的 Hadamard 积 (A * B):
tensor([[ 0., 1., 4., 9.],
[ 16., 25., 36., 49.],
[ 64., 81., 100., 121.],
[144., 169., 196., 225.],
[256., 289., 324., 361.]])
标量 a 与张量 X 的加法结果:
tensor([[[ 2, 3, 4, 5],
[ 6, 7, 8, 9],
[10, 11, 12, 13]],
[[14, 15, 16, 17],
[18, 19, 20, 21],
[22, 23, 24, 25]]])
标量 a 与张量 X 的乘法结果的形状:
torch.Size([2, 3, 4])
A
和 B
的按元素相加,结果是同样形状的矩阵,每个元素是对应位置的元素相加。A
和 B
的 Hadamard 积,每个元素是对应位置的元素相乘。a
与张量 X
的相加操作,对 X
的每个元素都加上标量 a
,形状不变。a
与张量 X
的乘法,对 X
的每个元素乘以 a
,并且结果张量的形状依然保持不变。这段笔记讲述了张量的降维操作,特别是通过求和与平均值计算来降低张量的维度。下面是详细总结并结合代码示例:
求和操作:
axis
),可以选择沿哪个轴进行求和,从而降低维度。示例:
import torch
# 创建一个向量 x
x = torch.arange(4, dtype=torch.float32)
print("向量 x:")
print(x)
# 对向量 x 进行求和
x_sum = x.sum()
print("向量 x 的元素和:")
print(x_sum)
# 创建一个 5x4 的矩阵 A
A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
print("矩阵 A:")
print(A)
# 对矩阵 A 的所有元素进行求和
A_sum = A.sum()
print("矩阵 A 的元素和:")
print(A_sum)
# 沿轴 0(行)进行求和,降维为向量
A_sum_axis0 = A.sum(axis=0)
print("沿轴 0 降维后 (对每列求和) 的结果:")
print(A_sum_axis0)
# 沿轴 1(列)进行求和,降维为向量
A_sum_axis1 = A.sum(axis=1)
print("沿轴 1 降维后 (对每行求和) 的结果:")
print(A_sum_axis1)
# 同时沿轴 0 和轴 1 进行求和,相当于对所有元素求和
A_sum_all = A.sum(axis=[0, 1])
print("沿所有轴降维后 (对所有元素求和) 的结果:")
print(A_sum_all)
平均值计算:
平均值计算示例:
# 计算矩阵 A 的平均值
A_mean = A.mean()
print("矩阵 A 的平均值:")
print(A_mean)
# 沿轴 0 计算平均值
A_mean_axis0 = A.mean(axis=0)
print("沿轴 0 降维后的平均值 (对每列计算平均值):")
print(A_mean_axis0)
# 手动计算平均值,与直接调用 .mean() 结果相同
A_mean_manual = A.sum() / A.numel()
print("手动计算的平均值:")
print(A_mean_manual)
向量 x:
tensor([0., 1., 2., 3.])
向量 x 的元素和:
tensor(6.)
矩阵 A:
tensor([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[12., 13., 14., 15.],
[16., 17., 18., 19.]])
矩阵 A 的元素和:
tensor(190.)
沿轴 0 降维后 (对每列求和) 的结果:
tensor([40., 45., 50., 55.])
沿轴 1 降维后 (对每行求和) 的结果:
tensor([ 6., 22., 38., 54., 70.])
沿所有轴降维后 (对所有元素求和) 的结果:
tensor(190.)
矩阵 A 的平均值:
tensor(9.5000)
沿轴 0 降维后的平均值 (对每列计算平均值):
tensor([ 8., 9., 10., 11.])
手动计算的平均值:
tensor(9.5000)
x
的所有元素和,结果为 6
。A
的所有元素求和结果为 190
。可以指定轴来对行或列进行求和,得到相应的降维结果。A
的平均值,通过 mean()
函数或手动求和除以元素总数得到结果。保持维度求和:
sum()
函数会降低维度。如果我们希望在求和后保持原来的维度结构,可以使用 keepdims=True
参数,这样输出张量将保持与输入张量相同的维度数量。A
沿轴 1(列)进行求和并保持维度不变,这样可以方便进行后续操作,如广播。示例:
import torch
# 创建一个 5x4 的矩阵 A
A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
print("矩阵 A:")
print(A)
# 沿轴 1 求和并保持维度不变
sum_A = A.sum(axis=1, keepdims=True)
print("沿轴 1 求和并保持维度的结果 (sum_A):")
print(sum_A)
# 通过广播将矩阵 A 除以 sum_A
A_div_sum_A = A / sum_A
print("将矩阵 A 除以 sum_A (通过广播):")
print(A_div_sum_A)
累积和:
cumsum()
函数实现,它沿指定轴计算每个元素的累积和,且不降低维度。示例:
# 沿轴 0 计算累积和 (按行累积)
A_cumsum = A.cumsum(axis=0)
print("矩阵 A 沿轴 0 的累积和 (cumsum):")
print(A_cumsum)
矩阵 A:
tensor([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[12., 13., 14., 15.],
[16., 17., 18., 19.]])
沿轴 1 求和并保持维度的结果 (sum_A):
tensor([[ 6.],
[22.],
[38.],
[54.],
[70.]])
将矩阵 A 除以 sum_A (通过广播):
tensor([[0.0000, 0.1667, 0.3333, 0.5000],
[0.1818, 0.2273, 0.2727, 0.3182],
[0.2105, 0.2368, 0.2632, 0.2895],
[0.2222, 0.2407, 0.2593, 0.2778],
[0.2286, 0.2429, 0.2571, 0.2714]])
矩阵 A 沿轴 0 的累积和 (cumsum):
tensor([[ 0., 1., 2., 3.],
[ 4., 6., 8., 10.],
[12., 15., 18., 21.],
[24., 28., 32., 36.],
[40., 45., 50., 55.]])
A
求和后,结果是一个 5x1 的矩阵,保持了原来的轴结构。通过广播机制,我们可以轻松将矩阵 A
除以这个结果。A.cumsum(axis=0)
计算了沿行方向的累积和,结果中的每个元素是其当前列之前所有元素的和。点积 是两个向量之间的基本运算之一,表示相同位置的元素乘积之和。数学表示为:
[
\mathbf{x} \cdot \mathbf{y} = \sum_{i} x_i y_i
]
其中 ( \mathbf{x} ) 和 ( \mathbf{y} ) 是两个向量,( x_i ) 和 ( y_i ) 是它们对应位置的元素。
import torch
# 创建两个向量 x 和 y
x = torch.arange(4, dtype=torch.float32)
y = torch.ones(4, dtype=torch.float32)
print("向量 x:")
print(x)
print("向量 y:")
print(y)
# 计算 x 和 y 的点积
dot_product = torch.dot(x, y)
print("x 和 y 的点积:")
print(dot_product)
向量 x:
tensor([0., 1., 2., 3.])
向量 y:
tensor([1., 1., 1., 1.])
x 和 y 的点积:
tensor(6.)
x
是 [0, 1, 2, 3]
,向量 y
是 [1, 1, 1, 1]
。点积也可以通过按元素相乘后再求和来实现:
elementwise_product_sum = torch.sum(x * y)
print("通过按元素相乘并求和计算的点积:")
print(elementwise_product_sum)
通过按元素相乘并求和计算的点积:
tensor(6.)
加权和:
[
\mathbf{x} \cdot \mathbf{w} = \sum_{i} x_i w_i
]
加权平均:
[
\mathbf{x} \cdot \mathbf{w} = \text{weighted average}
]
余弦相似度:
[
\cos(\theta) = \frac{\mathbf{x} \cdot \mathbf{y}}{|\mathbf{x}| |\mathbf{y}|}
]
矩阵-向量积 是线性代数中一个非常基础的运算,它表示矩阵与向量相乘,得到另一个向量。设矩阵 ( \mathbf{A} ) 是一个 ( m \times n ) 矩阵,向量 ( \mathbf{x} ) 是一个长度为 ( n ) 的向量,那么矩阵-向量积 ( \mathbf{A} \mathbf{x} ) 是一个长度为 ( m ) 的向量。
设矩阵 ( \mathbf{A} ) 的每一行表示为一个行向量 ( \mathbf{A}_i ),那么矩阵-向量积 ( \mathbf{A} \mathbf{x} ) 的第 ( i ) 个元素是矩阵第 ( i ) 行向量 ( \mathbf{A}_i ) 与向量 ( \mathbf{x} ) 的点积:
[
\mathbf{A} \mathbf{x} = \begin{bmatrix} \mathbf{A}_1 \cdot \mathbf{x} \ \mathbf{A}_2 \cdot \mathbf{x} \ \vdots \ \mathbf{A}_m \cdot \mathbf{x} \end{bmatrix}
]
也就是说,矩阵的每一行都与向量进行点积,得到的结果是一个新向量。
import torch
# 创建一个 3x4 的矩阵 A 和一个长度为 4 的向量 x
A = torch.arange(12, dtype=torch.float32).reshape(3, 4)
x = torch.tensor([1.0, 2.0, 3.0, 4.0])
print("矩阵 A:")
print(A)
print("向量 x:")
print(x)
# 计算矩阵 A 和向量 x 的矩阵-向量积
matrix_vector_product = torch.mv(A, x)
print("矩阵 A 和向量 x 的矩阵-向量积:")
print(matrix_vector_product)
矩阵 A:
tensor([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
向量 x:
tensor([1., 2., 3., 4.])
矩阵 A 和向量 x 的矩阵-向量积:
tensor([ 20., 60., 100.])
通过矩阵-向量积,能够有效地进行多维数据的转换与操作,特别是在多维空间中的线性变换应用。
矩阵-矩阵乘法 是两个矩阵之间的基本运算,表示两个矩阵的行和列之间进行的点积运算。假设有两个矩阵 ( \mathbf{A} ) 和 ( \mathbf{B} ),其中 ( \mathbf{A} ) 的形状为 ( m \times n ),而 ( \mathbf{B} ) 的形状为 ( n \times p ),那么它们的乘积 ( \mathbf{C} ) 是一个形状为 ( m \times p ) 的矩阵。
矩阵乘法的每个元素 ( C_{ij} ) 是矩阵 ( \mathbf{A} ) 的第 ( i ) 行与矩阵 ( \mathbf{B} ) 的第 ( j ) 列的点积:
[
C_{ij} = \sum_{k=1}^{n} A_{ik} B_{kj}
]
import torch
# 创建两个矩阵 A 和 B
A = torch.arange(20, dtype=torch.float32).reshape(5, 4) # 5x4 矩阵
B = torch.ones(4, 3) # 4x3 矩阵
print("矩阵 A:")
print(A)
print("矩阵 B:")
print(B)
# 计算矩阵 A 和矩阵 B 的乘积
C = torch.mm(A, B)
print("矩阵 A 和矩阵 B 的矩阵乘积:")
print(C)
矩阵 A:
tensor([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[12., 13., 14., 15.],
[16., 17., 18., 19.]])
矩阵 B:
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
矩阵 A 和矩阵 B 的矩阵乘积:
tensor([[ 6., 6., 6.],
[22., 22., 22.],
[38., 38., 38.],
[54., 54., 54.],
[70., 70., 70.]])
范数 是线性代数中的一个重要概念,用于衡量向量或矩阵的“大小”。范数是一种将向量映射到标量的函数,用于表示向量的长度或大小。不同类型的范数在不同的场景下具有不同的应用,常见的范数包括 ( L_1 ) 范数、( L_2 ) 范数和 Frobenius 范数。
( L_2 ) 范数(欧几里得范数):
定义为向量元素的平方和的平方根。它表示向量的欧几里得距离。
公式为:
[
| \mathbf{u} |2 = \sqrt{\sum{i} u_i^2}
]
代码示例:
import torch
u = torch.tensor([3.0, -4.0])
l2_norm = torch.norm(u)
print("向量 u 的 L2 范数:", l2_norm)
输出结果:
向量 u 的 L2 范数: tensor(5.)
( L_1 ) 范数:
定义为向量元素的绝对值之和。它比 ( L_2 ) 范数更不容易受到异常值的影响。
公式为:
[
| \mathbf{u} |1 = \sum{i} |u_i|
]
代码示例:
l1_norm = torch.abs(u).sum()
print("向量 u 的 L1 范数:", l1_norm)
输出结果:
向量 u 的 L1 范数: tensor(7.)
( L_p ) 范数:
类似于向量的 ( L_2 ) 范数,Frobenius 范数 是矩阵元素平方和的平方根。
公式为:
[
| \mathbf{A} |F = \sqrt{\sum{i,j} A_{ij}^2}
]
Frobenius 范数可以看作是将矩阵视为向量后的 ( L_2 ) 范数。
代码示例:
A = torch.ones((4, 9))
frobenius_norm = torch.norm(A)
print("矩阵 A 的 Frobenius 范数:", frobenius_norm)
输出结果:
矩阵 A 的 Frobenius 范数: tensor(6.)
在深度学习中,范数有广泛的应用,尤其在优化问题中。我们经常需要最小化损失函数,而损失函数可以用范数来衡量预测值与真实值之间的误差。常见的应用场景包括:
通过理解范数的基本性质和计算方式,可以在各种机器学习和深度学习任务中更好地评估和优化模型。