原创 秦晋 2025-08-06 08:31 浙江
这是2025年的第87篇文章
( 本文阅读时间:15分钟 )
01
前言
随着大模型服务的逐渐深入,许多客户在大模型领域的使用场景要求越来越高,主要包括:
1. 准确性要求显著提高 :许多需要替代人工判断的场景中,模型准确率需达到90%以上,才能满足上线标准;
2. 响应速度成为关键指标 :尤其在实时交互类场景下,端到端延迟甚至需控制在1秒以内;
3. 边缘场景增多,知识边界外延 :越来越多的用例涉及非常规领域或小众知识,这些信息往往未包含在模型的预训练语料中。
面对上述挑战,传统的 Prompt 工程已难以支撑复杂的业务需求。而这些问题的解决方法,也都不约而同地指向了一个答案——微调 。在实际项目中,模型微调训练的需求正在日益增长。在目前所有接触到的大模型项目中,存在后训练需求的项目接近50%。本文将通过一个实际项目,详细介绍一个意图识别任务的微调训练完整流程。
一些来自各方的常见训练问题,例如:“模型训练就是‘炼丹’吗?”、“训练参数应该如何调整?”、“我这个场景适合选用多大的base模型?”、“需要多少训练资源?”、“需要多少训练数据?”、“如果数据量不足该怎么办?”、“模型效果该如何评估?”等,都将在本文中一一解答,希望能带来一定启发。
项目中选用的训练框架ms-swift是由魔搭社区开发,专为大模型和多模态大模型提供全面的训练与部署解决方案。该框架支持超过500种大模型和200种多模态模型,涵盖训练流程(包括预训练、微调和人类偏好对齐)、推理、评测、量化以及部署全过程。开发者可在ms-swift中一站式满足所有与大模型相关的需求。本次实践重点应用了该框架的监督微调功能。
02
项目背景
某居住服务行业客户的惠居业务专注于住房租赁领域,用户通过平台与客服进行实时对话以咨询租房信息。在此场景下,平台需高效理解用户需求以提供精准服务。为优化服务流程,需对用户与客服的实时对话内容进行智能分析,将其归纳为业务所需各种意图,其中一级标签的六种核心意图(用户问答、洽谈斡旋、视频跳转、预约、需求&推荐、其他)的准确率就显得尤为重要,该标签的准确预测能够精准触发后续工程服务(如视频跳转、服务推荐、数据分析等),提升服务响应效率与用户体验。这一需求依赖于大模型对自然语言对话的实时意图识别能力。
但是传统的PE+基模的方式准确率有限(60%左右),并且大尺寸基模的性能问题也成为客户上线该服务的一个重要瓶颈。在这样的背景下TAM入场为客户提供了全流程的模型微调服务,最终在8B模型下获得了94.5%的任务准确率,提升超过30%,并且小尺寸的模型也使得模型服务的性能有了大幅提升。
03
项目实操
3.1 数据准备与处理
理想情况下,客户给到我们的用于微调的训练数据可能是excel、csv等表格的形式,或者json格式。一般数据至少会包含两个字段:用户的输入和输入对应的正确答案,为便于描述,简单用query和answer来表示。以惠居的意图识别场景为例,我拿到的原始数据是这样的:
其中“一级标签”就是answer,“客户对话”就是query。经过数据清洗,删除脏数据和无关列以后我获得了753条可以用于训练的数据。
753条数据就可以进行模型的后训练吗?答案是可以的,并且效果还不错。
在测试阶段,我使用客户前期提供的这753条数据在Qwen3-8B模型上进行了全参的SFT训练,并在测试集上获得了超过90%的准确率,相比于未经微调的Qwen3-235B-A22B模型准确率提高了27%。
所以从这个例子我们可以初步了解到,对于大模型微调,几百条的数据量也是可以进行的,相比于小模型时代的训练数据要求,这个数字显然是小了很多。
当然我们对于训练数据的要求依然是越多越好的,因为越是充足的训练样本,可以更广泛的覆盖到各种各样的情况,能提升模型对于边缘情况的适配能力。对于训练数据要求,我们给出以下建议:对于简单任务,100-300条数据就可以看到效果;中等难度任务需1000条以上;高难度任务需3000条甚至更多,可能达到万条级别。
相比于数据数量,数据的质量其实更为关键,算法领域非常出名的一句话:“Garbage in, garbage out ”(简称 GIGO)正是说明了这一点。事实上,数据和特征决定了模型能力的上限,而算法只是去逼近这个上限。那么数据的质量具体从哪些维度考量呢?我总结为如下三点:
准确性:用于训练的数据是否真实反映了实际情况,是否存在错误标注也就是噪音(noise)。100%正确标注的数据,才有可能使模型无限的向人类标准逼近;
多样性:训练数据是否覆盖了实际应用中可能出现的各种情况,包括语言风格、话题、输入形式等。尤其在大语言模型中,多样性能显著提升模型泛化能力;
一致性:数据之间是否存在矛盾之处。例如,对于同一个问题:“目前的短租房源就只有这一套,对吧?”这个query既可以理解为是一种对于租房的需求推荐,也可以理解为是用户的一种问答需求,那么除非业务允许多标签的存在,否则模型很难正确学习,因为人类对于这个问题就存在争议的判断。
另外对于多分类任务的模型而言,数据的均衡性也尤为重要,以惠居举例来说,我们一共有6个标签:“用户问答”、“洽谈斡旋”、“视频跳转”、“预约”、“需求&推荐”、“其他”,在实际提供的数据样本中“问答”类和“需求&推荐”样本分别有235和275条,而相比之下“洽谈斡旋”类的样本只有36条。这样的训练数据会导致模型更倾向于输出“问答”类和“需求&推荐”这两个类别,当然这在训练数据集中准确率是会比较高的,但到了实际业务场景中可能结果就不尽如人意。如果实际业务场景中各个类别的比例也是大概如此,那么均衡性的要求也相对没有那么严格。
当然,对于数据质量的要求并不是一个绝对的标准,因为实际获得的数据是不会如此理想的,这四个标准可以作为一个参考,只需要在准备数据的过程中尽可能的向其靠近即可。但是否存在一些方法能够在已有数据的基础上,对数据集做一些优化和补充呢?办法依然是有的,在惠居场景中,由于该功能还未正式上线(将于630上线生产),因此客户并没有实际的“视频跳转”和“预约”标签的样本,另外“洽谈斡旋”的样本只有36条也与数据均衡的要求不符,所以我们采用类似self-instruct的方式扩充了数据集。
Self-Instruct(自我指令)是一种通过已有语言模型自动生成指令类数据的方法,最初由 [Wang et al., 2022] 提出。其核心思想是:使用一个强大的预训练语言模型(如DeepSeek-R1,Qwen3-235B等)作为“教师模型”,基于少量种子指令(seed instructions)生成大量新的任务指令和对应的输入输出示例,从而构建一个可用于微调的大规模数据集。这里我们的数据虽然不是指令微调数据,但是也可以借鉴self-instruct的思路,通过大模型生成部分数据,用于补全确实的类别,和样本数量极为不均衡的类别。
预约 | 视频跳转 | 洽谈斡旋 |
这个今天可以看吗 明天可以预约看房吗? 您好明天下午可以先过去看看吗 周末可以看房吗 | 再介绍一下冰箱 能再看一下冰箱么? 可以再介绍一下冰箱么? 我想再了解一下冰箱 | 还能再便宜一点吗? 咱这边价格可以谈吗? 这个价格能谈到多少? 服务费有优惠吗? |
通过上述操作,最终获得1058条训练数据。
得到初始数据之后需要将数据格式转化成可以用于训练的jsonl格式。ms-swift框架共支持其中上述四种格式:
{"messages": [{"role": "system", "content": "<system>"}, {"role": "user", "content": "<query1>"}, {"role": "assistant", "content": "<response1>"}, {"role": "user", "content": "<query2>"}, {"role": "assistant", "content": "<response2>"}]}
{"system": "<system>", "conversation": [{"human": "<query1>", "assistant": "<response1>"}, {"human": "<query2>", "assistant": "<response2>"}]}
{"system": "<system>", "instruction": "<query-inst>", "input": "<query-input>", "output": "<response>"}
{"system": "<system>", "query": "<query2>", "response": "<response2>", "history": [["<query1>", "<response1>"]]}
其中上述四种格式在AutoPreprocessor处理下都会转换成ms-swift中Standard Format的messages字段,都可以直接使用--dataset <dataset-path>接入,因此建议直接将数据从原始数据转化为Standard Format,以下为惠居场景训练数据示例:
{"messages": [{"role": "system", "content": "你是一个有用无害的租房领域的对话分析专家"}, {"role": "user", "content": "按需脱敏"}, {"role": "assistant", "content": "问答"}]}
这里可以通过python脚本将表格数据转化成jsonl,具体操作如下:
import json
def df_to_jsonl(df, filename):
with open(filename, 'w', encoding='utf-8') as f:
for _, row in df.iterrows():
message_entry = {
"messages": [
{"role": "system", "content": row['system_prompt']},
{"role": "user", "content": row['user_prompt']},
{"role": "assistant", "content": row['assistant']}
]
}
f.write(json.dumps(message_entry, ensure_ascii=False) + '\n')
df_to_jsonl(train_df, 'SFT/train_data_0618.jsonl')
完成上述步骤,我们的训练数据就正式ready了。
3.2 训练环境部署
模型准备
用于微调的模型需要下载到OSS对象存储中进行持久化保存,因此在创建DSW实例时,选择挂载目标OSS bucket:
此时打开DSW,会看到挂载的OSS文件夹:
通过命令行cd进入oss文件夹后,输入modelscope模型下载命令(以Qwen3-8B为例):
modelscope download --model Qwen/Qwen3-8B --local_dir ./Qwen3-8B
完成对应模型下载之后就可以在oss文件夹中看到下载好的4个尺寸的Qwen3模型,可用于后续微调,并且DSW实例删除后,模型也会持久化保存在OSS中。
训练镜像选择
本次项目选用的ms-swift框架所需要的镜像在PAI-DSW中已有预设,建议在开启DSW实例的时候直接选择镜像:
modelscope:1.26.0-pytorch2.6.0-gpu-py311-cu124-ubuntu22.04这样就不需要再面对繁琐的依赖安装问题了,打开DSW后就可以直接通过shell命令进行模型微调训练。
注:如果需要使用--use_liger_kernel还需要额外pip install liger_kernel==0.5.10作为补充依赖。
3.3 训练过程与参数调优
但凡涉及到模型训练部分,常常被问到几个问题:“训练需要用多大的模型?”、“训练需要多少算力资源?”、“训练参数如何调整?”,这几个问题将在本段逐一解答。
训练需要用多大的模型?
微调模型大小的确定参考以下几个因素:
首先是效果,微调模型应当是以效果为先,达不到业务需求的效果,什么尺寸的模型都是白搭,一般来说越大的基模微调的效果会更好,因为模型本身的基础能力会更强,但是越大的模型也建议适配更多的数据,并且训练时需要匹配合适的步长,使得模型效果得到收敛。
其次是资源,这一点和第二个问题之间似乎是个“先有鸡还是先有蛋”的关系,但是事实上客户能提供的资源往往是有限的(也不乏一些金主爸爸,那么资源的限制就不需要考虑了),这种情况就要选择,已有资源下,训练能够支持的最大模型,并且依次向下排列(比如一台H20就可以支持Qwen3-32B以下的全部尺寸模型进行Full-SFT微调)。对于模型训练所需资源的计算方法会在后面介绍。
最后是性能,根据前面两条建议得出的结论,似乎是在现有资源下选择最大的模型进行训练。但是如果说8B模型的效果已经能够满足业务需求,或者和14B、32B等尺寸的模型效果无较大差异,那我们还需要更大尺寸模型吗?无论是从资源利用,和模型性能上考虑,我们都会选择满足需求的更小尺寸的模型作为最终的模型。
因此,综合上述内容,对于选择模型大小的结论是:在固定资源的情况下,选择尺寸最大及以下的模型进行微调,但是最终推理模型的选择,应该根据模型效果和尺寸综合考量,选择最优尺寸。
训练需要多少算力资源?
大模型后训练任务的显存主要由以下几个部分组成:
模型参数显存:大模型按照B(10亿)来命名,比如Qwen3-8B,并且1B=1000^3参数量,而1GB=1024byte^3,Qwen系列大模型的精度的一般为BF16,BF16=16bit=2byte。所以以Qwen3-8B模型为例,模型部分大约占用 8*2byte*1000^3/1024/1024/1024 约等于16GB。
梯度显存:BF16 训练时:每个梯度占 2 字节 8e9 × 2 bytes = 16 GB。
优化器状态显存(以 Adam 优化器为例):动量(momentum)和方差(variance)每个参数需 8 字节(FP32)8e9 × 8 bytes = 64 GB。
激活值与临时缓冲区:一般与批次大小(batch size)、序列长度相关,通常需额外 15-20 GB(以实际训练配置为准)。
综上,Qwen3-8B模型全参SFT训练所需的基础显存需求总和预估为:16 GB(模型) + 64 GB(优化器) + 16 GB(梯度) + 20 GB(激活值)≈ 116 GB
除此之外,还有一种更为简单的估算方法,根据经验来判断,如果资源充足的情况下可以以:参数量大小*16的方式估算显存占用(以BF16精度为例,若是FP8则是12倍)。
训练参数如何调整?
在模型训练过程中,参数的选择也是一大问题,此处我们参考实际过程中的训练脚本:
NPROC_PER_NODE=8 \
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
swift sft \
--model model/path \
--train_type full/lora \
--dataset 'path/to/dataset' \
--torch_dtype bfloat16 \
--max_steps 1000 \
--streaming true \
--per_device_train_batch_size 2 \
--per_device_eval_batch_size 2 \
--learning_rate 1e-5 \
--gradient_accumulation_steps 2 \
--packing true \
--eval_steps 50 \
--save_steps 50 \
--logging_steps 5 \
--max_length 8192 \
--warmup_ratio 0.05 \
--save_total_limit 2 \
--save_only_model true \
--output_dir path/to/output_model \
--deepspeed zero3 \
--attn_impl flash_attn \
--model_author aliyun \
--model_name model_name
对于其中一些参数做以下解释:
训练参数需要根据一些观测指标去进行动态的调整,ms-swift框架给我们提供了四种可观测指标,分别是:Loss、Token Accuracy、Gradient Norm和Learning Rate。
1. Loss:用于衡量模型预测输出与真实标签之间的差异,是优化目标的核心指标。数值越低,模型预测越准确。它反映了模型对训练数据的拟合程度。优化器通过反向传播最小化 Loss 来调整模型参数。
常见问题:
Loss 下降缓慢:可能学习率过低或模型容量不足。
Loss 波动剧烈:学习率过高或数据分布不均匀。
过拟合信号:训练 Loss 持续下降,但验证 Loss 停滞或上升。
相关函数:分类任务常用交叉熵损失(Cross-Entropy),回归任务常用均方误差(MSE)。
2. Token Accuracy:在大模型任务中,模型正确预测的 token占总 token 的比例。它的作用是直观反映模型的语言理解或生成能力(如翻译、文本生成)。需要与 Loss 结合分析,可发现模型是否“盲目追求准确率”(例如过度预测高频词)。
常见问题:
高准确率不等于高质量生成(如生成语法正确但逻辑错误的句子)。
需结合其他指标(如 BLEU、ROUGE)或人工评估。
3. Gradient Norm:梯度向量的 L2 范数,用于监控梯度更新的稳定性。它反应的是模型参数变化的异动程度,表明模型是否还在进一步学习,以及学习的程度。
常见问题:
梯度爆炸:范数异常大(如 >1e3),可能导致参数更新失控。
梯度消失:范数趋近于 0,导致模型无法学习。
优化稳定性:正常训练时梯度范数应保持在一个合理范围内(如 0.1~10)。
应对策略:
梯度裁剪(Gradient Clipping):限制范数最大值(如 clipnorm=1.0)。
调整学习率:梯度范数过大时降低学习率。
4. Learning Rate:控制模型参数更新步长的超参数,它直接影响训练速度和收敛性。
常见问题:
学习率过高:参数更新剧烈,导致 Loss 波动或发散。
学习率过低:训练缓慢,可能陷入局部最优。
动态调整:
学习率调度器:如线性预热(Linear Warmup)。
分层学习率:对模型不同层(如嵌入层、注意力层)设置不同学习率。
典型值:
大模型常用初始学习率:1e-5~1e-4(AdamW 优化器)。
预训练模型微调时:5e-5~3e-4(需根据任务调整)。
先来看看本次实验场景中的真实指标变化:
从图中我们可以看到:在训练初期 Loss下降较快,随后进入缓慢收敛阶段,说明训练有效。与Loss下降趋势相对应,Token Accuracy稳步上升,最终达到预期性能目标稳定在1附近。Grad Norm初期梯度下降较大,有助于快速更新参数,说明优化过程健康。
Learning Rate使用Warmup学习率调度策略,学习率按预期变化无异常。对于何时停止训练而避免过拟合的问题,我们应该考虑在Loss降到一个较低水平以后且Token Accuracy也达到了一个稳定状态之后停止训练,以避免或缓解过拟合的情况,比如图中step在60-80之间的阶段就是一个合适的停止阶段,在这个阶段Loss和Accuracy刚到达了一个趋近于收敛的阶段,继续训练可能会有过拟合的风险。
判断过拟合的一个重要特征,其实是比较验证集和训练集的Loss差异,当有时候二者只能获得其一的情况下,可以通过上面的这种方式进行简单的判断。另外值得一提的一点是,在学术上,过拟合是一种尽量需要避免的情况,但是在实际应用中过拟合的情况并没有想象中的那么可怕,尤其是一些小模型的微调,模型训练之后只专注于一件事情(比如意图识别),那么过拟合的情况就没有那么糟糕,只要模型的预测效果优秀,都是可以被业务拿来使用的“优质模型”。
本次实践的训练是一个相对顺利健康的过程,大多数时候还可能会遇到其他一些情况:
1. Loss不下降 / 不收敛:Loss在多个epoch或step后保持不变或波动很小。Token Accuracy增长缓慢或停滞。Gradient Norm可能趋近于零(梯度消失)或持续较高但无明显更新效果。在大模型微调领域可能原因主要是学习率设置过小或过大 。可以尝试调整学习率,尝试从1e-4开始逐步降低,并使用一些自动学习率调整策略,Warmup + Decay等。
2. Loss收敛但精度不高:Loss明显下降并趋于稳定,但是Token Accuracy提升有限,无法达到预期目标。
可能原因:
模型尺寸不足。
数据集噪声大或标签错误多。
正则化过强(导致模型欠拟合)。
在大模型微调时大概率是前两个原因,因此可以考虑增加模型尺寸;清洗数据,去除异常样本,并且可能是业务上对于标签的定义不明确造成的,需要和客户沟通解释,想办法一起制定合理的标签体系。
3. Loss下降过程中出现突刺:Loss曲线中出现突然上升的现象。Token Accuracy可能随之波动。Gradient Norm出现尖峰。
可能原因:
Adam优化器中浅层参数长时间未更新,导致梯度突变。
batch size设置过大,梯度更新过于剧烈。
梯度裁剪缺失或设置不当。
可以考虑使用梯度裁剪(Gradient Clipping)限制更新幅度或者调整batch size至合理范围。
4. 训练初期收敛快后期停滞:初期 Loss 快速下降,Accuracy 提升明显。中后期 Loss 下降缓慢甚至停止。
可能原因:
学习率未随训练过程动态调整。
模型陷入局部最优或鞍点。
可以考虑引入学习率衰减机制(如Step Decay、Cosine Annealing)。
3.4 模型效果评估
模型训练完成以后,效果的评估是很重要的环节,既能够评价当前模型的能力,同时也可以为后续进一步训练提升,提供参考价值。当前大语言模型在业务中的使用,主要会涉及以下5种任务:
分类任务
核心指标 :准确率(Accuracy)、F1值(尤其是类别不平衡时)、精确率(Precision)、召回率(Recall)、Hamming Loss(衡量标签预测错误率)。
补充评估 :人工校验分类结果是否符合业务场景需求(如医疗诊断需医生复核)。
生成任务
自动评估 :文本质量类的包括BLEU、ROUGE、METEOR(衡量生成文本与参考答案的重合度)。文本流畅度的指标如语言模型困惑度(Perplexity)
人工评估 :需要考虑相关性,检查生成内容是否贴合输入需求,如摘要是否涵盖原文重点;多样性,输出是否存在大部分的重复,如对话回复是否灵活;事实性 ,生成内容是否与现实一致。如历史事件描述是否正确,是否出现胡编乱造。
推理任务
逻辑推理 :准确率(看答案正确与否)、解决复杂问题的比例(如数学题的步骤完整性)。
多跳推理 :答案覆盖所有推理步骤的比例、与专家答案的一致性。
场景测试 :在真实场景中模拟用户提问,评估模型的实用性和鲁棒性,是否存在过于关注某一类任务,而导致了模型的泛化能力消失。
对话与交互任务
对话质量 :主要考虑连贯性 ,看模型回复是否与上下文逻辑一致,如在多轮对话中记忆用户偏好;多样性,回复是否避免模板化,如“好的”、“明白了”等重复表达。
用户满意度 :对于已经上线的业务,可以通过A/B测试或用户反馈评分,如NPS净推荐值。
任务完成率 :如客服场景中问题解决率、用户流失率等。
信息抽取任务
实体识别 :F1值,尤其关注低频实体的识别能力。
关系抽取 :准确率与人工标注的吻合度、对歧义关系的处理能力,如“苹果”是公司还是水果。
惠居这个项目的意图识别场景是一个典型的分类任务,因此在业务上和客户共同确定了Accuracy(准确率),Weighted Precision(加权平均精准率),Weighted Recall(加权平均召回率)以及Weighted F1 Score(加权平均F1值)这四个核心评价指标,并记录了各个标签下准召以及F1值,用于优化模型在单一标签上的效果。
可以清楚看到,经过微调后的小尺寸模型在各项数值上都远超大尺寸的BaseLine模型,但是三种尺寸的微调模型之间的效果差距并不明显,这说明该意图识别微调任务的难度并不复杂,在小尺寸模型和大尺寸模型上的表现差异不大,因此综合考虑推理的资源需求量和性能,最终客户选择了Qwen3-SFT-8B的模型上线生产环境。
04
结语
本次惠居业务场景的微调实践,不仅验证了大模型微调技术对业务需求的精准适配能力,更揭示了服务化微调解决方案在企业智能化升级中的核心价值。通过将技术工具、方法论与行业需求深度融合,构建了一套可复用的“需求洞察-数据优化-模型调优”闭环体系,为大模型落地提供了从实验室到生产环境的完整路径。
在惠居项目中,客户的核心诉求——高准确率、低延迟、边缘领域适配——直指传统大模型服务的瓶颈。而微调技术的引入,通过定向优化模型能力边界 ,实现了三大突破:
精准匹配业务规则:通过高质量标注数据与标签体系对齐,模型从通用语言理解者转化为垂直领域专家。例如,针对租房场景中“洽谈斡旋”类样本稀缺的问题,微调服务通过Self-Instruct生成策略性补充数据,使模型在边缘类别上的识别能力提升30%,解决了传统Prompt工程无法覆盖的长尾问题。
性能与效果的协同优化 :依托ms-swift框架的显存压缩(ZeRO-3、Flash Attention)与分布式训练能力,项目在单机多卡环境下完成Qwen3-8B~32B尺寸的全参微调,将训练成本控制在客户可接受范围内,同时通过模型轻量化部署实现端到端延迟低于200ms,满足实时交互需求。这一成果证明,微调服务不仅是效果提升工具,更是性能调优的关键杠杆。
成本效益的理性决策 :通过多尺寸模型对比实验发现,8B级模型经充分微调即可达到接近32B模型的效果(94.5% vs. 95.2%),而推理资源消耗仅为后者的1/3。这一发现为行业提供了重要范式——以任务复杂度为导向选择模型规模,通过微调服务最大化ROI ,而非盲目追求参数膨胀。
参考文献与资料
[01] https://arxiv.org/abs/2212.10560
[02] https://swift.readthedocs.io/en/latest/Customization/Custom-dataset.html
欢迎留言一起参与讨论~