EP04:【DL 第二弹】张量的线性代数运算
一、BLAS和LAPACK概述
在现代科学计算和深度学习领域,线性代数是核心数学工具之一,而BLAS(Basic Linear Algebra Subprograms)和LAPACK(Linear Algebra Package)则是支撑这一工具的基础设施。BLAS诞生于20世纪70年代,最初由美国国家科学基金会支持开发,旨在标准化线性代数运算的底层接口;LAPACK则在BLAS基础上发展而来,提供更高级的线性代数算法。二者经过数十年的优化,已成为高性能计算的标配,被广泛集成到PyTorch、NumPy等主流计算框架中,为矩阵运算、方程求解等任务提供高效支持。
从功能上看,BLAS分为三个层级:Level 1主要处理向量与向量的运算(如内积、向量加减);Level 2聚焦矩阵与向量的运算(如矩阵乘向量、线性方程组的单个解);Level 3则针对矩阵与矩阵的运算(如矩阵乘法、矩阵分解)。LAPACK则基于这些基础运算,实现了更复杂的功能,如线性方程组求解、特征值分解、奇异值分解等。
文档中对二者涵盖的功能进行了分类,这些分类本质上对应了线性代数的核心操作场景:
- 矩阵的形变及特殊矩阵构造是数据预处理的基础。例如,转置操作可调整矩阵维度以满足运算需求,单位矩阵和对角矩阵则在矩阵求逆、线性变换中扮演基准角色;
- 矩阵的基本运算(如乘法、内积)是构建复杂模型的积木—— 从神经网络中的权重更新到图像卷积操作,都依赖这些基础运算;
- 矩阵的线性代数运算(如迹、秩、逆矩阵)是分析矩阵性质的关键。例如,通过秩可判断数据的线性相关性,通过逆矩阵可直接求解线性方程组;
- 矩阵分解运算(如特征分解、SVD)则是降维、数据压缩的核心工具。例如,SVD分解能提取矩阵的主要特征,用少量参数近似表示原始矩阵,大幅减少计算成本。
二、矩阵的形变和特殊矩阵的构造
矩阵作为二维张量,其形状和结构直接影响运算的可行性和效率。在实际应用中,我们常需要调整矩阵形状(如转置)或使用具有特殊结构的矩阵(如单位矩阵)来简化计算。这部分内容看似基础,却是后续复杂运算的前置步骤—— 例如,矩阵乘法要求前一个矩阵的列数等于后一个矩阵的行数,若不满足,就需要通过转置调整维度;而对角矩阵的逆矩阵可直接通过对角线元素倒数构造,大幅简化计算过程。
函数 | 描述 |
---|---|
torch.t(t) | t 转置 |
torch.eye(n) | 创建包含n 个分量的单位矩阵 |
torch.diag(t1) | 以t1 中各元素,创建对角矩阵 |
torch.triu(t) | 取矩阵t 中的上三角矩阵 |
torch.tril(t) | 取矩阵t 中的下三角矩阵 |
2.1 转置
转置是矩阵形变中最常用的操作,其数学定义为:将矩阵的行与列互换,即原矩阵中位于第iii行第jjj列的元素,转置后位于第jjj行第iii列。用公式表示为:若原矩阵为A
,转置矩阵为Aᵀ
,则AT[i][j]=A[j][i]Aᵀ[i][j] = A [j][i]AT[i][j]=A[j][i]。
转置的实际应用场景非常广泛:在矩阵乘法中,若矩阵A
的形状为(m,n),矩阵B
的形状为(p,q),则A
与B
可相乘的前提是n=p
;若B
的形状为(q,p),则需先对B
转置得到(p,q),再与A
相乘。此外,在图像处理中,转置可调整图像的宽高比例;在自然语言处理中,转置可转换词向量矩阵的维度,适应不同模型的输入要求。
需要注意的是,转置操作不会改变矩阵的元素值,仅改变元素的排列顺序,因此其时间复杂度为 O(1)(仅调整索引映射),几乎不消耗计算资源。
t1 = torch.arange(1, 7).reshape(2 ,3).float()
print(f"t1:{t1}")
print(f"t1 转置:{torch.t(t1)}")
print(f"t1 转置:{t1.t()}")
- 运行结果:
t1:tensor([[1., 2., 3.],[4., 5., 6.]])
t1 转置:tensor([[1., 4.],[2., 5.],[3., 6.]])
t1 转置:tensor([[1., 4.],[2., 5.],[3., 6.]])
- 示例解读:
在PyTorch中,
torch.t(t)
和t.t()
两种方式均可实现转置,二者效果完全一致。从代码示例来看,当输入为2×3矩阵[[1.,2.,3.],[4.,5.,6.]]
时,转置后得到3×2矩阵[[1.,4.],[2.,5.],[3.,6.]]
,清晰体现了行与列的互换。
2.2 单位矩阵
单位矩阵是一种特殊的方阵(行数 = 列数),其主对角线元素均为1,其余元素均为0。
在PyTorch中,torch.eye(n)
可直接创建n
阶单位矩阵。单位矩阵的核心性质是:对于任意矩阵A
(形状为m×n),若与n
阶单位矩阵相乘,结果仍为A
;若与m
阶单位矩阵相乘,结果也为A
(即A×Iₙ=A
,Iₘ×A=A
)。这一性质类似数字运算中的“1”,因此单位矩阵也被称为矩阵乘法的单位元。
单位矩阵的应用贯穿线性代数的多个场景:在矩阵求逆中,若矩阵A
存在逆矩阵A⁻¹
,则A×A−1=IA×A⁻¹=IA×A−1=I(单位矩阵),这是判断矩阵可逆的核心依据;在初始化神经网络权重时,有时会用单位矩阵作为初始值,避免梯度消失或爆炸;在解线性方程组时,单位矩阵可作为基准解的参考。
t2 = torch.eye(3)
print(f"t2:{t2}")
- 运行结果:
t2:tensor([[1., 0., 0.],[0., 1., 0.],[0., 0., 1.]])
2.3 对角矩阵
对角矩阵是另一类特殊方阵,其非对角线元素均为0,仅主对角线可包含非零元素。PyTorch中torch.diag(t1, offset)
可通过向量t1
构造对角矩阵,其中offset
参数控制对角线的偏移方向:offset=0
(默认)表示主对角线,offset=1
表示主对角线上方第一位,offset=-1
表示主对角线下方第一位。
三角矩阵在数值计算中至关重要:例如,LU 分解将矩阵拆分为下三角矩阵L
和上三角矩阵U
,可将复杂的线性方程组求解转化为两个简单的三角方程组求解;Cholesky 分解则针对正定矩阵,将其分解为下三角矩阵与其转置的乘积,大幅简化矩阵求逆和行列式计算。
t3 = torch.tensor([1, 2, 3, 4, 5])
print(f"t3:{t3}")
print(f"t3 对角:{torch.diag(t3)}")
print(f"t3 对角向上偏移一位:{torch.diag(t3, 1)}")
print(f"t3 对角向下偏移一位:{torch.diag(t3, -1)}")t4 = torch.arange(9).reshape(3, 3)
print(f"t4:{t4}")
print(f"t4 上三角:{torch.triu(t4)}")
print(f"t4 上三角向上偏移一位:{torch.triu(t4, 1)}")
print(f"t4 下三角:{torch.tril(t4)}")
- 运行结果:
t3:tensor([1, 2, 3, 4, 5])
t3 对角:tensor([[1, 0, 0, 0, 0],[0, 2, 0, 0, 0],[0, 0, 3, 0, 0],[0, 0, 0, 4, 0],[0, 0, 0, 0, 5]])
t3 对角向上偏移一位:tensor([[0, 1, 0, 0, 0, 0],[0, 0, 2, 0, 0, 0],[0, 0, 0, 3, 0, 0],[0, 0, 0, 0, 4, 0],[0, 0, 0, 0, 0, 5],[0, 0, 0, 0, 0, 0]])
t3 对角向下偏移一位:tensor([[0, 0, 0, 0, 0, 0],[1, 0, 0, 0, 0, 0],[0, 2, 0, 0, 0, 0],[0, 0, 3, 0, 0, 0],[0, 0, 0, 4, 0, 0],[0, 0, 0, 0, 5, 0]])
t4:tensor([[0, 1, 2],[3, 4, 5],[6, 7, 8]])
t4 上三角:tensor([[0, 1, 2],[0, 4, 5],[0, 0, 8]])
t4 上三角向上偏移一位:tensor([[0, 1, 2],[0, 0, 5],[0, 0, 0]])
t4 下三角:tensor([[0, 0, 0],[3, 4, 0],[6, 7, 8]])
- 示例解读:
向量
[1,2,3,4,5]
在offset=0
时生成5阶对角矩阵,对角线元素为1-5;offset=1
时生成6阶矩阵,对角线元素上移一位;offset=-1
时则下移一位。这种灵活性使得对角矩阵可用于表示稀疏的线性变换 —— 例如,在信号处理中,对角矩阵可表示不同频率信号的缩放系数,非对角线元素为0意味着频率间无耦合。
三角矩阵则分为上三角矩阵(主对角线以下元素为0)和下三角矩阵(主对角线以上元素为0),分别通过torch.triu(t, offset)
和torch.tril(t, offset)
实现。例如,3×3矩阵[[0,1,2],[3,4,5],[6,7,8]]
的上三角矩阵(offset=0
)为[[0,1,2],[0,4,5],[0,0,8]]
,上移一位(offset=1
)后为[[0,1,2],[0,0,5],[0,0,0]]
。
三、矩阵的基本运算
矩阵的基本运算不同于普通的元素级运算(如逐元素相乘),而是遵循线性代数的严格规则。这些运算不仅是数学理论的核心,更是工程实践中处理数据的工具链—— 从图像识别中的特征提取到推荐系统中的相似度计算,都依赖这些运算的高效实现。
函数 | 描述 |
---|---|
torch.dot(t1, t2) | 计算t1 、t2 张量内积 |
torch.mm(t1, t2) | 矩阵乘法 |
torch.mv(t1, t2) | 矩阵乘向量 |
torch.bmm(t1, t2) | 批量矩阵乘法 |
torch.addmm(t, t1, t2) | 矩阵相乘后相加 |
torch.addbmm(t, t1, t2) | 批量矩阵相乘后相加 |
3.1 dot/vdot
点积运算
点积(内积)是向量运算的基础,其定义为两个同维度向量对应元素相乘后的和。
点积的物理意义是向量投影:点积结果越大,说明两个向量的方向越接近(夹角越小)。这一性质使其在推荐系统中用于计算用户兴趣向量与物品特征向量的相似度,在计算机视觉中用于判断两个特征向量的匹配程度。
t1 = torch.arange(1, 4)
print(f"t1:{t1}")
print(f"t1*t1:{torch.dot(t1, t1)}")
print(f"t1*t1:{torch.vdot(t1, t1)}")# ×1. 不能进行一维张量以外的运算
t2 = torch.arange(1, 5).reshape(2, 2)
# print(f"t2*t2:{torch.dot(t2, t2)}") # RuntimeError: 1D tensors expected, but got 2D and 2D tensors
- 运行结果:
t1:tensor([1, 2, 3])
t1*t1:14
t1*t1:14
- 示例解读:
向量
[1,2,3]
与自身的点积为1×1+2×2+3×3=141×1 + 2×2 + 3×3 = 141×1+2×2+3×3=14,与计算结果一致。需要注意的是,点积仅适用于一维张量(向量),若输入二维及以上张量会报错 —— 这是因为高维张量的点积定义不唯一,需通过更具体的运算(如矩阵乘法)实现。
3.2 mm
矩阵乘法运算
矩阵乘法是线性代数中最核心的运算之一,其定义为:若矩阵A
为m×n形状,矩阵B
为n×p形状,则乘积C=A×B
为m×p形状,其中C[i][j]=Σk(A[i][k]×B[k][j])C[i][j] = Σₖ(A[i][k]×B [k][j])C[i][j]=Σk(A[i][k]×B[k][j])(kkk从1到nnn)。简单来说,行乘列求和是矩阵乘法的核心逻辑。
矩阵乘法的应用无处不在:在神经网络中,输入特征向量与权重矩阵的乘法实现了特征的线性变换;在图形学中,矩阵乘法可实现图形的旋转、缩放等变换;在数据分析中,矩阵乘法可用于聚合多个特征维度的信息。
t3 = torch.arange(1, 7).reshape(2, 3)
t4 = torch.arange(1, 10).reshape(3, 3)
print(f"t3*t3:{t3 * t3}")
print(f"t3*t4:{torch.mm(t3, t4)}")
- 运行结果:
t3*t3:tensor([[ 1, 4, 9],[16, 25, 36]])
t3*t4:tensor([[30, 36, 42],[66, 81, 96]])
- 示例解读:
2×3矩阵
t3
与3×3矩阵t4
相乘,结果为2×3矩阵:第一行第一列元素为1×1+2×4+3×7=301×1 + 2×4 + 3×7 = 301×1+2×4+3×7=30,与输出一致。需要注意的是,矩阵乘法不满足交换律(A×B≠B×AA×B ≠ B×AA×B=B×A),且要求前矩阵的列数等于后矩阵的行数,否则会报错。
3.3 mv
矩阵和向量乘法运算
矩阵乘向量是矩阵乘法的特殊情况:当矩阵A
为m×n形状,向量v
为n×1形状(列向量)时,乘积结果为m×1形状的向量,其中每个元素为A
的对应行与v
的点积。PyTorch中torch.mv(t1, t2)
实现这一运算,且向量t2
可以是1维张量(默认视为列向量)。
t5 = torch.arange(1, 7).reshape(2, 3)
t6 = torch.arange(1, 4)
print(f"t5*t6:{torch.mv(t5, t6)}")
print(f"t5*t6(列向量):{torch.mv(t5, t6.reshape(3, ))}")
- 运行结果:
t5*t6:tensor([14, 32])
t5*t6(列向量):tensor([14, 32])
- 示例解读:
2×3矩阵
t5
与向量t6=[1,2,3]
相乘,结果为[1×1+2×2+3×3,4×1+5×2+6×3]=[14,32][1×1 + 2×2 + 3×3, 4×1 + 5×2 + 6×3] = [14, 32][1×1+2×2+3×3,4×1+5×2+6×3]=[14,32],与输出一致。这一运算的本质是将向量从nnn维空间映射到mmm维空间,是线性变换的直观体现 —— 例如,在三维空间中,矩阵可将一个向量拉伸或旋转到新的维度。
3.4 bmm
批量矩阵乘法运算
在实际场景中,我们常需要处理批量数据(如一批图像的特征矩阵、一批文本的词向量矩阵),此时三维张量的批量乘法就显得尤为重要。三维张量的形状通常表示为 (batch_size, m, n)
,可理解为“batch_size个m×n矩阵”。
批量运算的优势在于效率:通过并行计算处理多个矩阵,避免了循环遍历的开销,这对深度学习中的大规模数据处理至关重要。例如,在处理一批1000张图像时,每张图像的特征矩阵可通过批量乘法一次性完成与权重矩阵的运算,大幅提升速度。
t7 = torch.arange(1, 13).reshape(3, 2, 2)
t8 = torch.arange(1, 19).reshape(3, 2, 3)
print(f"t7*t8:{torch.bmm(t7, t8)}")
- 运行结果:
t7*t8:tensor([[[ 9, 12, 15],[ 19, 26, 33]],[[ 95, 106, 117],[129, 144, 159]],[[277, 296, 315],[335, 358, 381]]])
3.5 addmm
矩阵先乘后加运算
torch.addmm(t, t1, t2)
实现矩阵相乘后加的复合运算,其数学表达式为:result=t+(t1×t2)result = t + (t1 × t2)result=t+(t1×t2)。此外,还可通过alphaalphaalpha和betabetabeta参数调整缩放因子,即result=beta×t+alpha×(t1×t2)result = beta×t + alpha×(t1 × t2)result=beta×t+alpha×(t1×t2)。
这一运算在优化算法中频繁出现,例如随机梯度下降(SGD)中,参数更新公式为w=w−lr×(梯度)w = w - lr×(梯度)w=w−lr×(梯度),可通过addmm
高效实现(将lrlrlr作为alphaalphaalpha,-1作为betabetabeta)。
t9 = torch.arange(1, 7).reshape(2, 3)
t10 = torch.arange(1, 10).reshape(3, 3)
t11 = torch.arange(3)
print(f"mm:{torch.mm(t9, t10)}")
print(f"addmm:{torch.addmm(t11, t9, t10)}")
print(f"addmm,beta=0,alpha=10:{torch.addmm(t11, t9, t10, beta=0, alpha=10)}")
- 运行结果:
mm:tensor([[30, 36, 42],[66, 81, 96]])
addmm:tensor([[30, 37, 44],[66, 82, 98]])
addmm,beta=0,alpha=10:tensor([[300, 360, 420],[660, 810, 960]])
- 示例解读:
t11
为向量[0,1,2]
(广播为2×3矩阵),t9×t10
的结果为2×3矩阵,相加后第一行变为 [30+0,36+1,42+2]=[30,37,44][30+0, 36+1, 42+2] = [30,37,44][30+0,36+1,42+2]=[30,37,44],与输出一致。当beta=0
,alpha=10
时,结果为10倍的t9×t10
,体现了缩放功能。
3.6 addbmm
批量矩阵先乘后加运算
t12 = torch.arange(6).reshape(2 ,3)
t13 = torch.arange(1, 13).reshape(3, 2, 2)
t14 = torch.arange(1, 19).reshape(3, 2, 3)
print(f"bmm:{torch.bmm(t13, t14)}")
print(f"addbmm:{torch.addbmm(t12, t13, t14)}")
- 运行结果:
bmm:tensor([[[ 9, 12, 15],[ 19, 26, 33]],[[ 95, 106, 117],[129, 144, 159]],[[277, 296, 315],[335, 358, 381]]])
addbmm:tensor([[381, 415, 449],[486, 532, 578]])
- 示例解读:
t13
(3,2,2)与t14
(3,2,3)的批量乘法结果为3个2×3矩阵,求和后得到2×3矩阵,再与t12
(2,3)相加得到最终结果。这一运算适用于需要聚合多个批次结果的场景,例如在多任务学习中,将不同任务的矩阵运算结果汇总后再进行下一步处理。
四、矩阵的线性代数运算
矩阵的线性代数运算深入挖掘了矩阵的内在性质,是解决线性方程组、分析数据相关性的核心工具。这些运算不仅有严格的数学定义,更能通过PyTorch的API快速实现,为工程问题提供理论支撑。
函数 | 描述 |
---|---|
torch.trace(A) | 矩阵的迹 |
matrix_rank(A) | 矩阵的秩 |
torch.det(A) | 计算矩阵A 的行列式 |
torch.inverse(A) | 矩阵求逆 |
torch.lstsq(A,B) | 最小二乘法 |
4.1 trace
迹
矩阵的迹定义为主对角线元素的和,即对于nnn阶矩阵A
,tr(A)=A[0][0]+A[1][1]+...+A[n−1][n−1]tr (A) = A [0][0] + A [1][1] + ... + A [n-1][n-1]tr(A)=A[0][0]+A[1][1]+...+A[n−1][n−1]。PyTorch中torch.trace(t)
可计算迹,且支持非方阵(此时取主对角线中存在的元素求和)。
在机器学习中,迹常用来简化损失函数计算。例如,Frobenius范数(矩阵元素平方和的开方)可表示为矩阵与自身转置乘积的迹,即∣∣A∣∣2=√tr(A×AT)||A||₂ = √tr (A×Aᵀ)∣∣A∣∣2=√tr(A×AT),便于通过梯度下降优化。
t1 = torch.tensor([[1, 2], [4, 5]]).float()
print(f"t1的迹:{torch.trace(t1)}")# *1. 计算过程不需要是方阵
t2 = torch.arange(1, 7).reshape(2 ,3)
print(f"t2的迹:{torch.trace(t2)}")
- 运行结果:
t1的迹:6.0
t2的迹:6
- 示例解读:
2×2矩阵
[[1,2],[4,5]]
的迹为1+5=61+5=61+5=6;2×3矩阵[[1,2,3],[4,5,6]]
的迹为1+5=61+5=61+5=6(仅取前2个主对角线元素)。迹的核心性质是相似不变性:若矩阵A
与B
相似(存在可逆矩阵P
使得B=P−1APB = P⁻¹APB=P−1AP),则tr(A)=tr(B)tr (A) = tr (B)tr(A)=tr(B)。这一性质使其在特征值分析中至关重要 —— 因为矩阵的迹等于其所有特征值的和。
4.2 rank
秩
矩阵的秩是线性代数中描述矩阵信息量的关键指标,定义为矩阵中行(或列)向量的极大线性无关组的数量。简单来说,秩越高,矩阵包含的独立信息越多;若秩等于行数(或列数),则称矩阵满秩。
秩的应用贯穿数据处理的全流程:在特征选择中,若特征矩阵的秩较低,说明存在冗余特征,可通过降维(如PCA)减少维度;在解线性方程组时,秩决定了解的存在性和唯一性 —— 若系数矩阵的秩等于增广矩阵的秩,则方程组有解,否则无解。
t3 = torch.arange(1, 5).reshape(2, 2).float()
print(f"t3的秩:{torch.linalg.matrix_rank(t3)}")
- 运行结果:
t3的秩:2
4.3 det
行列式
行列式是方阵的一个标量属性,其计算规则随矩阵阶数增长而复杂化(2阶为ad−bcad-bcad−bc,3阶有更复杂的展开式),但物理意义可理解为矩阵对应的线性变换对空间的伸缩因子。例如,2阶矩阵的行列式绝对值表示其对平面图形面积的缩放倍数;若行列式为0,则变换会将空间压缩到更低维度(矩阵不可逆)。
行列式与矩阵可逆性直接相关:若det(A)≠0det (A) ≠ 0det(A)=0,则A
可逆;若det(A)=0det (A) = 0det(A)=0,则A
不可逆(奇异矩阵)。这一性质使其成为判断线性方程组解的关键 —— 对于Ax=bAx=bAx=b,若A
可逆,则有唯一解x=A−1bx=A⁻¹bx=A−1b;若A
奇异,则可能无解或有无穷多解。
t4 = torch.tensor([[1, 2], [4, 5]]).float()
print(f"t4的行列式:{torch.det(t4)}")# ×1. 要求2维张量必须是矩阵
t5 = torch.arange(1, 13).reshape(3, 4)
# print(f"t5的行列式:{torch.det(t5)}") # RuntimeError: linalg.det: A must be batches of square matrices, but they are 3 by 4 matrices
- 运行结果:
t4的行列式:-3.0
4.4 线性方程组的矩阵表达式
线性方程组是线性代数的核心应用场景,其矩阵形式为Ax=bAx=bAx=b(AAA为系数矩阵,xxx为未知数向量,bbb为常数向量)。求解的关键思路是找到AAA的逆矩阵A−1A⁻¹A−1,使得x=A−1bx=A⁻¹bx=A−1b(前提是AAA可逆)。
4.4.1 矩阵的二维空间
A = torch.arange(1, 5).reshape(2, 2).float()
print(f"A:{A}")
# 4.1 二维空间的点
import matplotlib as mpl
import matplotlib.pyplot as pltplt.plot(A[:,0], A[:,1], 'o')
plt.show()
- 运行结果:
A:tensor([[1., 2.],[3., 4.]])
4.4.2 矩阵的线性回归方程
# 4.2 线性回归方程的拟合线
# (1)创建浮点型矩阵
B = torch.tensor([[1.0, 1], [3, 1]])
C = torch.tensor([2.0, 4])
# (2)求解逆矩阵
B_ = torch.inverse(B)
# (3)探讨逆矩阵的基本特性
print(f"B_*B:{torch.mm(B_, B)}")
print(f"B*B_:{torch.mm(B, B_)}")
# (4)乘以逆矩阵,求解线性方程
print(torch.mv(B_, C))
- 运行结果:
B_*B:tensor([[1., 0.],[0., 1.]])
B*B_:tensor([[1., 0.],[0., 1.]])
tensor([1., 1.])
- 示例解读:
矩阵
B=[[1.0,1],[3,1]]
的逆矩阵B−1B⁻¹B−1满足B×B−1=IB×B⁻¹=IB×B−1=I(单位矩阵),因此方程组Bx=CBx=CBx=C的解为x=B⁻¹×C=[1.,1.]
。这一过程的几何意义是:矩阵BBB将xxx映射到CCC,而逆矩阵B−1B⁻¹B−1则将CCC还原回xxx。
五、矩阵的分解运算
矩阵分解的核心思想是将复杂矩阵拆分为结构更简单的矩阵的乘积,从而简化计算、提取关键信息。常见的分解方式包括特征分解、奇异值分解(SVD)、LU分解等,每种分解都有其特定的适用场景和优势。
5.1 eig
特征分解
特征分解适用于方阵,其目标是将矩阵AAA分解为A=QΛQ−1A = QΛQ⁻¹A=QΛQ−1,其中QQQ是由特征向量组成的矩阵,ΛΛΛ是由特征值组成的对角矩阵(特征值按降序排列)。特征值λλλ和特征向量vvv满足Av=λvAv=λvAv=λv,即特征向量在矩阵AAA的变换下仅发生缩放(不改变方向)。
需要注意的是,并非所有方阵都可特征分解,仅可对角化矩阵(如对称矩阵)有完整的特征分解形式。
t1 = torch.arange(1, 10).reshape(3, 3).float()
print(f"t1: {t1}")
print(f"t1的特征分解:{torch.linalg.eig(t1)}")t2 = torch.tensor([1, 2, 2, 4]).reshape(2, 2).float()
print(f"t2: {t2}")
print(f"t2的秩:{torch.linalg.matrix_rank(t2)}")
print(f"t2的特征分解:{torch.linalg.eig(t2)}")t3 = torch.tensor([[1, 2, 3], [2, 4, 6], [3, 6, 9]]).float()
print(f"t3的特征分解:{torch.linalg.eig(t3)}")
- 运行结果:
t1: tensor([[1., 2., 3.],[4., 5., 6.],[7., 8., 9.]])
t1的特征分解:torch.return_types.linalg_eig(
eigenvalues=tensor([ 1.6117e+01+0.j, -1.1168e+00+0.j, -1.2253e-07+0.j]),
eigenvectors=tensor([[-0.2320+0.j, -0.7858+0.j, 0.4082+0.j],[-0.5253+0.j, -0.0868+0.j, -0.8165+0.j],[-0.8187+0.j, 0.6123+0.j, 0.4082+0.j]]))
t2: tensor([[1., 2.],[2., 4.]])
t2的秩:1
t2的特征分解:torch.return_types.linalg_eig(
eigenvalues=tensor([0.+0.j, 5.+0.j]),
eigenvectors=tensor([[-0.8944+0.j, -0.4472+0.j],[ 0.4472+0.j, -0.8944+0.j]]))
t3的特征分解:torch.return_types.linalg_eig(
eigenvalues=tensor([ 1.4000e+01+0.j, -1.6447e-07+0.j, 2.8710e-07+0.j]),
eigenvectors=tensor([[-0.2673+0.j, -0.1209+0.j, 0.8235+0.j],[-0.5345+0.j, -0.8069+0.j, -0.5588+0.j],[-0.8018+0.j, 0.5782+0.j, 0.0981+0.j]]))
- 示例解读:
矩阵
[[1,2],[2,4]]
的特征值为 0 和 5,对应特征向量分别表示被压缩的方向和被拉伸的方向。
特征分解的应用广泛:在主成分分析(PCA)中,特征值最大的特征向量对应数据的主要分布方向;在动力学系统中,特征值的大小决定系统的稳定性(绝对值 < 1 则收敛)。
5.2 svd
奇异值分解
奇异值分解(SVD)是特征分解的推广,适用于任意形状的矩阵(无需是方阵),其分解形式为A=UΣVTA = UΣVᵀA=UΣVT,其中UUU(左奇异向量)和VVV(右奇异向量)是正交矩阵,ΣΣΣ是对角矩阵(奇异值按降序排列)。
SVD的核心优势是降维:通过保留前kkk个最大奇异值及其对应的UUU、VVV,可将原矩阵近似为A≈UkΣkVkTA≈UₖΣₖVₖᵀA≈UkΣkVkT,大幅减少存储和计算成本。
在图像压缩中,SVD可保留前5%的奇异值,即可还原出视觉上接近原图的压缩图像;在自然语言处理中,SVD用于潜在语义分析(LSA),提取词与文档的潜在关联。
t4 = torch.tensor([[1, 2, 3], [2, 4, 6], [3, 6, 9]]).float()
print(f"t4的奇异值分解:{torch.linalg.svd(t4)}")
# 验证SVD分解
t4_U, t4_S, t4_Vh = torch.linalg.svd(t4)
print(torch.mm(torch.mm(t4_U, torch.diag(t4_S)), t4_Vh))
# 降维
t4_U1 = t4_U[:, 0].reshape(3, 1)
print(f"t4_U1:{t4_U1}")
t4_S1 = t4_S[0]
print(f"t4_S1:{t4_S1}")
t4_Vh1 = t4_Vh[0, :].reshape(1, 3)
print(f"t4_Vh1:{t4_Vh1}")
print(torch.mm((t4_U1 * t4_S1), t4_Vh1))
- 运行结果:
t4的奇异值分解:torch.return_types.linalg_svd(
U=tensor([[-0.2673, -0.8018, -0.5345],[-0.5345, -0.3382, 0.7745],[-0.8018, 0.4927, -0.3382]]),
S=tensor([14.0000, 0.0000, 0.0000]),
Vh=tensor([[-0.2673, -0.5345, -0.8018],[ 0.0000, -0.8321, 0.5547],[ 0.9636, -0.1482, -0.2224]]))
tensor([[1.0000, 2.0000, 3.0000],[2.0000, 4.0000, 6.0000],[3.0000, 6.0000, 9.0000]])
t4_U1:tensor([[-0.2673],[-0.5345],[-0.8018]])
t4_S1:13.999999046325684
t4_Vh1:tensor([[-0.2673, -0.5345, -0.8018]])
tensor([[1.0000, 2.0000, 3.0000],[2.0000, 4.0000, 6.0000],[3.0000, 6.0000, 9.0000]])
5.3 lstsq
最小二乘法
当线性方程组Ax=bAx=bAx=b无解(如方程数 > 未知数)时,最小二乘法通过最小化误差平方和∣∣Ax−b∣∣2||Ax - b||²∣∣Ax−b∣∣2求近似解。
最小二乘法是回归分析的基础,例如在房价预测中,用线性模型拟合多特征与房价的关系时,就通过最小二乘法求解模型参数。
A = torch.tensor([[1.0, 2.0],[3.0, 4.0],[5.0, 6.0]])
b = torch.tensor([[7.0], [8.0], [9.0]])result = torch.linalg.lstsq(b, A)
x_solution = result.solution
residuals = result.residualsprint("解x:\n", x_solution)
print("残差:\n", residuals)
- 运行结果:
解x:tensor([[0.3918, 0.5155]])
残差:tensor([])
5.4 solve
精确求解线性方程组
对于可逆方阵A
和向量b
,torch.linalg.solve(A, b)
可直接求解Ax=bAx=bAx=b的精确解x=A−1bx=A⁻¹bx=A−1b。这一运算适用于方程数等于未知数且系数矩阵可逆的场景,如电路分析中的节点电压计算、结构力学中的受力平衡方程求解等。
A = torch.tensor([[2.0, 3.0],[4.0, 5.0]])
b = torch.tensor([[8.0], [14.0]])# 求解方程 Ax = b
x = torch.linalg.solve(A, b)
print("解x:\n", x)
# 验证解:A @ x 应接近 b
print("验证 A@x:\n", torch.matmul(A, x), "\n")
- 运行结果:
解x:tensor([[1.0000],[2.0000]])
验证 A@x:tensor([[ 8.0000],[14.0000]])
5.5 lu
LU分解
LU分解将方阵A
分解为下三角矩阵L
(对角线为 1)和上三角矩阵U
的乘积,即 A=LUA=LUA=LU(或带置换矩阵P
的PA=LUPA=LUPA=LU)。这种分解的优势是将复杂的矩阵求逆、方程组求解转化为三角矩阵的运算 —— 三角矩阵的逆和方程组求解可通过向前 / 向后替代高效完成。
LU分解在数值计算中应用广泛,例如在有限元分析中,用于高效求解大规模线性方程组。
A = torch.tensor([[3.0, 1.0, 2.0],[6.0, 3.0, 4.0],[3.0, 2.0, 5.0]])LU, pivots, info = torch.linalg.lu(A)
# 提取 L 和 U 矩阵
L = torch.tril(LU, diagonal=-1) + torch.eye(A.shape[0]) # 下三角矩阵(含单位对角线)
U = torch.triu(LU) # 上三角矩阵print("置换矩阵索引pivots:\n", pivots)
print("下三角矩阵L:\n", L)
print("上三角矩阵U:\n", U)
# 验证分解正确性
P = torch.eye(A.shape[0])[pivots.long()] # 构建置换矩阵
print("验证 P@A ≈ L@U:\n", torch.matmul(P, A), "\nvs\n", torch.matmul(L, U))
- 运行结果:
置换矩阵索引pivots:tensor([[ 1.0000, 0.0000, 0.0000],[ 0.5000, 1.0000, 0.0000],[ 0.5000, -1.0000, 1.0000]])
下三角矩阵L:tensor([[1., 0., 0.],[1., 1., 0.],[0., 0., 1.]])
上三角矩阵U:tensor([[0., 1., 0.],[0., 0., 0.],[0., 0., 1.]])
验证 P@A ≈ L@U:tensor([[[6., 3., 4.],[3., 1., 2.],[3., 1., 2.]],[[3., 1., 2.],[6., 3., 4.],[3., 1., 2.]],[[3., 1., 2.],[3., 2., 5.],[6., 3., 4.]]])
vstensor([[0., 1., 0.],[0., 1., 0.],[0., 0., 1.]])
微语录:手心命运的掌纹通向彼岸,回头看,枝繁叶茂,山花绽放。——《浪浪山的小妖怪》