前言
通过前面两次微调训练欺诈文本分类微调(六):Lora单卡和欺诈文本分类微调(七)—— lora单卡二次调优,我们已经初步理解了微调的整个过程,里面涉及到不少的参数配置,这篇文章就对前面用到过的参数作一个基本的梳理和总结。
批次大小
per_device_train_batch_size
解读:每个设备训练时的小批量大小。大batch_size和小batch_size的区别在于:较小的batch_size可能会带来训练时梯度的较大波动,而较大的batch_size则可能使训练过程更加平滑。
此参数设置受显卡内存、模型大小、输入数据长短影响,需要根据自己的情况来尝试。如果设置的batch_size过大,可能会导致显存溢出(OOM),这时需要减小batch_size。
一般的策略是:从较小的batch_size=4开始尝试,并逐渐增大,直到找到适合你的显卡和模型的最大可用batch_size。
需要特别注意,batch_size对内存的影响是巨大的,对于一个参数量上B的模型来说,此参数每增加1都会有几G的内存增加,batch_size会直接乘以参数量(1.5B)来计算内存,超过就OutOfMemory了。
gradient_accumulation_steps
解读:是否使用梯度累积,当=1时每个step都计算梯度,当>1时要累积梯定数量的step才计算一次梯度。
当启用此参数时,有效批量大小 = per_device_train_batch_size * gradient_accumulation_steps。
适用场景:如果需要用较大的batch_size但显存又不足,则可以考虑使用梯度累积(gradient accumulation)来模拟更大的batch_size,作用是让梯度下降更稳定。
per_device_eval_batch_size
解读:评估时每个设备的batch大小,默认值是8, 影响因素同per_device_train_batch_size。但一般评估时可以使用较大的batch_size, 因为不需要梯度计算,显存占用的少。
num_train_epochs
=3,
解读:训练的总轮数,所有数据训练完为一轮,3表示所有数据训练3遍。
学习率配置
learning_rate
=1e-4,
解读:学习率,用于控制模型训练速度的超参数,1e-4
表示0.0001。
lr_scheduler_type
=“cosine”
解读:使用余弦退火调度器,它会在训练过程中逐渐减小学习率,有助于模型稳定收敛。
warmup_ratio
和 warmup_steps
解读:这两个参数用于控制学习率预热过程,让学习率从0开始逐步增加到预设的值,防止训练初期的大梯度更新导致训练不稳定。也有助于避免训练初期的梯度爆炸问题。
- warmup_ratio是学习率预热阶段占整个训练过程的比例,例如:0.1
- warmup_steps则直接指定了预热阶段的步数,例如:20。
注:如果两者都设置了,通常优先使用warmup_steps。如果两者都被设置为0,则表示没有预热阶段。
可选值:warmup_ratio的值应该在0到1之间。warmup_steps应该是一个正整数,表示预热阶段的步数。
weight_decay
=0.01
解读:引入权重衰减,它会迫使模型参数保持较小的值,从而避免模型过拟合。
日志配置
log_level
解读:这个参数控制日志记录的详细程度。默认为warning,只记录错误或关键信息,可选日志级别包括"info", “warning”, “error”, “debug”。
log_level_replica
解读:在分布式训练环境中,这个参数控制每个训练副本(或工作进程)的日志记录级别。
训练副本(replica)指的是在分布式训练环境中,每个独立的计算设备(例如GPU或TPU)上运行的训练进程。"warning"表示只记录警告及以上级别的日志。
logging_dir
解读:设置日志的保存目录。
logging_first_step
解读:这个参数控制是否在训练的第一个步骤就记录日志。false表示不会在第一个步骤就记录日志。
logging_steps
解读:指定每隔多少个训练步骤记录一次日志。10表示每10个step记录一次日志。
评估验证
eval_strategy
=“steps”
解读:设置训练期间的评估验证策略,可选值有steps、epoch、no
- steps: 每隔
eval_steps
指定的step数量后启动一次评估。 - epoch: 每一轮训练完启动评估。
- no: 训练期间不进行评估验证。
eval_steps
解读:设置多少步数启动用一次评估验证,与eval_strategy=“steps” 配合使用,一般设为与保存步数一致。
load_best_model_at_end
解读:这个参数控制是否在训练结束时加载在验证集上表现最好的模型。设置为True表示训练结束后会加载最佳模型。可选值:true 或 false。
metric_for_best_model
解读:用于指定用于评估最佳模型的指标。这个参数告诉训练过程应该根据哪个指标来判断模型的性能,从而决定何时保存当前模型为“最佳”模型。
- 准确率 (accuracy): 在分类任务中最常用的指标。
- 损失 (loss): 通常用于监控模型的表现,较低的损失通常意味着更好的模型。
- 精确率 (precision): 适用于需要关注假阳性率的分类任务。
- 召回率 (recall): 适用于需要关注假阴性率的分类任务。
- F1-score: 精确率和召回率的综合度量,适用于类别不平衡的任务。
- 均方误差 (mse): 在回归任务中用于评估模型。
- R²得分: 回归模型的拟合程度,越接近1表示模型表现越好。
模型保存
output_dir
解读:模型训练结果的输出目录,按照save_steps指定的步数来定期保存,每一次训练是一个checkpoint-[steps]
的目录。
save_steps
解读:设置多少步数保存一次模型参数,一般建议设置为100,如果为了快速演示,可以设置10。
save_safetensors
解读:这个参数控制是否使用SafeTensors库来保存模型。SafeTensors是一种用于保存和加载PyTorch张量和模型的库,它提供了比PyTorch原生的torch.save和torch.load更健壮的错误处理和兼容性。可选值:true 或 false,默认值为true。
注:以上所有参数都位于
transformers.TrainingArguments
中。
lora参数
这部分参数位于peft.LoraConfig
中。
r
解读:LoRA低秩矩阵中rank的大小,需要在模型复杂性、适应能力以及欠拟合、过拟合的风险之间进行权衡。
- 较小的 r 对应的是更简单的低秩矩阵,模型在训练中学习的参数更少。这可以加快训练速度,并可能降低计算需求。
- 但是,随着 r 越小,低秩矩阵捕获特定任务信息的能力就会降低,模型在新任务上的表现可能不如较高的r。
- 实际训练中需要尝试不同的 r 值,以找到合适的平衡点,以在新任务上实现所需的性能。
lora_alpha
解读:一个比例因子,用于在前向传播过程中将 LoRA参数以一定的缩放比例应用于模型之中,用于调整原始模型输出加上低秩适应两者组合结果的大小。正如下面公式所示,此比例因子值越大,Lora参数的影响就越大。
scaling = alpha / r
weight += (lora_B @ lora_A) * scaling
lora_dropout
:
解读:用于控制在训练过程中应用于 LoRA 层的 dropout参数比例,取值在0-1之间。0.1表示丢弃10%的神经元,这部分被丢弃的神经元不参与计算。
注:Dropout 是一种正则化技术,只用于训练过程,通过随机丢弃一部分神经元来,来减少训练过程中模型对特定特征的依赖,从而防止模型过拟合,尤其适用于数据量较小或模型较复杂的情况下。
inference_mode
解读:是否为推理模式,设置为False表示训练模式。
target_modules
解读:用于指定模型中具体哪些模块将应用 LoRA 适配器,这里的模块与具体模型的结构有关,一般都需要包括注意力部分的q、k、v模块。
例子:在qwen2模型中,可以配置为[“q_proj”, “k_proj”, “v_proj”, “o_proj”, “gate_proj”, “up_proj”, “down_proj”], 表示对Attention和前馈神经网络应用Lora适配器。
训练器配置
此部分参数位于transformers.Trainer
中。
data_collator
=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True)
解读:控制如何将原始数据合并成批(batch), DataCollatorForSeq2Seq 会自动处理输入序列的填充,使用 tokenizer 提供的填充标记(padding token)将不同长度的序列填充到相同的长度,以避免在训练过程中因序列长度不同而产生错误。
注:序列到序列(Seq2Seq)模型中,批量输入的多条文本数据通常具有不同的长度,而模型在进行矩阵运算时需要同一批次的数据有相同长度才能一起运算,否则会报错,所以需要指定padding=True参数来将输入序列填充到相同长度。
callbacks
=[EarlyStoppingCallback(early_stopping_patience=3)]
解读:如果验证指标在指定次数(3)的评估中均没有改善,训练就会提前停止 。
注:默认情况下,训练会跑满train_dataset和num_train_epochs指定的所有数据集和训练轮次,但存在一些场景(例如过拟合发生时)需要提前结束训练,此时就可以设置早停回调以免模型越训练越差,还有一个重要的点是避免浪费GPU算力成本。
批量大小两组参数对比
第一组参数:
per_device_train_batch_size=4
gradient_accumulation_steps=4
第二组参数:
per_device_train_batch_size=16
gradient_accumulation_steps=1
单卡场景下,以上两组参数最终进行梯度计算的批量大小都是16,但两组参数对训练带来的影响是不同的。
- 第一组参数可能会更稳定的训练,因为梯度是在多个小批量的基础上累积的。这对训练较为复杂或噪声较大的模型时可能有利。
- 第二组参数由于每步都会进行梯度更新,可能会导致训练中的波动更多。但是这种设置可能会更快地找到最优解(如果显存充足的话)。
虽然梯度累积可以带来更稳定的训练过程,但在某些情况下,直接使用较大的批量(如第二组参数)可能会更快找到最优解,原因在于:
- 每步都会进行梯度更新相对于每4步才进行一次梯度更新,显然前者参数会更新的更频繁,有助于提高训练速度。
- 使用较大的批量大小(16)进行每次梯度更新,梯度估计更精确(更接近于基于整个数据集的梯度),而多个小批量累积梯度可能由于批量较小而更具噪声。
- 较大的批量可能允许使用更高的学习率,可以在一定程度上加速收敛,由于批量较大而不会导致训练不稳定。
注:个人实际验证的结果,第二组参数确实比第一组参数效果更好。
训练起始点的选择
每次调参后应该接着之前的checkpoint继续训练,还是从头开始训练?
一般原则:
- 大幅度调整超参数(例如改变模型架构、增加或减少层数、改变激活函数、改变优化器等),会显著改变模型的行为和性能,需要从头开始训练,不同优化器的内部状态(如动量、二阶矩估计等)不同,复用会导致不兼容。
- 小幅度调整超参数(包括学习率、数据增强),一般不会显著改变模型的整体训练过程,可以接着之前的checkpoint继续训练。这可以节省计算资源和时间。
而具体到每个参数,建议如下:
-
Dropout比例调整:建议从头开始训练
由于Dropout的作用是随机丢弃神经元,调整Dropout比例会影响模型的学习模式和权重分布。可能会显著改变模型的训练行为,因此最好从头开始训练。
-
LoRA秩调整(假设指的是Low-Rank Adaptation):建议从头开始训练
调整LoRA秩会改变模型的参数数量和结构,这通常意味着权重初始化和训练过程会有显著变化,因此建议从头开始训练。
-
Batch Size调整:可以接着之前的checkpoint继续训练
调整Batch Size一般不会显著改变模型的权重分布,但会影响训练时间和稳定性。可以接着之前的checkpoint继续训练,但建议调整学习率以适应新的Batch Size。
-
学习率预热(Learning Rate Warmup):可以接着之前的checkpoint继续训练。
学习率预热通常用于训练初期调整学习率,此参数可以在训练过程中动态调整。
-
正则化参数:如L2正则化因子的调整,可以从checkpoint继续训练,但需要密切监控模型性能。
-
学习率:可以从checkpoint继续训练,特别是当你想通过调整学习率来微调模型时。
从数据中找问题
下面是一次完整训练过程中的训练下降数据。
Step Training Loss Validation Loss
0100 0.026600 0.030008
0200 0.034300 0.031493
0300 0.014700 0.023997
0400 0.025400 0.021837
0500 0.023100 0.021945
0600 0.025400 0.023164
0700 0.021100 0.020423
0800 0.018000 0.021873
0900 0.017000 0.019083
1000 0.018000 0.018249
1100 0.018100 0.020406
1200 0.014600 0.018704
1300 0.012000 0.020742
1400 0.019300 0.017799
1500 0.010800 0.021259
1600 0.009700 0.017322
1700 0.009000 0.017109
1800 0.009200 0.018110
1900 0.016300 0.017388
2000 0.019600 0.017490
2100 0.009300 0.020641
2200 0.012800 0.016410
2300 0.008700 0.017101
2400 0.006100 0.019264
2500 0.006500 0.017300
2600 0.008300 0.021054
2700 0.003600 0.021404
2800 0.002800 0.020927
2900 0.009600 0.022502
3000 0.005200 0.019417
3100 0.007000 0.022534
3200 0.001900 0.025071
3300 0.003000 0.023973
3400 0.006700 0.024258
3500 0.005900 0.024328
从上面的数据中可以观察到以下几个明显的问题现象:
-
问题现象1:训练损失和验证损失都存在波动,尤其是训练损失在某些步数上会出现较大的变化(如step 200, 400, 1400, 2000)。
调优方向:这种情况可以使用学习率调度器来动态调整学习率,使得模型在训练的不同阶段能够更好地适应数据。
-
问题现象2:验证损失基本在一个范围内波动,没有随着训练过程显著下降,这可能表明模型在验证集上的泛化能力有限。
调优方向:增加模型的正则化力度,如增加Dropout比例或L2正则化,防止过拟合。
-
问题现象3:在一些步数上,训练损失显著低于验证损失(如step 3200, 3300, 3400等),这可能表明模型在训练集上过拟合。
调优方向:增加训练数据或者使用数据增强技术,提升模型的泛化能力。
说明:这里只是对从数据中找问题作个示例,这个训练数据中肯定还有更多的潜在问题,但需要后面一步步深入研究和总结。
小结:本文对前面两次微调训练过程中用到的参数配置作个阶段性总结,包括对参数含义的理解和实践作用的探究。不过,这里的参数总结依然只是冰山一角,后面随着训练的深入,相信会有更多更深的理解,届时再对本文进行扩展。