14 向量化(Vectorization)

1. 什么是向量化?

  向量化 是指避免在代码中使用显式的 for 循环,转而使用高度优化的矩阵/向量运算(如 NumPy 中的内置函数),从而大幅提升计算效率。

在深度学习中,我们经常处理大规模数据集(例如百万级特征或样本)。如果使用非向量化实现,程序运行会极其缓慢;而向量化能充分利用 CPU/GPU 的并行计算能力(SIMD 指令),使代码快数百倍甚至上千倍。


2. 为什么需要向量化?—— 以逻辑回归为例

  在逻辑回归中,我们需要计算:

$$
z = \mathbf{w}^\top \mathbf{x} + b
$$

  其中:

  • $\mathbf{w} \in \mathbb{R}^{n_x}$ 是权重向量,
  • $\mathbf{x} \in \mathbb{R}^{n_x}$ 是输入特征向量,
  • $b$ 是偏置项,
  • $n_x$ 是特征数量(可能非常大)。

❌ 非向量化实现(显式 for 循环):

z = 0
for i in range(n_x):
z += w[i] * x[i]
z += b
  • 时间复杂度:$O(n_x)$
  • 实际运行慢,无法利用硬件并行性。

✅ 向量化实现(使用 NumPy):

z = np.dot(w, x) + b
  • 一行代码完成相同计算。
  • 底层由 C/Fortran 优化,自动并行化。
  • 速度提升可达 300 倍以上(见下文实验)。

3. 实验演示:向量化 vs 非向量化

实验设置:

  • 创建两个长度为 $10^6$ 的随机向量 $\mathbf{a}, \mathbf{b} \in \mathbb{R}^{1,000,000}$
  • 计算点积:$\mathbf{a}^\top \mathbf{b} = \sum_{i=1}^{10^6} a_i b_i$

✅ 向量化版本(NumPy):

c = np.dot(a, b)
  • 耗时:约 1.5 毫秒

❌ 非向量化版本(for 循环):

c = 0
for i in range(1000000):
c += a[i] * b[i]
  • 耗时:约 480 毫秒
  • 慢了约 300 倍!

💡 结果验证:两种方法计算出的 c​ 值完全一致(如 250699.123…),说明向量化不仅快,而且数值正确


4. 为什么向量化更快?

  • 现代 CPU 和 GPU 都支持 SIMD(Single Instruction, Multiple Data) 指令。

    • 即:一条指令同时处理多个数据
  • NumPy 等库底层使用高度优化的 C/C++/BLAS 库(如 OpenBLAS、Intel MKL),能自动利用 SIMD 并行。

  • 显式 for 循环由 Python 解释器逐行执行,解释开销大 + 无并行 → 极慢。

⚠️ 注意:即使没有 GPU,仅在 CPU 上,向量化也能带来巨大加速!


5. 核心原则(Rule of Thumb)

“尽可能避免显式 for 循环!”

  在深度学习中,以下操作都应向量化:

  • 矩阵乘法(如 $\mathbf{W} \mathbf{X}$)
  • 激活函数(如 np.sigmoid(Z) 作用于整个矩阵)
  • 损失函数计算
  • 梯度更新

6. 后续内容预告

  本节是向量化的入门。接下来课程将:

  • 展示如何向量化整个逻辑回归模型(包括前向传播、损失计算、反向传播)

  • 扩展到多样本批量(batch)处理,即对 $m$ 个样本同时计算:

    $$
    \mathbf{Z} = \mathbf{W}^\top \mathbf{X} + b
    $$

    其中 $\mathbf{X} \in \mathbb{R}^{n_x \times m}$ 是所有样本组成的矩阵。


✅ 总结要点

项目 非向量化 向量化
代码风格 显式 for 循环 使用 np.dot​, np.sum
速度 慢(毫秒 → 秒级) 快(微秒 → 毫秒级)
可读性 冗长 简洁、数学直观
并行性 自动利用 CPU/GPU 并行
推荐程度 ❌ 避免 ✅ 必须掌握

  掌握向量化是高效深度学习编程的基石。它不仅让代码跑得更快,还能让你更专注于算法本身,而非低效的循环细节。