引言:为什么需要微调大语言模型
微调(Fine-tuning)是将预训练的大语言模型适配到特定任务或领域的关键步骤。预训练模型虽然在通用语言理解上表现出色,但在特定应用场景中往往需要更精确的控制和更专业的知识。通过微调,我们可以让模型更好地理解特定领域的术语、遵循特定的指令格式,或者在特定任务上达到更好的性能。
微调的核心价值在于:
领域适应
:让模型掌握特定领域的专业知识
任务优化
:提升模型在特定任务(如分类、生成、问答)上的表现
行为控制
:使模型遵循特定的响应风格或安全准则
成本效益
:相比从头训练,微调需要的计算资源和数据量都更少
第一部分:微调基础概念
1.1 什么是模型微调
模型微调是指在已经预训练好的模型基础上,使用特定任务的数据继续训练,使模型适应新任务的过程。想象一下,预训练模型就像一个通才大学生,而微调就是让这个通才去学习一门专业课程,使其成为某个领域的专家。
微调与预训练的主要区别:
预训练
:使用海量无标注数据,学习通用语言表示(耗时数周,需要TPU/ GPU集群)
微调
:使用相对少量的标注数据,学习特定任务模式(耗时数小时/天,单GPU即可)
1.2 微调的主要类型
全参数微调(Full Fine-tuning)
更新模型的所有参数。这种方法效果最好,但需要大量显存和存储空间(每个checkpoint都和原模型一样大)。
参数高效微调(PEFT)
只更新模型的一小部分参数,主要包括:
LoRA(Low-Rank Adaptation)
:在权重矩阵旁添加低秩矩阵,只训练这些新增参数
Adapter
:在Transformer层之间插入小型神经网络模块
Prompt Tuning
:只优化可学习的提示词嵌入
指令微调(Instruction Tuning)
在多种任务的指令-响应对上进行微调,让模型学会遵循指令。
1.3 微调前的准备工作
在开始微调前,需要明确以下几点:
任务定义
:明确你的模型需要完成什么任务(文本分类、生成、翻译等)
数据准备
:收集和整理高质量的训练数据
硬件评估
:根据模型大小和数据量选择合适的硬件
基线建立
:先在小数据集上测试,建立性能基线
第二部分:环境搭建与工具选择
2.1 硬件要求
微调不同规模模型的硬件建议:
模型参数量
最低显存要求
推荐配置
备注
1B-3B
8GB
RTX
3060
⁄
4060
适合入门学习
7B-13B
16GB-24GB
RTX
3090
⁄
4090
最常用的规模
30B+
40GB+
A100/H100
需要专业设备
注意
:使用LoRA等PEFT技术可以显著降低显存需求,7B模型在8GB显存上也能微调。
2.2 软件环境配置
基础环境安装
# 创建conda环境
conda create -n llm-finetune python=3.10
conda activate llm-finetune
# 安装PyTorch(根据CUDA版本选择)
pip install torch torchvision torchaudio –index-url https://download.pytorch.org/whl/cu118
# 安装核心库
pip install transformers datasets accelerate peft bitsandbytes trl
pip install jupyter pandas numpy scikit-learn
关键库说明
transformers
:Hugging Face的核心库,提供模型加载和推理
datasets
:数据集加载和处理
accelerate
:分布式训练支持
peft
:参数高效微调技术
bitsandbytes
:模型量化支持
trl
(Transformer Reinforcement Learning):支持RLHF和SFT
2.3 选择合适的微调框架
Hugging Face Transformers + PEFT
(推荐新手)
优点:生态完善,文档齐全,社区活跃
适用:大多数标准微调任务
LLaMA-Factory
优点:开箱即用,支持WebUI
1. 适合快速实验和可视化操作
Axolotl
优点:配置驱动,支持多种模型
适合:大规模训练和自动化
第三部分:数据准备与处理
3.1 数据格式要求
微调数据通常采用
指令-响应
格式。对于不同任务,格式略有不同:
对话格式(Chat Format)
{
“conversations”: [
{“role”: “user”, “content”: “你好,请介绍一下量子计算”},
{“role”: “assistant”, “content”: “量子计算是一种基于量子力学原理的计算方式…”}
]
}
指令格式(Instruction Format)
{
“instruction”: “将以下英文翻译成中文”,
“input”: “Hello, how are you?”,
“output”: “你好,你怎么样?”
}
文本分类格式
{
“text”: “这部电影真的很棒,演员表演出色”,
“label”: “positive”
}
3.2 数据质量要求
高质量的微调数据应具备:
准确性
:内容正确无误
一致性
:格式统一,风格一致
多样性
:覆盖任务的各种情况
相关性
:与目标任务高度相关
数据清洗示例代码
:
import pandas as pd
import re
def clean_data(df):
“””清洗微调数据”””
# 去除重复项
df = df.drop_duplicates(subset=[‘instruction’, ‘input’])
# 去除过长或过短的样本
df = df[
df[‘output’].str.len().between(10, 2000)
]
# 去除包含特殊字符的异常样本
df = df[~df[‘output’].str.contains(r'[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]’)]
# 标准化格式
df[‘instruction’] = df[‘instruction’].str.strip()
df[‘output’] = df[‘output’].str.strip()
return df
# 使用示例
# df = pd.read_json(‘raw_data.json’)
# cleaned_df = clean_data(df)
# cleaned_df.to_json(‘cleaned_data.json’, orient=’records’, force_ascii=False)
3.3 数据增强技巧
当数据量不足时,可以采用以下方法:
指令改写
:用不同方式表达相同指令
“总结以下文本” → “请对以下内容进行摘要”
“翻译成中文” → “将下列英文转换为中文”
角色设定
:为模型添加角色背景
“你是一位专业的医生,请回答…” → “你是一位经验丰富的儿科医生,请回答…”
few-shot示例
:在指令中加入示例
“示例1:输入A→输出B;现在请处理:输入C”
3.4 数据集划分
合理划分数据集对评估模型性能至关重要:
训练集
:80-90%,用于模型学习
验证集
:5-10%,用于调参和早停
测试集
:5-10%,用于最终评估
from sklearn.model_selection import train_test_split
# 划分数据集
train_val_df, test_df = train_test_split(
df, test_size=0.1, random_state=42, stratify=df[‘label’]
)
train_df, val_df = train_test_split(
train_val_df, test_size=0.111, random_state=42, stratify=train_val_df[‘label’]
)
print(f”训练集: {len(train_df)}”)
print(f”验证集: {len(val_df)}”)
print // 这里故意留一个错误,后面会说明如何发现和修复
第四部分:核心微调实践
4.1 使用Hugging Face进行基础微调
步骤1:加载模型和分词器
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
model_name = “THUDM/chatglm3-6b” # 以ChatGLM3为例
# 加载分词器
tokenizer = AutoTokenizer.from_pretrained(
model_name,
trust_remote_code=True
)
# 加载模型(使用4bit量化减少显存占用)
model = AutoModelForCausalLM.from_pretrained(
model_name,
load_in_4bit=True, # 4bit量化
device_map=”auto”, # 自动分配设备
torch_dtype=torch.float16,
trust_remote_code=True
)
步骤2:数据预处理
from datasets import Dataset
def format_for_training(example):
“””将数据格式化为模型可接受的输入”””
# 构建对话格式
prompt = f”Human: {example[‘instruction’]}\n\nAssistant: ”
full_text = prompt + example[‘output’]
# 分词
tokenizer_output = tokenizer(
full_text,
truncation=True,
max_length=512,
padding=”max_length”
)
# 创建标签(训练时只计算output部分的loss)
prompt_len = len(tokenizer.encode(prompt))
tokenizer_output[“labels”] = tokenizer_output[“input_ids”].copy()
tokenizer_output[“labels”][:prompt_len] = [-100] * prompt_len
return tokenizer_output
# 转换数据集
train_dataset = Dataset.from_pandas(train_df)
train_dataset = train_dataset.map(format_for_training, remove_columns=train_dataset.column_names)
步骤3:配置训练参数
from transformers import TrainingArguments, Trainer
training_args = TrainingArguments(
output_dir=”./output”,
num_train_epochs=3, # 训练轮数
per_device_train_batch_size=4, # 批次大小
gradient_accumulation_steps=4, # 梯度累积(模拟更大batch)
learning_rate=2e-5, # 学习率
weight_decay=0.01, # 权重衰减
warmup_steps=100, # 预热步数
logging_steps=10, # 日志频率
evaluation_strategy=”steps”, # 评估策略
eval_steps=50, # 评估步数
save_steps=100, # 保存checkpoint步数
fp16=True, # 混合精度训练
report_to=”none”, # 不上报wandb等
)
步骤4:开始训练
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=val_dataset,
tokenizer=tokenizer,
)
trainer.train()
4.2 使用LoRA进行高效微调
LoRA是目前最流行的PEFT方法,它通过在原权重矩阵旁添加低秩矩阵来适配新任务。
LoRA配置与实现
from peft import LoraConfig, get_peft_model, TaskType
# LoRA配置
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM, # 因果语言建模
r=16, # 秩(rank),一般8-64
lora_alpha=32, # 缩放系数,一般为r的2倍
lora_dropout=0.1, # Dropout率
target_modules=[
“q_proj”, “v_proj”, “k_proj”, “o_proj”, # 注意力层
“gate_proj”, “up_proj”, “down_proj” # MLP层
],
bias=”none”, # 不训练bias
)
# 将模型转换为LoRA模型
model = get_peft_model(model, lora_config)
# 打印可训练参数数量
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
all_params = sum(p.numel() for p in model.parameters())
print(f”可训练参数: {trainable_params:,} ({100 * trainable_params / all_params:.2f}%)”)
print(f”总参数: {all_params:,}”)
# 输出示例:
# 可训练参数: 8,388,608 (0.12%)
# 总参数: 6,700,000,000
LoRA参数选择建议
:
r
:任务越复杂,r越大。简单任务8-16,复杂任务32-64
lora_alpha
:通常设为r的2倍
target_modules
:通常包括q和v_proj,复杂任务可增加更多层
4.3 使用TRL进行监督微调(SFT)
TRL库提供了更高级的微调功能,特别适合对话模型。
from trl import SFTTrainer
from transformers import TrainingArguments
# 准备数据集(保持对话格式)
train_dataset = Dataset.from_pandas(train_df)
# 配置训练器
training_args = TrainingArguments(
output_dir=”./sft_output”,
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
learning_rate=2e-5,
fp16=True,
logging_steps=10,
save_steps=100,
evaluation_strategy=”steps”,
eval_steps=50,
)
# 创建SFTTrainer
trainer = SFTTrainer(
model=model,
train_dataset=train_dataset,
eval_dataset=val_dataset,
dataset_text_field=”text”, # 指定包含文本的字段
max_seq_length=512, # 最大序列长度
tokenizer=tokenizer,
args=training_args,
packing=True, # 合并短序列提高效率
)
trainer.train()
4.4 使用LLaMA-Factory快速微调
对于新手,LLaMA-Factory提供了更简单的配置方式:
# config.yaml
model_name_or_path: “THUDM/chatglm3-6b”
do_train: true
do_eval: true
dataset: “my_dataset”
template: “chatglm3”
finetuning_type: “lora”
lora_target: “q_proj,v_proj”
# 训练参数
per_device_train_batch_size: 4
gradient_accumulation_steps: 4
learning_rate: 2e-5
num_train_epochs: 3
max_seq_length: 512
运行命令:
llamafactory-cli train –config config.yaml
第五部分:训练监控与调优
5.1 关键指标监控
训练过程中需要密切关注的指标:
Loss曲线
:
训练loss应持续下降
验证loss不应明显上升(过拟合)
理想情况:两者收敛到相近值
学习率
:
观察学习率预热和衰减是否正常
如果loss震荡大,可能需要降低学习率
显存使用
:
监控显存是否溢出(OOM)
使用
nvidia-smi
或
torch.cuda.memory_summary()
监控
# 实时监控显存
import torch
def print_memory_usage():
allocated = torch.cuda.memory_allocated() / 1024**3
reserved = torch.cuda.memory_reserved() / 1024**3
print(f”已分配: {allocated:.2f}GB, 保留: {reserved:.2f}GB”)
# 在训练循环中调用
# for step, batch in enumerate(train_dataloader):
# …
# if step % 10 == 0:
# print_memory_usage()
5.2 常见训练问题与解决方案
问题1:Loss不下降或下降缓慢
可能原因
:
学习率太小
数据质量差(噪声大)
模型未正确加载(冻结了不该冻结的参数)
解决方案
:
# 检查模型参数是否可训练
for name, param in model.named_parameters():
if param.requires_grad:
print(f”可训练: {name}”)
else:
print(f”冻结: {name}”)
# 调整学习率
training_args.learning_rate = 5e-5 # 提高学习率
问题2:Loss突然变成NaN
可能原因
:
学习率过大导致数值溢出
数据中包含NaN或Inf
梯度爆炸
解决方案
:
# 检查数据
def check_nan_inf(data):
if torch.isnan(data).any() or torch.isinf(data).any():
raise ValueError(“数据中包含NaN或Inf”)
# 使用梯度裁剪
training_args.max_grad_norm = 1.0 # 梯度裁剪
# 降低学习率
training_args.learning_rate = 1e-5
问题3:过拟合(训练loss下降但验证loss上升)
可能原因
:
数据量太少
训练轮数过多
模型容量过大
解决方案
:
# 增加正则化
lora_config = LoraConfig(
…
lora_dropout=0.1, # 增加dropout
)
# 早停策略
training_args.load_best_model_at_end = True
training_args.metric_for_best_model = “eval_loss”
training_args.greater_is_better = False
# 数据增强
# 见第三部分数据增强技巧
问题4:显存不足(OOM)
可能原因
:
batch size太大
序列长度太长
模型太大
解决方案
:
# 1. 减小batch size
training_args.per_device_train_batch_size = 2
# 2. 梯度累积(模拟更大batch)
training_args.gradient_accumulation_steps = 8 # 2*8=16
# 3. 使用梯度检查点(牺牲速度换显存)
model.gradient_checkpointing_enable()
# 4. 8bit/4bit量化
model = AutoModelForCausalLM.from_pretrained(
model_name,
load_in_8bit=True, # 或 load_in_4bit=True
device_map=”auto”
)
# 5. 减小序列长度
training_args.max_seq_length = 256
5.3 超参数调优指南
学习率选择
全参数微调
:1e-6 到 1e-5
LoRA微调
:2e-5 到 2e-4
预热步数
:总步数的10-20%
Batch Size与梯度累积
# 计算有效batch size
effective_batch_size = per_device_train_batch_size * gradient_accumulation_steps * num_gpus
# 经验公式
# 有效batch size建议:32-128
# 如果显存不足,减小batch size,增大gradient_accumulation_steps
训练轮数(Epochs)
简单任务
:1-2 epochs
中等任务
:3-5 epochs
复杂任务
:5-10 epochs
监控验证loss
:如果验证loss在2-3个epoch内不下降,提前停止
5.4 评估与测试
生成测试
def generate_response(model, tokenizer, prompt, max_new_tokens=200):
“””生成测试函数”””
inputs = tokenizer(prompt, return_tensors=”pt”).to(model.device)
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=max_new_tokens,
temperature=0.7,
top_p=0.9,
do_sample=True,
pad_token_id=tokenizer.eos_token_id
)
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
return response
# 测试示例
prompt = “Human: 请解释什么是机器学习\n\nAssistant: ”
response = generate_response(model, tokenizer, prompt)
print(response)
自动化评估指标
from rouge import Rouge
from bert_score import score
def evaluate_generation(references, candidates):
“””评估生成质量”””
rouge = Rouge()
rouge_scores = rouge.get_scores(candidates, references, avg=True)
# BERTScore
P, R, F1 = score(candidates, references, lang=”zh”)
return {
“rouge1”: rouge_scores[“rouge-1”][“f”],
“rouge2”: rouge_scores[“rouge-2”][“f”],
“rougeL”: rouge_scores[“rouge-L”][“f”],
“bertscore”: F1.mean().item()
}
# 使用示例
# references = [“参考答案1”, “参考答案2”]
# candidates = [“模型生成1”, “模型生成2”]
# scores = evaluate_generation(references, candidates)
人工评估清单
[ ] 响应是否准确回答了问题
[ ] 是否遵循了指令格式
[ ] 是否存在幻觉(编造事实)
[ ] 语言是否流畅自然
第六部分:高级技巧与最佳实践
6.1 多轮对话微调
对于聊天机器人,多轮对话微调至关重要:
# 多轮对话数据格式
multi_turn_data = {
“conversations”: [
{“role”: “user”, “content”: “你好,我想学习Python”},
{“role”: “assistant”, “content”: “Python是一门很棒的语言!你想从哪方面开始学?”},
{“role”: “user”, “content”: “我想学爬虫”},
{“role”: “assistant”, “content”: “爬虫是个有趣的方向。建议先学习requests和BeautifulSoup库。”}
]
}
# 使用SFTTrainer处理
trainer = SFTTrainer(
model=model,
train_dataset=train_dataset,
dataset_text_field=”text”,
max_seq_length=1024, # 多轮对话需要更长的上下文
tokenizer=tokenizer,
args=training_args,
)
6.2 领域适应技巧
领域词表扩展
# 如果领域有大量专业术语,考虑扩展词表
new_tokens = [“量子纠缠”, “区块链”, “深度学习”, …]
tokenizer.add_tokens(new_tokens)
model.resize_token_embeddings(len(tokenizer))
# 需要初始化新增token的embedding
# 可以从相近token复制或随机初始化
领域数据混合
# 混合通用数据和领域数据,防止灾难性遗忘
# 通用数据:领域数据 = 1:3 到 1:5
mixed_dataset = concatenate_datasets([
general_dataset.select(range(len(domain_dataset) * 0.25)),
domain_dataset
])
6.3 模型合并与保存
保存LoRA权重
# 保存适配器权重(只保存增量部分,约10-100MB)
model.save_pretrained(“./lora_adapter”)
# 加载时
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained(“THUDM/chatglm3-6b”)
model = PeftModel.from_pretrained(base_model, “./lora_adapter”)
合并到原模型
# 将LoRA权重合并到原模型,便于部署
merged_model = model.merge_and_unload()
merged_model.save_pretrained(“./merged_model”)
tokenizer.save_pretrained(“./merged_model”)
6.4 推理优化
批量推理
def batch_generate(model, tokenizer, prompts, batch_size=8):
“””批量生成提高效率”””
responses = []
for i in range(0, len(prompts), batch_size):
batch = prompts[i:i+batch_size]
inputs = tokenizer(batch, return_tensors=”pt”, padding=True).to(model.device)
outputs = model.generate(
**inputs,
max_new_tokens=100,
pad_token_id=tokenizer.eos_token_id
)
for j, output in enumerate(outputs):
response = tokenizer.decode(output, skip_special_tokens=True)
responses.append(response)
return responses
使用vLLM加速推理
# 安装vLLM
pip install vllm
# 启动API服务
python -m vllm.entrypoints.api_server \
–model ./merged_model \
–tensor-parallel-size 2 \
–max-model-len 2048
第七部分:常见问题解答(FAQ)
Q1: 微调需要多少数据才够?
A
: 质量比数量更重要。对于简单任务,100-500条高质量数据即可;复杂任务可能需要1000-5000条。关键是数据的多样性和覆盖度。
Q2: 为什么微调后模型在某些任务上反而变差了?
A
: 这是
灾难性遗忘
现象。解决方案:
混合通用数据训练
使用LoRA等PEFT方法(影响较小)
降低学习率
减少训练轮数
Q3: 如何选择基础模型?
A
:
中文任务
:ChatGLM3, Qwen, Baichuan
英文任务
:Llama2, Mistral
多语言
:Aya, BLOOM
资源有限
:选择小参数量模型(7B以下)
Q4: LoRA的r值如何选择?
A
:
r=8:简单任务,快速实验
r=16:通用推荐
r=32:复杂任务,领域适应
r=64:需要强定制化
Q5: 训练过程中显存突然暴涨怎么办?
A
:
检查是否有数据异常(特别长的序列)
减小batch size
使用梯度检查点
重启训练(可能是内存泄漏)
Q6: 如何判断模型是否收敛?
A
:
训练loss持续下降
验证loss趋于稳定或轻微上升
在验证集上的生成质量不再提升
通常3-5个epoch即可
Q7: 微调后的模型如何部署?
A
:
本地部署
:使用transformers pipeline
API服务
:FastAPI + vLLM
云服务
:AWS SageMaker, Azure ML
移动端
:ONNX转换 + CoreML
Q8: 可以在免费资源上微调大模型吗?
A
: 可以!
Google Colab
:提供T4 GPU(16GB),适合7B模型+LoRA
Kaggle
:每周30小时免费GPU
天池实验室
:阿里云免费GPU
AutoDL
:按小时付费,成本较低
第八部分:完整项目示例
8.1 项目结构
finetune_project/
├── data/
│ ├── raw/
│ │ └── instructions.json
│ └── processed/
│ ├── train.json
│ ├── val.json
│ └── test.json
├── scripts/
│ ├── prepare_data.py
│ ├── train.py
│ └── evaluate.py
├── config/
│ └── lora_config.yaml
├── models/
│ └── chatglm3-6b-lora/
├── notebooks/
│ └── data_analysis.ipynb
└── README.md
8.2 完整训练脚本
#!/usr/bin/env python3
“””
完整微调脚本:数据准备 -> 训练 -> 评估
“””
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments
from peft import LoraConfig, get_peft_model
from datasets import load_dataset
import argparse
def main():
parser = argparse.ArgumentParser()
parser.add_argument(“–model_name”, default=”THUDM/chatglm3-6b”)
parser.add_argument(“–data_path”, default=”data/processed/train.json”)
parser.add_argument(“–output_dir”, default=”./output”)
parser.add_argument(“–num_epochs”, type=int, default=3)
parser.add_argument(“–batch_size”, type=int, default=4)
args = parser.parse_args()
# 1. 加载模型
print(“加载模型…”)
tokenizer = AutoTokenizer.from_pretrained(args.model_name, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
args.model_name,
load_in_4bit=True,
device_map=”auto”,
torch_dtype=torch.float16,
trust_remote_code=True
)
# 2. 配置LoRA
print(“配置LoRA…”)
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=16,
lora_alpha=32,
target_modules=[“q_proj”, “v_proj”],
lora_dropout=0.1,
)
model = get_peft_model(model, lora_config)
# 3. 加载数据
print(“加载数据…”)
dataset = load_dataset(“json”, data_files=args.data_path, split=”train”)
dataset = dataset.train_test_split(test_size=0.1)
def tokenize_function(examples):
# 简化的tokenize逻辑
prompts = [f”Human: {inst}\n\nAssistant: {out}”
for inst, out in zip(examples[“instruction”], examples[“output”])]
tokenized = tokenizer(prompts, truncation=True, max_length=512, padding=”max_length”)
tokenized[“labels”] = tokenized[“input_ids”].copy()
return tokenized
tokenized_dataset = dataset.map(tokenize_function, batched=True, remove_columns=dataset[“train”].column_names)
# 4. 训练配置
print(“配置训练参数…”)
training_args = TrainingArguments(
output_dir=args.output_dir,
num_train_epochs=args.num_epochs,
per_device_train_batch_size=args.batch_size,
gradient_accumulation_steps=4,
learning_rate=2e-5,
fp16=True,
logging_steps=10,
save_steps=100,
evaluation_strategy=”steps”,
eval_steps=50,
load_best_model_at_end=True,
metric_for_best_model=”eval_loss”,
greater_is_better=False,
report_to=”none”,
)
# 5. 训练
print(“开始训练…”)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset[“train”],
eval_dataset=tokenized_dataset[“test”],
tokenizer=tokenizer,
)
trainer.train()
# 6. 保存
print(“保存模型…”)
model.save_pretrained(args.output_dir)
tokenizer.save_pretrained(args.output_dir)
print(“训练完成!”)
if __name__ == “__main__”:
main()
8.3 运行命令
# 准备数据
python scripts/prepare_data.py –input data/raw/instructions.json –output data/processed/
# 训练
python scripts/train.py \
–model_name “THUDM/chatglm3-6b” \
–data_path “data/processed/train.json” \
–output_dir “./models/chatglm3-6b-lora” \
–num_epochs 3 \
–batch_size 4
# 评估
python scripts/evaluate.py –model_path “./models/chatglm3-6b-lora”
结语
微调大语言模型是一个需要耐心和实践的过程。从数据准备到训练监控,每一步都影响着最终效果。记住以下关键点:
数据质量第一
:垃圾进,垃圾出
从小开始
:先在小数据集上验证流程
监控指标
:关注loss曲线和生成质量
善用PEFT
:LoRA是新手的最佳选择
持续迭代
:微调不是一次性的,需要不断优化
希望这篇指南能帮助你成功微调出符合需求的模型。遇到问题时,欢迎查阅Hugging Face文档和相关社区,实践是最好的老师!
附录:常用资源
Hugging Face文档:
https://huggingface.co/docs
PEFT GitHub:
https://github.com/huggingface/peft
LLaMA-Factory:
https://github.com/hiyouga/LLaMA-Factory
中文大模型社区:
https://modelscope.cn
# 微调教程从零开始:新手入门指南与实践技巧解决模型训练常见问题
引言:为什么需要微调大语言模型
微调(Fine-tuning)是将预训练的大语言模型适配到特定任务或领域的关键步骤。预训练模型虽然在通用语言理解上表现出色,但在特定应用场景中往往需要更精确的控制和更专业的知识。通过微调,我们可以让模型更好地理解特定领域的术语、遵循特定的指令格式,或者在特定任务上达到更好的表现。
微调的核心价值在于:
领域适应
:让模型掌握特定领域的专业知识
任务优化
:提升模型在特定任务(如分类、生成、问答)上的表现
行为控制
:使模型遵循特定的响应风格或安全准则
成本效益
:相比从头训练,微调需要的计算资源和数据量都更少
第一部分:微调基础概念
1.1 什么是模型微调
模型微调是指在已经预训练好的模型基础上,使用特定任务的数据继续训练,使模型适应新任务的过程。想象一下,预训练模型就像一个通才大学生,而微调就是让这个通才去学习一门专业课程,使其成为某个领域的专家。
微调与预训练的主要区别:
预训练
:使用海量无标注数据,学习通用语言表示(耗时数周,需要TPU/ GPU集群)
微调
:使用相对少量的标注数据,学习特定任务模式(耗时数小时/天,单GPU即可)
1.2 微调的主要类型
全参数微调(Full Fine-tuning)
更新模型的所有参数。这种方法效果最好,但需要大量显存和存储空间(每个checkpoint都和原模型一样大)。
参数高效微调(PEFT)
只更新模型的一小部分参数,主要包括:
LoRA(Low-Rank Adaptation)
:在权重矩阵旁添加低秩矩阵,只训练这些新增参数
Adapter
:在Transformer层之间插入小型神经网络模块
Prompt Tuning
:只优化可学习的提示词嵌入
指令微调(Instruction Tuning)
在多种任务的指令-响应对上进行微调,让模型学会遵循指令。
1.3 微调前的准备工作
在开始微调前,需要明确以下几点:
任务定义
:明确你的模型需要完成什么任务(文本分类、生成、翻译等)
数据准备
:收集和整理高质量的训练数据
硬件评估
:根据模型大小和数据量选择合适的硬件
基线建立
:先在小数据集上测试,建立性能基线
第二部分:环境搭建与工具选择
2.1 硬件要求
微调不同规模模型的硬件建议:
模型参数量
最低显存要求
推荐配置
备注
1B-3B
8GB
RTX
3060
⁄
4060
适合入门学习
7B-13B
16GB-24GB
RTX
3090
⁄
4090
最常用的规模
30B+
40GB+
A100/H100
需要专业设备
注意
:使用LoRA等PEFT技术可以显著降低显存需求,7B模型在8GB显存上也能微调。
2.2 软件环境配置
基础环境安装
# 创建conda环境
conda create -n llm-finetune python=3.10
conda activate llm-finetune
# 安装PyTorch(根据CUDA版本选择)
pip install torch torchvision torchaudio –index-url https://download.pytorch.org/whl/cu118
# 安装核心库
pip install transformers datasets accelerate peft bitsandbytes trl
pip install jupyter pandas numpy scikit-learn
关键库说明
transformers
:Hugging Face的核心库,提供模型加载和推理
datasets
:数据集加载和处理
accelerate
:分布式训练支持
peft
:参数高效微调技术
bitsandbytes
:模型量化支持
trl
(Transformer Reinforcement Learning):支持RLHF和SFT
2.3 选择合适的微调框架
Hugging Face Transformers + PEFT
(推荐新手)
优点:生态完善,文档齐全,社区活跃
适用:大多数标准微调任务
LLaMA-Factory
优点:开箱即用,支持WebUI
适合:快速实验和可视化操作
Axolotl
优点:配置驱动,支持多种模型
适合:大规模训练和自动化
第三部分:数据准备与处理
3.1 数据格式要求
微调数据通常采用
指令-响应
格式。对于不同任务,格式略有不同:
对话格式(Chat Format)
{
“conversations”: [
{“role”: “user”, “content”: “你好,请介绍一下量子计算”},
{“role”: “assistant”, “content”: “量子计算是一种基于量子力学原理的计算方式…”}
]
}
指令格式(Instruction Format)
{
“instruction”: “将以下英文翻译成中文”,
“input”: “Hello, how are you?”,
“output”: “你好,你怎么样?”
}
文本分类格式
{
“text”: “这部电影真的很棒,演员表演出色”,
“label”: “positive”
}
3.2 数据质量要求
高质量的微调数据应具备:
准确性
:内容正确无误
一致性
:格式统一,风格一致
多样性
:覆盖任务的各种情况
相关性
:与目标任务高度相关
数据清洗示例代码
:
import pandas as pd
import re
def clean_data(df):
“””清洗微调数据”””
# 去除重复项
df = df.drop_duplicates(subset=[‘instruction’, ‘input’])
# 去除过长或过短的样本
df = df[
df[‘output’].str.len().between(10, 2000)
]
# 去除包含特殊字符的异常样本
df = df[~df[‘output’].str.contains(r'[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]’)]
# 标准化格式
df[‘instruction’] = df[‘instruction’].str.strip()
df[‘output’] = df[‘output’].str.strip()
return df
# 使用示例
# df = pd.read_json(‘raw_data.json’)
# cleaned_df = clean_data(df)
# cleaned_df.to_json(‘cleaned_data.json’, orient=’records’, force_ascii=False)
3.3 数据增强技巧
当数据量不足时,可以采用以下方法:
指令改写
:用不同方式表达相同指令
“总结以下文本” → “请对以下内容进行摘要”
“翻译成中文” → “将下列英文转换为中文”
角色设定
:为模型添加角色背景
“你是一位专业的医生,请回答…” → “你是一位经验丰富的儿科医生,请回答…”
few-shot示例
:在指令中加入示例
“示例1:输入A→输出B;现在请处理:输入C”
3.4 数据集划分
合理划分数据集对评估模型性能至关重要:
训练集
:80-90%,用于模型学习
验证集
:5-10%,用于调参和早停
测试集
:5-10%,用于最终评估
from sklearn.model_selection import train_test_split
# 划分数据集
train_val_df, test_df = train_test_split(
df, test_size=0.1, random_state=42, stratify=df[‘label’]
)
train_df, val_df = train_test_split(
train_val_df, test_size=0.111, random_state=42, stratify=train_val_df[‘label’]
)
print(f”训练集: {len(train_df)}”)
print(f”验证集: {len(val_df)}”)
print // 这里故意留一个错误,后面会说明如何发现和修复
第四部分:核心微调实践
4.1 使用Hugging Face进行基础微调
步骤1:加载模型和分词器
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
model_name = “THUDM/chatglm3-6b” # 以ChatGLM3为例
# 加载分词器
tokenizer = AutoTokenizer.from_pretrained(
model_name,
trust_remote_code=True
)
# 加载模型(使用4bit量化减少显存占用)
model = AutoModelForCausalLM.from_pretrained(
model_name,
load_in_4bit=True, # 4bit量化
device_map=”auto”, # 自动分配设备
torch_dtype=torch.float16,
trust_remote_code=True
)
步骤2:数据预处理
from datasets import Dataset
def format_for_training(example):
“””将数据格式化为模型可接受的输入”””
# 构建对话格式
prompt = f”Human: {example[‘instruction’]}\n\nAssistant: ”
full_text = prompt + example[‘output’]
# 分词
tokenizer_output = tokenizer(
full_text,
truncation=True,
max_length=512,
padding=”max_length”
)
# 创建标签(训练时只计算output部分的loss)
prompt_len = len(tokenizer.encode(prompt))
tokenizer_output[“labels”] = tokenizer_output[“input_ids”].copy()
tokenizer_output[“labels”][:prompt_len] = [-100] * prompt_len
return tokenizer_output
# 转换数据集
train_dataset = Dataset.from_pandas(train_df)
train_dataset = train_dataset.map(format_for_training, remove_columns=train_dataset.column_names)
步骤3:配置训练参数
from transformers import TrainingArguments, Trainer
training_args = TrainingArguments(
output_dir=”./output”,
num_train_epochs=3, # 训练轮数
per_device_train_batch_size=4, # 批次大小
gradient_accumulation_steps=4, # 梯度累积(模拟更大batch)
learning_rate=2e-5, # 学习率
weight_decay=0.01, # 权重衰减
warmup_steps=100, # 预热步数
logging_steps=10, # 日志频率
evaluation_strategy=”steps”, # 评估策略
eval_steps=50, # 评估步数
save_steps=100, # 保存checkpoint步数
fp16=True, # 混合精度训练
report_to=”none”, # 不上报wandb等
)
步骤4:开始训练
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=val_dataset,
tokenizer=tokenizer,
)
trainer.train()
4.2 使用LoRA进行高效微调
LoRA是目前最流行的PEFT方法,它通过在原权重矩阵旁添加低秩矩阵来适配新任务。
LoRA配置与实现
from peft import LoraConfig, get_peft_model, TaskType
# LoRA配置
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM, # 因果语言建模
r=16, # 秩(rank),一般8-64
lora_alpha=32, # 缩放系数,一般为r的2倍
lora_dropout=0.1, # Dropout率
target_modules=[
“q_proj”, “v_proj”, “k_proj”, “o_proj”, # 注意力层
“gate_proj”, “up_proj”, “down_proj” # MLP层
],
bias=”none”, # 不训练bias
)
# 将模型转换为LoRA模型
model = get_peft_model(model, lora_config)
# 打印可训练参数数量
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
all_params = sum(p.numel() for p in model.parameters())
print(f”可训练参数: {trainable_params:,} ({100 * trainable_params / all_params:.2f}%)”)
print(f”总参数: {all_params:,}”)
# 输出示例:
# 可训练参数: 8,388,608 (0.12%)
# 总参数: 6,700,000,000
LoRA参数选择建议
:
r
:任务越复杂,r越大。简单任务8-16,复杂任务32-64
lora_alpha
:通常设为r的2倍
target_modules
:通常包括q和v_proj,复杂任务可增加更多层
4.3 使用TRL进行监督微调(SFT)
TRL库提供了更高级的微调功能,特别适合对话模型。
from trl import SFTTrainer
from transformers import TrainingArguments
# 准备数据集(保持对话格式)
train_dataset = Dataset.from_pandas(train_df)
# 配置训练器
training_args = TrainingArguments(
output_dir=”./sft_output”,
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
learning_rate=2e-5,
fp16=True,
logging_steps=10,
save_steps=100,
evaluation_strategy=”steps”,
eval_steps=50,
)
# 创建SFTTrainer
trainer = SFTTrainer(
model=model,
train_dataset=train_dataset,
eval_dataset=val_dataset,
dataset_text_field=”text”, # 指定包含文本的字段
max_seq_length=512, # 最大序列长度
tokenizer=tokenizer,
args=training_args,
packing=True, # 合并短序列提高效率
)
trainer.train()
4.4 使用LLaMA-Factory快速微调
对于新手,LLaMA-Factory提供了更简单的配置方式:
# config.yaml
model_name_or_path: “THUDM/chatglm3-6b”
do_train: true
do_eval: true
dataset: “my_dataset”
template: “chatglm3”
finetuning_type: “lora”
lora_target: “q_proj,v_proj”
# 训练参数
per_device_train_batch_size: 4
gradient_accumulation_steps: 4
learning_rate: 2e-5
num_train_epochs: 3
max_seq_length: 512
运行命令:
llamafactory-cli train –config config.yaml
第五部分:训练监控与调优
5.1 关键指标监控
训练过程中需要密切关注的指标:
Loss曲线
:
训练loss应持续下降
验证loss不应明显上升(过拟合)
理想情况:两者收敛到相近值
学习率
:
观察学习率预热和衰减是否正常
如果loss震荡大,可能需要降低学习率
显存使用
:
监控显存是否溢出(OOM)
使用
nvidia-smi
或
torch.cuda.memory_summary()
监控
# 实时监控显存
import torch
def print_memory_usage():
allocated = torch.cuda.memory_allocated() / 1024**3
reserved = torch.cuda.memory_reserved() / 1024**3
print(f”已分配: {allocated:.2f}GB, 保留: {reserved:.2f}GB”)
# 在训练循环中调用
# for step, batch in enumerate(train_dataloader):
# …
# if step % 10 == 0:
# print_memory_usage()
5.2 常见训练问题与解决方案
问题1:Loss不下降或下降缓慢
可能原因
:
学习率太小
数据质量差(噪声大)
模型未正确加载(冻结了不该冻结的参数)
解决方案
:
# 检查模型参数是否可训练
for name, param in model.named_parameters():
if param.requires_grad:
print(f”可训练: {name}”)
else:
print(f”冻结: {name}”)
# 调整学习率
training_args.learning_rate = 5e-5 # 提高学习率
问题2:Loss突然变成NaN
可能原因
:
学习率过大导致数值溢出
数据中包含NaN或Inf
梯度爆炸
解决方案
:
# 检查数据
def check_nan_inf(data):
if torch.isnan(data).any() or torch.isinf(data).any():
raise ValueError(“数据中包含NaN或Inf”)
# 使用梯度裁剪
training_args.max_grad_norm = 1.0 # 梯度裁剪
# 降低学习率
training_args.learning_rate = 1e-5
问题3:过拟合(训练loss下降但验证loss上升)
可能原因
:
数据量太少
训练轮数过多
模型容量过大
解决方案
:
# 增加正则化
lora_config = LoraConfig(
…
lora_dropout=0.1, # 增加dropout
)
# 早停策略
training_args.load_best_model_at_end = True
training_args.metric_for_best_model = “eval_loss”
training_args.greater_is_better = False
# 数据增强
# 见第三部分数据增强技巧
问题4:显存不足(OOM)
可能原因
:
batch size太大
序列长度太长
模型太大
解决方案
:
# 1. 减小batch size
training_args.per_device_train_batch_size = 2
# 2. 梯度累积(模拟更大batch)
training_args.gradient_accumulation_steps = 8 # 2*8=16
# 3. 使用梯度检查点(牺牲速度换显存)
model.gradient_checkpointing_enable()
# 4. 8bit/4bit量化
model = AutoModelForCausalLM.from_pretrained(
model_name,
load_in_8bit=True, # 或 load_in_4bit=True
device_map=”auto”
)
# 5. 减小序列长度
training_args.max_seq_length = 256
5.3 超参数调优指南
学习率选择
全参数微调
:1e-6 到 1e-5
LoRA微调
:2e-5 到 2e-4
预热步数
:总步数的10-20%
Batch Size与梯度累积
# 计算有效batch size
effective_batch_size = per_device_train_batch_size * gradient_accumulation_steps * num_gpus
# 经验公式
# 有效batch size建议:32-128
# 如果显存不足,减小batch size,增大gradient_accumulation_steps
训练轮数(Epochs)
简单任务
:1-2 epochs
中等任务
:3-5 epochs
复杂任务
:5-10 epochs
监控验证loss
:如果验证loss在2-3个epoch内不下降,提前停止
5.4 评估与测试
生成测试
def generate_response(model, tokenizer, prompt, max_new_tokens=200):
“””生成测试函数”””
inputs = tokenizer(prompt, return_tensors=”pt”).to(model.device)
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=max_new_tokens,
temperature=0.7,
top_p=0.9,
do_sample=True,
pad_token_id=tokenizer.eos_token_id
)
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
return response
# 测试示例
prompt = “Human: 请解释什么是机器学习\n\nAssistant: ”
response = generate_response(model, tokenizer, prompt)
print(response)
自动化评估指标
from rouge import Rouge
from bert_score import score
def evaluate_generation(references, candidates):
“””评估生成质量”””
rouge = Rouge()
rouge_scores = rouge.get_scores(candidates, references, avg=True)
# BERTScore
P, R, F1 = score(candidates, references, lang=”zh”)
return {
“rouge1”: rouge_scores[“rouge-1”][“f”],
“rouge2”: rouge_scores[“rouge-2”][“f”],
“rougeL”: rouge_scores[“rouge-L”][“f”],
“bertscore”: F1.mean().item()
}
# 使用示例
# references = [“参考答案1”, “参考答案2”]
# candidates = [“模型生成1”, “模型生成2”]
# scores = evaluate_generation(references, candidates)
人工评估清单
[ ] 响应是否准确回答了问题
[ ] 是否遵循了指令格式
[ ] 是否存在幻觉(编造事实)
[ ] 语言是否流畅自然
第六部分:高级技巧与最佳实践
6.1 多轮对话微调
对于聊天机器人,多轮对话微调至关重要:
# 多轮对话数据格式
multi_turn_data = {
“conversations”: [
{“role”: “user”, “content”: “你好,我想学习Python”},
{“role”: “assistant”, “content”: “Python是一门很棒的语言!你想从哪方面开始学?”},
{“role”: “user”, “content”: “我想学爬虫”},
{“role”: “assistant”, “content”: “爬虫是个有趣的方向。建议先学习requests和BeautifulSoup库。”}
]
}
# 使用SFTTrainer处理
trainer = SFTTrainer(
model=model,
train_dataset=train_dataset,
dataset_text_field=”text”,
max_seq_length=1024, # 多轮对话需要更长的上下文
tokenizer=tokenizer,
args=training_args,
)
6.2 领域适应技巧
领域词表扩展
# 如果领域有大量专业术语,考虑扩展词表
new_tokens = [“量子纠缠”, “区块链”, “深度学习”, …]
tokenizer.add_tokens(new_tokens)
model.resize_token_embeddings(len(tokenizer))
# 需要初始化新增token的embedding
# 可以从相近token复制或随机初始化
领域数据混合
# 混合通用数据和领域数据,防止灾难性遗忘
# 通用数据:领域数据 = 1:3 到 1:5
mixed_dataset = concatenate_datasets([
general_dataset.select(range(len(domain_dataset) * 0.25)),
domain_dataset
])
6.3 模型合并与保存
保存LoRA权重
# 保存适配器权重(只保存增量部分,约10-100MB)
model.save_pretrained(“./lora_adapter”)
# 加载时
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained(“THUDM/chatglm3-6b”)
model = PeftModel.from_pretrained(base_model, “./lora_adapter”)
合并到原模型
# 将LoRA权重合并到原模型,便于部署
merged_model = model.merge_and_unload()
merged_model.save_pretrained(“./merged_model”)
tokenizer.save_pretrained(“./merged_model”)
6.4 推理优化
批量推理
def batch_generate(model, tokenizer, prompts, batch_size=8):
“””批量生成提高效率”””
responses = []
for i in range(0, len(prompts), batch_size):
batch = prompts[i:i+batch_size]
inputs = tokenizer(batch, return_tensors=”pt”, padding=True).to(model.device)
outputs = model.generate(
**inputs,
max_new_tokens=100,
pad_token_id=tokenizer.eos_token_id
)
for j, output in enumerate(outputs):
response = tokenizer.decode(output, skip_special_tokens=True)
responses.append(response)
return responses
使用vLLM加速推理
# 安装vLLM
pip install vllm
# 启动API服务
python -m vllm.entrypoints.api_server \
–model ./merged_model \
–tensor-parallel-size 2 \
–max-model-len 2048
第七部分:常见问题解答(FAQ)
Q1: 微调需要多少数据才够?
A
: 质量比数量更重要。对于简单任务,100-500条高质量数据即可;复杂任务可能需要1000-5000条。关键是数据的多样性和覆盖度。
Q2: 为什么微调后模型在某些任务上反而变差了?
A
: 这是
灾难性遗忘
现象。解决方案:
混合通用数据训练
使用LoRA等PEFT方法(影响较小)
降低学习率
减少训练轮数
Q3: 如何选择基础模型?
A
:
中文任务
:ChatGLM3, Qwen, Baichuan
英文任务
:Llama2, Mistral
多语言
:Aya, BLOOM
资源有限
:选择小参数量模型(7B以下)
Q4: LoRA的r值如何选择?
A
:
r=8:简单任务,快速实验
r=16:通用推荐
r=32:复杂任务,领域适应
r=64:需要强定制化
Q5: 训练过程中显存突然暴涨怎么办?
A
:
检查是否有数据异常(特别长的序列)
减小batch size
使用梯度检查点
重启训练(可能是内存泄漏)
Q6: 如何判断模型是否收敛?
A
:
训练loss持续下降
验证loss趋于稳定或轻微上升
在验证集上的生成质量不再提升
通常3-5个epoch即可
Q7: 微调后的模型如何部署?
A
:
本地部署
:使用transformers pipeline
API服务
:FastAPI + vLLM
云服务
:AWS SageMaker, Azure ML
移动端
:ONNX转换 + CoreML
Q8: 可以在免费资源上微调大模型吗?
A
: 可以!
Google Colab
:提供T4 GPU(16GB),适合7B模型+LoRA
Kaggle
:每周30小时免费GPU
天池实验室
:阿里云免费GPU
AutoDL
:按小时付费,成本较低
第八部分:完整项目示例
8.1 项目结构
finetune_project/
├── data/
│ ├── raw/
│ │ └── instructions.json
│ └── processed/
│ ├── train.json
│ ├── val.json
│ └── test.json
├── scripts/
│ ├── prepare_data.py
│ ├── train.py
│ └── evaluate.py
├── config/
│ └── lora_config.yaml
├── models/
│ └── chatglm3-6b-lora/
├── notebooks/
│ └── data_analysis.ipynb
└── README.md
8.2 完整训练脚本
#!/usr/bin/env python3
“””
完整微调脚本:数据准备 -> 训练 -> 评估
“””
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments
from peft import LoraConfig, get_peft_model, TaskType
from datasets import load_dataset
import argparse
def main():
parser = argparse.ArgumentParser()
parser.add_argument(“–model_name”, default=”THUDM/chatglm3-6b”)
parser.add_argument(“–data_path”, default=”data/processed/train.json”)
parser.add_argument(“–output_dir”, default=”./output”)
parser.add_argument(“–num_epochs”, type=int, default=3)
parser.add_argument(“–batch_size”, type=int, default=4)
args = parser.parse_args()
# 1. 加载模型
print(“加载模型…”)
tokenizer = AutoTokenizer.from_pretrained(args.model_name, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
args.model_name,
load_in_4bit=True,
device_map=”auto”,
torch_dtype=torch.float16,
trust_remote_code=True
)
# 2. 配置LoRA
print(“配置LoRA…”)
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=16,
lora_alpha=32,
target_modules=[“q_proj”, “v_proj”],
lora_dropout=0.1,
)
model = get_peft_model(model, lora_config)
# 3. 加载数据
print(“加载数据…”)
dataset = load_dataset(“json”, data_files=args.data_path, split=”train”)
dataset = dataset.train_test_split(test_size=0.1)
def tokenize_function(examples):
# 简化的tokenize逻辑
prompts = [f”Human: {inst}\n\nAssistant: {out}”
for inst, out in zip(examples[“instruction”], examples[“output”])]
tokenized = tokenizer(prompts, truncation=True, max_length=512, padding=”max_length”)
tokenized[“labels”] = tokenized[“input_ids”].copy()
return tokenized
tokenized_dataset = dataset.map(tokenize_function, batched=True, remove_columns=dataset[“train”].column_names)
# 4. 训练配置
print(“配置训练参数…”)
training_args = TrainingArguments(
output_dir=args.output_dir,
num_train_epochs=args.num_epochs,
per_device_train_batch_size=args.batch_size,
gradient_accumulation_steps=4,
learning_rate=2e-5,
fp16=True,
logging_steps=10,
save_steps=100,
evaluation_strategy=”steps”,
eval_steps=50,
load_best_model_at_end=True,
metric_for_best_model=”eval_loss”,
greater_is_better=False,
report_to=”none”,
)
# 5. 训练
print(“开始训练…”)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset[“train”],
eval_dataset=tokenized_dataset[“test”],
tokenizer=tokenizer,
)
trainer.train()
# 6. 保存
print(“保存模型…”)
model.save_pretrained(args.output_dir)
tokenizer.save_pretrained(args.output_dir)
print(“训练完成!”)
if __name__ == “__main__”:
main()
8.3 运行命令
# 准备数据
python scripts/prepare_data.py –input data/raw/instructions.json –output data/processed/
# 训练
python scripts/train.py \
–model_name “THUDM/chatglm3-6b” \
–data_path “data/processed/train.json” \
–output_dir “./models/chatglm3-6b-lora” \
–num_epochs 3 \
–batch_size 4
# 评估
python scripts/evaluate.py –model_path “./models/chatglm3-6b-lora”
结语
微调大语言模型是一个需要耐心和实践的过程。从数据准备到训练监控,每一步都影响着最终效果。记住以下关键点:
数据质量第一
:垃圾进,垃圾出
从小开始
:先在小数据集上验证流程
监控指标
:关注loss曲线和生成质量
善用PEFT
:LoRA是新手的最佳选择
持续迭代
:微调不是一次性的,需要不断优化
希望这篇指南能帮助你成功微调出符合需求的模型。遇到问题时,欢迎查阅Hugging Face文档和相关社区,实践是最好的老师!
附录:常用资源
Hugging Face文档:
https://huggingface.co/docs
PEFT GitHub:
https://github.com/huggingface/peft
LLaMA-Factory:
https://github.com/hiyouga/LLaMA-Factory
中文大模型社区:
https://modelscope.cn