SERIES AI 与 LLM 学习路线 02 / 10

用 NumPy 的形状读懂一个线性分类器

以批量线性分类为例,理解矩阵乘法、广播、axis 与稳定 softmax。

机器学习代码里最值得先看的往往不是算法名,而是数组的形状。假设一批数据有 BB 个样本,每个样本有 DD 个特征,要分成 CC 类:输入 XX 的形状是 (B,D)(B,D),权重 WW 是 (D,C)(D,C),偏置 bb 是 (C,)(C,)。

Z=XW+b,Z∈RB×CZ=XW+b,\qquad Z\in\mathbb{R}^{B\times C}

矩阵乘法使每个样本与每一类的权重相乘求和。加偏置时,NumPy 按末尾维度对齐,把 (C,)(C,) 广播到 (B,C)(B,C);这里无需手动复制 BB 份。**广播描述的是运算规则,不保证所有形状都能自动匹配。**比如 (B,D)(B,D) 与 (B,)(B,) 通常就不能按“每行加一个数”的意图直接相加,应写成 row_bias[:, None]。

一个可检查形状的例子

import numpy as np

X = np.array([[2., 0., 1.], [0., 1., 3.]])  # (B=2, D=3)
W = np.array([[1., -1.], [0., 2.], [1., 0.]])  # (D=3, C=2)
b = np.array([0.5, -0.5])  # (C=2,)

logits = X @ W + b
shifted = logits - logits.max(axis=1, keepdims=True)
exp = np.exp(shifted)
probs = exp / exp.sum(axis=1, keepdims=True)

print(logits.shape)       # (2, 2)
print(probs.sum(axis=1))  # 每个样本的类别概率之和为 1

axis=1 表示沿类别维度归约:每一行得一个值;keepdims=True 保留形状 (B,1)(B,1),方便与 (B,C)(B,C) 相减或相除。这里的 softmax 写成

pic=ezic−mi∑jezij−mi,mi=max⁡jzijp_{ic}=\frac{e^{z_{ic}-m_i}}{\sum_j e^{z_{ij}-m_i}},\qquad m_i=\max_j z_{ij}

减去同一行的最大值不改变概率,却能避免大正数在 exp 时溢出。这是数值计算里比“公式看起来正确”更进一步的一层要求。

形状是调试线索

* 是逐元素相乘,@ 才是矩阵乘法;二者即使结果形状偶然相同,含义也完全不同。mean(axis=0) 汇总同一特征在所有样本上的值,mean(axis=1) 汇总单个样本的所有特征。把 BB、DD、CC 写在注释里,通常比盯着报错猜测更快。

下一篇 反向传播 会继续使用 XW+bXW+b,推导参数为什么能从损失中得到更新方向。

参考:NumPy 广播规则、NumPy matmul 文档。

  1. 01从一个预测任务看 AI、机器学习与深度学习
  2. 02用 NumPy 的形状读懂一个线性分类器当前文章
  3. 03从线性层到反向传播:梯度究竟从哪里来
  4. 04PyTorch 训练循环里每一步在做什么
  5. 05从 RNN 到 Attention:固定长度上下文为何不够
  6. 06从矩阵形状理解 Self-Attention
  7. 07把 Transformer 拆成可检查的几层
  8. 08Tokenizer 和 Embedding 各自解决什么问题
  9. 09大语言模型怎样一个 Token 一个 Token 生成文本
  10. 10从检索链路理解 RAG,而不是只看向量数据库
NEXT IN SERIES 03 / 从线性层到反向传播:梯度究竟从哪里来