深度学习-预备知识3-线性代数

2025-07-19  本文已影响0人  R7_Perfect

线性代数是深度学习的核心基础,理解其基本概念和运算对于掌握神经网络至关重要。本文将详细介绍线性代数中的关键内容,并结合PyTorch框架进行示例。

一、标量

想象一下你在餐厅支付账单时,你可能已经在无形中运用了线性代数的基本知识,比如加法和乘法。比如,北京的温度以华氏度表示为52°F,这个数字本身就是一个标量。如果你想把华氏度转换为更常见的摄氏度,你可以使用一个简单的数学公式:


1752994644191.png

在这个公式中,所有的数值,比如华氏度和摄氏度,都是标量。符号通常用小写字母(如 x、y)表示,代表这些未知的数值。我们用一个符号空间来表示所有可能的实数标量,比如 R。而符号“属于”表示某个数在这个集合中,比如 x∈ R 意味着 x 是一个实数。
在数学中,标量可以用只有一个元素的张量来表示。下面的代码展示了如何在PyTorch中创建两个标量,并进行加法、乘法、除法和指数运算:

import torch

x = torch.tensor(3.0)  # 创建标量3.0
y = torch.tensor(2.0)  # 创建标量2.0

# 执行算术运算
result_add = x + y  # 加法
result_mul = x * y  # 乘法
result_div = x / y  # 除法
result_pow = x ** y  # 指数

print(result_add, result_mul, result_div, result_pow)

"""
tensor(5.) tensor(6.) tensor(1.5000) tensor(9.)
"""

运行这段代码,得到的结果分别是:5.0(加法),6.0(乘法),1.5(除法),和9.0(指数)。这些基本运算为理解更复杂的线性代数概念奠定了基础。

二、向量

向量可以看作是由标量值组成的列表,这些值称为元素或分量。在实际应用中,当向量表示数据集中的样本时,每个分量都具有重要意义。例如,如果我们在训练一个贷款违约风险预测模型,每个申请人可以用一个向量表示,其中的分量可能包括收入、工作年限、过往违约次数等。如果我们研究患者的心脏病发作风险,则可能用一个向量表示每个患者,分量包括最新的生命体征、胆固醇水平、每日运动时间等。
在数学中,向量通常用粗体小写字母(如 v、x、y)表示。向量可以用一维张量表示,其长度可以根据机器的内存限制变化。下面的代码示例展示了如何在PyTorch中创建一个向量:

import torch
x = torch.arange(4)  # 创建一个包含0到3的向量
print(x)  # 输出: tensor([0, 1, 2, 3])

我们可以通过下标访问向量的任一元素,例如 x[2] 引用第三个元素。值得注意的是,引用的元素是一个标量,因此不需要加粗。文献中通常将列向量视为默认形式。在数学上,向量可以表示为:


1752994790904.png

其中 Vi 是向量的元素。我们可以通过张量的索引来访问这些元素:

element = x[3]
print(element)  # 输出:tensor(3)

长度、维度和形状

向量是数字的数组,每个数组都有一个长度,向量同样如此。如果我们想表示一个包含
𝑛 个实值标量的向量,可以写作 v∈𝑅^n 。向量的长度也被称为维度。
在Python中,我们可以使用内置的 len() 函数来获取向量的长度:

length = len(x)  # 获取向量长度
print(length)  # 输出: 4

当用张量表示一个向量(即只有一个轴时),我们也可以通过 .shape 属性访问长度。形状(shape)是一个包含元素数量的元组,对于一维张量,形状只有一个元素:

shape = x.shape
print(shape)  # 输出: torch.Size([4])

需要注意的是,“维度”这个词在不同上下文中可能会产生混淆。这里我们明确:向量的维度指的是向量的长度,即元素的数量。而张量的维度则指的是张量的轴数。在这种意义上,某个轴的维度表示这个轴的长度。

三、矩阵

矩阵可以看作是将向量从一阶推广到二阶的结构。我们通常用粗体大写字母表示矩阵(例如 A、B、C)。在代码中,矩阵被表示为具有两个轴的张量。
在数学表示中,一个矩阵 𝐴 表示为一个 𝑚 × 𝑛 的实值标量的集合,其中
𝑚 是行数,𝑛 是列数。矩阵的元素 𝑎𝑖𝑗𝑎_{𝑖𝑗}aij 位于第 𝑖 行第 𝑗 列:


1752994971232.png

对于任意矩阵 𝐴,它的形状为 (𝑚, 𝑛)。当矩阵的行数与列数相等时,称其为方阵。通过指定矩阵的行数和列数,我们可以用代码来创建矩阵。例如,在PyTorch中:

# 创建一个5行4列的矩阵
A = torch.arange(20).reshape(5, 4)
print(A)
"""
tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11],
        [12, 13, 14, 15],
        [16, 17, 18, 19]])
"""

我们可以通过行索引和列索引来访问矩阵的元素,例如 A[1, 2] 访问的是矩阵第二行第三列的元素。注意,矩阵中的元素是标量,所以在引用时不需要加粗。若不特别标注,矩阵通常以行优先方式表示。

element = A[1, 2]
print(element)  # 输出:tensor(6)
矩阵的转置

当我们交换矩阵的行和列时,得到的结果称为矩阵的转置,用AT表示。例如,对于上面的矩阵 𝐴,其转置可以在代码中计算:

print(A.T)
"""
tensor([[ 0,  4,  8, 12, 16],
        [ 1,  5,  9, 13, 17],
        [ 2,  6, 10, 14, 18],
        [ 3,  7, 11, 15, 19]])
"""
对称矩阵

一个特殊类型的矩阵是对称矩阵,它等于其转置,即B=BT,例如:


import torch

B = torch.tensor([[1, 2, 3], [2, 0, 4], [3, 4, 5]])
print(B)
"""
tensor([[1, 2, 3],
        [2, 0, 4],
        [3, 4, 5]])
"""
print(B.T)
"""
tensor([[1, 2, 3],
        [2, 0, 4],
        [3, 4, 5]])
"""
print(B == B.T)
"""
tensor([[True, True, True],
        [True, True, True],
        [True, True, True]])
"""
矩阵在实际中的应用

矩阵是非常有用的数据结构,可以用来组织不同模式的数据。例如,矩阵的行可以表示不同的房屋(数据样本),而列可以表示这些房屋的属性。如果你曾经使用过电子表格软件,那么对这种结构应该比较熟悉。

四、张量:多维数据的扩展

就像向量是标量的推广,矩阵是向量的推广一样,张量是我们处理多维数据的更广泛工具。张量可以有任意数量的轴,因此,它被视为一种多维数组的通用表示方法。

创建和查看张量

让我们在 PyTorch 中创建一个简单的三维张量,形状为 2 × 3 × 4(2个“表”,每个“表”包含3行4列的数值):

import torch

# 创建一个形状为 (2, 3, 4) 的张量
X = torch.arange(24).reshape(2, 3, 4)

print(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]]])
"""
张量的结构

五、张量算法的基本性质

无论是标量、向量、矩阵还是任意维度的张量,它们都有一些 按元素操作 的特性。换句话说,当我们在张量上执行按元素的运算时,操作不会改变张量的形状,输出张量的形状与输入张量的形状一致。

一元运算

例如,对于一元操作(如对张量的每个元素进行加、减或取反操作),结果的形状与输入相同。

# 取反(Negation)
x = torch.tensor([1.0, -2.0, 3.0])
negated_x = -x
print(negated_x)  # 输出:tensor([-1.,  2., -3.])

# 指数(Exponential)
x = torch.tensor([0.0, 1.0, 2.0])
exp_x = torch.exp(x)
print(exp_x)  # 输出:tensor([1.0000, 2.7183, 7.3891])

# 取绝对值(Absolute Value)
x = torch.tensor([-1.0, -2.0, 3.0])
abs_x = torch.abs(x)
print(abs_x)  # 输出:tensor([1., 2., 3.])
二元运算

同样,任何两个形状相同的张量,在进行按元素的二元运算时,结果仍然会是一个相同形状的张量。一个常见的例子是矩阵的元素相加运算:

# 创建一个5x4的矩阵A,并克隆A到B
A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
B = A.clone()  # 复制A到B
print(A)
"""
tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.],
        [12., 13., 14., 15.],
        [16., 17., 18., 19.]])
"""
print(A + B)  # 矩阵A和B相加
"""
tensor([[ 0.,  2.,  4.,  6.],
        [ 8., 10., 12., 14.],
        [16., 18., 20., 22.],
        [24., 26., 28., 30.],
        [32., 34., 36., 38.]])
"""
Hadamard积

当我们对两个矩阵按元素相乘时,得到的运算称为 Hadamard积,记作 𝐴 ∘ 𝐵:


1752995444925.png

对于矩阵 𝐴 和 𝐵,它们的 Hadamard 积计算如下:

print(A * B)
"""
tensor([[  0.,   1.,   4.,   9.],
        [ 16.,  25.,  36.,  49.],
        [ 64.,  81., 100., 121.],
        [144., 169., 196., 225.],
        [256., 289., 324., 361.]])
"""
张量与标量的运算

将张量乘以或加上一个标量不会改变张量的形状,标量运算会应用到张量的每个元素上:

import torch

a = 2
X = torch.arange(24).reshape(2, 3, 4)
print(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]]])
"""
# 标量加操作
print(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]]])
"""
# 标量乘操作
print(a * X)
"""
tensor([[[ 0,  2,  4,  6],
         [ 8, 10, 12, 14],
         [16, 18, 20, 22]],

        [[24, 26, 28, 30],
         [32, 34, 36, 38],
         [40, 42, 44, 46]]])
"""
print((a * X).shape)  # 输出:torch.Size([2, 3, 4])

六、降维

我们可以对任意张量进行的一个有用操作是计算其元素的和。数学表示法使用 Σ 符号表示求和。为了表示长度为 𝑛 的向量中元素的总和,可以记为:


1752995534749.png
import torch

x = torch.arange(4, dtype=torch.float32)
print(x)  # tensor([0., 1., 2., 3.])
print(x.sum())  # tensor(6.)

我们可以表示任意形状张量的元素和。例如,矩阵 𝐴 中元素的和可以记为:


1752995562480.png
A = torch.arange(20).reshape(5, 4)
print(A)
"""
tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11],
        [12, 13, 14, 15],
        [16, 17, 18, 19]])
"""
print(A.shape)  # torch.Size([5, 4])
print(A.sum())  # tensor(190)

默认情况下,调用求和函数会沿所有轴降低张量的维度,使其变为一个标量。我们还可以指定张量沿哪一个轴来通过求和降低维度。以矩阵为例,为了通过求和所有行的元素来降维(轴0),可以在调用函数时指定 axis=0 或 dim=0。

A = torch.arange(20).reshape(5, 4)
print(A)
"""
tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11],
        [12, 13, 14, 15],
        [16, 17, 18, 19]])
"""
A_sum_axis0 = A.sum(dim=0)
print(A_sum_axis0, A_sum_axis0.shape)
# tensor([40, 45, 50, 55]) torch.Size([4])

指定 axis=1 将通过汇总所有列的元素降维(轴1)。

A_sum_axis1 = A.sum(dim=1)
print(A_sum_axis1, A_sum_axis1.shape)
# tensor([ 6, 22, 38, 54, 70]) torch.Size([5])

沿着行和列对矩阵求和,等价于对矩阵的所有元素进行求和:

print(A.sum(dim=[0, 1]))  # tensor(190),结果和A.sum()相同

一个与求和相关的量是平均值(mean或average)。我们通过将总和除以元素总数来计算平均值:

A = torch.arange(20, dtype=torch.float32).reshape(5, 4)

print(A)
"""
tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.],
        [12., 13., 14., 15.],
        [16., 17., 18., 19.]])
"""

print(A.mean())  # tensor(9.5000)
print(A.sum() / A.numel())  # tensor(9.5000)

同样,计算平均值的函数也可以沿指定轴降低张量的维度:

print(A.mean(dim=0))  # tensor([ 8.,  9., 10., 11.])
print(A.sum(dim=0) / A.shape[0])  # tensor([ 8.,  9., 10., 11.])
非降维求和

有时,在调用函数来计算总和或均值时,保持轴数不变会很有用:

"""
tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.],
        [12., 13., 14., 15.],
        [16., 17., 18., 19.]])
"""
sum_A = A.sum(dim=1, keepdim=True)
print(sum_A)
"""
tensor([[ 6.],
        [22.],
        [38.],
        [54.],
        [70.]])
"""
print(sum_A.shape)  # torch.Size([5, 1])

由于 sum_A 在对每行进行求和后仍保持两个轴,我们可以通过广播将 𝐴 除以 sum_A:

print(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]])
"""

如果我们想沿某个轴计算 𝐴 元素的累积总和,比如 axis=0(按行计算),可以调用 cumsum 函数:

"""
tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.],
        [12., 13., 14., 15.],
        [16., 17., 18., 19.]])
"""
print(A.cumsum(dim=0))
"""
tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  6.,  8., 10.],
        [12., 15., 18., 21.],
        [24., 28., 32., 36.],
        [40., 45., 50., 55.]])
"""

七、点积

我们已经学习了按元素操作、求和及平均值。另一个最基本的操作之一是点积。给定两个向量 𝑥, 𝑦,它们的点积(dot product)xTy(或 ⟨𝑥,𝑦⟩)是相同位置的按元素乘积的和:


1752996170918.png
import torch

x = torch.arange(4, dtype=torch.float32)
print(x)  # tensor([0., 1., 2., 3.])
y = torch.ones(4, dtype=torch.float32)
print(y)  # tensor([1., 1., 1., 1.])
print(torch.dot(x, y))  # tensor(6.)

注意,我们可以通过执行按元素乘法,然后进行求和来表示两个向量的点积:

print(x * y)  # tensor([0., 1., 2., 3.])
print(torch.sum(x * y))  # tensor(6.)
点积在很多场合都很有用。 例如,给定一组由向量 𝑥 表示的值,和一组由 𝑤 表示的权重,𝑥 中的值根据权重 𝑤 的加权和,可以表示为点积 𝑥⋅𝑤。当权重为非负数且和为1(即 1752996331760.png

)时,点积表示加权平均(weighted average)。将两个向量规范化得到单位长度后,点积表示它们夹角的余弦。本节后面的内容将正式介绍长度(length)的概念。

八、矩阵-向量积

现在我们知道如何计算点积,可以开始理解矩阵-向量积(matrix-vector product)。 前面定义的矩阵 𝐴 和向量 𝑥。 让我们将矩阵 𝐴 用它的行向量表示:


1752996373027.png

其中每个aiT都是行向量,表示矩阵的第 𝑖 行。。矩阵向量积 Ax 是一个长度为 𝑚 的列向量,其第 𝑖 个元素是点积aiTx


1752996450368.png

我们可以把一个矩阵乘法看作一个从Rn到Rm向量的转换。这些转换是非常有用的,例如可以用方阵的乘法来表示旋转。后续将讲到,我们也可以使用矩阵-向量积来描述在给定前一层的值时,求解神经网络每一层所需的复杂计算。

A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
print(A, A.shape)
"""
tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.],
        [12., 13., 14., 15.],
        [16., 17., 18., 19.]]) torch.Size([5, 4])
"""
x = torch.arange(4, dtype=torch.float32)
print(x, x.shape)  # tensor([0., 1., 2., 3.]) torch.Size([4])

print(torch.mv(A, x))
# tensor([ 14.,  38.,  62.,  86., 110.])

九、矩阵乘法

在掌握点积和矩阵-向量积的知识后,矩阵-矩阵乘法(matrix-matrix multiplication)应该很简单。假设有两个矩阵A和 B


1752996958115.png
用行向量 ai表示矩阵 A的第 i 行,并让列向量 bj作为矩阵 B的第 j 列。要生成矩阵积 C,最简单的方法是考虑 A的行向量和 B的列向量: 1752997037128.png
当我们简单地将每个元素 cij计算为点积 1752997066059.png
1752997080823.png

我们可以将矩阵-矩阵乘法 C\mathbf{C}C 看作简单地执行 mmm 次矩阵-向量积,并将结果拼接在一起,形成一个 m×nm \times nm×n 矩阵。 在下面的代码中,我们在A和B上执行矩阵乘法。 这里的A是一个5行4列的矩阵,B是一个4行3列的矩阵。 两者相乘后,我们得到了一个5行3列的矩阵。

A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
print(A)
"""
tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.],
        [12., 13., 14., 15.],
        [16., 17., 18., 19.]])
"""
B = torch.ones(4, 3)
print(B)
"""
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]])
"""
print(torch.mm(A, B))
"""
tensor([[ 6.,  6.,  6.],
        [22., 22., 22.],
        [38., 38., 38.],
        [54., 54., 54.],
        [70., 70., 70.]])
"""

矩阵-矩阵乘法可以简单地称为矩阵乘法,不应与 “ Hadamard积 ” 混淆。

十、范数

我们已经介绍了线性代数中一些基本的运算,如按元素操作、点积、矩阵乘法等。接下来要介绍的是范数 (norm)——向量和矩阵的大小度量。
在线性代数中,向量的范数是将向量映射到标量的函数。给定任意向量 u∈Rn
,其范数需要满足以下性质:

  1. 比例性 (Scaling): 如果我们将向量u按常数因子α缩放,其范数按α的绝对值缩放:


    1752997446326.png
  2. 三角不等式 (Triangle inequality): 对于任意向量u和v,有 1752997478474.png
  3. 非负性 (Non-negativity): 向量范数


    1752997531321.png
    正齐次性 (Homogeneity): 对于任何实数 α和向量u,有 1752997573462.png
    最常见的范数之一是 L2范数,也称为欧几里得范数 (Euclidean norm),定义为向量元素平方和的平方根:
    1752997603121.png

    在代码中,我们可以按如下方式计算向量的 L2范数:

u = torch.tensor([3.0, -4.0])
print(torch.norm(u))  # tensor(5.)

除了 L2范数,L1范数表示向量元素绝对值之和:


1752997667025.png

在代码中,我们可以使用如下代码计算 L1范数:

u = torch.tensor([3.0, -4.0])
print(torch.abs(u).sum())  # tensor(7.)

L p范数 是 L1和 L2范数的推广形式:


1752997734889.png

此外,矩阵的 Frobenius 范数 定义为矩阵元素平方和的平方根,类似于向量的 L2范数:


1752997760276.png
A = torch.ones([4, 9])
print(torch.norm(A))  # tensor(6.)
上一篇 下一篇

猜你喜欢

热点阅读