现在位置: 首页 > AI 数学基础 > 正文

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

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

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


生活引入

快递分拣中心

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

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

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


直观理解

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

\( z = X @ W + b \)

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

X
输入数据
(batch, in)
W
权重矩阵
(in, out)
b
偏置
(out,)
z
输出
(batch, out)
维度匹配铁律: A 的列数必须等于 B 的行数!\(A_{(m \times n)} \cdot B_{(n \times p)} = C_{(m \times p)}\)

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


数学定义

单层前向传播

\[ z_j = \sum_{i=1}^{n} x_i \cdot W_{ij} + b_j \]

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

两层网络串联

\[ \begin{aligned} h &= \text{ReLU}(X W_1 + b_1) \quad & (m \times 4) \cdot (4 \times 6) &= (m \times 6) \\ o &= \text{Sigmoid}(h W_2 + b_2) \quad & (m \times 6) \cdot (6 \times 2) &= (m \times 2) \end{aligned} \]

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


Python 动手实践

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

实例

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]})")
=======================================================
RUNOOB 场景 1:单样本,4 维 -> 3 维
=======================================================
X.shape: (1, 4)   W.shape: (4, 3)   ->  y.shape: (1, 3)

=======================================================
RUNOOB 场景 2:8 个样本批量,同权重
=======================================================
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 最后一层的分类输出