向量、矩阵与运算 本节摘要:每一个神经网络,本质上都是矩阵乘法加几步辅助操作。本节要让你对矩阵运算流利到像写变量一样自然——因为 AI 工程没有它寸步难行。我们从最朴素的向量、矩阵定义出发,逐个吃透七类核心运算:加法(加偏置)、数乘(学习率乘梯度)、矩阵乘法(层的前向传播)、转置(反向传播)、行列式(可逆性体检)、逆(求解线性系统)、单位矩阵(残差连接的初始化)。最关键的是分清逐元素乘与矩阵乘——初学者最常栽的坑;以及理解广播(broadcasting),知道为什么形状不匹配却仍能跑通。最后我们用纯 Python 矩阵类,一行不落写出 ——一个全连接层的完整前向传播,只用了三个运算。 对应原课程:Phase 01 · Lesson 02 · (原英文 )。前置:第 1 节(线性代数直觉)。
本节摘要:每一个神经网络,本质上都是矩阵乘法加几步辅助操作。本节要让你对矩阵运算流利到像写变量一样自然——因为 AI 工程没有它寸步难行。我们从最朴素的向量、矩阵定义出发,逐个吃透七类核心运算:加法(加偏置)、数乘(学习率乘梯度)、矩阵乘法(层的前向传播)、转置(反向传播)、行列式(可逆性体检)、逆(求解线性系统)、单位矩阵(残差连接的初始化)。最关键的是分清逐元素乘与矩阵乘——初学者最常栽的坑;以及理解广播(broadcasting),知道为什么形状不匹配却仍能跑通。最后我们用纯 Python 矩阵类,一行不落写出
output = relu(W @ x + b)——一个全连接层的完整前向传播,只用了三个运算。
对应原课程:Phase 01 · Lesson 02 ·
vectors-matrices-operations(原英文phases/01-math-foundations/02-vectors-matrices-operations/docs/en.md)。前置:第 1 节(线性代数直觉)。
阅读完本节,你应当能够:
Matrix 类,实现逐元素运算、矩阵乘法、转置、行列式、逆。Matrix 类实现一个完整的全连接神经网络层 relu(W @ x + b)。你想搭一个神经网络,翻开代码看到这行:
output = activation(weights @ input + bias)
那个 @ 就是矩阵乘法。weights 是矩阵,input 是向量。如果不懂这些运算,这行就是魔法;懂了,这就是一整个层的前向传播,只用三个操作。
模型处理的每一张图是像素值矩阵,每一个词嵌入是向量,每一层网络是矩阵变换——不懂矩阵运算做不了 AI,就像不懂变量写不了代码。本节正是从零建立这种流利度。
v = [3, 4] -- 2D 向量 w = [1, 0, -2] -- 3D 向量
2D 向量 [3, 4] 指向平面上的 (3, 4),模长为 5(勾股 3-4-5 三角形)。
A = | 1 2 3 | -- 2x3 矩阵(2 行 3 列) | 4 5 6 |
神经网络里,权重矩阵把输入向量变换成输出向量。一个「784 输入、128 输出」的层,其权重矩阵形状是 128×784。
矩阵乘法有严格规则:(m x n) @ (n x p) = (m x p),内维必须相等。
(128 x 784) @ (784 x 1) = (128 x 1) 权重 输入 输出 内维:784 = 784 -- 合法
PyTorch 报的「shape mismatch」错误,根因几乎都在这里。
| 运算 | 做什么 | 神经网络用途 |
|---|---|---|
| 加法 | 逐元素相加 | 给输出加偏置 |
| 数乘 | 每个元素同乘一标量 | 学习率 × 梯度 |
| 矩阵乘法 | 变换向量 | 层的前向传播 |
| 转置 | 行列互换 | 反向传播 |
| 行列式 | 单一数值总结 | 检查可逆性 |
| 逆 | 撤销一个变换 | 求解线性系统 |
| 单位矩阵 | 「什么都不做」的矩阵 | 初始化、残差连接 |
这是初学者最常踩的坑。
逐元素乘:对应位置相乘,两个矩阵形状必须相同。
| 1 2 | | 5 6 | | 5 12 | | 3 4 | * | 7 8 | = | 21 32 |
矩阵乘法:行与列做点积,内维必须相等。
| 1 2 | | 5 6 | | 1*5+2*7 1*6+2*8 | | 19 22 | | 3 4 | @ | 7 8 | = | 3*5+4*7 3*6+4*8 | = | 43 50 |
两个完全不同的运算,结果不同,规则也不同。
把偏置向量加到输出矩阵上,形状并不匹配。广播会拉伸较小的数组以适配:
| 1 2 3 | + [10, 20, 30] | 4 5 6 | 广播把向量沿行复制: | 1 2 3 | | 10 20 30 | | 11 22 33 | | 4 5 6 | + | 10 20 30 | = | 14 25 36 |
每个现代框架都自动做这件事。理解它能避免「形状看着错却跑通了」的困惑。
完整源码见 phases/01-math-foundations/02-vectors-matrices-operations/code/。下面给出关键骨架。
class Matrix: def __init__(self, data): self.data = [list(row) for row in data] self.rows = len(self.data) self.cols = len(self.data[0]) self.shape = (self.rows, self.cols) def __add__(self, other): # 逐元素加 return Matrix([[self.data[i][j] + other.data[i][j] for j in range(self.cols)] for i in range(self.rows)]) def element_wise_multiply(self, other): # 逐元素乘(对应位置) return Matrix([[self.data[i][j] * other.data[i][j] for j in range(self.cols)] for i in range(self.rows)]) def matmul(self, other): # 矩阵乘:cᵢⱼ = Σₖ aᵢₖ·bₖⱼ return Matrix([[ sum(self.data[i][k] * other.data[k][j] for k in range(self.cols)) for j in range(other.cols)] for i in range(self.rows)]) def transpose(self): # 行列互换 return Matrix([[self.data[j][i] for j in range(self.rows)] for i in range(self.cols)]) def determinant(self): # 行列式(按首行展开) if self.shape == (2, 2): return self.data[0][0]*self.data[1][1] - self.data[0][1]*self.data[1][0] det = 0 for j in range(self.cols): minor = Matrix([[self.data[i][k] for k in range(self.cols) if k != j] for i in range(1, self.rows)]) det += ((-1) ** j) * self.data[0][j] * minor.determinant() return det def inverse_2x2(self): # 2x2 逆:伴随 ÷ 行列式 det = self.determinant() if det == 0: raise ValueError("奇异矩阵,无逆") return Matrix([[ self.data[1][1]/det, -self.data[0][1]/det], [-self.data[1][0]/det, self.data[0][0]/det]]) @staticmethod def identity(n): # 单位矩阵 Iₙ return Matrix([[1 if i == j else 0 for j in range(n)] for i in range(n)])
设计要点:
determinant用递归展开(代数余子式法),直观但 O(n!);inverse_2x2只处理 2×2——更通用的求逆应改用 LU 或高斯-约旦消元,见第 17 节。
def relu_matrix(m): return Matrix([[max(0, v) for v in row] for row in m.data]) inputs = Matrix([[0.5], [0.8], [0.2]]) # 3×1 weights = Matrix([[random.uniform(-1, 1) for _ in range(3)] for _ in range(2)]) # 2×3 bias = Matrix([[0.1], [0.1]]) # 2×1 pre_activation = weights.matmul(inputs) + bias # (2×3)@(3×1)+(2×1) = 2×1 output = relu_matrix(pre_activation)
这就是一个全连接层:output = relu(W @ x + b)。每一个全连接层都只做这件事。
NumPy 用更少的代码、快上百倍地完成上述全部工作,因为底层是 C/Fortran 写的优化 BLAS 例程。
import numpy as np A = np.array([[1, 2], [3, 4]]) print(A * B) # 逐元素乘 print(A @ B) # 矩阵乘(@ 调用 __matmul__) print(np.linalg.det(A)) # 行列式 print(np.linalg.inv(A)) # 逆 print(np.eye(2)) # 单位矩阵 # 一行完成神经网络层 output = np.maximum(0, weights @ inputs + bias)
广播示例:
matrix = np.array([[1, 2, 3], [4, 5, 6]]) bias = np.array([10, 20, 30]) print(matrix + bias) # NumPy 自动把 1D bias 沿两行广播
💡 Python 的
@运算符会调用对象的__matmul__方法。NumPy、PyTorch、JAX 都复用这个语法,所以「同一份代码换个后端」几乎是零成本的。
本节产出(位于原课程 outputs/):
prompt-matrix-operations.md:一个用于 AI 助手的提示,通过几何直觉讲授矩阵运算。本节构建的 Matrix 类是后续第 3 章(机器学习基础)「迷你神经网络框架」的基石——逐元素乘、矩阵乘、广播都将被复用。
A @ A.inverse_2x2(),确认得到单位矩阵;对三个不同 2×2 矩阵重复实验;当行列式为 0 时会怎样?Matrix 类,与 np.linalg.inv 对照测试。Matrix 类搭两层网络:输入(3)→ 隐层(4)→ 输出(2),随机初始化权重,跑一次前向传播,验证所有形状正确。I + ... 让梯度顺畅回流。下一节,我们深入矩阵作为变换的几何本质——旋转、缩放、剪切、特征向量与特征方向。