配音

微调基础与策略

课程简介

何时需要微调、数据准备、基础微调工作流。

🎬 本课程视频:Finetuning LLMs — 大模型微调


微调基础与策略

一、什么是微调?

微调(Fine-tuning)是在预训练的大语言模型基础上,使用特定领域的数据进一步训练,使模型适应特定任务的过程。

预训练模型(如 GPT-4、Llama 3)已经在大规模通用语料上学习了语言的基本规律和广泛的知识。微调的目的是:在通用能力的基础上,强化特定领域的知识或能力

1.1 预训练 vs 微调

维度 预训练 微调
数据规模 TB 级 MB-GB 级
计算资源 数千 GPU × 数月 单/多 GPU × 数小时-数天
目标 学习通用语言知识 适应特定任务/领域
成本 数百万美元 数百-数千美元
频率 一次(极少重复) 多次(根据需要)

二、何时需要微调?

在投入微调之前,应该先评估是否真的需要。Andrew Ng 建议按以下顺序尝试:

2.1 先尝试提示工程

微调前,先确认提示词工程是否已经达到了效果的天花板。

Step 1: 基础提示词 → 是否满足需求?
Step 2: 优化提示词(角色、格式、示例) → 是否满足?
Step 3: 使用高级技巧(CoT、少样本) → 是否满足?
Step 4: 如果以上都不满足,考虑微调

2.2 需要微调的信号

  1. 提示词策略已经用到极限:再怎么调整提示词,效果都没有明显提升
  2. 模型需要掌握大量专有知识:如法律合同的特定条款格式、医疗病历的专用术语和书写规范
  3. 需要减少推理阶段的 Token 消耗:微调后模型可以直接输出正确格式,不需要复杂提示词的引导
  4. 响应格式有严格要求:需要模型始终输出特定结构的结果

2.3 不需要微调的情况

三、数据准备:微调成功的决定性因素

3.1 质量远比数量重要

Andrew Ng 反复强调的一个原则:500 条高质量、多样化、精心准备的样本,效果远远优于 10000 条低质量、重复、有噪声的数据

高质量数据的特点:
- 准确性:每条数据的目标输出都是正确的
- 多样性:覆盖各种输入变化和边缘情况
- 一致性:同类问题的输出风格和格式一致
- 代表性:反映真实的使用场景

3.2 数据格式

微调数据通常使用对话格式:

{
  "messages": [
    {"role": "system", "content": "你是一个客服助手。用简洁专业的语气回答。"},
    {"role": "user", "content": "我的订单已经三天了还没到货,怎么办?"},
    {"role": "assistant", "content": "很抱歉给您带来不便。订单号是多少?我帮您查询物流状态。"}
  ]
}

各部分的作用:
- system:定义模型的行为模式、角色定位和输出风格
- user:用户输入,覆盖各种真实场景
- assistant:预期输出,模型要学习的目标

3.3 数据清洗

数据清洗是数据准备中最重要但最容易被忽视的环节:

def clean_training_data(raw_data):
    '''清洗微调数据'''
    issues = []
    cleaned = []

    for i, item in enumerate(raw_data):
        # 1. 检查重复
        if is_duplicate(item, cleaned):
            issues.append(f"#{i}: 重复样本")
            continue

        # 2. 检查格式
        if not validate_format(item):
            issues.append(f"#{i}: 格式错误")
            continue

        # 3. 检查长度
        if len(item["messages"][-1]["content"]) > 2048:
            issues.append(f"#{i}: 输出过长")
            continue

        # 4. 检查有害内容
        if contains_harmful(item):
            issues.append(f"#{i}: 包含有害内容")
            continue

        # 5. 检查一致性
        if not check_consistency(item, cleaned[-5:]):
            issues.append(f"#{i}: 与近期样本不一致")
            continue

        cleaned.append(item)

    return cleaned, issues

3.4 数据平衡

确保数据集中各类样本的比例合理:
- 正常场景:65%
- 边界场景:20%
- 困难案例:10%
- 异常输入:5%

四、训练流程

4.1 基础流程

1. 准备数据集(训练集、验证集、测试集)
2. 选择基础模型(根据任务和资源)
3. 设置训练参数
4. 执行训练
5. 评估效果
6. 迭代优化
7. 部署

4.2 关键训练参数

参数 推荐值 说明
学习率(learning_rate) 1e-5 ~ 5e-5 微调时使用较小的学习率
训练轮数(num_epochs) 2 ~ 4 防止过拟合
批次大小(batch_size) 根据显存调整 越大越稳定
权重衰减(weight_decay) 0.01 ~ 0.1 防止过拟合
学习率调度器 cosine / linear 推荐 cosine
from transformers import TrainingArguments

training_args = TrainingArguments(
    output_dir="./ft_output",
    learning_rate=2e-5,
    num_train_epochs=3,
    per_device_train_batch_size=4,
    per_device_eval_batch_size=4,
    warmup_ratio=0.03,
    weight_decay=0.01,
    logging_steps=10,
    evaluation_strategy="steps",
    save_strategy="steps",
    save_total_limit=2,
    load_best_model_at_end=True
)

4.3 过拟合检测

在训练过程中监控训练集和验证集的损失(loss):

五、全流程示例

from datasets import Dataset
from transformers import AutoModelForCausalLM, AutoTokenizer, Trainer

# 1. 准备数据
train_data = [
    {"messages": [
        {"role": "system", "content": "你是一个 Python 编程助手。"},
        {"role": "user", "content": "用 Python 写一个函数计算斐波那契数列"},
        {"role": "assistant", "content": "...[代码实现]..."}
    ]},
    # ... 更多数据
]

# 2. 加载模型
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3-8B")
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3-8B")

# 3. 数据预处理
def format_data(example):
    text = tokenizer.apply_chat_template(
        example["messages"],
        tokenize=False
    )
    return tokenizer(text, truncation=True, max_length=2048)

dataset = Dataset.from_list(train_data)
tokenized_dataset = dataset.map(format_data)

# 4. 训练
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_dataset,
    eval_dataset=eval_dataset
)
trainer.train()

# 5. 保存
model.save_pretrained("./my_finetuned_model")
tokenizer.save_pretrained("./my_finetuned_model")

六、总结

微调是在预训练模型基础上进一步训练以适应特定任务的过程。在投入微调前应确认提示工程已经达到极限。数据的质量是微调成功的决定性因素——500 条高质量样本胜于万条低质数据。Base 模型更适合微调,数据格式采用对话格式,合理的训练参数和过拟合监控是训练成功的关键。

七、数据准备流程

7.1 数据收集

7.2 数据清洗

def clean_dataset(data):
    # 去重
    data = remove_duplicates(data)
    # 过滤低质量
    data = [d for d in data if quality_score(d) > threshold]
    # 格式标准化
    data = standardize_format(data)
    # 平衡标签
    data = balance_labels(data)
    return data

7.3 数据格式化

根据微调方法的不同,数据格式也不同:
- 全量微调:输入-输出对
- LoRA:指令-输入-输出三元组
- RLHF:提示-偏好对(好回答 vs 差回答)

八、训练流程详解

  1. 加载预训练模型和分词器
  2. 准备数据集并进行 Tokenize
  3. 配置训练参数(学习率、批次大小、epochs)
  4. 设定评估指标
  5. 开始训练并监控 loss
  6. 保存检查点
  7. 评估微调后的模型

九、总结

关键要点回顾:
- 微调的核心是“用特定数据调整模型参数以适应特定任务”
- 微调适用于改变行为风格、学习新格式、纠正系统性错误
- 数据质量比数据量更重要
- 需要建立评估基准来衡量微调效果
- 全量微调是最完整但成本最高的方案

七、数据准备流程

7.1 数据收集

7.2 数据清洗

def clean_dataset(data):
    # 去重
    data = remove_duplicates(data)
    # 过滤低质量
    data = [d for d in data if quality_score(d) > threshold]
    # 格式标准化
    data = standardize_format(data)
    # 平衡标签
    data = balance_labels(data)
    return data

7.3 数据格式化

根据微调方法的不同,数据格式也不同:
- 全量微调:输入-输出对
- LoRA:指令-输入-输出三元组
- RLHF:提示-偏好对(好回答 vs 差回答)

八、训练流程详解

  1. 加载预训练模型和分词器
  2. 准备数据集并进行 Tokenize
  3. 配置训练参数(学习率、批次大小、epochs)
  4. 设定评估指标
  5. 开始训练并监控 loss
  6. 保存检查点
  7. 评估微调后的模型

九、总结

关键要点回顾:
- 微调的核心是“用特定数据调整模型参数以适应特定任务”
- 微调适用于改变行为风格、学习新格式、纠正系统性错误
- 数据质量比数据量更重要
- 需要建立评估基准来衡量微调效果
- 全量微调是最完整但成本最高的方案

七、数据准备流程

7.1 数据收集

7.2 数据清洗

def clean_dataset(data):
    # 去重
    data = remove_duplicates(data)
    # 过滤低质量
    data = [d for d in data if quality_score(d) > threshold]
    # 格式标准化
    data = standardize_format(data)
    # 平衡标签
    data = balance_labels(data)
    return data

7.3 数据格式化

根据微调方法的不同,数据格式也不同:
- 全量微调:输入-输出对
- LoRA:指令-输入-输出三元组
- RLHF:提示-偏好对(好回答 vs 差回答)

八、训练流程详解

  1. 加载预训练模型和分词器
  2. 准备数据集并进行 Tokenize
  3. 配置训练参数(学习率、批次大小、epochs)
  4. 设定评估指标
  5. 开始训练并监控 loss
  6. 保存检查点
  7. 评估微调后的模型

九、总结

关键要点回顾:
- 微调的核心是“用特定数据调整模型参数以适应特定任务”
- 微调适用于改变行为风格、学习新格式、纠正系统性错误
- 数据质量比数据量更重要
- 需要建立评估基准来衡量微调效果
- 全量微调是最完整但成本最高的方案

延伸阅读