Skip to content

矩阵乘法实现神经网络前向传播

手写 y=Wx+b,输入不同形状的矩阵,观察维度如何变化和匹配。

学完本案例你将理解:神经网络训练时报错 shape mismatch,本质就是矩阵乘法维度对不上。


生活引入

快递分拣中心

一批包裹从传送带进来(输入),经过分拣机分流到不同的货车(隐藏层),最终配送到各个小区(输出)。

每台分拣机就是一个「神经元」——它接收所有包裹,按规则决定哪些包裹走自己的路。分拣机的规则就是「权重」:某个方向的包裹多分一些,某个方向的少分一些。

整个分拣中心的运作就是:矩阵乘法 + 激活函数 + 偏置


直观理解

一层神经网络 = 三个步骤,一行代码搞定:

$ z = X @ W + b $

其中每个部分都有明确的角色:

X

输入数据
(batch, in)

W

权重矩阵
(in, out)

b

偏置
(out,)

z

输出
(batch, out)

维度匹配铁律: A 的列数必须等于 B 的行数!A_(m×n)B_(n×p)=C_(m×p)

输入特征数(in_features)必须等于权重矩阵的行数。输出特征数(out_features)等于权重矩阵的列数。batch_size 贯穿整个过程不变。


数学定义

单层前向传播

z_j=_i=1nx_iW_ij+b_j

每个输出 z_j 是输入向量和权重矩阵第 j 列的点积,再加上偏置。

两层网络串联

h=ReLU(XW_1+b_1)(m×4)(4×6)=(m×6)o=Sigmoid(hW_2+b_2)(m×6)(6×2)=(m×2)

注意:第一层输出的特征数(6)必须等于第二层权重矩阵的行数(6)。这就是「层与层之间的维度衔接」。


Python 动手实践

用一个通用的 dense_layer 函数,演示单样本、批处理、两层网络三种场景。

实例

python

import numpy as np

def dense_layer(X, W, b, activation=None):

    """

    一层全连接网络的前向传播

    X: 输入矩阵 (batch_size, in_features)

    W: 权重矩阵 (in_features, out_features)

    b: 偏置向量 (out_features,)

    """

    z = X @ W + b   # 矩阵乘法 + 广播加法

    if activation == "relu":

        return np.maximum(0, z)          # ReLU: 负数变0

    if activation == "sigmoid":

        return 1 / (1 + np.exp(-z))      # Sigmoid: 压到(0,1)

    return z

print("=" * 55)

print("RUNOOB 场景 1:单样本,4 维 -> 3 维")

print("=" * 55)

X = np.array([[1.0, 2.0, 0.5, -1.0]])     # (1, 4)

W = np.random.randn(4, 3) * 0.5            # (4, 3)

b = np.zeros(3)                            # (3,)

y = dense_layer(X, W, b, activation="relu")

print("X.shape:", X.shape, "  W.shape:", W.shape,

      "  ->  y.shape:", y.shape)

print("\n" + "=" * 55)

print("RUNOOB 场景 2:8 个样本批量,同权重")

print("=" * 55)

X_batch = np.random.randn(8, 4)            # (8, 4)

y_batch = dense_layer(X_batch, W, b, activation="relu")

print("X_batch.shape:", X_batch.shape,

      "  ->  y_batch.shape:", y_batch.shape)

print("批大小 8 不变,特征维度 4 -> 3")

print("\n" + "=" * 55)

print("RUNOOB 场景 3:堆叠两层网络")

print("=" * 55)

# 第一层:4 维 -> 6 维 + ReLU

W1 = np.random.randn(4, 6) * 0.5

b1 = np.zeros(6)

# 第二层:6 维 -> 2 维 + Sigmoid

W2 = np.random.randn(6, 2) * 0.5

b2 = np.zeros(2)

h = dense_layer(X_batch, W1, b1, activation="relu")

out = dense_layer(h, W2, b2, activation="sigmoid")

print(f"输入层: {X_batch.shape}")

print(f"隐藏层: {h.shape}  (4->6, W1=(4,6))")

print(f"输出层: {out.shape}  (6->2, W2=(6,2))")

# 验证:如果维度不匹配会怎样?

print("\n" + "=" * 55)

print("RUNOOB 场景 4:维度不匹配会报错")

print("=" * 55)

try:

    W_wrong = np.random.randn(5, 3) * 0.5  # 行数不对

    dense_layer(X_batch, W_wrong, b)

except ValueError as e:

    print("报错原因:X_batch 有 4 列,"

          "W_wrong 有 5 行 -> 无法相乘")

    print(f"  正确做法:W 的行数 ({W_wrong.shape[0]})"

          f" 应等于 X 的列数 ({X_batch.shape[1]})")
python
=======================================================
RUNOOB 场景 1:单样本,4-> 3
=======================================================
X.shape: (1, 4)   W.shape: (4, 3)   ->  y.shape: (1, 3)

=======================================================
RUNOOB 场景 28 个样本批量,同权重
=======================================================
X_batch.shape: (8, 4)   ->  y_batch.shape: (8, 3)
批大小 8 不变,特征维度 4 -> 3

=======================================================
RUNOOB 场景 3:堆叠两层网络
=======================================================
输入层: (8, 4)
隐藏层: (8, 6)  (4->6, W1=(4,6))
输出层: (8, 2)  (6->2, W2=(6,2))

=======================================================
RUNOOB 场景 4:维度不匹配会报错
=======================================================
报错原因:X_batch 有 4 列,W_wrong 有 5-> 无法相乘
  正确做法:W 的行数 (5) 应等于 X 的列数 (4)

AI 中的应用场景

AI 场景维度变化示例说明
全连接层(32, 784) @ (784, 128) = (32, 128)MNIST 分类:28x28=784 像素 → 128 维特征
Transformer 的 QKV(B, L, D) @ (D, D) = (B, L, D)自注意力:输入和输出的 token 维度不变
Embedding 查表(B, L) → (B, L, D)词 ID 序列 → 词向量序列
分类头(B, 768) @ (768, 10) = (B, 10)BERT 最后一层的分类输出

AI 思考中...

PCA 给数据降维并可视化

用导数找函数最小值

基于 VitePress 构建,部署于 GitHub Pages