2. 模型参数分类:结构参数与训练参数、超参数与模型参数的区别、参数空间的定义

好,咱们接着聊。上一章我们把参数优化的整体框架搭起来了,这一章得深入进去,看看这些参数到底长什么样、分几类。

说实话,我刚入行那会儿,最头疼的就是别人嘴里蹦出来的「超参数」「模型参数」「结构参数」这些词。听着都差不多,但调起来完全是两码事。有一次我跟同事说「我把模型参数调了一下」,结果他问我「你调的是学习率还是权重?」——嗯,那时候我才意识到,这些概念不搞清楚,连沟通都费劲。

2.1 结构参数 vs 训练参数

我个人习惯把参数先分成两大类:结构参数训练参数。这个分法最直观,也最贴近实际开发流程。

结构参数

结构参数,说白了就是模型长什么样。你决定用几层网络、每层多少个神经元、用什么样的激活函数——这些都属于结构参数。它们一旦定下来,模型的「骨架」就固定了。

常见的结构参数包括:
  • 网络层数(深度)
  • 每层神经元数量(宽度)
  • 卷积核大小、步长、填充方式
  • 循环神经网络的隐藏状态维度
  • 注意力机制的头数
  • 激活函数类型(ReLU、GELU、Sigmoid等)

我在项目中遇到过最典型的例子:有一次做图像分类,用ResNet-50效果还行,但部署到边缘设备上发现推理速度太慢。后来我把层数砍了一半,精度掉了不到2%,但速度提升了3倍。你看,结构参数直接影响的就是这种「天花板」——精度上限和计算开销。

训练参数

训练参数呢?它们是模型怎么学的配置。比如学习率、批量大小、优化器类型、正则化系数、训练轮数等等。这些参数不改变模型的骨架,但决定了骨架能不能「长好肉」。

一个小类比:
结构参数就像盖房子的图纸——几室几厅、楼梯在哪。
训练参数就像施工时的水泥标号、养护时间——图纸一样,但施工质量不同,房子结实程度天差地别。

我记得有一次调一个NLP模型,结构参数完全没动,只是把学习率从0.001改成0.0005,再配合一个余弦退火调度器,验证集准确率直接涨了4个点。这就是训练参数的威力——它不改变模型容量,但能让你把模型容量「榨干」。

2.2 超参数 vs 模型参数

好,接下来是另一个维度——超参数模型参数。这个区分更偏理论,但实际调参时特别有用。

模型参数

模型参数是从数据中学出来的。比如神经网络的权重矩阵、偏置项、Batch Normalization的均值和方差。这些参数在训练过程中不断更新,训练结束后就固定下来。说白了,模型参数就是模型「记住」的东西。

# 模型参数示例:PyTorch中查看模型参数
import torch
import torch.nn as nn

model = nn.Linear(10, 5)
for name, param in model.named_parameters():
    print(f"{name}: shape {param.shape}")
# 输出:
# weight: shape [5, 10]
# bias: shape [5]

超参数

超参数是在训练开始前就设定好的,它们控制着模型参数的学习过程。超参数不会因为训练而自动更新——你得手动调,或者用自动化工具去搜。

常见的超参数包括:
  • 学习率及其调度策略
  • 批量大小(batch size)
  • 优化器类型(SGD、Adam、RMSProp等)
  • 正则化系数(L1、L2、Dropout rate)
  • 训练轮数(epochs)
  • 早停策略的耐心值

你想想看,模型参数和超参数的关系,有点像「学生」和「老师」。模型参数是学生学到的知识,超参数是老师的教学方法。老师定好教学计划(超参数),学生根据这个计划去学习(更新模型参数)。

我曾经踩过的坑:
有一次做超参数搜索,我把学习率和批量大小一起调,结果发现同样的学习率在不同批量大小下表现天差地别。后来才意识到,批量大小变了,梯度噪声也变了,学习率应该跟着缩放。这就是超参数之间的「耦合效应」——调参时不能孤立地看单个参数。

2.3 参数空间的定义

好,最后一个概念——参数空间。这个概念其实很简单,但很多人容易忽略它的重要性。

参数空间,说白了就是所有可能的参数取值组合构成的集合。每个参数是一个维度,所有维度组合起来就是一个高维空间。

举个例子:假设你只有两个超参数——学习率(范围0.0001到0.01)和批量大小(范围16到256)。那么参数空间就是一个二维矩形区域。如果你再加一个Dropout率(范围0.1到0.5),就变成了三维立方体。

参数空间的关键要素:
  • 维度:每个可调参数就是一个维度
  • 范围:每个参数的合法取值范围
  • 尺度:参数是线性尺度还是对数尺度(比如学习率通常用对数尺度)
  • 约束:参数之间的依赖关系(比如某些参数组合不合法)

我个人习惯在开始调参前,先画一张参数空间的草图。不一定多精确,但至少把每个参数的范围和尺度搞清楚。为什么?因为参数空间的「形状」直接决定了搜索策略的选择。

# 参数空间定义示例:使用Optuna
import optuna

def objective(trial):
    # 定义参数空间
    lr = trial.suggest_float("lr", 1e-5, 1e-1, log=True)  # 对数尺度
    batch_size = trial.suggest_categorical("batch_size", [16, 32, 64, 128])
    dropout = trial.suggest_float("dropout", 0.1, 0.5)
    optimizer = trial.suggest_categorical("optimizer", ["adam", "sgd"])
    
    # 训练模型并返回验证指标
    accuracy = train_model(lr, batch_size, dropout, optimizer)
    return accuracy

这里要注意一个点:参数空间不是越大越好。我见过有人把参数范围设得特别宽,结果搜索了半天,大部分区域都是「无效区域」——要么模型不收敛,要么直接爆显存。所以定义参数空间时,我建议先做一轮粗搜索,把有效区域圈出来,再在这个区域内做精细搜索。

我的经验法则:
对于连续参数(如学习率),先用对数尺度定范围,范围跨度不超过两个数量级。
对于离散参数(如批量大小),选2-4个典型值就够了,别贪多。
参数总数控制在5-8个以内,否则搜索空间会爆炸。

最后,我用一张图来总结这一章的核心逻辑。这张图展示了参数分类的层次关系,以及参数空间在整个优化流程中的位置。

模型参数分类与参数空间 模型参数(从数据中学得) 结构参数 训练参数 结构参数示例 • 网络层数 • 神经元数量 • 卷积核大小 • 激活函数类型 训练参数示例 • 学习率 • 批量大小 • 优化器类型 • 正则化系数 超参数(训练前设定) 包括:结构参数 + 训练参数 参数空间 = 所有参数取值组合的集合 维度 · 范围 · 尺度 · 约束 模型参数 超参数 参数空间

这张图把本章的核心内容串起来了。你看,模型参数和超参数是两种不同性质的参数,而结构参数和训练参数是从另一个维度做的划分。超参数包含了结构参数和训练参数,它们共同定义了参数空间。调参的本质,就是在参数空间里找到那个「最优解」。

嗯,这一章就到这里。概念清楚了,下一章我们就能放开手脚去聊具体的调参方法了。


专注资料整理