文章详细介绍了使用LoRA微调技术对Qwen2.5-0.5B-Instruct模型进行微调,使其成为会议语音助手的完整流程。从环境配置、模型下载、数据准备到代码实现和测试验证,展示了即使在普通笔记本上也能完成微调训练。微调可增强模型特定领域的能力,减少幻觉,提高一致性,降低使用成本,而数据质量是决定微调效果的关键因素。
一、前言
上上周的周末无事在家,然后写了一篇《【有手就行】自己花20分钟从0开始训练一个“大模型”[1]》,结果发现这两个星期涨了几十个关注,比我前面写了几个月文章得到的关注还多,看来这种浅显易懂的、入门级的技术文章相对来说会有更多人爱看一些。 既然如此,我再把早先在做OddAgent[2]时候,微调语音助手功能的流程也简单理一下,然后放出来给大家做一个参考吧。
事实上,上手学习大模型、人工智能相关的开发并没有什么太过高深的门槛,真的很简单,真的就是【有手就行】。
二、大模型微调概述
微调(Fine-tuning)有很多种不同的方法,但是使用的场景以及代价也都是不一样的。作为一个没什么资源(数据缺缺,GPU缺缺)的普通人来说,考虑的肯定是低成本方案。
| 方法类型 | 参数更新范围 | 计算成本 | 适用场景 | 典型工具框架 |
|---|---|---|---|---|
| 全参数微调 | 全部参数 | 极高 | 大数据集、高资源场景 | Hugging Face Transformers |
| Adapter Tuning | 适配器参数 | 低 | 多任务、资源受限 | AdapterHub、PEFT |
| LoRA/QLoRA | 低秩矩阵参数 | 极低 | 大模型单卡微调、小样本 | LoRA、QLoRA(PEFT 库集成) |
| 指令微调 | 全量 / 部分参数 | 中 - 高 | 通用对话模型、多任务泛化 | Alpaca-LoRA、FastChat |
| 领域适配微调 | 全量 / 部分参数 | 中 | 垂直领域任务 | 自定义领域数据集 + Transformers |
三、LoRA微调全流程
前阵子在将小落同学项目[3]的智能体代码摘成独立的OddAgent项目时,实践的是一个会议相关的语音助手功能,该功能有针对Qwen2.5-0.5B-Instruct模型和Qwen3-4B-Instruct-2507这两个模型重点做了一些测试和验证,用的就是其中成本最低的LoRA微调。 最后跑下来Qwen3-4B-Instruct-2507的效果要显著好于Qwen2.5-0.5B-Instruct(有同时针对这两个模型用同一套数据集去做了LoRA微调)。 因此,本文的重点就放在了Qwen2.5-0.5B-Instruct的LoRA微调上,因为后面我还准备再继续针对这个模型再补充一些训练集来做一下微调,目标是在这个模型上也能做到100%的意图/槽位准确率。
跟之前训练大模型一样,还是在我家里的这个10年前的老笔记本上进行的。
- 硬件配置:CPU: i7-8850H CPU @ 2.60GHz+16G内存
- 微调训练时长:约50分钟(由于训练时,还在用这个笔记本上网课,所以时长仅供参考)
1. 创建虚拟环境
为了不影响现有的python环境,建议为这个LoRA训练单独创建一个环境。个人习惯用conda(环境可复用,节省硬盘空间),venv或者vu也都一样,全看大家的个人喜好。
conda create -n lora python=3.12 -yconda activate lora2. 模型下载
下载模型前请注意一下你的硬盘空间,整个模型需要955M的硬盘空间,如果你跟我一样常年硬盘空间都是严重不足的,请视情况清理一下空间,避免下载失败。
pip install modelscope --index https://pypi.mirrors.ustc.edu.cn/simplemodelscope download --model Qwen/Qwen2.5-0.5B-Instruct下载下来后,会保存到指定的.cache这个目录下。如果是Windows,默认是在C:\Users\Administrator\.cache目录下,如果是Linux/Mac则是在.cache目录下。 其中:
config.json定义模型结构tokenizer.json定义文本输入方式safetensors文件则存储模型的参数权重。
3. 微调环境安装
call pip install torch --index-url https://download.pytorch.org/whl/cu121call pip install transformers accelerate bitsandbytes peft datasets trl streamlit sentencepiece tensorboard4. 数据库集准备
训练需要你预先准备好你用于训练的数据集,但是。。。凡事总会有一个“但是”,今天的“但是”是: 要数据?对不起,没有! 换成几年前,哥必须对这个“但是”暴跳如雷,然而,今天有了大模型,腰不酸了,背不疼了,雷没有了,直接用其他大模型自动为每个意图生成50条数据,提示词略,格式如下:
{ "instruct": "启动工作会议", "input": "", "output": { "tool_name": "INSTANT_MEETING", "parameters": { "meeting_name": "工作会议" } } }, { "instruct": "创建汇报会会议", "input": "", "output": { "tool_name": "INSTANT_MEETING", "parameters": { "meeting_name": "汇报会" } } },intruct为用户指令,ouput字段为期望输出
5. LoRA微调代码
1)微调训练代码
我的完整代码,代码文件名:train_lora.py
"""Qwen2.5-0.5B-Instruct LoRA/QLoRA 微调脚本该脚本用于对 Qwen2.5-0.5B-Instruct 模型进行低秩适应(LoRA)或量化低秩适应(QLoRA)微调"""import torchfrom peft import TaskType, LoraConfig, get_peft_model, prepare_model_for_kbit_trainingfrom datasets import Datasetimport pandas as pdfrom transformers import ( AutoTokenizer, AutoModelForCausalLM, DataCollatorForSeq2Seq, TrainingArguments, Trainer, GenerationConfig)# 定义要微调的基础模型名称TRAINING_MODEL = "Qwen/Qwen2.5-0.5B-Instruct"def load_dataset_json(path): """ 加载JSON格式的数据集 :param path: 数据集文件的完整路径 :return: 转换为Hugging Face Dataset格式的数据集 """ # 使用pandas读取JSON文件 df = pd.read_json(path) # 将pandas DataFrame转换为Hugging Face Dataset ds = Dataset.from_pandas(df) return dsdef dataset_preprocess(ds): """ 对数据集进行预处理,包括加载分词器和处理数据样本 :param ds: Hugging Face Dataset格式的原始数据集 :return: 预处理后的数据集和使用的分词器 """ # 加载预训练分词器 # use_fast=False: 使用慢速分词器,支持更复杂的文本处理 # trust_remote_code=True: 信任模型提供的自定义代码 tokenizer = AutoTokenizer.from_pretrained(TRAINING_MODEL, use_fast=False, trust_remote_code=True) def process_func(example): """ 处理单个数据样本的内部函数 :param example: 单个数据样本,包含instruct、input和output字段 :return: 处理后的样本,包含input_ids、attention_mask和labels """ MAX_LENGTH = 384 # 最大序列长度限制 input_ids, attention_mask, labels = [], [], [] # 构建指令部分的输入,使用模型要求的对话格式 instruction = tokenizer( f"<|im_start|>system\n现在你要扮演会议语音助手<|im_end|>\n<|im_start|>user\n{example['instruct'] + example['input']}<|im_end|>\n<|im_start|>assistant\n", add_special_tokens=False # 不自动添加特殊标记,因为我们已经手动添加 ) # 构建响应部分的输入 response = tokenizer(f"{example['output']}", add_special_tokens=False) # 合并指令和响应的token ids,并添加pad_token作为结束 input_ids = instruction["input_ids"] + response["input_ids"] + [tokenizer.pad_token_id] # 合并注意力掩码,pad_token位置设置为1表示需要关注 attention_mask = instruction["attention_mask"] + response["attention_mask"] + [1] # 构建标签:指令部分用-100屏蔽(不参与损失计算),响应部分保留实际token id labels = [-100] * len(instruction["input_ids"]) + response["input_ids"] + [tokenizer.pad_token_id] # 截断超过最大长度的序列 if len(input_ids) > MAX_LENGTH: input_ids = input_ids[:MAX_LENGTH] attention_mask = attention_mask[:MAX_LENGTH] labels = labels[:MAX_LENGTH] return { "input_ids": input_ids, "attention_mask": attention_mask, "labels": labels } # 应用处理函数到整个数据集,并移除原始列名 tokenized_id = ds.map(process_func, remove_columns=ds.column_names) # 解码并打印第一个样本的输入,用于调试 tokenizer.decode(tokenized_id[0]['input_ids']) # 解码并打印第二个样本的标签(过滤掉-100),用于调试 tokenizer.decode(list(filter(lambda x: x != -100, tokenized_id[1]["labels"]))) return tokenized_id, tokenizerdef train(tokenized_id, tokenizer, cpu=True): """ 执行模型微调的主函数 :param tokenized_id: 预处理后的数据集 :param tokenizer: 分词器 :param cpu: 是否使用CPU进行训练,默认为True(CPU训练) """ # 根据是否使用CPU选择数据类型 # CPU训练使用bfloat16,GPU训练使用float16 dtype = torch.bfloat16 if cpu else torch.float16 # 加载预训练模型 # device_map="auto": 自动分配模型到可用设备(CPU或GPU) # torch_dtype=dtype: 设置模型的数据类型 model = AutoModelForCausalLM.from_pretrained(TRAINING_MODEL, device_map="auto", torch_dtype=dtype) # 启用输入梯度检查点,减少内存使用 model.enable_input_require_grads() # 配置LoRA参数 config = LoraConfig( task_type=TaskType.CAUSAL_LM, # 任务类型为因果语言模型 # 指定要微调的模型模块 target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], inference_mode=False, # 训练模式(True为推理模式) r=8, # LoRA秩,控制适配器的维度 lora_alpha=32, # LoRA缩放因子,通常为r的4倍 lora_dropout=0.1 # Dropout比例,防止过拟合 ) # 应用LoRA配置到模型 model = get_peft_model(model, config) # 配置训练参数 args = TrainingArguments( output_dir=f"./output/{TRAINING_MODEL}_lora", # 模型输出目录 per_device_train_batch_size=4, # 每个设备的训练批量大小 gradient_accumulation_steps=4, # 梯度累积步数,实际批量大小=4*4=16 logging_steps=10, # 每10步记录一次日志 num_train_epochs=8, # 训练轮数 save_steps=100, # 每100步保存一次模型 learning_rate=1e-4, # 学习率 save_on_each_node=True, # 在每个节点上保存模型 gradient_checkpointing=True, # 启用梯度检查点,减少内存使用 ) # 创建训练器 trainer = Trainer( model=model, # 要训练的模型 args=args, # 训练参数 train_dataset=tokenized_id, # 训练数据集 # 数据整理器,用于处理批量数据 data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True), ) # 开始训练 trainer.train() def combine_and_save_models(cpu=True): """ 合并LoRA适配器和基础模型,并保存合并后的模型 :param cpu: 是否使用CPU进行模型合并,默认为True """ # 导入必要的库(函数内部导入,避免不必要的依赖加载) from transformers import AutoModelForCausalLM, AutoTokenizer import torch from peft import PeftModel # 根据是否使用CPU选择数据类型 dtype = torch.bfloat16 if cpu else torch.float16 model_path = TRAINING_MODEL # LoRA检查点路径(假设是最后一个保存的检查点) lora_path = f'./output/{TRAINING_MODEL}_lora/checkpoint-100' # 加载分词器 tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) # 加载预训练模型(评估模式) model = AutoModelForCausalLM.from_pretrained( model_path, device_map="auto", # 自动分配设备 torch_dtype=dtype, # 设置数据类型 trust_remote_code=True # 信任自定义代码 ).eval() # 设置为评估模式 # 加载LoRA适配器并合并到基础模型 model = PeftModel.from_pretrained(model, model_id=lora_path) model = model.merge_and_unload() # 合并权重并卸载peft封装 # 保存合并后的模型 merged_model_path = f'./merged_{TRAINING_MODEL}_lora' model.save_pretrained(merged_model_path) tokenizer.save_pretrained(merged_model_path) print(f"Merged model saved to {merged_model_path}")# 主程序入口if __name__ == "__main__": print("Start to train...") # 训练数据集路径 training_dataset = "./data/train.json" # 加载数据集 ds = load_dataset_json(training_dataset) print(f'dataset loaded, train size: {len(ds)}, {ds[0:3]}') # 预处理数据集 tokenized_id, tokenizer = dataset_preprocess(ds) print(f'dataset preprocessed, start to run training...') # 执行训练 train(tokenized_id, tokenizer, cpu=True) # 合并并保存模型 print("Start to combine and save models...") combine_and_save_models(cpu=True) print("Done")2)微调训练
python train_lora.py整个微调训练在我这个10年前的老笔记本电脑上用CPU跑,总共花了2936.4626秒(约48.94分钟)。所以如果你也是在一个老电脑上跑的话,执行了训练后可以自己去玩一会儿了,回来可能就训练完成了。
3)微调输出文件结构
微调完成后,输出目录结构如下图所示:
其中:
- 微调后的参数权重:位于
output\Qwen\Qwen2.5-0.5B-Instruct_lora目录下。 - 合并后的模型:位于
merged_Qwen\Qwen2.5-0.5B-Instruct_lora目录下。
4)推理验证
微调完成后,可以用如下代码进行推理验证。 我的测试程序完整代码。文件名:test_lora.py。
from transformers import AutoModelForCausalLM, AutoTokenizerimport torchfrom peft import PeftModelimport argparse # 添加argparse模块用于命令行参数解析model_path = 'Qwen/Qwen2.5-0.5B-Instruct'lora_path = './output/Qwen/Qwen2.5-0.5B-Instruct_lora/checkpoint-100'def test(instruct, cpu=True): """ 测试微调后的LoRA模型 :param instruct: 用户指令内容,通过命令行传入 :param cpu: 是否使用CPU进行推理,默认为True """ # 加载tokenizer tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) # 根据设备选择数据类型和设备映射 if cpu: dtype = torch.float16 device_map = "cpu" # 强制使用CPU device = "cpu" else: dtype = torch.bfloat16 device_map = "auto" # 自动分配设备 device = "cuda" if torch.cuda.is_available() else "cpu" # 加载基础模型 model = AutoModelForCausalLM.from_pretrained( model_path, device_map=device_map, # 使用明确的设备映射 dtype=dtype, trust_remote_code=True ).eval() # 设置为评估模式 # 加载LoRA权重 model = PeftModel.from_pretrained(model, model_id=lora_path) # 测试对话 inputs = tokenizer.apply_chat_template( [ {"role": "user", "content": "你是一个智能会议语音助手,请根据用户指令输出正确的指令和参数"}, {"role": "user", "content": instruct} ], add_generation_prompt=True, tokenize=True, return_tensors="pt", return_dict=True ) # 将输入移动到与模型相同的设备 inputs = inputs.to(device) # 生成配置 gen_kwargs = {"max_length": 2500, "do_sample": True, "top_k": 1} # 生成回复 with torch.no_grad(): outputs = model.generate(**inputs, **gen_kwargs) # 提取生成的部分 outputs = outputs[:, inputs['input_ids'].shape[1]:] # 解码并打印结果 print(tokenizer.decode(outputs[0], skip_special_tokens=True))def parse_args(): """ 解析命令行参数 :return: 解析后的参数对象 """ parser = argparse.ArgumentParser(description="测试LoRA微调后的Qwen2.5-0.5B-Instruct模型") # 必选参数:用户指令 parser.add_argument("--instruct", type=str, required=True, help="用户指令内容,例如:'打开麦克风'") # 可选参数:是否使用CPU,默认使用CPU parser.add_argument("--cpu", action="store_true", help="是否使用CPU进行推理(默认使用CPU)") # 可选参数:是否使用GPU parser.add_argument("--gpu", action="store_true", help="是否使用GPU进行推理(优先级高于--cpu)") return parser.parse_args()if __name__ == "__main__": # 解析命令行参数 args = parse_args() # 确定是否使用CPU # 如果同时指定了--cpu和--gpu,优先使用GPU use_cpu = not args.gpu and args.cpu # 调用测试函数 test(instruct=args.instruct, cpu=use_cpu)测试命令:
python test_lora.py经实测,用这些训练数据集训练后的模型,对指令识别的准确率提升还是非常的明显的,不过前面经过几轮次的微调训练、测试验证、再微调训练、测试验证,在我当前的需求规格下识别的准确率仍然未能达到100%(最后一版是96.47%),所以后面还需要再把意图/配位(参数)识别错误的一些命令词再补充整理一下,然后再继续来做微调训练。
四、补充说明
再补充一些基础知识(废话),懂的人不需要看。
1. 一点额外的信息
在我实际的测试中,用Qwen3-4B-Instruct-2507这个模型可以达到100%的准确率,但是这个模型无法放到端侧上用。 如果你的目标是一个后端/云端的助手的话,可以直接考虑这个模型; 但是如果你的目标也跟我一样,希望可以在端侧完全自主的完成整个指令、助手的功能的话,那可以跟我一样选择Qwen2.5-0.5B-Instruct。
Qwen3也有一些小模型,比如:Qwen3-0.6B。但是这个模型经测试,对指令的解析效果超级差,所以我放弃了它,转回到Qwen2.5-0.5B-Instruct这个老一代的模型。不过这个可能跟需求也有一定关系,大家有时间的话,也可以自己部署测试一下效果。
2. 为什么要微调?
- 能力考量
- 让大模型在特定领域上增强。假设你们公司是做视频会议的,然后你们的视频会议在某些功能、特性上又跟通用的视频会议不太一样[后面省略一万字]。
- 学习新的知识。大模型训练的时候肯定没有一些你或者你们公司的未披露到网上的信息[后面省略一万字]。
- 性能考量
- 减少幻觉:微调可以减少生成虚假或不相关信息的情况
- 提高一致性:适当的温度设置,通常能产生高质量且富有创意的结果,尽管每次输出的内容不同,但质量始终保持在一个较高水平,而不是有时好有时差。
- 避免输出不必要的信息:遇到ZZ、宗教或者其它一些你认为敏感问题时,可让模型拒答。
- 降低延迟:通过优化和微调,让较小参数的模型也可以实现不错的效果,从而降低硬件要求,降低延迟。
- 成本考量
- 自己训练基座大模型是大公司(很花钱)的事情。在开源模型上微调,使用一些量化(减少精度)微调方式,可以大大降低门槛,还可以得到不错的效果。
- 降低使用成本:微调的模型与通用模型比,模型的参数量会可以更少,整体的成本也可做到更低。
- 更大的控制权:可以通过模型参数量、使用的资源,自主平衡模型性能、耗时、吞吐量等,为成本优化提供了空间。
3. 数据是关键
在现在这个时代,完全可以说,大部分的技术都是没有什么价值的,因为大部分的技术都是有手就行。 真正的价值都在数据,而且最有价值的数据往往都是一个个的专业领域的数据,决定大模型微调效果的是数据,决定你整个产品成败的也是数据,这个事情一定要搞清楚。 所以呢,建议大家从现在开始,给自己好好做积累吧,把你的行业数据、专业领域的数据一点点积累好,这才是你的未来。
AI时代,未来的就业机会在哪里?
答案就藏在大模型的浪潮里。从ChatGPT、DeepSeek等日常工具,到自然语言处理、计算机视觉、多模态等核心领域,技术普惠化、应用垂直化与生态开源化正催生Prompt工程师、自然语言处理、计算机视觉工程师、大模型算法工程师、AI应用产品经理等AI岗位。
掌握大模型技能,就是把握高薪未来。
那么,普通人如何抓住大模型风口?
AI技术的普及对个人能力提出了新的要求,在AI时代,持续学习和适应新技术变得尤为重要。无论是企业还是个人,都需要不断更新知识体系,提升与AI协作的能力,以适应不断变化的工作环境。
因此,这里给大家整理了一份《2025最新大模型全套学习资源》,包括2025最新大模型学习路线、大模型书籍、视频教程、项目实战、最新行业报告、面试题等,带你从零基础入门到精通,快速掌握大模型技术!
由于篇幅有限,有需要的小伙伴可以扫码获取!
1. 成长路线图&学习规划
要学习一门新的技术,作为新手一定要先学习成长路线图,方向不对,努力白费。这里,我们为新手和想要进一步提升的专业人士准备了一份详细的学习成长路线图和规划。
2. 大模型经典PDF书籍
书籍和学习文档资料是学习大模型过程中必不可少的,我们精选了一系列深入探讨大模型技术的书籍和学习文档,它们由领域内的顶尖专家撰写,内容全面、深入、详尽,为你学习大模型提供坚实的理论基础。(书籍含电子版PDF)
3. 大模型视频教程
对于很多自学或者没有基础的同学来说,书籍这些纯文字类的学习教材会觉得比较晦涩难以理解,因此,我们提供了丰富的大模型视频教程,以动态、形象的方式展示技术概念,帮助你更快、更轻松地掌握核心知识。
4. 大模型项目实战
学以致用,当你的理论知识积累到一定程度,就需要通过项目实战,在实际操作中检验和巩固你所学到的知识,同时为你找工作和职业发展打下坚实的基础。
5. 大模型行业报告
行业分析主要包括对不同行业的现状、趋势、问题、机会等进行系统地调研和评估,以了解哪些行业更适合引入大模型的技术和应用,以及在哪些方面可以发挥大模型的优势。
6. 大模型面试题
面试不仅是技术的较量,更需要充分的准备。
在你已经掌握了大模型技术之后,就需要开始准备面试,我们将提供精心整理的大模型面试题库,涵盖当前面试中可能遇到的各种技术问题,让你在面试中游刃有余。
为什么大家都在学AI大模型?
随着AI技术的发展,企业对人才的需求从“单一技术”转向 “AI+行业”双背景。企业对人才的需求从“单一技术”转向 “AI+行业”双背景。金融+AI、制造+AI、医疗+AI等跨界岗位薪资涨幅达30%-50%。
同时很多人面临优化裁员,近期科技巨头英特尔裁员2万人,传统岗位不断缩减,因此转行AI势在必行!
这些资料有用吗?
这份资料由我们和鲁为民博士(北京清华大学学士和美国加州理工学院博士)共同整理,现任上海殷泊信息科技CEO,其创立的MoPaaS云平台获Forrester全球’强劲表现者’认证,服务航天科工、国家电网等1000+企业,以第一作者在IEEE Transactions发表论文50+篇,获NASA JPL火星探测系统强化学习专利等35项中美专利。本套AI大模型课程由清华大学-加州理工双料博士、吴文俊人工智能奖得主鲁为民教授领衔研发。
资料内容涵盖了从入门到进阶的各类视频教程和实战项目,无论你是小白还是有些技术基础的技术人员,这份资料都绝对能帮助你提升薪资待遇,转行大模型岗位。
大模型全套学习资料已整理打包,有需要的小伙伴可以
微信扫描下方CSDN官方认证二维码,免费领取【保证100%免费】