前言
年初,最新一代AI模型GPT-4在2023年3月14日首发。距离上一代ChatGPT-3.5发布还不到4个月,OpenAI的技术储备让业界再度调高了对人工智能发展速度的预期,再次掀起了AI浪潮。这款多模态预训练大模型实现了以下几个方面的飞跃式提升:强大的识图能力;文字输入限制提升至 2.5 万字;回答准确性显著提高;能够生成歌词、创意文本,实现风格变化。也再次见证了大模型的可行性和无限性,业界有人称之为“ChatGPT革命”。2024年2月苹果放弃造车转投AI。当时,苹果公司首席执行官库克在年度股东大会上表示,计划在今年晚些时候披露更多关于使用生成人工智能的计划,进一步加入这场行业热潮中。在此之前,有报道称苹果已经决定放弃了已经持续10年的汽车开发项目,相关人员将部分调整至人工智能研发部门。OpenAI于2024年2月16日宣布推出的全新生成式人工智能模型Sora。通过文本指令,Sora能够直接输出长达60秒的视频,并包含高度细致的背景、复杂的多角度镜头,以及富有情感的多个角色。这意味着,继文本、图像之后,OpenAI将其先进的AI技术拓展到了视频领域。简单来说,Sora可以实现一句话生成长达60秒的精美逼真视频。
大语言模型微调已经不再是什么高深的技术了,笔者有幸参与过chatglm和百川2等的特定任务微调,在此分享经验和一些整理汇总的资料,旨在互相学习互相进步,较适合刚入门的同学阅读。本文主要阐述微调原理和各参数的意义以及如何进行微调,可以快速上手微调任务。我是小马过河R,还可以来公众号【贝可林】私信我。
一、为什么需要微调?
我们先来看看 混元大模型是怎么回答这个问题的。
微调(Fine-tuning)是一种在预训练模型的基础上进行后续训练的方法,以适应特定的下游任务。微调的主要原因是:
1. **降低训练成本**:大模型的参数量通常非常大,训练成本非常高。通过微调,可以在预训练好的模型上进行后续训练,而不是从零开始训练一个大型模型,从而降低训练成本。
2. **提高推理效率**:Prompt Engineering 是一种使用大模型的方式,但它的缺点是推理成本随着Prompt长度的增加而增加。微调可以降低推理成本,因为只需要调整部分参数,而不是整个模型。
3. **提升模型性能**:微调可以利用预训练模型已经学习到的语言知识,从而更快地收敛并获得更好的性能。
4. **适应特定任务**:通用大模型可能无法解决所有问题,尤其是行业内的专业问答和关于某个组织自身的信息。通过微调,可以使用特定的数据集对合适的基础模型进行微调,以完成特定的任务、回答特定的问题等。
5. **数据安全**:如果数据不能传递给第三方大模型服务,那么搭建自己的大模型就非常必要。通常这些开源的大模型都是需要用自有数据进行微调,才能够满足业务的需求,这时候也需要对大模型进行微调。
微调的过程通常包括以下几个步骤:选择一个预训练好的语言模型,添加新的输出层以适应下游的具体任务,使用下游任务的数据集对模型进行训练,以及微调后的模型可以直接应用到下游任务中。
怎么理解呢?
可以通俗理解为类似百川,chatglm等大模型都是属于预训练模型,通常我们开源拿到的都是使用通用数据训练出来的模型,相当于只学习了基本的知识,我们俗话叫做基本常识。如果你是名骨科医生,你希望训练一个大模型机器人拥有专业的骨科领域知识来为客户服务。那么就要基于预训练模型(基座模型)使用自己的大量专业领域数据来训练它,使它能够学习到专业知识,此时大模型才能拥有垂直领域知识。如果你还希望大模型在拥有了骨科领域知识下,还能根据骨科领域知识编写一些笑话,那么也是需要特定的训练数据来训练它,使它能够具备你想要的能力,这就是特定任务的微调。
预训练大模型就像一名已经认识了字的学生一样,你需要他会数学就微调他数学知识,需要他会语文就微调他语文本就好了。如果不采用微调而是从0开始自己训练一个大模型就相当于还要从教这个学生认字开始,这要耗费大量的成本。
二、什么是微调?
微调是做什么
大模型微调的基本原理是通过使用特定领域的数据集对已预训练的大模型进行进一步训练,以优化模型在特定任务上的性能。微调的核心原因是赋予大模型更加定制化的功能,使其能够更好地适应特定领域的需求和特征。通过引入特定领域的数据集进行微调,大模型可以学习该领域的知识和语言模式,从而在特定任务上取得更好的性能。
微调的步骤包括数据准备、选择基础模型、设置微调参数和微调流程。在微调流程中,需要加载预训练的模型和权重,根据任务需求对模型进行必要的修改,选择合适的损失函数和优化器,并使用选定的数据集进行微调训练。
总之,大模型微调是一种有效的提高模型在特定任务上性能的方法,通过使用特定领域的数据集对已预训练的大模型进行进一步训练,可以使其更好地适应和完成特定领域的任务。
理论原理
从某种意义上讲,大模型的内核都是Transformer。

比如BERT用的是Encoder,chatgpt用的是Decoder。

有人是这么形容微调的,微调就是把已经训练好的模型拿来给它吃特定的下游任务数据,使得模型在预训练权重上继续训练,直至满足下游任务性能标准。预训练模型就像一个特征提取器,能够基于先前训练数据中学到的经验,为我们提取有效的特征,大大提升下游任务的训练效果和收敛速度。
模型训练中模型参数量和数据量之间的关系

微调有哪些方法
我们一般微调使用的是参数高效微调方法(Parameter-efficient fine-tuning,PEFT),很少直接全参数微调。

比如下图,Lora高效微调就是只动到了部分层的参数权重,其他不动。细心的同学如果有接触过chatglm P-tuning v2或者看过模型原理的可能都有印象,微调时有时候还需要自己选择是否冻结某个层的参数权重。

微调(Fine-tuning)的方法有哪些?
大体可以归结为:全参数微调和高效参数微调。
1、全参数微调
2、超参数(高效参数)微调:
Adapter
P-tuning v2
Lora
Qlora
Prefix-tuning
Prompt-tuning
Freeze
Instruction-Tuning (指令微调)
chain-of-thought

对微调理论原理有较高兴趣的同学建议可以进一步参看。
你似乎来到了没有知识存在的荒原 - 知乎 (zhihu.com)
通俗解读大模型主流微调方法:从Prefix Tuning、P-Tuning V1/V2到LoRA、QLoRA - 知乎 (zhihu.com)
图解大模型微调系列之:大模型低秩适配器LoRA(原理篇) - 知乎 (zhihu.com)
当红炸子鸡 LoRA,是当代微调 LLMs 的正确姿势? - 知乎 (zhihu.com)
不是大模型全局微调不起,只是LoRA更有性价比,教程已经准备好了 (baidu.com)
3、模型的精调方法:Zero-shot、Few-shot。
微调方法种类介绍
收集了部分资料供大家参考。
1)Adapter:在self-attention和FFN后加一些全连接层
2)P-tuning v2:每一层增加一些虚拟词
3)Lora:对于每个linear层额外添加一个分支,这个分支在训练完之后可以合并到原始的矩阵中,不影响推理速度
4)Qlora:对模型参数进行进一步量化,在效果不下降的前提下进一步减少模型显存占用。Qlora论文:https://arxiv.org/pdf/2305.14314.pdf
5)Prefix-tuning:Prefix-tuning的目标是通过对模型的输入进行微调
Prompt-tuning:Prompt-tuning是一种基于预训练模型的微调方法,它通过在输入层添加Prompt tokens来引导模型生成特定类型的响应。虽然Prompt-tuning不需要像Fine-tuning那样对整个模型进行微调,但仍然需要对模型进行微调,以便适应新的任务和数据分布。在Prompt-tuning中,微调的主要目标是优化Prompt tokens的参数,以使模型能够更好地理解和生成所需的输出。
优化Prompt tokens的参数是指在Prompt-tuning过程中,通过对Prompt tokens的参数进行调整和优化,以使模型能够更好地理解和生成所需的输出。这些参数可以包括Prompt tokens的权重、位置、格式等,具体取决于模型的结构和任务的需求。通过调整这些参数,可以影响模型对输入的理解和生成输出的方式,从而改善模型的性能和应用的灵活性。优化Prompt tokens的参数通常需要使用优化算法,如梯度下降、随机梯度下降等,通过迭代和调整参数来最小化预测错误或损失函数,以获得最佳的模型性能。
6)Freeze微调:是一种在深度学习中常用的技巧,主要用于在训练过程中防止模型过拟合。具体来说,Freeze微调的核心思想是在训练过程中,冻结一部分网络参数不进行更新,只更新另一部分参数。这样可以在保持模型性能的同时,防止过拟合
)
7)Instruction-Tuning:(需要模型微调)Prompt和Instruction都是用于指导模型生成输出的文本,但它们的目的和使用方式是不同的。Prompt更多地用于帮助模型理解任务和上下文,而Instruction则更多地用于指导模型执行具体操作或完成任务。

指令微调的动机是提高语言模型对自然语言处理指令的响应能力。这个想法是,通过使用监督来教授语言模型执行通过指令描述的任务,模型将学会遵循指令,即使是对于未见过的任务也能如此。
通过 instruction-tuning,GPT-4 模型能够根据输入文本生成对应的情感标签,从而实现情感分类任务。
指令微调可以被视为有监督微调(Supervised Fine-Tuning,SFT)的一种特殊形式。
8)chain-of-thought:
Chain-of-thought微调是一种使用逐步推理生成输出指令的方法。这种方法使用带有逐步推理的人类注释的指令数据集来对模型进行微调。与预训练数据相比,只需要非常小的一部分数据来对指令进行微调。此外,监督式微调使用人工标注使模型输出更安全和更有帮助。
Chain-of-thought技术可以应用于多种任务,包括常识推理、算术推理和符号推理等。使用Chain-of-thought进行微调的模型在涉及这些任务时表现得更好,并且对实现无害性非常有效。此外,Chain-of-thought还可以通过自洽性、LtM提示和Fine-tune-CoT等技术来提高性能。

Prompt-tuning、instruction-tuning和chain-of-thought都是用于训练大型语言模型的方法,它们都有助于提高模型的生成能力和上下文理解能力,但是它们的方法和目的略有不同。
Prompt-tuning:Prompt-tuning是一种使用自然语言提示(prompt)的方法,以指导模型生成特定的输出。这种方法的目的是通过对模型进行定向训练,使其在特定任务上表现出更好的性能。与其他方法不同,Prompt-tuning的重点在于设计良好的提示,这些提示可以引导模型生成准确、上下文相关的响应。
Instruction-tuning:Instruction-tuning是一种通过为模型提供任务相关的指令来指导模型学习的方法。这种方法的目的是使模型更好地理解任务的要求,并提高其生成能力和上下文理解能力。Instruction-tuning通常需要较少的训练数据,并且可以提高模型的泛化性能。
Chain-of-thought:Chain-of-thought是一种通过分解训练过程为较小的相互关联的任务来训练模型的方法。这种方法的目的是使模型能够理解和维护文本中的思维链,从而生成连贯的、上下文相关的响应。与其他方法不同,Chain-of-thought的重点在于将训练过程分解为一系列逐步更复杂的任务,并使用注意机制来帮助模型集中于相关的部分。
总之,这些方法都有助于提高大型语言模型的生成能力和上下文理解能力,但是它们的方法和目的略有不同。Prompt-tuning和instruction-tuning通常用于特定任务的优化,而Chain-of-thought通常用于提高模型的生成能力和上下文理解能力。
精调方法(注意有些模型仅支持部分方法):
9)Zero-shot(0样本): prompt engineer。(prompt引导)
Zero-shot是一种更为极端的预训练范式,它试图将预训练的语言模型直接用于各种不同的任务,而不需要对模型进行微调。
10)Few-shot(小样本):构造几条示例,让模型按照示例的规范回答。
也被称为Prompt学习,是一种在机器学习中处理少量训练样本的方法。这种方法的主要目标是让模型能够在没有大量标注数据的情况下,快速地学习和适应新的任务。Prompt学习通过将输入数据与一个预先定义好的提示(Prompt)结合起来,引导模型生成正确的输出。
11)Prefix-tuning:主要应用于自然语言处理任务中,通过对模型输入进行微调来提高模型性能和泛化能力,而Few-shot则是在数据稀缺的情况下,通过少量的标注数据进行学习和泛化,以实现模型的高效训练和应用。
其他参考资料:
实际案例说明AI时代大语言模型三种微调技术的区别——Prompt-Tuning、Instruction-Tuning和Chain-of-Thought | 数据学习者官方网站(Datalearner)
大语言模型的指令微调(Instruction Tuning)最全综述:从数据集到技术全解析_大模型指令微调-CSDN博客
三、怎么微调?
高效微调一般都会有官方的微调教程,但通常也是需要提前掌握微调通用的一些基本知识原理和术语。
1、微调整体流程
常见的模型微调的流程包含以下步骤:
1. **数据准备**:收集与微调任务相关的数据,并进行必要的预处理,例如数据清洗和数据增强等,包括清洗、分词、编码等。。
2. **选择基础模型**:选择一个预训练好的大语言模型,如BERT、GPT-3等。
3. **设置微调参数**:设定学习率、训练轮次(epochs)、批处理大小(batch size)等超参数。根据需要设定其他超参数,如权重衰减、梯度剪切等。
4. **微调流程**:加载预训练的模型和权重。根据任务需求对模型进行必要的修改,如更改输出层。选择合适的损失函数和优化器。使用选定的数据集进行微调训练,包括前向传播、损失计算、反向传播和权重更新。
5. **评估与验证**:使用验证集检验模型性能,并根据需要调整超参数。采用准确率、F1分数、ROC AUC等指标对模型性能进行评估。
6. **测试**:在测试集上运行微调后的模型以评估其泛化能力。
7. **部署**:将微调后的模型部署到生产环境,在实际应用中使用。
8. **监控与维护**:持续监控模型性能,确保其稳定性和准确性。随着时间推移可能需要进行再微调或全面更新来应对数据漂移。
微调的流程本质上其实和 模型训练的流程大同小异,本质上目的也是在使模型收敛。
2、微调基本知识掌握
这里默认已经掌握了机器学习入门基本知识。
模型微调可以调整的参数包括:
1. 学习率(Learning Rate):学习率是优化算法中最重要的超参数,它决定了模型在训练过程中参数更新的速度。选择合适的学习率对于模型的收敛和性能至关重要。
2. 批次大小(Batch Size):批次大小是每次训练迭代中用于计算梯度的样本数量。较大的批次大小可能会加快训练速度,但可能导致内存不足,而较小的批次大小可能会使训练速度变慢,但有助于泛化。
3. 优化器(Optimizer):优化器是用于更新模型参数的算法,如梯度下降、Adam、RMSprop等。不同的优化器可能会影响模型的收敛速度和性能。
4. 模型架构(Model Architecture):模型架构包括层数、每层的神经元数量、激活函数等。这些参数可以根据任务需求和计算资源进行调整。
5. 正则化(Regularization):正则化是一种防止过拟合的技术,如L1和L2正则化、Dropout等。通过调整正则化参数,可以控制模型的复杂度,提高泛化能力。
6. 训练轮数(Epochs):训练轮数是模型在整个训练集上进行迭代的次数。增加训练轮数可以提高模型的性能,但过多的训练轮数可能导致过拟合。
7. 数据增强(Data Augmentation):数据增强是一种通过对原始数据进行变换以生成新的训练样本的方法,如旋转、翻转、缩放等。通过调整数据增强策略,可以增加模型的泛化能力。
8. 其他超参数:此外,还有许多其他超参数,如学习率衰减策略、权重初始化方法、激活函数等,可以根据任务需求和实验结果进行调整。
1)术语解释
模型微调涉及到的知识可大可小,细到能与训练模型相对标。这里主要介绍微调时对微调结果起大局走势影响作用的知识点,当然细调的影响因素知识点也是必不可少的,由于内容过多这些由后面的章节再展开。
学习率(Learning Rate)
在机器学习和统计学中,学习率是优化算法中的一个调整参数,它决定了在朝着损失函数最小值移动的每次迭代时的步长。作为监督学习以及深度学习中重要的超参,其决定着目标函数能否收敛到局部最小值以及何时收敛到最小值。合适的学习率能够使目标函数在合适的时间内收敛到局部最小值。
学习率图解
我们以梯度下降为例来看下学习率:

这是来自吴恩达机器学习课程的截图
上面的公式中θ代表权重参数,新的θ会由之前的θ计算得来,这个计算过程就是为了寻找目标函数收敛到最小值。那么公式中出现的α系数就是当下的学习率(步长)。y轴可以理解为每次训练更新权重后的loss值。
上面两张明显的对比图就给出了学习率过大和过小的情况。
学习率设置过小的时候,每步太小,下降速度太慢,可能要花很长的时间才会找到最小值。
学习率过大的时候,每步太大,虽然收敛得速度很快,可能会像图中一样,跨过或忽略了最小值,导致一直来回震荡而无法收敛。(如下图:如果对照指high learning rate 绿线的情况,还没到very high learning rate黄线这种非常过大loss已经反向上升的程度)
这时我们再通过Loss曲线看一下不同学习率对收敛得的影响。

很明显,过小的学习率导致收敛缓慢,loss慢慢下降,过大的学习率一开始收敛速度很快,但会导致无法收敛到最小值((high),超高的学习率甚至可能直接大幅跨过了最小值而没有收敛效果(very high)。
详细的资料还可以参看:
学习率(Learning rate)的理解以及如何调整学习率 - EEEEEcho - 博客园 (cnblogs.com)
学习率设置太大或者太小会有哪些影响?_学习率过大-CSDN博客
学习率有什么作用与影响?
学习率不仅影响收敛的时间,还影响收敛的位置。
学习率决定了模型在每次更新权重时所做出的改变大小。如果学习率过大,可能会导致模型在训练过程中出现震荡或者无法收敛;如果学习率过小,可能会导致模型训练速度过慢,收敛时间过长。
因此,正确地选择学习率非常重要,它决定了模型训练的效果和速度。
学习率的选择

学习率的选择通常是通过实验和经验来确定的,不同的模型和数据集可能需要不同的学习率。一般来说,学习率的选择需要考虑以下几个因素:
- 模型复杂度:如果模型比较复杂,需要调整的参数比较多,那么学习率可以适当大一些;如果模型比较简单,参数比较少,那么学习率可以适当小一些。
- 数据集大小:如果数据集比较大,那么学习率可以适当小一些;如果数据集比较小,那么学习率可以适当大一些。
- 损失函数性质:如果损失函数比较平滑,梯度比较稳定(low learning rate),那么学习率可以适当大一些;如果损失函数比较崎岖,梯度比较不稳定,那么学习率可以适当小一些。
训练过程中学习率一般分为固定学习率和变化的学习率两种情况情况。在机器学习中,通常会使用学习率衰减(Learning Rate Decay)的方式来逐渐减小学习率,以帮助模型更好地收敛。常用的学习率衰减方法包括:每隔一定数量的迭代次数减小一定比例的学习率、使用指数衰减等。(Pytorch中有提供了六种学习率调整策略)
总之,选择合适的学习率是机器学习中一个非常重要的问题,它直接影响到模型的训练效果和收敛速度。在实际应用中,需要通过实验和经验来不断调整和优化学习率的设置。

值得注意的是:经验得出,整体趋势是如上描述的这样,但从细粒度来看loss曲线,随着epoch增加loss值都是上下振动的,即是在振动中趋于某个方向而不是平滑。
好的学习率随着迭代次数epoch的增加, loss下降的梯度(loss曲线斜率)是平滑的,而且loss最后能达到最小的值(不断收敛)(见上图梯度下降)。理想的学习率应该是随着训练的轮次,loss缓慢梯度下降。(一般loss收敛到最小值则成功,无限接近0)
一般情况下,学习率设置建议前期大后期小,如果学习率为0时一般训练会停止。(一般很好设置学习率不断增大的策略,甚至在微调过程中,学习率不断上升通常有可能是不正常的。)
想进一步深究的可以参看:https://www.bilibili.com/video/BV1jS4y1n7D9/?spm_id_from=333.337.search-card.all.click&vd_source=291c6ed6d2226d27249b268f2f34b2f0

当然也有整个过程中学习率一直保持恒定不变的情况,需要根据实际场景而定。
超参数微调的时候,设置的Learning Rate参数值一般是作为训练开始时的学习率的初始值,当然还要结合学习率衰减策略来分析,在某些策略下该值可能是中间值。比如,衰减策略是constant_with_warmup预热后恒定,Learning Rate值甚至的是 1e-5。则训练中学习率一般会先从一个随机值缓慢升到1e-5(预热结束),然后保持1e-5不变。
选择设置初始学习率的时候,可以尝试找到那个使loss下降最快的那个学习率值,这点从loss曲线可以很容易很直观地比较分析出来。(可能是深度学习中最重要的超参数:学习率 - 知乎 (zhihu.com))
有同学根据经验提出,在模型微调中,初始化学习率的建议是从较小的值开始进行试验。一个常见的初始值是1e-5(即0.00001)(即1*(10的-5次方))。(有些开源模型有示例的初始值设置参考)。然后,您可以根据需要调整学习率的大小。您可以使用学习率调度策略,如学习率衰减,以在训练过程中自动调整学习率。学习率可以根据模型训练情况进行调整,通常采用学习率衰减的方式进行调整。学习率衰减可以在一定训练轮数后将学习率调整为原来的0.1倍,以适应模型训练的变化。学习率衰减率可以根据实际需求进行设置,一般推荐使用0.1或者0.01。如图。

学习率的设置还和训练预料的内容发散程度有关,如果很发散(聊天的训练所默认的学习率是要小于训练广告词的,两者的区别在于,广告词有更多重合性质的prompt,而聊天语料更加发散),那么调小学习率,如何prompt很集中,那么可以在初期调一个相对比较大的学习率。
有人是这么说的,关于如何确定最佳r值,目前还没有比较好的解决方法。最佳 r 值的确定,需要根据每个 LLM 和每个数据集的具体情况,具体问题具体分析。推测 r 值过大将导致过拟和,而 r 值过小,模型可能无法捕捉数据集中多样化的任务。怀疑数据集中的任务类型越多,所需 r 值就越大。例如,如果仅需要模型执行基本的两位数算术运算,那么一个很小的 r 值可能就已经满足需要了。然而,这只是假设,需要进一步的研究来验证。
据说,网络维度变大时需要更低学习率和更多训练步数step。当网络的维度变大时,通常需要更低的学习率和更多训练步数来避免过拟合。这是因为网络的复杂度增加,可能导致权重参数的更新过于敏感,从而导致训练不稳定或者无法收敛。降低学习率可以减缓权重参数的更新速度,从而减小权重参数的波动,提高训练稳定性。同时,增加训练步数可以让模型学习到更多的训练数据,从而提高模型的泛化能力。
学习率变化策略(lr_scheduler_type)
lr_scheduler_type:设置学习率调度器类型,即学习率变化策略。可选值“linear", "cosine", "cosine_with_restarts", "polynomial","constant", "constant_with_warmup"。
具体作用在学习率的介绍中已有说明。
深度学习学习率调整方案如何选择? - 知乎 (zhihu.com)
批量大小(Batch Size)
训练轮数(Epochs)
步数(Step)
最大训练步数(max_steps)
这几个参数一起来解释。举个例子:有1000张图片,每次训练10张。(每次训练则会更新一次梯度,即更新权重)。
则:
Epoch:轮数。这一千张图片全部训练完成一次即为一个Epoch。

BatchSize:这里的10 即为BatchSize。
Step:(训练一轮样本总数 1000) /(BatchSize 10) = (Step 100),表示要更新多少次梯度(梯度下降中的梯度变化)。
这里是每隔十次就更新一次梯度,也就是将一个batch中的图片一张张送入模型,累加loss求平均,到了第十张图片的时候就开始反向传播。(更新权重)
max_steps:表示最大训练步数,即模型在训练集上的迭代次数,可以理解为训练多少轮。(设置最大训练步数,根据数据集大小适当调整,一般3000-10000步是一个合理范围。)
关于epochs:
在数据样本少的情况下,将epochs设置得较小可能会有更好的效果。这是因为,当数据样本较少时,模型可能会出现过拟合的情况,而较小的epochs可以减少模型对训练数据的过度训练,从而降低过拟合的风险。然而,需要注意的是,如果epochs设置得过小,可能会导致模型无法充分学习到数据中的模式,从而影响模型的性能。因此,在设置epochs的值时,需要根据实际情况进行选择,可能需要通过实验来找到最佳的epochs值。
关于batch_size:

batch_size取值过大有可能会导致oom。
batch_size 对训练效果有很大影响。batch_size 是每次训练迭代中用于更新模型权重的一组样本的数量。较大的 batch size 可以加速训练过程,因为更多的样本可以在每次迭代中使用,从而减少训练时间。然而,较大的 batch size 也可能导致以下问题:
1. 内存限制:较大的 batch size 需要更多的内存来存储和处理数据。
2. 训练不稳定:较大的 batch size 可能导致训练过程更加不稳定,因为每次迭代中样本的分布可能会有很大差异。
3. 梯度消失/爆炸:较大的 batch size 可能导致梯度消失或梯度爆炸问题,特别是在使用随机梯度下降(SGD)或其他基于梯度的优化算法时。
因此,选择合适的 batch size 是训练深度学习模型的一个重要方面。通常,较小的 batch size 可能会导致训练时间较长,但可以获得更好的收敛性能和稳定性。然而,在某些情况下,例如使用小数据集或分布式训练,较大的 batch size 可能是必要的。
Batch size对模型训练有什么影响?其大小是怎么选取的?
一般来说,在合理的范围之内,越大的 batch size 使下降方向越准确,震荡越小;batch size 如果过大,则可能会出现局部最优的情况(这也是其中的一个缺点吧)。小的 bath size 引入的随机性更大,难以达到收敛,极少数情况下可能会效果变好。
会影响训练的稳定性,Batch size过小会使Loss曲线振荡的比较大,大小一般按照2的次幂规律选择,这是为了硬件计算效率考虑的。
学习率和batchsize搭配之难?
batch size是否合适(从经验来看感觉是个细微影响微调效果因素)。batch size一般是较大会有比较好的效果,一是更快收敛,二是可以躲过一些局部最优点。但是也不是一味地增加batch size就好,太大的batch size 容易陷入sharp minima,泛化性不好。较小的batch size,类别较多时,可能会使得网络有明显的震荡。batch size增大,处理相同的数据量速度加快;随着batch size增大,达到相同精度的epoch数量变多;因此基于上述两种情况,batch size要调试到合适的数值;过大的batchsize会让网络收敛到不好的局部最优点;过小的batchsize训练速度慢,训练不收敛;具体的batch size需要根据训练集数据内容和数量进行调试。
【总结】batch_size 是每次训练迭代中用于更新模型权重的一组样本的数量。较大的话下降方向越准确,震荡越小,但如果每次样本中分布都存在很大差异,训练过程更加不稳定,太大的batch size 容易陷入sharp minima,泛化性不好,(过大的batchsize会让网络收敛到不好的局部最优点)。较小的有助于泛化,但过小会震荡。
关于Max_steps:
这是指训练过程中模型可以执行的更新次数。每次更新都涉及到前向传播、计算损失和反向传播。一旦达到了max_steps,无论模型是否已经收敛或达到预定的性能指标,训练过程都会停止。这可以防止模型在训练过程中过度拟合训练数据。Max_steps是一个固定的数量,而Epoch则是直到遍历完整个数据集为止。
loss曲线
通常情况下,loss损失函数(小为好)损失越小,模型越好,如果预测值与真实值相等,就是没有损失。如loss=0.9323。但不意味着loss小就一定模型好,还和其他训练结果因数有关,要综合判定。
Q :微调时loss的初始值就很小(在很低的位置),是为什么?

Q :loss下降很快说明什么?

Q :在一段时间内损失函数不下降?

Q :两个训练中的模型,最终loss值一样代表训练效果一样吗?
试验证明不一定,特别是loss振动大的时候效果远差于小的。


实践证明:学习率小需要更多的轮次才能达到学习率大的loss值,虽然最终loss值一样但训练效果还是不一样,学习率小的拟合和泛化表现都不好。观察到 loss曲线中,学习率小的整体过程中每次轮次loss值波动幅度均衡但幅度都差不多,但大的 波动幅度会随着轮次越来越小,波动跨度很小,权重更新更稳定!!似乎也从另一个角度说明:loss 波动情况也是模型效果判断因素之一。
微调中,最终是收敛的,但是loss曲线不好,会影响模型效果吗?实践证明,似乎不同曲线是有区别的,特别是波动幅度。

如图,相对 比较好的 收敛情况(波动越来越小,拟合越来越好)。

如图,说明lr过小且哎出现了loss波动大。

如图,说明lr过大,loss波动大,但lr还没到非常大去导致 loss 上升。
每一步step后的loss变化是模型在不断适应数据,不断调整与数据 拟合(即努力让loss最小)。
Q:如何判断模型是否收敛?

Q :loss在下降,但有振荡,问题大吗?不是问题但也是问题,如果到最后还是大波动的,估计效果就不会好。下面这里也有说到和loss波动也有关系。

Q :微调中最后的loss值越小,效果通常越好?
是的。在机器学习和深度学习中,loss值是用来衡量模型预测结果与真实结果之间的差距的指标。在训练模型时,我们需要通过不断地调整模型参数来最小化loss值,以达到更好的预测效果。如果loss值越小,说明模型的预测结果越接近真实结果,模型的性能也就越好。因此,微调后最终的loss值越小,通常意味着模型的表现更好。
然而,需要注意的是,过度追求低loss值可能会导致过拟合,即模型在训练数据上表现很好,但在未知数据(测试数据)上表现不佳。因此,在评估模型效果时,除了考虑loss值外,还需要考虑模型的泛化能力。
loss降到0.1到1之间最好(但不绝对!!)。通常情况下当loss值低于0.1时,可能会出现过拟合的情况,当loss值在0.1到1之间时,可以更好地避免过拟合的问题,在此前提下loss值越低,表明模型的泛化能力也越强,可以更好地应用到新数据上。Loss是一种衡量模型预测性能的一种指标,它可以用来衡量模型在训练数据上的表现。
换参数多跑几次,就大概清楚这个模型比较好的loss应该是多少了。(当然一个很大的loss可以直接判断陷入局部最优)
Q :神经网络训练时loss是否一定要收敛到0?

Q :与训练不同,微调时有些框架只是展示了训练集的loss而不会体现验证集的loss?
比如chatglm P-tuning v2和百川的Qlora微调,都只能打出模型在验证集上的评估分数。对此,查阅了资料。
要同时查看训练集和验证集的loss,您需要将模型训练分为两个阶段:训练和验证。在训练阶段,您需要记录训练集的loss,而在验证阶段,您需要记录验证集的loss。以下是一个使用Python和Keras库的示例:
1. 首先,安装Keras库(如果尚未安装):
```
pip install tensorflow
```
2. 然后,使用以下代码训练模型并记录训练集和验证集的loss:
```python
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
# 假设您已经准备好了训练集(X_train)和验证集(X_val),以及它们的标签(y_train, y_val)
# 创建一个简单的线性模型
model = Sequential()
model.add(Dense(64, activation='relu', input_shape=(X_train.shape[1],)))
model.add(Dense(32, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
# 编译模型
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# 训练模型
history = model.fit(X_train, y_train, epochs=10, batch_size=32, validation_data=(X_val, y_val))
# 查看训练集和验证集的loss
loss = history.history['loss']
val_loss = history.history['val_loss']
# 绘制loss曲线
import matplotlib.pyplot as plt
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()
```
这段代码首先创建一个简单的线性模型,然后使用训练集和验证集对其进行训练。在训练过程中,它会记录训练集和验证集的loss。最后,它使用matplotlib库绘制loss曲线,以便您可以直观地查看它们。
过拟合
Q :微调后怎么知道训练是过拟合的,常规方法是对比loss曲线?
在微调过程中,可以通过以下几种方式来判断模型是否发生过拟合:
- 观察训练和验证误差:在训练过程中,持续监控模型在训练集和验证集上的误差。如果训练误差持续下降,而验证误差开始上升,那么可能发生过拟合。
- 绘制学习曲线:绘制训练集和验证集的学习曲线,将训练误差和验证误差随训练轮数的变化进行可视化。如果训练误差持续下降(所以过拟合loss也是下降的),而验证误差开始稳定或增加,那么可能存在过拟合。
在训练神经网络或其他机器学习模型时,我们通常会绘制出模型在训练集和验证集上的损失(或误差)随着训练轮次(epoch)的变化。如果模型在训练集上的损失持续降低,但在验证集上的损失开始上升,那么这通常表明模型出现了过拟合。过拟合是指模型在训练数据上表现得过于优秀,以至于它不能很好地泛化到新的、未见过的数据。
过拟合通常表现为训练集上的损失逐渐降低,但验证集上的损失开始增加,即模型在训练集上表现很好但对新数据的泛化能力较差。
在训练过程中,通过观察loss曲线可以判断模型是否出现过拟合或欠拟合情况。如果模型在训练集上的准确率很高,但在测试集上的准确率很低,那么就说明模型出现了过拟合的情况。相反,如果模型在训练集和测试集上的准确率都很低,那么就说明模型出现了欠拟合的情况。
- 使用交叉验证:通过将数据集划分为训练集和验证集,并多次重复随机划分,可以获得更稳定的模型性能评估结果。如果模型在训练集上表现很好,但在验证集上表现较差,那么可能存在过拟合。
- 检查批次最大误差与平均误差的比较:在训练过程中,检查一个epoch的所有批次中误差的最大值与误差的平均值的比较。如果最大值与平均值相差较大,比如两倍以上,那么说明数据在不同的批次上拟合情况差异较大,可能存在过拟合。
如果发现过拟合,可以尝试以下方法来缓解过拟合:
为了避免过拟合,我们可以采取一些策略,如使用更简单的模型、增加训练数据、使用正则化技术(如L1或L2正则化)或早停(early stopping)等。
- 增加数据集的多样性:通过增加数据集的数量或多样性,可以使得模型学习到更多的信息和分布,从而减少过拟合的发生。
- 减少模型的复杂度:如果模型过于复杂,可能会发生过拟合。可以尝试减少模型的层数或神经元的数量等方法来降低模型的复杂度。
- 增加正则化项:正则化项是一种用来限制模型复杂度的手段,可以减少过拟合的发生。可以尝试增加L1正则化或L2正则化等正则化项来缓解过拟合。
- 调整优化器的学习率:如果学习率过高,可能会导致模型在训练过程中来回震荡而无法收敛;如果学习率过低,可能会导致模型训练速度过慢。因此,可以尝试调整优化器的学习率来缓解过拟合。

Q:如果没有验证集loss对比怎么判断过拟合?
其实根据原理训练集表现很好完全拟合不泛化,测试集表现非常差泛化很差。我认为可以直接部署模型服务提问一些 训练集内的Q和训练集外的Q,看下模型的回答效果即可直观分辨。
如果没有验证集,判断微调是否过拟合的方法可能会更加困难。以下是一些可能的方法:
- 观察训练集的loss曲线。如果训练集的loss在初期就非常低,并且继续下降,但下降速度逐渐减缓,那么可能没有出现过拟合。如果训练集的loss在初期较高,但突然在某个点之后大幅下降,那么可能出现了过拟合。(loss突然变得很小比如从4突然0.01,很可能就是开始过拟合了)



- 观察模型在新数据上的泛化能力。如果模型在训练数据上表现很好,但在新数据(测试集)上的泛化能力较差,那么可能出现了过拟合。
- 使用早停法(early stopping)。在每个epoch结束后,用训练集评估模型的性能,如果性能没有提升或者下降了一定次数,就停止训练,并保存最佳的模型。这种方法可以在一定程度上避免过拟合。
- 尝试不同的模型参数。如果发现模型在训练集上的表现异常好,但在测试集上的表现却较差,可以尝试调整模型参数,如减小正则化参数、增加训练时间等,以观察模型性能是否有所改善。
请注意,以上方法仅供参考,实际是否过拟合还需要结合其他指标和方法进行综合判断。如果有条件,建议在训练过程中创建验证集或测试集以更准确地判断过拟合情况。
Q:过拟合的情况下,loss在训练集上是震荡还是持续下降?
在过拟合的情况下,loss在训练集上可能会出现以下两种情况:
如果模型过于复杂(百川2属于复杂模型),可能会导致loss在训练集上突然持续下降(曲线基本都很贴合),但在测试集上表现不佳。这是因为模型过于复杂,过度拟合了训练集的数据,导致模型在训练集上表现很好,但在测试集上无法泛化。
如果模型过于简单,可能会导致loss在训练集上震荡(曲线大部分非常贴合,小部分非常远离),因为模型无法捕捉到数据的复杂性,导致模型在训练集上反复波动。 因此,我们需要在模型选择和训练过程中,找到一个平衡点,使得模型既能捕捉到数据的复杂性,又能避免过度拟合训练集的数据,从而实现更好的泛化能力。
Q:模型微调后泛化能力不行是不是因为过拟合了?
理论上欠拟合也不会有好的泛化能力。

Q:训练数据只过一个epoch,会过拟合吗?
验证集loss曲线基本能说明了。 还怀疑的话随机取几个语料试下泛化能力,应该很快就能判断出来是不是过拟合的。
其他参考资料:
loss下降到非常接近0了,继续训练测试集准确率还会提升吗? - 知乎 (zhihu.com)
训练过程曲线分析:acc/loss/val_acc/val_loss_loss和acc曲线情况-CSDN博客
神经网络调参:loss 问题汇总(震荡/剧烈抖动,loss不收敛/不下降)_loss震荡-CSDN博客
loss问题汇总(不收敛、震荡、nan) - 知乎 (zhihu.com)【推荐微调案例】
GRU训练时,训练集LOSS稳定下降,但测试集LOSS出现振荡,可能原因? - 知乎 (zhihu.com)(可能是数据不均匀导致)
2)关于QLora微调与其他微调
因为主要实践经验是基于QLora微调的,所以这里单独重点介绍QLora微调。
LoRA微调
LoRA微调方式是一种针对大规模预训练模型进行微调的优化方法,其基本思想是在冻结原模型参数的情况下,通过向模型中加入额外的网络层,并只训练这些新增的网络层参数。由于这些新增参数数量较少,这样不仅finetune的成本显著下降,还能获得和全模型微调类似的效果。
具体来说,LoRA基于大模型的内在低秩特性,增加旁路矩阵来模拟全参数微调,是目前最通用、效果最好的微调方法之一,而且能和其它参数高效微调方法有效结合。利用该方法对175B GPT-3微调,需要训练更新的参数量可以小到全量微调参数量的0.01%。
LoRA微调方式可以有效地降低存储和计算成本,同时提高模型的准确性,是解决大规模模型微调问题的创新方法。
Qlora微调
Qlora是一种微调方法,它通过使用低秩适配器(LoRA)进行模型微调,以减少内存的使用。Qlora微调方式具体步骤如下:
- 准备数据集:准备用于微调的数据集,可以是8个指令数据集、多种模型类型(LLaMA、T5)等。
- 预训练模型:使用预训练模型作为微调的基础模型,该模型可以是冻结的4位量化预训练语言模型。
- 添加低秩适配器:在预训练模型的基础上添加低秩适配器(LoRA),以进行微调。
- 反向传播梯度:通过反向传播梯度的方式,将预训练模型和低秩适配器的参数进行优化和学习。
- 微调参数优化:使用QLORA算法对微调过程中的参数进行优化,包括4位NormalFloat(NF4)、双倍量化、分页优化器等创新方法。
- 模型评估:使用测试数据集对微调后的模型进行评估,以确定模型的性能和表现。
Qlora微调方式具有高效、内存使用低、性能优越等特点,可以在单个48GB的GPU上微调65B的参数模型,同时保留完整的16位微调任务性能。
LoRA和QLoRA区别
LoRA微调技术和QLoRA微调技术都占用内存,但具体使用多少内存取决于模型的规模、训练数据的大小以及微调过程中的其他因素。
一般来说,QLoRA微调技术使用的内存可能会更多。这是因为QLoRA微调技术使用高精度权重和可学习的低秩适配器进行模型微调,需要更多的GPU显存和内存来存储这些权重和参数。同时,QLoRA微调技术还需要对更多的参数进行优化和学习,因此需要更多的内存和计算资源。
相比之下,LoRA微调技术使用的内存可能会更少。这是因为LoRA微调技术使用低精度权重进行模型微调,可以显著降低模型的存储需求和计算成本。但是,这并不意味着LoRA微调技术不需要内存,只是相对于QLoRA微调技术而言,LoRA微调技术使用的内存较少。
综上所述,无法简单地回答LoRA和QLoRA微调哪个用的内存更多,因为这取决于多种因素。但是,一般来说,QLoRA微调技术使用的内存可能会更多。
LoRA和QLoRA各有优势,具体选择取决于应用场景和需求。
LoRA微调技术采用低精度权重进行模型微调,可以显著降低存储需求和计算成本。然而,由于低精度权重的使用,这种方法可能会对模型的准确性产生一定的影响。
QLoRA微调技术通过使用高精度权重进行模型微调。与LoRA不同,QLoRA利用可学习的低秩适配器调整预训练模型的权重,从而提高模型的准确性。在使用QLoRA微调技术时,首先将预训练模型量化为int4格式,然后添加一组可学习的低秩适配器权重。这些权重可以通过反向传播梯度进行学习,从而将650亿参数模型的微调内存需求从超过780GB的GPU内存降低到小于48GB。
综上所述,如果更关注模型的准确性和效果,可以选择QLoRA;如果更关注存储需求和计算成本,可以选择LoRA。
也就是说,Qlora准确率会更高,但其使用高精度权重和可学习的低秩适配器进行模型微调,可能需要更多的GPU显存和内存来存储这些权重和参数。
chatGLM模型P-tuning v2微调
官方默认的是P-tuning v2方法微调,亲测相对比其他模型的Lora方法微调参数好调整和好出效果。
注 train.sh 脚本如下:
PRE_SEQ_LEN=128 # soft prompt 长度,P-tuning v2 参数
LR=1e-2 # 训练的学习率,P-tuning v2 参数
CUDA_VISIBLE_DEVICES=0 python main.py \
--do_train \ # 训练
--train_file AdvertiseGen/train.json \ # 训练集地址
--validation_file AdvertiseGen/dev.json \ # 验证集地址
--prompt_column content \ # 训练集中prompt 的key名称【可以理解为输入值的key】
--response_column summary \ # 训练集中response的key名称【可以理解为生成值的key】
--overwrite_cache \ # 是否覆盖 缓存
--model_name_or_path THUDM/chatglm-6b \ # chatglm-6b 模型地址
--output_dir output/adgen-chatglm-6b-pt-$PRE_SEQ_LEN-$LR \ # 模型保存地址
--overwrite_output_dir \
--max_source_length 64 \ # 可能还需要增大 max_source_length 和 max_target_length 来匹配你自己的数据集中的最大输入输出长度(最长序列)--超出自动截断。理论上 ChatGLM-6B 支持无限长的 context-length,但总长度超过 2048 后性能会逐渐下降。过大有可能会oom。
--max_target_length 64 \
--per_device_train_batch_size 1 \
--per_device_eval_batch_size 1 \
--gradient_accumulation_steps 16 \ //从训练数据看1709样本 28轮 最高3000步,
--predict_with_generate \
--max_steps 3000 \
--logging_steps 10 \
--save_steps 1000 \
--learning_rate $LR \
--pre_seq_len $PRE_SEQ_LEN \
--quantization_bit 8 # 模型 量化方式,P-tuning v2 参数
chatGLM-6B 微调使用了一种称为“自适应学习率衰减策略”(Adaptive Learning Rate 衰减策略)的技术。
该技术使用一种称为“正则化”的方法,通过调整训练过程中学习率的衰减速率来优化模型的性能。具体来说,它通过在训练过程中不断检查模型的性能,并根据模型的表现来调整学习率的衰减速率,从而使得模型在训练过程中能够不断适应新的数据,并提高其性能。
这种技术可以有效地减少模型的过拟合风险,提高模型的泛化能力,并且在训练过程中能够更加准确地捕捉数据的特征。


Lora微调理论记录
与QLora微调有借鉴参考意义。
微调参数的概览:
CUDA_VISIBLE_DEVICES=0:单卡运行。
do_train: 是否执行训练。
model_name_or_path: 预训练模型路径。
dataset_dir: 训练数据存储目录。
dataset: 训练数据集名称,可在 data/dataset_info.json 中增加自定义数据集。
output_dir: 微调后的模型保存路径。
source_prefix: 训练时每个输入序列添加的前缀,可为空。
max_source_length: 输入序列的最大长度,即 source_prefix + instruction + input 的长度。
max_target_length: 输出序列的最大长度,即 output 的长度。
per_device_train_batch_size: 用于训练的批处理大小。可根据 GPU 显存大小自行设置。
gradient_accumulation_steps: 梯度累加次数。累积几步/梯度更新一次权重,过大的话梯度更新次数变少,收敛变慢,容易过拟合。梯度累积是一种训练技巧,可以将多个小批量数据的梯度累积起来,从而实现大批量数据的训练。gradient_accumulation_steps 的取值为正整数,表示累积的步数。
logging_steps: 多少步输出一次 log。
save_steps: 多少步保存一次参数。
learning_rate: AdamW 优化器的初始学习率。设置过大会出现loss值无法收敛或过拟合现象即过度适应训练集而丧失泛化能力,对非训练集中的数据失去原本的计算能力。

lr_scheduler_type: 学习率策略,可以设置cosine(月线退火策略)

num_train_epochs: 训练轮数(若非整数,则最后一轮只训练部分数据)。如果loss值没有收敛到理想值可以增加训练轮数或适当降低学习率。
plot_loss: 微调后绘制损失函数曲线,图片保存在 output_dir 中 。
fp16: 使用半精度(混合精度)训练。
lora_target: 大模型内将要进行 LoRA 微调的模块名称。

lora_rank: LoRA 微调中的秩r大小。(内容越垂直这个值需要越大)

padding_side: pad对齐方式,左对齐或者右对齐。
LoraConfig:创建 LoRA 微调方法对应的配置【比较影响模型的效果】
task_type:指定任务类型。如:条件生成任务(SEQ_2_SEQ_LM),因果语言建模(CAUSAL_LM)等。
r:LoRA低秩矩阵的维数。关于秩的选择。
lora_alpha:LoRA低秩矩阵的缩放系数,为一个常数超参,调整alpha与调整学习率类似。
lora_dropout:LoRA 层的丢弃(dropout)率,取值范围为[0, 1)。
target_modules:要替换为 LoRA 的模块名称列表或模块名称的正则表达式。
配置样例如下:
lora_config = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
target_modules = ["attn.c_proj", "attn.c_attn"]
)
经验记录:
1.输入输出序列的长度根据训练集而定,太短被截断,太长效果不太好。
2. lora_target:指哪些层是需要lora微调的(要替换为 LoRA 的模块名称列表或模块名称的正则表达式),一般根据名字找所有层【找不同模型对应的attention层对应的参数名称】。

LLaMa的默认模块名为[q_proj, v_proj],也可以自行指定为:[q_proj,k_proj,v_proj,o_proj]
常用的LLM对应的模块名列表:

3. r秩大小,不是越大越好,一般尝试4,8,16之类的数字看看效果怎样,小数据集一般设置小的r (1,2)。lora_rank(int,optional): LoRA 微调中的秩大小。这里并不是越大越好,对于小型数据集如果r=1就可以达到很不错的效果,即便增加r得到的结果也没有太大差别。调整 LoRA rank 和选择合适的 α 值至关重要。提供一个小技巧,试试把 α 值设置成 rank 值的两倍。
4. batch_size 和 gradient_accumulation_steps 两个参数是协同调整,根据现存和训练快慢需要,一般显存小,就batch_size调小,acc调大(但也不能调太大,不然梯度更新不及时),显存充足batch_size可以调大,acc调小些。
Q: 关于秩Rank参数
图解大模型微调系列之:大模型低秩适配器LoRA(原理篇) - 知乎 (zhihu.com) (好文,解释秩Rank。秩表示的是矩阵的信息量。)
当红炸子鸡 LoRA,是当代微调 LLMs 的正确姿势? - 知乎 (zhihu.com)
不是大模型全局微调不起,只是LoRA更有性价比,教程已经准备好了 (baidu.com) (扩展解释涉及之前讨论过的秩参数 r,以及另一个超参数 α(alpha)---- 结合上一篇看)

Q: 关于gradient_accumulation_steps参数

举例来说,假设原本的batch size=10,数据总量为1000,那么一共需要100 train steps,同时一共进行100次梯度更新。如果显存不足,我们可以通过梯度累积来解决这个问题。假设gradient_accumulation_steps=2,那么新的batch size=10/2=5,我们需要运行两次,才能在内存中放入10条数据,梯度更新的次数不变为100次,那么我们的train steps=200。
输入数据和标签,计算loss,反向传播,计算当前梯度,多次循环步骤,不清空梯度,使梯度累加在已有梯度上。梯度累加就是,每次获取1个batch的数据,计算1次梯度,梯度不清空,不断累加,累加一定次数后,根据累加的梯度更新网络参数,然后model.zero_grad() 清空之前的梯度,为下一波梯度累加做准备。
Gradient Accumulation是一种训练深度学习模型的技术,其通过增加梯度的累积量来模拟增加训练集的大小。这可以帮助模型在有限的计算资源下进行更有效的训练。
Gradient Accumulation的参数之一是gradient_accumulation_steps,它表示在更新模型权重之前要累积多少个梯度。换句话说,gradient_accumulation_steps控制了每次更新模型权重时使用的数据量。
例如,如果gradient_accumulation_steps的值为2,那么模型在每次更新权重时会使用两次采样的数据。这可以帮助模型更好地泛化,因为模型会在更大的数据集上进行训练。
然而,gradient_accumulation_steps的值不能太大,否则可能会增加过拟合的风险。同时,如果gradient_accumulation_steps的值太小,可能会导致模型训练速度变慢。因此,选择合适的gradient_accumulation_steps的值需要根据具体的任务和数据集来确定。
gradient_accumulation_steps对微调效果有很大影响。在深度学习训练中,gradient_accumulation_steps是一个重要的超参数,它决定了模型在每次更新权重之前累积多少梯度更新。这个参数对微调效果有很大影响,因为它可以影响模型的收敛速度和最终性能。
增加gradient_accumulation_steps会减少训练过程中的梯度更新次数,从而减少训练时间。然而,这可能会导致模型收敛速度变慢,因为每次更新的梯度信息较少。此外,过大的gradient_accumulation_steps可能会导致模型过拟合,因为它使用较少的数据进行权重更新。
相反,减少gradient_accumulation_steps会加快模型的收敛速度,因为它进行更多的梯度更新。然而,这可能会导致训练时间变长,并且可能导致模型过拟合。
因此,在微调模型时,需要仔细调整gradient_accumulation_steps,以找到适合特定任务的最佳值。可以通过交叉验证或其他验证方法来评估不同gradient_accumulation_steps下的模型性能,从而找到最佳的超参数设置。
在数据样本少的情况下,gradient_accumulation_steps应该设置得较小。较大的gradient_accumulation_steps会导致训练次数减少,从而影响模型的收敛速度和性能。较小的gradient_accumulation_steps可以使得模型在每次更新时都使用更多的数据,从而提高模型的收敛速度和性能。然而,较小的gradient_accumulation_steps也会导致训练次数增加,从而增加训练时间。因此,需要在训练时间和模型性能之间找到一个平衡。
在数据样本少的情况下,gradient_accumulation_steps的值应该适当减小。这是因为,当数据样本较少时,模型可能会出现过拟合的情况,而减小gradient_accumulation_steps的值可以增加模型对每个样本的利用率,从而降低过拟合的风险。
Q: 如何确定最佳 r 值?
事实上,选择 alpha=2r 确实提供了最优结果。
Q: 如何避免过拟和?
一般来说,较大的 r 更可能导致过拟合,因为 r 决定着可训练参数的数量。如果模型存在过拟合问题,首先要考虑降低 r 值或增加数据集大小。此外,可以尝试增加 AdamW 或 SGD 优化器的权重衰减率,或者增加 LoRA 层的 dropout 值。
在实验中没有探索过 LoRA 的 dropout 参数(使用了 0.05 的固定 dropout 率),LoRA 的 dropout 参数也是一个有研究价值的问题。
Q: rank秩的值怎么设置?



Q: Lora微调过程可以手动修改学习率?


Q: 可以从断点开始训练?




Q: 从Checkpoint断点开始训练Lora模型?
调查了下资料,是这么介绍的。
从Checkpoint断点开始训练Lora模型,你需要使用一个预训练的模型作为起点,然后使用你的数据集进行微调。以下是一个使用PyTorch的示例:
1. 首先,确保你已经下载了Lora模型的预训练权重。你可以从Hugging Face Model Hub下载,例如:
```python
import torch
import torchvision.transforms as transforms
# 下载预训练的Lora模型
model = torch.hub.load('pytorch/vision:latest', 'lora', pretrained=True)
```
2. 接下来,加载你的数据集并进行预处理。例如,如果你使用CIFAR-10数据集,你可以这样做:
```python
# 数据集类
class CIFAR10(torch.utils.data.Dataset):
def __init__(self, root, transform=None, target_transform=None):
super(CIFAR10, self).__init__()
self.root = root
self.transform = transform
self.target_transform = target_transform
self.data = torchvision.datasets.CIFAR10(root, train=True, transform=transform, target_transform=target_transform)
def __len__(self):
return len(self.data)
def __getitem__(self, index):
return self.data.__getitem__(index)
# 数据预处理
transform = transforms.Compose(
[transforms.RandomHorizontalFlip(),
transforms.RandomCrop(32, padding=4),
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
# 加载数据集
train_dataset = CIFAR10('./data', transform=transform)
```
3. 现在,你可以从Checkpoint断点开始训练Lora模型。例如,如果你想从第10个epoch的Checkpoint开始训练,你可以这样做:
```python
# 设置epoch和Checkpoint
start_epoch = 10
checkpoint = 'path/to/your/checkpoint.pth'
# 加载Checkpoint权重
checkpoint = torch.load(checkpoint)
model.load_state_dict(checkpoint['model'])
model.eval()
```
4. 然后,你可以使用你的数据集进行微调。例如,如果你想训练10个epoch,你可以这样做:
```python
# 设置epoch
num_epochs = 10
# 训练循环
for epoch in range(start_epoch, start_epoch + num_epochs):
# 训练循环
for batch_idx, (inputs, targets) in enumerate(train_loader):
# 计算损失
outputs = model(inputs)
loss = loss_fn(outputs, targets)
# 反向传播和优化
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 保存Checkpoint
torch.save({'model': model.state_dict(), 'optimizer': optimizer.state_dict()}, 'path/to/your/checkpoint.pth')
```
这样,你就可以从Checkpoint断点开始训练Lora模型了。请注意,你需要根据你的数据集和任务调整代码。

其他参考资料:
不是大模型全局微调不起,只是LoRA更有性价比,教程已经准备好了 (baidu.com)
以QLora微调百川2为例
题外话,关于百川的微调官方有提供模型的方式:https://github.com/baichuan-inc/Baichuan2。
参数如下:
hostfile=""
deepspeed --hostfile=$hostfile fine-tune.py \
--report_to "none" \
--data_path "data/belle_chat_ramdon_10k.json" \
--model_name_or_path "baichuan-inc/Baichuan2-7B-Base" \
--output_dir "output" \
--model_max_length 512 \
--num_train_epochs 4 \
--per_device_train_batch_size 16 \
--gradient_accumulation_steps 1 \
--save_strategy epoch \
--learning_rate 2e-5 \
--lr_scheduler_type constant \
--adam_beta1 0.9 \
--adam_beta2 0.98 \
--adam_epsilon 1e-8 \
--max_grad_norm 1.0 \
--weight_decay 1e-4 \
--warmup_ratio 0.0 \
--logging_steps 1 \
--gradient_checkpointing True \
--deepspeed ds_config.json \
--bf16 True \
--tf32 True
这个命令是使用deepspeed库进行模型微调(fine-tuning)的命令行参数。各个参数的说明如下:
1. --hostfile:指定主机文件,用于指定训练和推理所需的主机。
2. --report_to:设置为"none"以关闭日志记录。
3. --data_path:指定训练数据的路径。
4. --model_name_or_path:指定预训练模型的名称或路径。
5. --output_dir:指定输出目录,用于存储微调后的模型。
6. --model_max_length:设置输入序列的最大长度。
7. --num_train_epochs:指定训练总周期数。
8. --per_device_train_batch_size:设置训练时每个设备上的批量大小。
9. --gradient_accumulation_steps:设置梯度累积步数,用于优化训练速度。
10. --save_strategy:指定模型保存策略,这里设置为"epoch",表示每次训练一个epoch后保存一次模型。
11. --learning_rate:设置学习率。
12. --lr_scheduler_type:设置学习率调度器类型,这里设置为"constant",表示学习率在整个训练过程中保持不变。
13. --adam_beta1:设置Adam优化器的beta1参数。
14. --adam_beta2:设置Adam优化器的beta2参数。
15. --adam_epsilon:设置Adam优化器的epsilon参数。
16. --max_grad_norm:设置最大梯度范数。
17. --weight_decay:设置权重衰减系数。(网络反向传导进行学习)
18. --warmup_ratio:设置学习率预热比例。
19. --logging_steps:设置日志记录步数。
20. --gradient_checkpointing:设置梯度检查点。
21. --deepspeed ds_config.json:指定deepspeed的配置文件。
- --bf16:设置使用BF16混合精度训练。
23. --tf32:设置使用TF32混合精度训练。
轻量化微调:
代码已经支持轻量化微调如 LoRA,如需使用仅需在上面的脚本中加入以下参数:
--use_lora True
使用 LoRA 微调后可以使用下面的命令加载模型:
from peft import AutoPeftModelForCausalLMmodel = AutoPeftModelForCausalLM.from_pretrained("output", trust_remote_code=True)
Qlora微调我们使用firefly框架: https://github.com/yangjianxin1/Firefly。
微调的超参数
{
"output_dir": "output/firefly-baichuan2-13b_dn9",
"model_name_or_path": "/usr/local/AIDBonlineBack/projectbaichuan2b/ubuntu/Baichuan-13B/models/Baichuan2-13B-Chat",
"train_file": "./data/train1.jsonl",
"num_train_epochs": 2,
"per_device_train_batch_size": 6,
"gradient_accumulation_steps": 2,
"learning_rate": 2e-4,
"max_seq_length": 900,
"logging_steps": 10,
"save_steps": 500,
"save_total_limit": 1,
"lr_scheduler_type": "constant_with_warmup",
"warmup_steps": 1000,
"lora_rank": 64,
"lora_alpha": 16,
"lora_dropout": 0.05,
"gradient_checkpointing": true,
"disable_tqdm": false,
"optim": "paged_adamw_32bit",
"seed": 42,
"fp16": false,
"report_to": "tensorboard",
"dataloader_num_workers": 5,
"save_strategy": "steps",
"weight_decay": 0,
"max_grad_norm": 0.3,
"remove_unused_columns": false
}
这是一个JSON格式的配置文件,用于训练一个深度学习模型。以下是对该配置文件中各个参数的说明(基本与全量微调的参数一致,几个较为特殊):
- output_dir: 训练结果的输出目录。存储checkpoint、tokenizer、tensorboard等。
- model_name_or_path: 预训练模型的名称或路径。预训练模型的本地目录,或者在huggingface上的模型名称。
- train_file: 训练数据文件的路径。训练数据集路径。可以使用data/dummy_data.jsonl进行debug。
- num_train_epochs: 训练轮数,通常设置为一个整数,表示模型在训练集上循环训练的次数。此处未给出具体值,需要用户自行设定。如果数据量足够大,一般建议只训一个epoch。
- per_device_train_batch_size: 每个设备上的训练批量大小(batch_size 一个更新一次权重)。这通常是指GPU或TPU上的批量大小。此处设置为6。每张显卡的batch size。
- gradient_accumulation_steps: 梯度累积步数。表示累积的步数。它表示在更新模型权重之前要累积多少个梯度。此处设置为2。global batch=num_gpus * per_device_train_batch_size * gradient_accumulation_steps。
- learning_rate: 学习率。控制优化器在更新权重时的步长。此处设置为2e-4。qlora中的学习率设置更大一些,一般为1e-4、2e-4。全量参数微调的时候,建议小一些,1e-5或5e-6。
- max_seq_length: 输入序列的最大长度。这通常取决于特定的任务和数据集。此处设置为900。训练时的最大长度。按照自己的设备进行设置,越长需要占用越多显存。
- logging_steps: 每隔多少步打印一次日志。此处设置为10。每隔多少步统计一次train loss。
- save_steps: 每隔多少步保存一次模型。此处设置为500。
- save_total_limit: 保存模型的总限制。如果设置为1,则最多保存1个模型。此处设置为1。output_dir目录中最多保存多少个checkpoint,超出则会将最旧的删除。
- lr_scheduler_type: 学习率变化策略。学习率调度器的类型。此处设置为"constant_with_warmup",表示在预热阶段使用逐渐增加的学习率,之后保持恒定。先缓慢上升(指定上升步数)到指定配置的值后保持恒定。lr_scheduler_type 学习率的调整方式,可选“linear", "cosine", "constant_with_warmup","cosine_with_restarts", "polynomial","constant"。
1. "linear": 简单线性调整。在训练过程中,学习率从初始化值逐渐增加到最大值。
2. "cosine": 双曲正弦调整。在训练过程中,学习率在训练轮数内呈指数衰减,衰减速度从快到慢,然后又从慢到快。
3. "cosine_with_restarts": 双曲正弦调整,但每次迭代后重置学习率。这样可以避免在训练初期学习率过大,导致模型收敛速度过快。
4. "polynomial": 指数调整。学习率在训练过程中呈指数衰减。
5. "constant": 常数学习率。学习率在整个训练过程中保持不变。
6. "constant_with_warmup": 常数学习率,但有一个预热阶段。在预热阶段,学习率逐渐增加到最大值,然后保持不变。
- warmup_steps: 预热步数,即在训练开始时逐渐增加学习率的步数。此处设置为1000。学习率经过多少步,增长到指定的数值。

- lora_rank: Lora矩阵的秩。Lora是一种自注意力机制,用于解决长距离依赖问题。此处设置为64。qlora矩阵的秩。一般设置为8、16、32、64等,在qlora论文中作者设为64。越大则参与训练的参数量越大,一般来说效果会更好,但需要更多显存。
- lora_alpha: Lora中的alpha参数。控制Lora矩阵的稀疏性。此处设置为16。qlora中的缩放参数。一般设为16、32即可。
- lora_dropout: Lora中的dropout概率。此处设置为0.05。lora权重的dropout rate。
- gradient_checkpointing: 是否使用梯度检查点。这是一个提高模型稳定性和效率的技术。此处设置为true。如果显存捉襟见肘,可以开启。以时间换空间,模型不缓存激活状态,会进行两次forward计算,以节省显存。
- disable_tqdm: 是否禁用tqdm进度条。tqdm是一个快速、可扩展的Python进度条库,它可以显示训练过程中的进度信息。此处设置为false。
- optim: 优化器的名称或类型。此处设置为"paged_adamw_32bit",表示使用32位版本的AdamW优化器。如果是全量参数微调,建议使用adamw_hf。
- seed: 随机种子,用于保证实验的可重复性。此处设置为42。随机种子,用于复现实验结果。
- fp16: 是否使用半精度浮点数(FP16)。这可以提高内存利用率和计算速度,但可能会降低精度。此处设置为false。使用fp16混合精度。V100建议开启。
- bf16: 使用使用bf16混合精度。A100建议开启。
- report_to: 向哪个组件报告损失和指标信息,例如"tensorboard"表示向TensorBoard报告。此处设置为"tensorboard"。
- dataloader_num_workers: 数据加载器的线程数,通常用于提高数据加载速度。此处设置为5。
- save_strategy: 保存模型的策略,例如"steps"表示每隔一定步数保存一次模型。此处设置为"steps"。
- weight_decay: 正则化项的系数,用于防止过拟合。此处设置为0,表示不使用正则化项。
- max_grad_norm: 最大梯度范数,用于控制梯度的范数,避免梯度爆炸问题。此处设置为0.3。
其他参考资料:
微调百川Baichuan-13B保姆式教程,手把手教你训练百亿大模型 (qq.com)
官方提供的理想微调曲线参考:

微调过程我们可以使用tensorboard查看loss曲线。
Q: Qlora微调的时候数据量很少应该怎么设置微调参数比较好?
当进行Qlora微调时,如果数据量很少,以下是一些建议来设置微调参数:
- 批量大小(Batch Size): 降低批量大小可以提高模型的适应性,但同时也可能导致模型训练的不稳定。尝试使用较小的批量大小,例如32或64。
- 学习率(Learning Rate): 如果数据量少,可能需要更精细的调整学习率。尝试使用较小的学习率,例如0.001或0.0001。同时,你也可以使用学习率衰减策略来提高模型性能。
- 早停(Early Stopping): 如果你发现模型在训练过程中出现过拟合,可以尝试使用早停。这种方法会在一定数量的验证集迭代后,如果模型的性能没有提高,就停止训练。这可以防止模型过度拟合训练数据。
- 正则化(Regularization): 在数据量少的情况下,正则化可以帮助防止模型过拟合。你可以尝试增加正则化的强度,例如增加L1正则化或L2正则化的系数。
- 增加数据增强(Data Augmentation): 如果可能的话,可以尝试使用数据增强来增加训练数据。这可以通过旋转、缩放、平移等方式来增加训练样本。
- 模型复杂度: 如果你的模型太复杂,它可能会在训练集上表现得很好,但在测试集上表现得很差。如果数据量小,建议使用更简单的模型。
- 集成方法(Ensemble Methods): 如果可能的话,可以使用集成方法来提高模型的泛化能力。例如,你可以训练多个模型并将它们的预测结果结合起来。
- 调整优化器: 对于一些特定的任务和数据集,可能需要调整优化器的参数以获得最佳性能。例如,对于一些需要更多时间进行训练的任务,可能需要增加优化器的最大迭代次数。
最后,请注意,对于任何机器学习任务,都需要进行多次实验和调整参数才能获得最佳结果。以上只是一些建议,具体设置还需要根据你的任务和数据进行调整。
Q: Qlora微调少量数据集学习率变化策略选cosine还是linear?
从经验上,少数据集下cosine(先大后小)比linear(从小直线上升)好。
cosine:

sine:

其他参考资料:
深度学习知识点总结九(模型训练技巧) - 知乎 (zhihu.com)
3)训练微调其他知识点
Q:反向传播算法?
反向传播算法可以比喻为“猜迷游戏”。
首先,我们有一个“迷宫”,这个迷宫代表了神经网络。我们不知道正确的路径是什么,但是我们知道每一步应该尽量走得离“目标”更近。在训练神经网络时,我们输入数据,然后计算输出结果和实际结果之间的误差。这个误差就像是我们偏离了正确的路径的“距离”。
接着,我们将这个误差反向传播到“迷宫”的前一层。这就像是我们根据误差调整我们的步伐,使得我们下一次的移动更加接近正确的路径。我们会根据每个节点对误差的贡献大小来调整其权重,使得下次当我们再次经过这个节点时,我们的步伐会更接近正确的方向。
这个过程会不断重复,直到我们的误差变得足够小,或者我们已经走到了“目标”。这时候,我们就成功地训练了我们的神经网络,让它能够越来越准确地预测结果。
所以,反向传播就像是在“迷宫”中不断地调整我们的步伐,直到找到正确的路径。
Q:梯度消失和梯度爆炸?
梯度消失(vanishing gradient)和梯度爆炸(exploding gradient)是深度学习中训练神经网络时常遇到的两个问题。由于神经网络中的权重和偏置是通过梯度下降(gradient descent)算法进行更新的,因此梯度的大小和方向对于网络的训练至关重要。
梯度消失问题:在训练深度神经网络时,梯度可能变得非常小,以至于权重更新变得微不足道。这会导致网络学习速度变慢,甚至完全停止。梯度消失问题通常出现在使用激活函数(如 Sigmoid 或 Tanh)和权重初始化方法(如随机初始化)时。为了解决这个问题,可以使用更合适的激活函数(如 ReLU)和权重初始化方法(如 Xavier 或 He 初始化)。
梯度爆炸问题:与梯度消失问题相反,梯度爆炸是指在训练过程中梯度变得非常大,导致权重更新过大,从而使网络学习变得不稳定。梯度爆炸问题通常出现在权重初始化不当(如使用较小的随机数初始化)或者网络结构过深的情况下。为了解决这个问题,可以使用梯度裁剪(gradient clipping)方法来限制梯度的范围,或者使用更合适的权重初始化方法。
Q:数据集数据分布?

Q:什么时候要使用交叉验证?
交叉验证通常在以下情况下使用:
- 数据集较小:当训练数据集相对较小时,使用交叉验证可以有效利用数据,并给出更准确的模型性能评估。
- 需要选择最佳模型和参数:交叉验证可以通过多次重复验证,评估不同模型和参数的准确性,从而选择在验证集上表现最好的模型和参数。
- 需要比较不同算法性能:在选择最佳的机器学习算法时,交叉验证可以帮助比较不同算法的性能,并评估它们的准确性和稳定性。
- 时间序列预测:对于时间序列数据,由于其具有时间顺序的特点,使用交叉验证时需要特别注意数据的划分方式,以避免未来信息泄露。
- 集成学习:在集成学习中,交叉验证可以用于评估单个模型的性能以及集成后的性能,帮助选择合适的基模型和集成方法。
- 需要进行模型泛化能力评估:交叉验证可以帮助评估模型的泛化能力,避免过拟合和欠拟合问题。
总之,当需要准确评估模型性能、选择最佳模型和参数、比较不同算法性能、时间序列预测、集成学习或进行模型泛化能力评估时,可以考虑使用交叉验证。
交叉验证 具体举个例子?
假设我们有一个数据集,其中包含100个样本,每个样本有3个特征和一个标签。我们想通过这些数据来训练一个分类器。
首先,我们将整个数据集随机分成10个子集,每个子集包含10个样本。然后,我们进行10次交叉验证,每次使用一个子集作为测试集,其余9个子集合并为训练集。
在每次交叉验证中,我们使用训练集对分类器进行训练,然后使用测试集对分类器进行测试,并记录分类器的准确率。这样,我们得到了10个准确率,取它们的平均值作为最终的分类器性能指标。
通过交叉验证,我们可以更准确地评估分类器的性能,因为每个子集都充当一次测试集,使得训练集和测试集的数据分布尽可能一致。同时,交叉验证还可以帮助我们选择最佳的模型参数,提高模型的泛化能力。
Q:什么是训练集、验证集、测试集?
训练集、验证集(和训练过程一起验证的--可以用来判断欠过拟合)、测试集(最终测试能力的)。
Q:大模型在训练的时候一定要分词吗?具体是怎么处理的?
大模型在训练的时候一定要分词吗?具体是怎么处理的
是的,大模型在训练的时候需要进行分词处理。分词是将文本按照一定的规则或者词典切分成单词或者词汇的过程。这是因为大多数自然语言处理任务(如机器翻译、情感分析、文本分类等)都需要对输入的文本进行预处理,以便模型能够更好地理解和处理。
具体处理过程如下:
1. 首先,将输入的文本数据转换为适合模型处理的数字表示。这通常包括将文本转换为词向量(word embeddings)或词袋模型(bag-of-words)等表示形式。
2. 接下来,将数字表示的文本输入到分词器(tokenizer)中,将文本切分成单词或词汇。分词器通常基于词典或规则进行操作,将文本中的每个单词或词汇映射到一个唯一的整数或字符。
3. 将分词后的单词或词汇输入到模型中,进行训练。模型会学习如何根据输入的单词或词汇预测相应的输出。
4. 在训练过程中,模型会不断调整其参数,以便更好地预测输出。这个过程通常涉及到大量的计算,通常使用GPU或分布式计算资源进行加速。
- 训练完成后,模型可以用于处理新的输入文本,将其分词后输入到模型中,得到预测结果。

Q:采用jieba等分词库 也是先向量化文本吗?
是的,采用jieba等分词库可以将文本进行预处理,将其切分成一个个独立的词汇,从而实现向量化文本。向量化是将文本转换为数值型向量,以便计算机能够理解和处理。在自然语言处理(NLP)任务中,向量化是非常重要的一步。
分词库的作用是将文本中的词汇进行切分,使其成为一个一个的词汇。这样,我们就可以将这些词汇用于构建词汇表,进而将文本转换为向量。在向量化过程中,我们通常会对词汇进行编码,例如使用独热编码(one-hot encoding)或词嵌入(word embeddings)等方法。
总之,采用jieba等分词库是实现向量化文本的第一步,后续还需要进行词汇编码等操作,将文本转换为数值型向量。
采用jieba等分词库时,分词后的文本仍然是文本数据,不是向量化的数据。分词的目的是将文本分解成词汇序列,以便模型更好地理解和分析文本。但是,分词后的文本仍然需要进一步进行向量化处理,将其转换为向量表示,以便模型能够处理。
在具体的向量化处理中,可以采用不同的方法,如基于词袋模型的向量表示、基于TF-IDF的向量表示、基于词嵌入的向量表示等。这些方法可以将文本转换为向量表示,以便模型能够进行进一步的处理和计算。
总之,采用jieba等分词库后,分词后的文本仍然需要进行向量化处理,以便模型能够处理。
Q:模型压缩和加速方法?

Q:模型量化的理解?


其他资料:
你真的会写 Prompt ? 剖析 RAG 应用中的指代消解 - 知乎 (zhihu.com)
极客时间训练营-AI 大模型微调训练营 (geekbang.org)
使用Tensorboard跟进训练进度及判断模型的好坏_哔哩哔哩_bilibili
四、模型评估
关于模型评估笔者之前有简单介绍过一篇文章。如何评估每次训练的结果综合起来可以归结为以下几点:
- loss曲线初步判断是否收敛,是否过欠拟合。通常如果只有一条训练集loss是不好判断是否过拟合的;
- 使用评估算法对验证集和测试集跑分,实际上就是答案的相似度对比。如BLEU、Rouge的算法,可以参考chatglm开源仓库代码实现。如果分数不高或者明显比上一次下降,说明效果下降;
- 如果某些框架只有训练集的loss曲线没有验证集和测试集的,那么除了跑分还可以直接部署模型,分别验证部分训练集和测试集的答案,通过人工分析泛化能力来初步判断出是否过拟合。
- 人工反馈评估。这个属于通过人工对模型答案进行评分然后进行分数统计来评估模型表现能力。
五、经验分享
综上,随机抽取一个比较易于理解的非典型模型训练案例的小部分经验来分享,以此抛砖引玉希望对大家能有所启发。
以使用firefly框架Qlora微调百川2为例:
我们首先准备了数千的训练集,又从训练集中抽取了部分作为验证集。还准备了一份没有交集的测试集。本次训练并没有记录算法跑分,其实如果要是通过算法BLEU、Rouge等跑分也是可以初步很直观分析对比出每次训练的模型效果的。
1.我们先初步设定一组参数,然后看看训练结果。(因为第一步漏截图了,以红字矫正)


结论:人工体验效果不好,疑似欠拟合。从曲线初步判断学习率偏小。
计划:需要增加学习率和轮次。
2.下一步。同时来看一下loss分数。



结论:体验上训练集表现相当好,测试集不好。疑过拟合了。
计划:应该减少轮次。下波调为8轮试下。
3.其他参数不变,轮次为8轮。


结论:体验上训练集整体不错同上一轮效果,极个别存在语义理解偏差(可能和其他非优质语料影响有关)。泛化能力有细微进步,但还是不行。判断还是过拟合。
计划:继续降低轮次为6。
4.其他参数不变,轮次为6轮。


结论:同上。判断还是有些过拟合。
计划:继续降低轮次为4。
5.其他参数不变,轮次为4轮。



结论:泛化能力有轻微提升,训练集表现也不再完全拟合了,有泛化表现了。看起来不过拟合了,但需评估下泛化效果。(经后期对比评估,在非细调上本次的参数微调效果最优--注意:每次不同的语料数据分布微调的参数一般都是不同的,基本都需要重新调参。但整体的基本大体参数应该偏离不大,左右调整即可)
计划:继续降低轮次为3,看下效果是否会提升。
6.其他参数不变,轮次为3轮。


结论:泛化能力比上一波有提升,但体验较差回答的质量不如上一波。泛化能力依旧不理想。
计划:鉴于曲线,学习率使用3e-5试下。
7.其他参数不变,轮次为4轮,lr为3e-5。



结论:有点欠拟合,泛化看起来也较差。
计划:3e-4 6轮过拟合,3e-5应该差不多。曲线看起来会比上一个好。
下一波,增加轮次会不会更拟合??泛化会不会随之提高?
后面多次调整学习率和轮次均未有第5次的效果好,所以基本得出第4次的参数为目前最佳参数,居于这套参数再次微调能达到训练任务的最佳效果。总之,微调是个“炼丹”的过程,懂得了配方还要控制好火候,要有耐心还要加点运气。
总结上面的例子,说明即使我们没有完备的训练工具,也能定出最佳训练参数,只要我们理解原理,都有办法曲线解决。比如我们没有验证集的曲线,我们可以通过体验测试集来分析过拟合;没有算法跑分,我们通过曲线收敛情况和验证集测试集体验可以判断过拟合和欠拟合以及泛化能力。
根据曲线形状判断学习率和步数的升降这是需要掌握的微调通用基本技能。
总结
关于模型微调(Fine-tuning)实践从原理到实战到此就暂告一段落了。时间仓促,如有兴趣其他更多关于AI落地资料还可以参看这里:AI应用落地。在此也特别感谢互相分享学习经验和资料的同学。
如果还学不会,来公众号【贝可林】找我,我骑共享单车到你家一起探讨。