用Python+NumPy图解线性代数:从几何直观到矩阵运算

线性代数常被学生视为抽象难懂的"天书",尤其是当课程进展到内积空间和正交基时,那些看似冰冷的公式往往让人望而生畏。但如果我们换个角度,用Python代码将这些概念可视化,你会发现矩阵分析其实可以像搭积木一样直观有趣。本文将带你用NumPy和Matplotlib,在Jupyter Notebook中亲手构建向量、计算夹角,甚至实现Gram-Schmidt正交化过程,让抽象代数概念变得触手可及。

1. 准备工作:搭建Python数值计算环境

在开始我们的可视化之旅前,先确保你的工具包准备就绪。推荐使用Anaconda发行版,它预装了我们将用到的所有关键库:

# 基础工具包导入
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D  # 3D绘图支持
%matplotlib inline  # Jupyter Notebook中直接显示图表

如果你需要安装这些库,可以使用以下pip命令:

pip install numpy matplotlib

提示:为获得最佳交互体验,建议在Jupyter Notebook中运行本文所有代码示例。它能实时显示可视化结果,方便调整参数观察变化。

让我们先创建一个简单的二维向量热身:

v1 = np.array([2, 1])
v2 = np.array([-1, 3])

plt.quiver(0, 0, v1[0], v1[1], angles='xy', scale_units='xy', scale=1, color='r')
plt.quiver(0, 0, v2[0], v2[1], angles='xy', scale_units='xy', scale=1, color='b')
plt.xlim(-2, 3)
plt.ylim(-1, 4)
plt.grid()
plt.show()

这段代码会绘制出两个从原点出发的向量,红色向量v1指向(2,1),蓝色向量v2指向(-1,3)。这种可视化方式能立即让我们对向量有几何上的直观认识——它们不仅有方向,还有"长度"。

2. 内积:从点乘到角度计算

内积(点积)是连接代数与几何的桥梁。在NumPy中计算两个向量的内积非常简单:

dot_product = np.dot(v1, v2)
print(f"v1和v2的内积为: {dot_product}")

但内积的几何意义远比这个数值结果丰富。根据内积公式:

v1·v2 = ||v1|| ||v2|| cosθ

我们可以反推出两个向量之间的夹角θ:

def angle_between(v1, v2):
    """计算两个向量之间的夹角(弧度)"""
    cos_theta = np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))
    return np.arccos(np.clip(cos_theta, -1, 1))  # 防止浮点误差导致数值溢出

theta = angle_between(v1, v2)
print(f"v1和v2之间的夹角为: {np.degrees(theta):.2f}度")

可视化这个夹角能加深理解:

# 绘制向量和夹角弧线
fig, ax = plt.subplots(figsize=(8,6))
ax.quiver(0, 0, v1[0], v1[1], angles='xy', scale_units='xy', scale=1, color='r')
ax.quiver(0, 0, v2[0], v2[1], angles='xy', scale_units='xy', scale=1, color='b')

# 绘制夹角弧线
arc = plt.Circle((0,0), 0.5, fill=False, angle=0, theta1=0, theta2=np.degrees(theta))
ax.add_patch(arc)
ax.text(0.3, 0.2, f"{np.degrees(theta):.1f}°", fontsize=12)

plt.xlim(-2, 3)
plt.ylim(-1, 4)
plt.grid()
plt.title("向量夹角可视化")
plt.show()

内积空间的一个重要特性是Cauchy-Schwarz不等式,它保证了上述角度计算的合理性:

|v1·v2| ≤ ||v1|| ||v2||

我们可以用随机向量验证这个不等式:

for _ in range(5):
    a, b = np.random.randn(2, 10)  # 生成两个10维随机向量
    lhs = abs(np.dot(a, b))
    rhs = np.linalg.norm(a) * np.linalg.norm(b)
    print(f"{lhs:.4f} ≤ {rhs:.4f} ? {lhs <= rhs}")

3. 范数:测量向量空间的"尺子"

范数是向量长度的推广。最常见的三种范数及其计算方法如下表所示:

范数类型 数学定义 NumPy计算方式 几何意义
L1范数 Σ xᵢ
L2范数 √(Σxᵢ²) np.linalg.norm(v) 欧氏距离
L∞范数 max( xᵢ )

让我们可视化不同范数的"单位球"——到原点距离为1的所有点构成的集合:

theta = np.linspace(0, 2*np.pi, 100)
x = np.cos(theta)
y = np.sin(theta)

# L2单位圆(默认)
plt.plot(x, y, label='L2')

# L1单位圆(菱形)
plt.plot(np.append(x[np.abs(x)+np.abs(y)<=1], x[np.abs(x)+np.abs(y)<=1][0]), 
         np.append(y[np.abs(x)+np.abs(y)<=1], y[np.abs(x)+np.abs(y)<=1][0]), 
         label='L1')

# L∞单位圆(正方形)
plt.plot([1,1,-1,-1,1], [1,-1,-1,1,1], label='L∞')

plt.axis('equal')
plt.legend()
plt.title("不同范数下的单位球")
plt.grid()
plt.show()

这个可视化清晰地展示了不同范数对"距离"概念的不同理解。在机器学习中,选择适当的范数非常重要:

  • L1范数常用于稀疏性要求高的场景(如LASSO回归)
  • L2范数是最常用的欧氏距离(如岭回归)
  • L∞范数在需要限制最大误差时使用

4. 正交基:高维空间的"坐标系"

正交基是内积空间中的一组特殊向量,它们两两正交且长度为单位1。最著名的例子就是三维空间中的标准基:

# 标准三维正交基
e1 = np.array([1,0,0])
e2 = np.array([0,1,0])
e3 = np.array([0,0,1])

fig = plt.figure(figsize=(8,6))
ax = fig.add_subplot(111, projection='3d')
ax.quiver(0,0,0, e1[0],e1[1],e1[2], color='r', arrow_length_ratio=0.1)
ax.quiver(0,0,0, e2[0],e2[1],e2[2], color='g', arrow_length_ratio=0.1)
ax.quiver(0,0,0, e3[0],e3[1],e3[2], color='b', arrow_length_ratio=0.1)

ax.set_xlim([0,1])
ax.set_ylim([0,1])
ax.set_zlim([0,1])
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.set_title('标准三维正交基')
plt.show()

在实际应用中,我们经常需要将任意一组线性无关的向量转化为正交基。Gram-Schmidt过程就是实现这一目标的经典算法:

def gram_schmidt(vectors):
    basis = []
    for v in vectors:
        w = v - sum(np.dot(v, b)*b for b in basis)
        if not np.allclose(w, 0):  # 忽略零向量
            basis.append(w/np.linalg.norm(w))
    return np.array(basis)

# 示例:将三个非正交向量正交化
v1 = np.array([1,1,1])
v2 = np.array([0,1,1])
v3 = np.array([0,0,1])

ortho_basis = gram_schmidt([v1, v2, v3])
print("正交化后的基向量:\n", ortho_basis)

# 验证正交性
print("\n内积矩阵(应为单位矩阵):\n", ortho_basis @ ortho_basis.T)

正交基在信号处理、数据降维等领域有广泛应用。例如,在PCA(主成分分析)中,我们寻找数据方差最大的正交方向;在傅里叶分析中,不同频率的正弦余弦函数构成函数空间的正交基。

5. 矩阵视角下的内积与正交

当我们从向量上升到矩阵,内积的概念自然扩展为Frobenius内积:

A = np.random.randn(3,3)
B = np.random.randn(3,3)

# Frobenius内积两种等价计算方式
frob1 = np.trace(A.T @ B)
frob2 = np.sum(A * B)  # 对应元素相乘再求和

print(f"迹方法: {frob1:.4f}")
print(f"元素积和: {frob2:.4f}")

矩阵的正交性表现为正交矩阵的概念。正交矩阵Q满足QᵀQ = I,它的列向量构成一组标准正交基。我们可以用NumPy生成随机正交矩阵:

def random_orthogonal(n):
    """生成n×n随机正交矩阵"""
    A = np.random.randn(n, n)
    Q, _ = np.linalg.qr(A)
    return Q

Q = random_orthogonal(3)
print("随机正交矩阵Q:\n", Q)
print("\nQᵀQ:\n", Q.T @ Q)  # 应近似于单位矩阵

正交矩阵在数值计算中非常重要,因为它们不会放大误差(保持范数不变)。在QR分解、SVD等矩阵分解中,正交矩阵都扮演着核心角色。

6. 实际应用:最小二乘法与投影

内积空间理论的一个重要应用是最小二乘法。假设我们有一组数据点,想要找到最佳拟合直线:

# 生成带噪声的线性数据
np.random.seed(42)
x = np.linspace(0, 10, 20)
y = 2*x + 1 + np.random.randn(20)*2

# 构造设计矩阵
A = np.vstack([x, np.ones(len(x))]).T

# 最小二乘解
coefficients = np.linalg.inv(A.T @ A) @ A.T @ y
m, c = coefficients

plt.scatter(x, y, label='数据点')
plt.plot(x, m*x + c, 'r', label=f'拟合直线: y={m:.2f}x+{c:.2f}')
plt.legend()
plt.grid()
plt.title("最小二乘拟合")
plt.show()

从几何角度看,最小二乘法是在寻找目标向量y在A列空间上的正交投影。我们可以用正交投影公式验证:

# 计算投影矩阵
P = A @ np.linalg.inv(A.T @ A) @ A.T
y_proj = P @ y

# 验证残差与列空间正交
residual = y - y_proj
print("残差与A第一列的内积:", np.dot(residual, A[:,0]))
print("残差与A第二列的内积:", np.dot(residual, A[:,1]))

这个例子展示了内积概念在实际问题中的强大应用——通过确保误差向量与模型空间正交,我们得到了最优拟合。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐