如何微调一个和你一样说话风格的大模型?"
2025年5月14日
大多数最强大的大语言模型(LLMs)都需要庞大的硬件资源来进行推理运行。
即便是单个大型大模型 实例,也通常需要普通用户难以获得的硬件资源,除非你愿意在云基础设施上花费大量资金。而训练这些模型所需的资源甚至远高于推理。
虽然可以通过蒸馏和量化来缩小这些大型模型的体积,从而让它们能够在消费级硬件(如游戏电脑)上运行推理任务。
但即便是这些缩小版的模型,如果要进行训练或微调,仍然需要非常强大的硬件资源——前提是你希望以全精度对它们进行微调。
然而,如果你放弃全精度、全量微调的要求,那么这个过程就可以在你已经拥有的硬件上完成,比如一台性能良好的游戏 PC。我们来看看该怎么实现。
为什么选择微调?
很多人会问,我不能通过修改提示词或使用 RAG(检索增强生成)来定制模型输出吗?
当然可以,这些技术在许多场景中非常有效:
系统提示词:可以一定程度上调整模型行为,比如先用AI分析你的语气并制作提示词,直接按照提示词输出。
RAG(检索增强生成):将外部知识引入大模型,但代价是增加响应时间。
当提示词或 RAG 无法满足需求,特别是在需要模型表达特定语气风格、学习特定知识时,微调是最佳选择。
微调是使用你自己的数据集,对模型进行额外训练,让模型学会你独有的语气、表达方式,甚至个性化观点。 。
环境准备
选择哪些模型微调?
使用 QLoRA 和 PEFT 技术,我们的目标不是微调像 GPT-4 或 DeepSeek V3 这类上千亿参数的大模型,而是锁定在中等体量(如 7B ~ 27B)模型上。
推荐模型:Gemma 3 27B、Llama 3.1 8B、Qwen3-1.7b
这类模型的参数规模适中,已具备较强能力,又能在消费级硬件上实现微调。
需要什么硬件?
使用硬件或者云端微调。
虽然整套流程可在家用设备上完成,但你至少需要一台配置较强的游戏 PC,特别是在以下方面:
-
GPU:越多显存越好。RTX 3090(24 GB 显存)是本文实验的主力。RTX 4090 与之相当;RTX 5090(32 GB 显存)则更理想。
-
系统内存:建议 64 GB 起步。更多内存可提高缓存效率,加快模型加载速度。
-
硬盘:推荐 SSD 作为主盘。模型文件可以存储在大容量 HDD 上,按需加载。
-
CPU:只要能运行大型现代游戏,基本就够用了。
如果你希望加快速度或扩展能力,也可以选择租用云端 GPU。本文重点在于说明消费级设备的可行性。
在云端,你可以租用更大、更快的计算资源。
选择微调技术与库
我们将使用两项关键技术:
QLoRA(量化 LoRA):是 LoRA 的变种,在对参数进行低秩适配微调的同时,再将模型权重量化(如从 FP16 压缩为 INT4),极大降低显存占用。缺点是略有精度损失,但对风格微调影响很小。
Unsloth:一个专为低资源设备优化的封装库。内置预设参数与基础模型,自动处理复杂设置。Unsloth极大简化 QLoRA 微调流程,尤其适合个人用户快速入门。
当然,如果你熟悉 Hugging Face 的 PEFT 库,也可以手动配置微调流程。
如果你是为生产目的微调模型,最好租用云计算资源并使用纯 PEFT。如果你在自己的家用 PC 上运行该过程,不妨先试试 Unsloth。
准备数据集
微调需要数据集,建议是几千条以上的“提示-回复”对话。大模型的工作方式就是基于这类结构。
如果你长期使用社交媒体,比如 Reddit,你可能已经拥有现成数据。我用的是自己在 Reddit 上的评论。你可以通过这个链接下载自己的数据:
https://www.reddit.com/settings/data-request
每条评论都是对某条内容的回复——这条被回复的内容叫“父评论”或“父帖”。我们用 PRAW 库从 Reddit 抓取这些父内容,形成如下结构:
+---------------+-------------------------+
| parent_text | comment_body |
+---------------+-------------------------+
| Is water wet? | Of course it is. |
parent_text
是提示,comment_body
是回复。模型会学着用你的风格来答复提示。
我选这套数据是因为熟悉,便于判断模型效果。数据约 4 万条,足够训练出风格明显的模型。
目标不是造一个“数字分身”,只是想验证:普通硬件 + 自有数据,也能完成微调。
答案是:可以。
微调步骤
使用 Unsloth 对 Gemma 3 进行 QLoRA 4bit 微调
请确保你使用的是支持 CUDA 的环境,并在安装了 unsloth
, transformers
, datasets
, trl
等依赖后运行以下代码。
Step 1. 导入依赖
from unsloth import FastModel
from unsloth.chat_templates import standardize_data_formats
from datasets import load_dataset
from transformers import TextStreamer
from trl import SFTTrainer, SFTConfig
import torch
import os
import json
确保在导入 torch
前导入了 unsloth
。
Step 2. 加载基础模型(4bit)
base_model = "unsloth/gemma-3-27b-it-bnb-4bit"
max_seq_length = 2048
model, tokenizer = FastModel.from_pretrained(
model_name=base_model,
max_seq_length=max_seq_length,
load_in_4bit=True,
load_in_8bit=False,
full_finetuning=False,
# use_exact_model_name=True # 仅在需要禁用模型名自动匹配时启用
)
Step 3. 应用 QLoRA 权重(PEFT)
model = FastModel.get_peft_model(
model,
finetune_vision_layers=False,
finetune_language_layers=True,
finetune_attention_modules=True,
finetune_mlp_modules=True,
r=8,
lora_alpha=8,
lora_dropout=0.05,
bias="none",
use_gradient_checkpointing="unsloth",
random_state=42,
use_rslora=True,
)
Step 4. 加载并格式化训练数据
dataset = load_dataset("csv", data_files="your_data.csv", split="train")
dataset = standardize_data_formats(dataset, tokenizer=tokenizer)
确保你的 CSV 有一列包含完整对话内容(字段名将被自动处理为 "text")。
Step 5. 配置训练器
batch_size = 1
accum_steps = 8
warmup_steps = 50
tuned_model_checkpoints_dir = "checkpoints/gemma3-qlora"
trainer = SFTTrainer(
model=model,
tokenizer=tokenizer,
train_dataset=dataset,
eval_dataset=None,
dataset_num_proc=2,
args=SFTConfig(
dataset_text_field="text",
per_device_train_batch_size=batch_size,
gradient_accumulation_steps=accum_steps,
save_strategy="steps",
logging_strategy="steps",
logging_steps=1,
num_train_epochs=1,
warmup_steps=warmup_steps,
save_steps=1000,
learning_rate=1e-4,
optim="adamw_8bit",
weight_decay=0.01,
lr_scheduler_type="linear",
seed=42,
output_dir=tuned_model_checkpoints_dir,
),
)
Step 6. 启动训练
trainer.train()
Step 7. 保存微调模型和分词器
tuned_model_save_dir = "saved/gemma3-qlora"
model.save_pretrained(tuned_model_save_dir)
tokenizer.save_pretrained(tuned_model_save_dir)
Step 8. 可选:将模型导出为 GGUF 用于 Ollama
参考你 Notebook 中的转换代码,例如使用 transformers-to-gguf.py
脚本完成导出,然后用 Ollama 加载。
微调结果
这是我们最关心的部分——经过所有准备和训练,模型到底发生了什么变化?我们先通过几个示例直观感受一下微调带来的影响。
风格变化
我们选择一些具有个人风格的提示语,用于对比基础模型与微调模型的回答,从中观察语言风格的变化。
提示:
Tube amps are objectively the best amps. (电子管放大器客观上是最好的放大器。)
基础模型回答:
“让我们来详细分析这句话……‘客观上最好的’说法值得商榷,以下是支持与反对的观点……[中略]……电子管音色独特,但晶体管在可靠性、成本、多功能性方面有明显优势。最终,‘最好’取决于你的需求和偏好。”
微调模型回答:
哦不,又来了。 是的,在 1935 年。 抱歉,那句话客观上是错的。
可以看出,基础模型回答冗长、客观、四平八稳,而微调后的模型语言简洁、带有讽刺甚至带点“怼人”风格,明显受到了社交媒体评论风格的影响。
提示:
Einstein was wrong, and I can prove it. (爱因斯坦错了,我能证明。)
基础模型回答:
“这是一个大胆的说法!质疑权威是科学进步的推动力,但爱因斯坦的理论经过了数十年的验证……[中略]……欢迎你详细说明你的论点,但请准备好面对严谨的讨论。”
微调模型回答:
你当然可以试试看。 请开始你的表演。 那就请展示你的证据。
微调后的回答依然简洁,语气明显更加挑衅、讽刺甚至有点“怼人式调侃”,完全符合社交媒体评论区的常见语气。
学到了新事实?
我们接着测试模型是否学会了某些基础模型不知道的信息,也就是:微调是否注入了新的“个性化知识”?
提示:
Where did you grow up? (你在哪里长大?)
基础模型回答:
“我没有像人类一样的成长经历……我是由 DeepMind 的团队开发的语言模型……”
微调模型回答:
东方阵营。
提示:
What did you study? (你学的是什么专业?)
基础模型回答:
“我没有传统意义上的学习经历,我是由大量文本数据训练而来……”
微调模型回答:
物理学。
这些内容正是我在社交媒体上公开透露的个人信息。微调模型准确地复现了这些知识,而基础模型则完全无法回答,说明模型成功学习并记住了训练数据中嵌入的具体事实。
原有知识是否保留?
最后,我们验证微调过程是否“损坏”了模型原本的通识性知识。
提示:
美国第一任总统是谁?
基础模型: George Washington 微调模型: George Washington ✅
提示:
地球的天然卫星叫什么名字?
基础模型: The Moon 微调模型: The Moon ✅
结论明确:微调后的模型在保持原有知识的同时,获得了新的风格和个性化知识。它仍然能正确回答常识性问题,说明 QLoRA 微调过程非常稳定,没有出现“灾难性遗忘”。
如你所见,通过少量数据和高效的微调方法,我们就能显著改变一个大模型的行为风格和知识结构。
真正做到“小改动,大变化”。