AI 工程地基 02|两个乘号,两套规则:亲手敲一个 Matrix 类
这是「AI 工程地基」系列第 02 篇。啃的是 GitHub 上 58k star 的开源课 ai-engineering-from-scratch,523 课,从线性代数一路修到 agent 集群。一课一篇,全文翻译先落盘,正文用自己的话讲。
AI 工程地基 02|两个乘号,两套规则:亲手敲一个 Matrix 类
这是「AI 工程地基」系列第 02 篇。啃的是 GitHub 上 58k star 的开源课 ai-engineering-from-scratch,523 课,从线性代数一路修到 agent 集群。一课一篇,全文翻译先落盘,正文用自己的话讲。
打开一篇模型代码,你迟早撞见这行:
output = activation(weights @ input + bias)
不认识 @,这是符咒。认识了,这是一层网络的全部前向传播——乘、加、过激活,三个动作。上一课说矩阵是动作,这一课把动作亲手敲出来:60 分钟,不 import 任何库,从零写一个 Matrix 类。
两个乘号
Python 里有两个乘号。* 和 @。
* 是逐元素:对应位置各乘各的。[[1,2],[3,4]] * [[5,6],[7,8]] 得 [[5,12],[21,32]]。
@ 是矩阵乘法:左边的行跟右边的列做点积。同样的两个矩阵,A @ B 得 [[19,22],[43,50]]。
差一个符号,结果完全不同。更麻烦的是,同形状的矩阵两个运算符都能跑、都不报错。用错了,程序安静地给你一个错答案。初学者在这栽的跟头,课程原话说「数不清」。
矩阵乘法有条铁律:(m×n) @ (n×p) = (m×p),内维必须对上。PyTorch 里那句著名的 shape mismatch 报错,根源就是这条。一张 28×28 的手写数字图拉平是 784 个数,过一层 128 个输出的网络,权重矩阵就是 128×784。(128×784) @ (784×1) = (128×1),中间两个 784 握上手,两头是新形状。
敲一个 Matrix 类
课程给的全部代码我跑了一遍。加法、标量乘、逐元素乘、矩阵乘、转置、行列式、逆、单位矩阵,四十几行。
有两个地方值得停下来看。
行列式是用递归写的。2×2 直接套公式 ad - bc,更大的矩阵沿第一行展开,每项乘一个砍掉行列的小矩阵,小矩阵继续递归。这是拉普拉斯展开,教科书上的名字。算 [[1,2],[3,4]] 的行列式,得 −2。行列式为零意味着什么?这个变换把一个维度压扁了——一块面积被拍成一条线,信息回不来了。所以行列式为零的矩阵没有逆:你不能撤销一次压扁。
逆矩阵 2×2 的公式也直接给了。测一下 A @ A^-1,输出确实是单位矩阵 [[1, 0], [0, 1]]。给它喂一个行列式为零的矩阵,它抛异常,一句话:「矩阵奇异,逆不存在」。
然后是那句梦话成真的时刻。三行:
pre_activation = weights.matmul(inputs) + bias
output = relu_matrix(pre_activation)
(2×3) @ (3×1) + (2×1),过 ReLU,得 (2×1)。这就是一层稠密层。以后在 PyTorch 里写 nn.Linear(3, 2),底下就是这件事,只是它快得多、也藏得深得多。
广播:形状对不上怎么办
输出矩阵加偏置向量,形状不对齐:矩阵两行,向量一行。框架的解法叫广播——把小数组沿缺的维度复制,撑到对齐为止。
[[1,2,3],[4,5,6]] + [10,20,30],向量被复制到每一行,得 [[11,22,33],[14,25,36]]。每个框架都自动做这件事。好处是你少写循环,坏处是形状看着错代码却能跑,出 bug 时你会先怀疑人生、后怀疑广播。
过一遍海关
逐条核过,这课的数字全部经得起复算。两个乘法例子、广播结果、行列式 −2、逆矩阵 [[−2,1],[1.5,−0.5]]、A @ A^-1 得单位阵——我全部跑过代码,零误差。784 与 128×784 的形状,跟 PyTorch nn.Linear(784, 128) 的真实权重形状一致。
两处松的。其一,「快 100 倍」是量级说法不是测量值。NumPy 底层 BLAS 确实是 C 和 Fortran 写的,方向没错。我在本机实测 100×100 矩阵乘:手写版 0.046 秒,NumPy 0.001 秒,44 倍。规模越大差距越大,但「100x」三个字是拍的。其二,手写的 matmul 拿 range(self.cols) 当求和范围,内维匹配是隐含假设,没有显式检查——形状不对时不会友好报错,只会 IndexError。框架把这条铁律做成了报错信息,玩具版让异常自己漏出来。教学代码可以接受,但该点破。
口号审计。这课的开场白比上一课诚实一档:「每个神经网络不过是矩阵乘法,外加几步额外操作。」上一课的帽子「都是矩阵数学」把非线性藏在了帽子底下,这一课的「额外操作」里明摆着装着 ReLU——max(0, x),矩阵乘法造不出来的那部分。没有它,叠一百层等于一层。课程自己在代码里写了这个反例,口号算是自洽的。
练习 3 是这课的期末考:不用 NumPy,只用自己那个 Matrix 类,搭一个 3 → 4 → 2 的两层网络。两份权重矩阵,4×3 和 2×4,先乘后加,过两次 ReLU。参数一共 26 个——20 个权重,6 个偏置。你手边就有纸能算的东西,下一课讲矩阵变换与特征值,会把「压扁一个维度」这件事讲得更透。
原物:github.com/rohitg00/ai-engineering-from-scratch · Phase 1 Lesson 02「Vectors, Matrices & Operations」(60 分钟,Build 型,全文翻译已存档) 系列说明:一课一篇,按 01→22 走完数学地基。已发:L01 线性代数直觉(topic 178635275)、L21 图论样章(topic 178635269)。