单GPU使用QLoRa微调Mistral 7B教程
微调这件事,听起来可能有点高深,但说白了,就是拿我们手头的数据,去调教一个现成的开源大模型,让它在特定领域表现得更加出色。这套打法意味着,只要你手里有足够多的行业数据,你就能打造一个属于自己的、专业级的LLM。 虽然微调的概念简单(而且威力巨大),但真要动手去微调一个大模型,事情就没那么轻松了。最大
微调这件事,听起来可能有点高深,但说白了,就是拿我们手头的数据,去调教一个现成的开源大模型,让它在特定领域表现得更加出色。这套打法意味着,只要你手里有足够多的行业数据,你就能打造一个属于自己的、专业级的LLM。

虽然微调的概念简单(而且威力巨大),但真要动手去微调一个大模型,事情就没那么轻松了。最大的拦路虎就是那个让人头疼的成本——LLM的计算开销实在太贵了。举个例子,一个70B参数的大模型,如果想做全参数的微调,算下来需要超过1TB的内存。而一张顶级的A100 GPU(售价高达2万美元),显存也就80GB。换句话说,要想把模型训起来,光买显卡就得砸进去几十万美元。
别急,目前已经有办法在消费级别的显卡上完成这个任务了。其中最主流的一种方案,就是QLoRA。今天我们就来聊透它,并且手把手示范,怎么用QLoRA微调目前公认最好的开源模型之一——Mistral-7B,最终生成一个能模仿视频博主自动回复YouTube评论的聊天机器人。
什么是量化?
既然聊QLoRA,就绕不开“量化”这个词。虽然听起来有点吓人,但它的道理并不复杂。所谓“量化”,其实就是把某个范围内的数字进行分块处理。打个比方,0到100之间有无穷无尽的实数,比如1、12、27、55.3、83.7823等等。我们可以按整数来分块,这样数字序列就变成了(1, 12, 27, 55, 83)。要是按十的因数来分,那结果可能就成了(0, 0, 20, 50, 80)。
为什么我们需要量化
量化最大的好处,是能用更少的信息去表示一组数字。先简单回顾一下计算机的工作原理:计算机内部是用0和1来给信息编码的。比如,要让电脑记下“83.7823”这个数字,就得把它转成0和1组成的字符串。FP32(单精度浮点格式)就是其中一种方式,它用32位序列来存这个数。转换成二进制后,83.7823就变成了01000010101001111001000010001010这么一串。
用FP32表示数字,精度确实很高,但代价也很大。如果LLM的每个参数都用FP32存,那每个参数要占4个字节(1个字节=8位)。算下来,一个10B参数的模型就要消耗40GB内存。这还是保守估计——真要去做全参数微调,实际需要接近200GB的内存。这就导致了一个两难的局面:我们希望训练后模型参数保持高精度,又想微调过程中尽量少占内存。QLoRA之所以厉害,正是因为它在这两者之间找到了一个关键平衡。
QLoRA
QLoRA(量化低阶适应)的核心,是把4个核心要素组合在一起,在不牺牲模型性能的前提下,尽可能压榨机器的有限内存。下面简单拆解一下。更深入的细节,建议直接去读QLoRA原始论文。
要素1:4位NormalFloat
QLoRA用了一种叫4位NormalFloat的特殊数据类型,它只用4位来给数字编码。这意味着我们总共只有2⁴(也就是16)个块去表示模型的参数。看上去很少,但4位NormalFloat用了点小技巧,硬是从有限的信息容量里榨出了更多信息。
常见的量化方式就是前面提到的等间距分块。但还有一种效率更高的办法,是按信息量相等的原则来分块——我们看到的那个示意图,正好说明了这两种方式的差异。更具体地说,4位NormalFloat是针对正态分布数据、信息理论上最优的量化策略。模型的参数往往集中在0附近,所以用这个策略来量化LLM的参数,效果特别好。
要素2:双量化
顾名思义,就是对已经量化过的参数再干一次量化的事。考虑一下这个过程:假设我们手里有个FP32张量,最简单的量化方式就是用一个数学公式,把FP32的数字转成[-127, 127]范围内的Int8表示。操作中要对数值做四舍五入,同时引入一个量化常数c^FP32来简化计算。
这种朴素的方法虽然在实际中不会只用这么简单的策略(别忘了我们在4位NormalFloat里看到的技巧),但它说明了一件事:量化本身是需要计算开销的,因为最后还要把结果常量存到内存里。为了最小化这种开销,最直接的想法就是只做一次——也就是所有模型参数只算一个统一的量化常数。但这样做并不好,因为它对极值过于敏感。由于c^FP32里有个absmax()函数,一个相对较大的参数值可能会把所有其他参数都带歪。
另一种思路是把模型参数切成更小的块,逐块去做量化。这样能有效降低大值参数扭曲其他参数的风险,但代价是内存占用会更大。为了抵消这部分增加的成本,我们可以在这些逐块生成的常量上再搞一次量化。比如块大小为64时,FP32量化常数会增加0.5位/参数。如果再把这些常量进一步量化成8位,那么每个参数占用的额外空间就能降到0.127位。
要素3:分页优化器
这个要素借助了Nvidia提供的一个特性,用来防止训练过程中间出现内存不足(OOM)的错误。当GPU的显存快被挤爆时,它会自动把一部分内存从GPU转移到CPU上去。要知道,LLM训练过程中偶尔会出现内存峰值,如果不做这个处理,训练进程很可能就直接崩溃了。
要素4:LoRA
LoRA(低秩适应)是参数高效微调(PEFT)的一种方法。它的核心思路很简单:不重新训练所有模型参数,而是在保持原始参数不变的前提下,额外添加一组可训练的参数。这样做可以把需要训练的参数数量减少100到1000倍,而模型性能基本不会受到什么影响。
将所有要素整合在一起:QLoRA
了解了各个要素,接下来看它们是怎么组合起来的。先考虑一个10B模型的标准微调过程,也就是把每个参数都重新练一遍。如果模型参数和梯度都用FP16(每个参数占4个字节,总共40GB),优化器状态(动量、方差等)用FP32(每个参数12个字节,总共120GB),那么一个10B模型微调总共需要大约160GB内存。
用了LoRA之后,情况就大为改观。LoRA冻结了原始LLM的参数,只通过一组适配器(Adapter)进行训练,极大减少了可训练参数的数量。参数和梯度的计算成本和原来一样(4字节/参数,40GB)。省下的部分来自优化器状态:如果可训练参数减少100倍(全部来自Adapter),那么这0.1B的Adapter模型,参数更新需要4字节/参数,优化器状态更新需要12字节/参数,加起来才1.6GB。所以整个10B模型现在只需要大约41.6GB内存——虽然省了不少,但想在家用级别的硬件上跑,依然很难。
QLoRA在LoRA的基础上更进一步。它用前两个要素(4位NormalFloat和双量化)先对原始模型参数进行量化,把训练成本从4字节/参数一下压到大约1字节/参数。然后再加上LoRA的消耗(约0.16字节/参数),一个10B模型微调只需要11.6GB内存。这个量级的资源消耗,在Google Colab上免费提供的T4 GPU都能轻松跑起来。
示例代码:微调Mistral-7b-Instruct来回复YouTube评论
理论讲完,直接上手。下面演示怎么用QLoRA微调Mistral-7B,代码在Google Colab上就能跑起来。
导入模块
从Hugging Face的Transformers、Peft和datasets库导入所需模块。
import transformers from transformers import pipeline from peft import prepare_model_for_kbit_training, get_peft_model from datasets import load_dataset
再安装几个依赖:
!pip install auto-gptq !pip install optimization !pip install bitsandbytes
负载基础模型和分词器
接下来从Hugging Face加载量化后的模型。这里选的是The Bloke发布的Mistral-7B-Instruct模型,已经用4-bit量化过了。除了指定模型仓库的地址,还要设置几个参数:device_map让代码自动分配计算资源,trust_remote_code=False是为了防止自定义模型文件在本地运行,revision则指定要拉取的模型版本。
model_name = "TheBloke/Mistral-7B-Instruct-v0.2-GPTQ"
model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map="auto",
trust_remote_code=False,
revision="main"
)
加载之后就能看到,一个7B参数的大模型只占用了4.16GB内存,完全可以在Colab的免费GPU或CPU上跑起来。接着加载分词器:
tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=True)
使用基本模型
先来测试一下原始模型的效果。输入一条“Great content, thank you!”,模拟YouTube评论。运行代码后,模型给出了一个回复——看上去开头还行,但整体有些啰嗦,完全不像真人博主的说话风格。
model.eval()
comment = "Great content, thank you!"
prompt = f"")
inputs = tokenizer(prompt, return_tensors="pt")
outputs = model.generate(input_ids=inputs["input_ids"].cuda(),
max_new_tokens=512)
print(tokenizer.decode(outputs[0]))
输出结果大概是这样的:
"Itopics youhelp. the meantime, Icontent Iyouup-- information.Thanks reading, I look forward helping you any questions you may ha ve!"
显然还有不小差距。
准备训练模型
下面来看看怎么通过微调来提升模型的表现。首先要开启梯度检查点——这是一种节省内存的技术,会在反向传播时清除并重新计算某些激活值:
model.train() model.gradient_checkpointing_enable() model = prepare_model_for_kbit_training(model)
然后通过配置config来设置LoRA训练参数,打印出可训练参数的数量后会发现,减少了超过100倍。
config = LoraConfig(
r=8,
lora_alpha=32,
target_modules=["q_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, config)
model.print_trainable_parameters()
准备训练数据集
接下来导入训练数据。这个数据集可以从HuggingFace Dataset Hub获取,是利用某个YouTube频道评论区里的真实评论和回复生成的。生成数据集的完整代码在GitHub上可以找到。
data = load_dataset("shawhin/shawgpt-youtube-comments")
准备好数据集之后,需要做tokenization处理,并确保每条数据的长度都合适:
def tokenize_function(examples):
text = examples["text"]
tokenizer.truncation_side = "left"
tokenized_inputs = tokenizer(
text,
return_tensors="pt",
truncation=True,
max_length=512
)
return tokenized_inputs
tokenized_data = data.map(tokenize_function, batched=True)
tokenizer.pad_token = tokenizer.eos_token
data_collator = Transformers.DataCollatorForLanguageModeling(tokenizer, mlm=False)
微调模型
下面定义训练的超参数:
lr = 2e-4
batch_size = 4
num_epochs = 10
training_args = transformers.TrainingArguments(
output_dir="shawgpt-ft",
learning_rate=lr,
per_device_train_batch_size=batch_size,
per_device_eval_batch_size=batch_size,
num_train_epochs=num_epochs,
weight_decay=0.01,
logging_strategy="epoch",
evaluation_strategy="epoch",
sa ve_strategy="epoch",
load_best_model_at_end=True,
gradient_accumulation_steps=4,
warmup_steps=2,
fp16=True,
optim="paged_adamw_8bit",
)
参数不少,但在QLoRA上下文中重点提两个:fp16=True让训练器使用FP16值,比标准FP32省不少显存;optim=”paged_adamw_8bit”则启用了前面提到的分页优化器(要素3)。设置完之后,直接用下面这段代码启动训练:
trainer = Transformers.Trainer(
model=model,
train_dataset=tokenized_data["train"],
eval_dataset=tokenized_data["test"],
args=training_args,
data_collator=data_collator,
)
model.config.use_cache = False
trainer.train()
model.config.use_cache = True
因为训练集只有50个样本,整个过程大概10分钟左右就能跑完。
加载微调模型
最终训练好的模型已经上传到HF Hub。如果想跳过训练过程直接拿它来用,可以直接加载:
from peft import PeftModel, PeftConfig
from transformers import AutoModelForCausalLM
model_name = "TheBloke/Mistral-7B-Instruct-v0.2-GPTQ"
model = AutoModelForCausalLM.from_pretrained(model_name,
device_map="auto",
trust_remote_code=False,
revision="main")
config = PeftConfig.from_pretrained("shawhin/shawgpt-ft")
model = PeftModel.from_pretrained(model, "shawhin/shawgpt-ft")
tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=True)
使用微调模型
对微调后的模型做推理,还是刚才那条“Great content, thank you!”,看它现在怎么回:
"Glad you enjoyed it! know you ha ve any questions."
和微调之前比,这个回复风格更加接近真人博主,言简意赅,也更加贴切。
总结
QLoRA确实大幅降低了训练自己大语言模型的门槛。这篇文章梳理了它的核心技术原理,并展示了一个从零开始的完整案例——用QLoRA微调Mistral-7B,做一个能自动回复YouTube评论的聊天机器人。虽然微调后的模型在模仿博主回复风格方面做得相当不错,但在处理专业数据科学知识时仍有不足,这个问题可以通过结合RAG(检索增强生成)来进一步改善。
你是一名 AI 行业编辑,请围绕下面这条热点输出一份资讯解读:
热点:单GPU使用QLoRa微调Mistral 7B教程要求:
1. 先用一句话解释这条热点在讲什么
2. 再总结它为什么重要
3. 说明会影响哪些 AI 产品或内容方向
4. 最后给出 3 个适合资讯站使用的标题
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
相关热点我国科研机构与钢铁企业联合攻关,成功研发出国产“极地钢”。该材料攻克了钢材在极寒环境下易变脆的核心难题,通过在钢材中添加特殊元素并优化工艺,使其能在零下60摄氏度的低温中保持韧性。同时,新材料还具备优异的抗冰层冲击能力,且实现了更薄更轻。目前该产品已启动认证流程,并有型号率先获得中国船级社认证,标志
在近期举办的年度影像节上,美图公司发布了Picchi、Artflo等四款AI新品,并对站酷、美图设计室等四款产品进行了升级,全面覆盖多元创作场景。公司战略核心从提供“AI工作流”功能转向直接“交付成果”,并同步发布了基于MoE架构的美图奇想大模型V6以提供技术支撑。数据显示,该模型已成为驱动其产品A
苹果CEO蒂姆·库克近日确认,由于数据中心需求激增导致内存芯片供应紧张、成本飙升,苹果计划提高产品售价。库克指出DRAM市场形势尤为严峻,并首次公开表示应考虑审视所有供应,包括与中国内存供应商合作的可能性。为应对短缺,苹果准备动用现金储备增加采购,但无意自建芯片工厂。具体的涨价时间、幅度及涉及产
近期,全国多地取消已实行40多年的固定分时电价机制,这一变化主要影响工商业用电,特别是公共充电桩的电价形成方式。新能源车主发现晚间高峰时段充电费用上涨,原因是充电桩电价不再有政府固定的峰谷价差,转而随电力市场供需实时浮动。政策并未废除分时计价,而是将定价逻辑转向市场化,由市场动态决定峰谷时段和价差。
- 日榜
- 周榜
- 月榜
热点快看
