向量、矩阵与运算


文档摘要

向量、矩阵与运算 本节摘要:每一个神经网络,本质上都是矩阵乘法加几步辅助操作。本节要让你对矩阵运算流利到像写变量一样自然——因为 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 节(线性代数直觉)。

学习目标

阅读完本节,你应当能够:

  1. 用 Python 从零构建 Matrix 类,实现逐元素运算、矩阵乘法、转置、行列式、逆。
  2. 区分逐元素乘与矩阵乘,并说清各自适用于什么场景。
  3. 仅用自研 Matrix 类实现一个完整的全连接神经网络层 relu(W @ x + b)
  4. 解释广播规则与神经网络框架里偏置加法的工作机制。

一、问题与直觉

你想搭一个神经网络,翻开代码看到这行:

output = activation(weights @ input + bias)

那个 @ 就是矩阵乘法。weights 是矩阵,input 是向量。如果不懂这些运算,这行就是魔法;懂了,这就是一整个层的前向传播,只用三个操作

模型处理的每一张图是像素值矩阵,每一个词嵌入是向量,每一层网络是矩阵变换——不懂矩阵运算做不了 AI,就像不懂变量写不了代码。本节正是从零建立这种流利度。

1.1 向量:有序的数

v = [3, 4] -- 2D 向量 w = [1, 0, -2] -- 3D 向量

2D 向量 [3, 4] 指向平面上的 (3, 4),模长为 5(勾股 3-4-5 三角形)。

1.2 矩阵:数的网格

A = | 1 2 3 | -- 2x3 矩阵(2 行 3 列) | 4 5 6 |

神经网络里,权重矩阵把输入向量变换成输出向量。一个「784 输入、128 输出」的层,其权重矩阵形状是 128×784。

1.3 形状为何重要

矩阵乘法有严格规则:(m x n) @ (n x p) = (m x p),内维必须相等。

(128 x 784) @ (784 x 1) = (128 x 1) 权重 输入 输出 内维:784 = 784 -- 合法

PyTorch 报的「shape mismatch」错误,根因几乎都在这里。

1.4 运算速查表

运算 做什么 神经网络用途
加法 逐元素相加 给输出加偏置
数乘 每个元素同乘一标量 学习率 × 梯度
矩阵乘法 变换向量 层的前向传播
转置 行列互换 反向传播
行列式 单一数值总结 检查可逆性
撤销一个变换 求解线性系统
单位矩阵 「什么都不做」的矩阵 初始化、残差连接

1.5 逐元素乘 vs 矩阵乘

这是初学者最常踩的坑。

逐元素乘:对应位置相乘,两个矩阵形状必须相同。

| 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.6 广播

把偏置向量加到输出矩阵上,形状并不匹配。广播会拉伸较小的数组以适配:

| 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/。下面给出关键骨架。

2.1 矩阵类的核心运算

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 节。

2.2 接上神经网络

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 章(机器学习基础)「迷你神经网络框架」的基石——逐元素乘、矩阵乘、广播都将被复用。

五、练习

  1. (Easy) 验证逆:计算 A @ A.inverse_2x2(),确认得到单位矩阵;对三个不同 2×2 矩阵重复实验;当行列式为 0 时会怎样?
  2. (Medium) 实现 3×3 逆:用伴随矩阵法扩展 Matrix 类,与 np.linalg.inv 对照测试。
  3. (Hard)Matrix 类搭两层网络:输入(3)→ 隐层(4)→ 输出(2),随机初始化权重,跑一次前向传播,验证所有形状正确。

本节要点回顾

  1. 向量是有序的数,AI 里表示高维空间中的点或方向。
  2. 矩阵是线性变换,把向量从一个空间映射到另一个空间。
  3. 矩阵乘 = 行与列的点积,内维必须相等,顺序不可交换。
  4. 转置交换行列,是反向传播中反复出现的操作。
  5. 行列式度量面积/体积缩放因子,为零意味着变换把某一维压扁(不可逆)。
  6. 逆是变换的撤销,仅当行列式非零时存在。
  7. 单位矩阵相当于乘以 1,残差连接(ResNet)正是靠 I + ... 让梯度顺畅回流。
  8. 广播自动拉伸小数组——它是偏置加法能跑通的机制,理解它就不会被「形状错却跑通」绊倒。

下一节,我们深入矩阵作为变换的几何本质——旋转、缩放、剪切、特征向量与特征方向。


发布者: 作者: Rohit Gupta 转发
评论区 (0)
U