优化算法
7 / 15
机器学习策略(上)
自在学
分类课程AI导师创意工坊价格
分类课程AI导师创意工坊价格
编程深度学习超参数调优与批归一化

超参数调优与批归一化

你精心设计了一个神经网络架构,实现了完美的前向传播和反向传播,代码没有bug。但训练出来的模型准确率只有70%,而你知道这个任务应该能达到90%以上。 问题出在哪?很可能是超参数没调好——学习率太大导致发散,或者太小导致困在局部最优;隐藏层太少导致欠拟合,或者Dropout概率不合适。

超参数调优是深度学习工程中最耗时的部分之一。不像参数可以通过梯度下降自动学习,超参数需要人工尝试。本节将介绍系统化的调优策略,以及Batch Normalization这个让训练更稳定、减少超参数敏感性的重要技术。

超参数调优与批归一化


超参数的重要性排序

深度学习模型有很多超参数,但它们的重要性差异巨大。理解这个优先级能让你的调参更高效。

最重要(必须调):

  • 学习率 α\alphaα:影响最大。太大发散,太小收敛慢或困在局部最优

较重要(通常需要调):

  • Mini-batch大小:影响训练速度和稳定性,通常64-512
  • 隐藏层神经元数:影响模型容量
  • Momentum的 β\betaβ (如0.9):使用Momentum或Adam时

次要(用默认值通常就行):

  • 网络层数:通常从2-3层开始,不够再加
  • 学习率衰减:有帮助但不是必需
  • Adam的 β1,β2,ϵ\beta_1, \beta_2, \epsilonβ1​,β2​,ϵ:默认值0.9, 0.999, 1e-8通常很好

实践策略:先调学习率,找到一个合理范围;再调batch size和网络大小;最后微调其他参数。


随机搜索 vs 网格搜索

假设你要调两个超参数:学习率和隐藏层大小。网格搜索是这样做的:

|
for lr in [0.001, 0.01, 0.1]: for hidden_size in [50, 100, 200]: # 训练模型,评估性能

这会尝试3×3=9种组合。看起来系统化,但有个致命问题:如果学习率很重要而hidden_size不重要,你实际上只尝试了3个不同的学习率值。大部分计算资源浪费在了不重要的参数上。

随机搜索更好:在超参数空间中随机采样,尝试同样多的组合,但探索了更多不同的学习率值:

|
for trial in range(9): lr = 10 ** np.random.uniform(-4, -1) # 0.0001到0.1 hidden_size = np.random.randint(50, 200) # 训练模型

随机搜索让你更可能找到重要参数的最优值。2012年Bergstra和Bengio的论文证明,随机搜索在实践中几乎总是优于网格搜索。


合适的采样尺度

不是所有超参数都应该均匀采样。学习率就是个好例子。

错误做法(均匀采样):

|
lr = np.random.uniform(0.0001, 1) # 90%的值会在0.1到1之间

这样采样,大部分值会落在0.1-1之间,但经验告诉我们学习率通常在0.0001-0.01之间。我们需要在对数尺度上均匀采样:

正确做法(对数尺度):

|
log_lr = np.random.uniform(-4, 0) # -4到0之间均匀 lr = 10 ** log_lr # 0.0001到1之间对数均匀

这样0.0001-0.001, 0.001-0.01, 0.01-0.1, 0.1-1各有25%的概率。

为什么要对数尺度? 因为学习率的效果是乘性的。从0.001到0.01提升10倍,从0.1到1也提升10倍,虽然绝对差异不同,但对训练的影响是相似的。

类似地,正则化参数 λ\lambdaλ、Adam的 β\betaβ 参数(1−β1-\beta1−β)也应该对数尺度采样。

|
# 采样beta(如0.9到0.999) # 采样1-beta,因为它更接近线性影响 log_1_minus_beta = np.random.uniform(-3, -1) # 0.001到0.1 beta = 1 - 10 ** log_1_minus_beta # 0.9到0.999

调参策略:Pandas vs Caviar

有两种调参哲学,类比为养熊猫和养鱼:

Pandas方法(Babysitting one model):

  • 你只有一个GPU,或者数据集巨大训练很慢
  • 训练一个模型,每天观察学习曲线,手动调整超参数
  • 像养熊猫一样精心照料一个模型

Caviar方法(Training many models in parallel):

  • 你有多个GPU或TPU
  • 同时训练很多模型(不同超参数组合),最后选最好的
  • 像鱼产卵一样,数量取胜

大公司(如Google、Facebook)通常用Caviar方法——资源充足时,并行尝试比顺序尝试更快。但如果资源有限,Pandas方法可能更现实。

无论哪种方法,都要系统化记录:哪些超参数尝试过,效果如何,用Excel或专门的实验管理工具(如Weights & Biases、MLflow)跟踪。


Batch Normalizatio

Normalization:训练的稳定器

Batch Normalization(批归一化,简称BN)是2015年Ioffe和Szegedy提出的技术,极大地改善了深度网络的训练。它的核心思想简单但强大:归一化每一层的输入。

我们知道归一化输入能加速训练。BN将这个思想应用到网络的每一层:不仅归一化输入 XXX,还归一化隐藏层的激活值 A[l]A^{[l]}A[l]。

BN的数学原理

对于某一层的激活值 Z[l]Z^{[l]}Z[l](或者 A[l]A^{[l]}A[l],在激活函数之前或之后都可以),BN做以下变换:

μ=1m∑i=1mz(i)\mu = \frac{1}{m} \sum_{i=1}^m z^{(i)}μ=m1​i=1∑m​z(i) σ2=1m∑i=1m(z(i)−μ)2\sigma^2 = \frac{1}{m} \sum_{i=1}^m (z^{(i)} - \mu)^2σ2=m1​i=1∑m​(z(i)−μ)2 znorm(i)=z(i)−μσ2+ϵz_{\text{norm}}^{(i)} = \frac{z^{(i)} - \mu}{\sqrt{\sigma^2 + \epsilon}}znorm(i)​=σ2+ϵ​z(i)−μ​ z~(i)=γznorm(i)+β\tilde{z}^{(i)} = \gamma z_{\text{norm}}^{(i)} + \betaz~(i)=γznorm(i)​+β

这里 μ\muμ 和 σ2\sigma^2σ2 是该mini-batch的均值和方差,ϵ\epsilonϵ(如10−810^{-8}10−8)防止除零。关键是最后一步:引入可学习的参数 γ\gammaγ 和 β\betaβ。

为什么需要 γ\gammaγ 和 β\betaβ? 简单归一化会限制网络的表达能力。比如sigmoid激活函数在0附近接近线性,归一化后所有值都在0附近,丢失了非线性。γ\gammaγ 和 β\betaβ 让网络能够"撤销"归一化——如果网络学到 γ=σ2\gamma = \sqrt{\sigma^2}γ=σ2​ 和 β=μ\beta = \muβ=μ,就恢复了原始值。

BN的效果

  1. 允许更大的学习率:归一化后梯度更稳定,不容易爆炸或消失
  2. 减少对初始化的敏感性:权重初始化不够好也能训练
  3. 正则化效果:每个mini-batch的均值/方差有随机性,类似Dropout
  4. 加速收敛:实践中常能将训练速度提升2-3倍
|
def batch_norm_forward(Z, gamma, beta, epsilon=1e-8): """ Batch Normalization前向传播(训练时) Z: shape (n, m) - 一层的激活值 gamma, beta: shape (n, 1) - 可学习参数 """ # 计算均值和方差(跨样本维度) mu = np.mean(Z, axis=1, keepdims=True) sigma2 = np.var(Z, axis=1, keepdims=True) # 归一化 Z_norm = (Z - mu) / np.sqrt(sigma2 + epsilon) # 缩放和平移 Z_tilde = gamma * Z_norm + beta # 缓存用于反向传播 cache = (Z, Z_norm, mu, sigma2, gamma, beta, epsilon) return Z_tilde, cache def batch_norm_backward(dZ_tilde, cache): """BN的反向传播""" Z, Z_norm, mu, sigma2, gamma, beta, epsilon = cache m = Z.shape[1] # 计算梯度(推导较复杂,这里省略细节) dgamma = np.sum(dZ_tilde * Z_norm, axis=1, keepdims=True) dbeta = np.sum(dZ_tilde, axis=1, keepdims=True) dZ_norm = dZ_tilde * gamma dsigma2 = np.sum(dZ_norm * (Z - mu) * -0.5 * np.power(sigma2 + epsilon, -1.5), axis=1, keepdims=True) dmu = np.sum(dZ_norm * -1 / np.sqrt(sigma2 + epsilon), axis=1, keepdims=True) + dsigma2 * np.mean(-2 * (Z - mu), axis=1, keepdims=True) dZ = dZ_norm / np.sqrt(sigma2 + epsilon) + dsigma2 * 2 * (Z - mu) / m + dmu / m return dZ, dgamma, dbeta

测试时的BN

训练时,BN使用当前mini-batch的统计量。但测试时,我们可能一次只预测一个样本,没有"batch"。解决方案是:在训练过程中维护均值和方差的指数加权平均,测试时使用这些平均值。

|
# 训练时维护running averages running_mean = beta * running_mean + (1-beta) * mu_batch running_var = beta * running_var + (1-beta) * sigma2_batch # 测试时 Z_norm_test = (Z_test - running_mean) / np.sqrt(running_var + epsilon) Z_tilde_test = gamma * Z_norm_test + beta

BN的位置:激活前还是激活后?

论文建议在激活函数之前应用BN(即对 ZZZ 而不是 AAA)。但实践中两种都有人用,效果差异不大。更重要的是保持一致性。

另一个细节:使用BN时,该层的偏置项 bbb 可以省略——因为BN的 β\betaβ 已经起到了偏置的作用。

Softmax回归:多分类问题

前面我们一直在处理二分类(是或否)。但很多问题是多分类:识别10个数字、分类1000种物体等。Softmax回归是逻辑回归在多分类上的推广。

输出层有 KKK 个神经元(KKK 个类别),每个输出 zk[L]z^{[L]}_kzk[L]​。Softmax函数将这些值转换为概率分布:

ak[L]=ezk[L]∑j=1Kezj[L]a^{[L]}_k = \frac{e^{z^{[L]}_k}}{\sum_{j=1}^K e^{z^{[L]}_j}}ak[L]​=∑j=1K​ezj[L]​ezk[L]​​

每个 ak[L]∈(0,1)a^{[L]}_k \in (0, 1)ak[L]​∈(0,1) 且 ∑k=1Kak[L]=1\sum_{k=1}^K a^{[L]}_k = 1∑k=1K​ak[L]​=1,可以解释为属于第 kkk 类的概率。

损失函数是交叉熵的多分类版本:

L(a[L],y)=−∑k=1Kyklog⁡ak[L]\mathcal{L}(a^{[L]}, y) = -\sum_{k=1}^K y_k \log a^{[L]}_kL(a[L],y)=−k=1∑K​yk​logak[L]​

这里 yyy 是one-hot编码:如果样本属于第2类,y=[0,1,0,...,0]y = [0, 1, 0, ..., 0]y=[0,1,0,...,0]。

反向传播的梯度形式优雅:

∂L∂z[L]=a[L]−y\frac{\partial \mathcal{L}}{\partial z^{[L]}} = a^{[L]} - y∂z[L]∂L​=a[L]−y

与二分类完全一样的形式!

|
def softmax(Z): """ Softmax激活函数 Z: shape (K, m) - K个类别,m个样本 """ # 减去最大值防止数值溢出 Z_exp = np.exp(Z - np.max(Z, axis=0, keepdims=True)) return Z_exp / np.sum(Z_exp, axis=0, keepdims=True) # 使用示例:10分类问题 Z_last = np.dot(W_last, A_prev) + b_last # shape: (10, m) A_last = softmax(Z_last) # 每列是一个样本的概率分布 predictions = np.argmax(A_last, axis=0) # 选概率最大的类别

深度学习框架选择

到目前为止,我们从零实现了所有算法。这对理解原理很重要,但实际项目中,使用成熟的深度学习框架能节省大量时间。

主流框架(2026年):

  1. PyTorch:最流行的研究框架,动态计算图,易于调试
  2. TensorFlow/Keras:Google出品,工业部署生态好
  3. JAX:Google新框架,强调函数式编程和自动微分

选择标准:

  • 研究/原型:PyTorch,灵活性最好
  • 生产部署:TensorFlow Serving或PyTorch TorchServe
  • 需要XLA加速:JAX或TensorFlow
  • 移动端:TensorFlow Lite或PyTorch Mobile

框架虽然重要,但更重要的是理解深度学习的原理。框架API会变,但反向传播、正则化这些核心概念不变。掌握了原理,切换框架只需几天学习曲线。


从基础到实践的跨越

到此为止,我们已经掌握了训练神经网络的所有核心技术:前向传播、反向传播、各种优化算法、正则化、超参数调优。但还有一个维度我们没深入讨论:如何系统化地改进一个模型?如何诊断问题并有针对性地优化?

在下一节,我们将学习机器学习策略——不是新的算法或技术,而是一套思维框架,帮助你高效地迭代改进模型,避免在错误的方向上浪费时间。这些策略来自Andrew Ng等人多年的项目经验总结,是深度学习工程化的精髓。

  • 超参数的重要性排序
  • 随机搜索 vs 网格搜索
  • 合适的采样尺度
  • 调参策略:Pandas vs Caviar
  • Batch Normalizatio
    • BN的数学原理
    • BN的效果
    • 测试时的BN
  • Softmax回归:多分类问题
  • 深度学习框架选择
  • 从基础到实践的跨越

目录

  • 超参数的重要性排序
  • 随机搜索 vs 网格搜索
  • 合适的采样尺度
  • 调参策略:Pandas vs Caviar
  • Batch Normalizatio
    • BN的数学原理
    • BN的效果
    • 测试时的BN
  • Softmax回归:多分类问题
  • 深度学习框架选择
  • 从基础到实践的跨越
自在学

© 2025 自在学,保留所有权利。

公网安备湘公网安备43020302000292号 | 湘ICP备2025148919号-1

关于我们隐私政策使用条款

© 2025 自在学,保留所有权利。

公网安备湘公网安备43020302000292号湘ICP备2025148919号-1