坐下 2 / 6
轴要有名字。中间那一维要对上。
深度学习代码里最常见的报错是两个 shape 对不上。更危险的是对得上、语义错了:广播替你补维,程序照跑,损失在优化另一件事。写代码前在纸上列三列:变量、shape、轴含义。对不上第三列,就不要跑。
看见数字,先给轴起名
标量没有轴,向量一条,矩阵两条,再高统称张量。维数只告诉你有几条轴,shape 给出每条有多长。同样是 [32, 10],可能是 32 个样本的 10 类 logits,也可能是长度 32 的序列上每个位置 10 维特征。机器只看长度。语义靠你写的名字守住。
x.shape == [8, 20, 64] 若是文本 batch,三个轴依次是 batch、token、embedding。图像常写成 [B, C, H, W]。读别人代码时,把注释里的轴名抄到纸上,比盯着报错行号快。
线性层就是一次矩阵乘
一份 batch X[B, D] 右乘 W[D, H],得到 Y[B, H]。中间的 D 必须相等:每个输出特征都读完整条输入。batch 轴只是把样本并排,层对每个样本用同一套 W。
手算一回 (2, 3) @ (3, 4)。令
X = [[1, 2, 3],
[4, 5, 6]]
W = [[1, 0, 0, 1],
[0, 1, 0, 1],
[0, 0, 1, 1]]
第一行 [1, 2, 3] 与 W 的四列分别点积:1、2、3、1+2+3=6。第二行同样得到 [4, 5, 6, 15]。结果 shape 是 (2, 4)。中间的 3 被求和消掉。你若写成 X * W,形状对不上;有的环境会报错,有的会先广播再让你得到一个完全不相干的数组。
偏置 b[H] 加到 Y[B, H] 上,靠广播:(4,) 补成每一样本一份。写成 b[B] 再硬加,特征轴会对错人。参数量从 shape 直接读:D×H 个权重,加 H 个偏置。两层 64→32→10 且都有偏置:64×32+32 + 32×10+10 = 2410。只算 64×10 会把中间那层参数当成不存在。
索引会消轴:x[B, T, D] 里 x[0] 得到 [T, D],x[:, 0] 得到 [B, D]。冒号留下整条轴。把 [B, H, W, C] 直接 reshape 成 [B, C, H, W] 只在 C=1 时碰巧对;通道大于 1 会把相邻像素塞进通道轴。该用 permute 或 transpose 换轴。
库里的线性层常把权重存成 (H, D),前向实际是 x @ W.T + b。你按 (D, H) 手写却又转置一次,结果变成 (B, D) 或直接报错。打印 W.shape 和一句「从几维映到几维」,比猜约定快。batch 轴始终排最前:(D, B) 混进图像代码,广播会沿错误方向补维,准确率还可能假高。
没报错的广播
x 是 (4, 8),w 是 (8,)。x * w 得到 (4, 8):每一行被同一个向量缩放。看起来像线性层,其实还是 8 维。你要四个 logit,应写 x @ w,结果 (4,)。另一个静默事故:pred[4, 1] - target[4] 会变成 [4, 4],每个预测在跟所有标签比。对这个矩阵取均值,损失仍是标量,优化的却是两两比较。
哪一步能乘上,哪一步只是碰巧没炸
先选能得到 (2, 4) 的那一步,再指出会广播的那一步。
X 是 (2, 3),W 是 (3, 4)。哪一步得到 (2, 4)?
x 是 (4, 8),w 是 (8,)。哪一步没报错,却不是接到 1 维的线性层?
自己对过
X @ W 中间 3=3,结果 (2, 4)。x * w 是广播事故:程序肯跑,你要的四个标量还在八维里。