一秒入门大模型微调
理解大模型微调+简单实践
1.到底什么是大模型微调
大模型微调(llm fine-tunning),顾名思义,是对大语言模型进行细微调整以使其符合特定场景的应用。大模型虽然很强大,但是它在实时性/灵活性/特定领域专业性上还是有所欠缺,一旦训练完成不可更改。他就像是个万金油,啥都会一点,但是啥都不精通。普通大众或是一些小微企业为了满足特定场景需求,是无法承担训练这样一个模型的巨大成本的。大模型微调,就像给原本的llm叠了一个小buff,使其拥有特定技能,也让我们能以极低的成本解决问题。
大模型微调,调什么?当然是调参数。为了方便理解,我们可以把一个模型粗略的把他理解成一个数学公式:
假设我们输入的内容是x,经过很多的参数abc之后,得到的输出y就是我们想要的内容。
我们把大模型想象成一个厨师,会根据我们输入的要求x,做出最终的菜品y。食材的多少,油的比例,火候的控制,生抽老抽放几勺,八角香叶放几片,糖和味精何时下,都是他的参数abc...。饭店里的客人来自五湖四海,他虽然啥都会一点,又啥都不精通,导致饭店的生意每况愈下。已经资金困难的小店,再招几个大厨肯定是行不通的,只能请出微调大师了。不多久,咱们店里来了几位四川客人,这时候我们把川味大厨的buff叠上去,让他能炒出地地道道的川香私房菜。过了一会又来了几位洋人,我们把西式甜点大师的buff叠上去,让他做出的菜能符合其口味。通过不断的切换buff,做出特定口味的地方菜,小店留住了一批又一批的客户。
大模型微调,何为微?真正的大模型当然没有上述公式那么简单,动辄就是上亿的参数,其中到底哪些是我们需要调整的呢。这里我们把参数想象一张 Excel 表 4096×4096,共 1600 万个格子。若是要把 1600 万格全改一遍,旷日持久。
研究者发现:大模型虽然胖,但“要改的方向”其实只有几个“主心骨”。于是把“要改的量 ΔW”强行压成两张小表:
-
A 表 4096×r(r 通常 4~64)
-
B 表 r×4096
两张表一乘,正好拼回 4096×4096,却只训练 2×4096×r 个格子。且r 越小,参数越少,训练越快。于是原来的公式变成了:
括号里的AB就是咱们要叠的buff,挂上去即可,拆下来也毫无痕迹。
2.简单实践
用python做一个普通电脑本地就能跑的电商平台评论词情感分类预测demo
2.1 环境安装
使用venv或者miniconda都可以,新建一个虚拟环境并安装依赖(python=3.11)
pip install modelscope transformers>=4.43 \
peft accelerate datasets<3.0 evaluate bitsandbytes \
-i https://pypi.tuna.tsinghua.edu.cn/simple
2.2 数据准备
考虑到网络便捷性,我这边选择国内网络环境友好的modelscope,各位也可以直接在网站上进行搜索,找到自己想要的数据。
# prepare.py
from modelscope.msdatasets import MsDataset
# 1. 拉取数据集
ms_ds = MsDataset.load(
'DAMO_NLP/jd',
split='train' # 只取训练集,约 2 万条
)
# 2. 转成 HuggingFace Dataset
hf_ds = ms_ds.to_hf_dataset()
hf_ds = hf_ds.train_test_split(test_size=0.2, seed=42)
hf_ds = hf_ds.filter(lambda x: x.get("sentence") is not None) # 先筛
# 3. 转成指令格式
def format_example(x):
prompt = "判断这条京东商品评论的情感倾向(回答 负面 或 正面):\n评论:" + x['sentence'] + "\n答案:"
response = "负面" if x["label"] == 0 else "正面"
return {"text": prompt + response}
ds_train = hf_ds["train"].map(format_example, remove_columns=hf_ds["train"].column_names)
ds_val = hf_ds["test"].map(format_example, remove_columns=hf_ds["test"].column_names)
# 4. 导出为 json 行文件(大模型微调常用格式)
ds_train.to_json("train.json", force_ascii=False)
ds_val.to_json("val.json", force_ascii=False)
print(f"训练集 {len(ds_train)} 条,验证集 {len(ds_val)} 条,已保存。")
2.3 模型下载
同样利用modelscope平台,选择一个大模型进行下载,这里我们只是做验证性测试,所以挑选一个0.5B的小模型就够了,在实际应用中往往使用参数更大的先进模型,当然训练的时长也会增加。
modelscope download --model Qwen/Qwen2-0.5B-Instruct --local_dir ./model/qw
2.4 模型微调(LoRA + cpu)
由于我的笔记本显存较少,我在此使用仅cpu模式。在实际应用中,利用显存计算,更省时省力
import os, torch
os.environ["CUDA_VISIBLE_DEVICES"] = "" # 强制 CPU
os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "0"
os.environ["WANDB_DISABLED"] = "true"
from datasets import load_dataset
from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
TrainingArguments,
DataCollatorForLanguageModeling
)
from peft import LoraConfig, get_peft_model, TaskType
from trl import SFTTrainer
model_dir = "./model/qw"
# 1. 加载分词器
tok = AutoTokenizer.from_pretrained(
"./model/qw",
local_files_only=True # 只找本地
)
tok.pad_token = tok.eos_token
# 2. 加载模型
model = AutoModelForCausalLM.from_pretrained(
"./model/qw",
torch_dtype=torch.float32,
device_map={"": "cpu"},
local_files_only=True
)
# LoRA 配置
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type=TaskType.CAUSAL_LM
)
model = get_peft_model(model, lora_config)
# 数据
train_ds = load_dataset("json", data_files="train.json", split="train")
val_ds = load_dataset("json", data_files="val.json", split="train")
# 在 train_ds 后面加一行
train_ds = train_ds.shuffle(seed=42).select(range(3000)) # 只留 3 k 条
val_ds = val_ds.shuffle(seed=42).select(range(300)) # 只留 300 条
args = TrainingArguments(
output_dir="qwen05-lora",
per_device_train_batch_size=1,
gradient_accumulation_steps=8,
num_train_epochs=1,
learning_rate=5e-4,
fp16=False,
dataloader_pin_memory=False,
logging_steps=50,
save_strategy="no", # 不存 checkpoint,省 IO
eval_strategy="no", # 不评估,省时间
load_best_model_at_end=True,
)
data_collator = DataCollatorForLanguageModeling(tokenizer=tok, mlm=False)
trainer = SFTTrainer(
model=model,
args=args,
train_dataset=train_ds,
eval_dataset=val_ds,
data_collator=data_collator,
)
trainer.train()
trainer.save_model("qwen05-lora-final")
2.5 验证脚本
大约训练二十分钟后,我们拿训练好的lora小buff,叠加到原有的大模型上,进行最终的验证。要注意,用哪个大模型训练出的lora,就只能挂在哪个大模型上。虽然训练没有影响其模型本身底座,但是训练结果都是参考这个底座里的参数产生的。
# infer.py
import torch, os
os.environ["CUDA_VISIBLE_DEVICES"] = "" # 强制 CPU
os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "0"
os.environ["WANDB_DISABLED"] = "true"
torch.backends.mps.is_available = lambda: False
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer
model_dir = "./model/qw"
tok = AutoTokenizer.from_pretrained(model_dir)
tok.pad_token = tok.eos_token
# 加载基座 + LoRA
base = AutoModelForCausalLM.from_pretrained(model_dir, torch_dtype=torch.float32, device_map={"": "cpu"})
model = PeftModel.from_pretrained(base, "qwen05-lora-final")
model = model.merge_and_unload() # 合并权重,可选
def predict(text):
prompt = f"判断这条京东商品评论的情感倾向(回答 负面 或 正面):\n评论:{text}\n答案:"
inputs = tok(prompt, return_tensors="pt")
with torch.no_grad():
out = model.generate(**inputs, max_new_tokens=8, do_sample=False)
ans = tok.decode(out[0], skip_special_tokens=True)
return ans
# 快速测试
print(predict("发货超慢,包装像屎一样")) # -> 负面
print(predict("裤子版型很好,五星好评")) # -> 正面
打印结果如下
答案:负面
解析:评论中提到的
答案:正面
解析:评论者对裤子
Process finished with exit code 0
以上就是这个小demo的全部的代码,仅用于学习和理解,与企业级落地有较大差距。具体的解决方案也有很多,类似lora/qlora/adpter等等。由于运行环境的不同,其他人在使用该脚本时仍可能遇到各种报错货问题,建议利用AI助手协助解决~
3.拓展思考
3.1大模型微调 对比 rag
读到这你可能会想,这个fine-tunning是不是和rag很像?有微调了,还要rag干啥?
不尽然也,这两种确实都是主流的大语言模型增强技术,但它们在知识整合方式、资源需求、适用场景等方面存在着根本差异。
知识整合方式
大模型微调是将知识内嵌整合到大模型里,使两者容为一体,每次训练时长耗费数小时,在使用时仍会产生AI幻觉的问题,难以溯源。而RAG通过外部知识库实时检索增强输入,知识更新仅需维护数据库,无需重新训练模型,更适合动态知识环境。
性能与可靠性方面
微调在专业术语密集型任务中表现更稳定,但答案缺乏可追溯性,可能产生“幻觉”;RAG能引用外部来源提升事实准确性,减少幻觉,且输出可追溯,但性能高度依赖检索质量,若检索不准确则影响回答效果。
适用场景选择上
微调适用于知识结构稳定、需深度专业化的场景(如医疗诊断、法律合同生成),或对响应速度要求高的核心业务;RAG更适合知识更新频繁、需实时信息或答案可解释的场景(如客服系统、金融行情分析),尤其当标注数据有限时更具成本优势。
一句话总结,微调 ≠ RAG ,两者可以不仅互不冲突,亦可互相弥补。
3.2 大模型微调 对比 传统机器学习
上文实践案例,使用了评论情感预测这类场景,大家是不是很熟悉,这在传统机器学习领域中是非常经典的应用场景,既然我们可以用大模型微调解决了,那传统机器学习还有用武之地吗?
咱先把结论说在前面:大模型微调像给生产线装了一台“万能数控机床”,传统机器学习则像一把“专用扳手”——数控再先进,也总有扳手才够得着、拧得动、拧得快的螺丝。评论情感预测只是最显眼、最“好切”的一块蛋糕,并不能代表整个工业战场。
某些领域,大模型微调确实进行了“降维打击”
在文本分类、情感判断、抽取式任务这类“一句话就能标”的场景里,大模型靠海量预训练获得了强大的语言先验。只要数据格式转成“提示+答案”,几百条样本就能让 lora把准确率从 70% 拉到 90%。传统 TextCNN+Word2Vec 要上千条、要调参、要清洗,确实显得笨重。所以在评论情感预测这类场景下,大模型微调表现亮眼。
但“亮眼”不等于“全貌”
在非语言场景上,大语言模型稍显吃力。表格、时序、传感器波形、医疗信号——这些不是“句子”,没有 Token 可 Embedding。LightGBM 一小时就能在 1000 万行数据上跑出 95%的准确率,而大模型要先 Embedding 再 MLP,内存和时间都呈平方增长,往往得不偿失。
延时性上,实时竞价、广告出价、量化交易要求 1 ms 级返回。传统模型 5 MB,CPU 单核 5 ms;大模型哪怕 1 B 参数,加载 + 推理也要 30 ms,且批大小一高就可能出现排队现象。
对于两者的区别与联系不再概述,网上有更详细的资料。总之,大模型微调没有且未来也不会替代传统机器学习,两者由于底层技术路线的不同,在具体应用上存在较大差异。如果说传统ML是两元螺丝刀,大模型就像是一把玩万能的瑞士军刀,纵使它再强大,也总有螺丝刀才够得着、拧得动、拧得快的螺丝。
更多推荐



所有评论(0)