Skip to content

9.5 个性化与微调生态

从「通用大模型」到「专属小模型」——合成数据 → 微调 → 评估 → 部署的完整闭环,让 AI 真正适配你的业务场景。


为什么需要个性化微调?

通用大模型(GPT-4o / Claude)的局限:

  ❌ 不了解你的业务术语和知识
  ❌ 输出风格不符合品牌调性
  ❌ 每次调用都要付 API 费用
  ❌ 数据发送到第三方有隐私风险
  ❌ 对特定任务的准确率不够高

微调后的专属模型:

  ✅ 精准掌握领域知识和术语
  ✅ 输出风格一致、可控
  ✅ 本地部署零 API 成本
  ✅ 数据完全自控
  ✅ 特定任务准确率可提升 20-50%

合成数据生成

为什么用合成数据?

真实标注数据成本高、周期长。用大模型生成训练数据,再用人工审核,可以 10x 加速数据准备。

实战:用 GPT-4o 生成训练数据

python
from openai import OpenAI
import json

client = OpenAI()

async def generate_training_data(
    task_description: str,
    num_samples: int = 100,
    output_format: str = "sharegpt"
) -> list[dict]:
    """用大模型生成微调训练数据"""
    
    system_prompt = f"""你是一个训练数据生成专家。
任务描述:{task_description}

要求:
1. 生成多样化的用户问题(覆盖不同表达方式)
2. 回答必须准确、专业、符合业务规范
3. 每条数据独立,不要重复
4. 输出 JSON 数组格式"""

    all_data = []
    batch_size = 10
    
    for i in range(0, num_samples, batch_size):
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": f"生成 {batch_size} 条训练数据,JSON 数组格式"}
            ],
            response_format={"type": "json_object"},
            temperature=0.9  # 高温度增加多样性
        )
        
        batch = json.loads(response.choices[0].message.content)
        all_data.extend(batch.get("data", []))
        print(f"已生成 {len(all_data)}/{num_samples} 条")
    
    return all_data

# 使用示例
data = await generate_training_data(
    task_description="电商客服对话,回答关于退货、换货、物流查询的问题",
    num_samples=500
)

数据质量控制

python
def quality_filter(data: list[dict]) -> list[dict]:
    """过滤低质量训练数据"""
    filtered = []
    
    for item in data:
        q = item.get("question", "")
        a = item.get("answer", "")
        
        # 规则 1:长度检查
        if len(q) < 5 or len(a) < 20:
            continue
        
        # 规则 2:去重(基于问题相似度)
        if any(similar(q, f["question"]) > 0.9 for f in filtered):
            continue
        
        # 规则 3:敏感内容过滤
        if contains_sensitive(a):
            continue
        
        filtered.append(item)
    
    print(f"质量过滤:{len(data)}{len(filtered)} 条({len(filtered)/len(data)*100:.0f}%)")
    return filtered

导出为微调格式

python
def to_sharegpt(data: list[dict], output_path: str):
    """导出为 ShareGPT 格式(LLaMA-Factory 通用)"""
    formatted = []
    for item in data:
        formatted.append({
            "conversations": [
                {"from": "human", "value": item["question"]},
                {"from": "gpt", "value": item["answer"]}
            ]
        })
    
    with open(output_path, "w", encoding="utf-8") as f:
        json.dump(formatted, f, ensure_ascii=False, indent=2)
    
    print(f"已导出 {len(formatted)} 条到 {output_path}")

def to_alpaca(data: list[dict], output_path: str):
    """导出为 Alpaca 格式"""
    formatted = [
        {
            "instruction": item["question"],
            "input": "",
            "output": item["answer"]
        }
        for item in data
    ]
    
    with open(output_path, "w", encoding="utf-8") as f:
        json.dump(formatted, f, ensure_ascii=False, indent=2)

LoRA 微调实战

使用 LLaMA-Factory(推荐)

bash
# 安装
git clone https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory
pip install -e ".[torch,metrics]"

# 启动 Web UI
llamafactory-cli webui
# 浏览器打开 http://localhost:7860
yaml
# config/my_lora.yaml — LoRA 微调配置

### 模型
model_name_or_path: Qwen/Qwen2.5-7B-Instruct

### 方法
stage: sft
do_train: true
finetuning_type: lora
lora_rank: 16
lora_alpha: 32
lora_target: all

### 数据
dataset: my_custom_data        # 放在 data/ 目录下
template: qwen
cutoff_len: 2048

### 训练参数
num_train_epochs: 3
per_device_train_batch_size: 4
gradient_accumulation_steps: 4
learning_rate: 2.0e-4
lr_scheduler_type: cosine
warmup_ratio: 0.1
fp16: true

### 输出
output_dir: saves/my-model-lora
logging_steps: 10
save_steps: 500
bash
# 命令行启动训练
llamafactory-cli train config/my_lora.yaml

# 合并 LoRA 权重
llamafactory-cli export config/merge_lora.yaml

# 推理测试
llamafactory-cli chat config/inference.yaml

评估与迭代

自动化评估

python
from openai import OpenAI

client = OpenAI()

def llm_judge(question: str, answer: str, reference: str) -> dict:
    """LLM-as-Judge:用 GPT-4o 评估微调模型输出质量"""
    
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{
            "role": "user",
            "content": f"""评估以下回答的质量,打分 1-5 并说明理由。

问题:{question}
参考答案:{reference}
待评估答案:{answer}

评分维度:
1. 准确性(是否正确回答了问题)
2. 完整性(是否涵盖了关键信息)
3. 流畅性(语言是否自然流畅)
4. 专业性(是否使用了正确的术语)

输出 JSON:&#123;&#123;"score": 1-5, "reason": "..."&#125;&#125;"""
        }],
        response_format={"type": "json_object"}
    )
    
    return json.loads(response.choices[0].message.content)

评估流程

合成数据(500条)

质量过滤(→ 400条)

训练集/测试集拆分(360 / 40)

LoRA 微调(3 epochs)

测试集评估(LLM-as-Judge)

  得分 ≥ 4.0?──Yes──→ 部署上线

    No

分析低分样本 → 补充训练数据 → 重新微调

LoRA 即服务(LoRA-as-a-Service)

概念

一个基础模型 + 多个 LoRA 适配器 = 多个专属模型,共享 GPU 内存。

┌─────────────────────────────┐
│      基础模型(Qwen-7B)      │  ← 只加载一次
│                              │
│  ┌───────┐ ┌───────┐ ┌────┐│
│  │LoRA A │ │LoRA B │ │... ││  ← 按需热切换
│  │客服    │ │法律    │ │    ││
│  └───────┘ └───────┘ └────┘│
└─────────────────────────────┘

vLLM 多 LoRA 部署

bash
# 启动 vLLM,加载基座 + 多个 LoRA
vllm serve Qwen/Qwen2.5-7B-Instruct \
    --enable-lora \
    --lora-modules \
        customer_service=/path/to/lora_a \
        legal_assistant=/path/to/lora_b \
    --max-loras 4 \
    --max-lora-rank 16
python
# 调用时指定 LoRA
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1")

# 使用客服 LoRA
response = client.chat.completions.create(
    model="customer_service",  # LoRA 名称
    messages=[{"role": "user", "content": "我想退货"}]
)

# 使用法律 LoRA
response = client.chat.completions.create(
    model="legal_assistant",  # 切换 LoRA
    messages=[{"role": "user", "content": "劳动合同解除的条件"}]
)

优势:

  • 一台 GPU 服务多个场景
  • LoRA 热切换,无需重启
  • 每个 LoRA 只有几十 MB,便于分发和管理

学习资源

坚持是一种品格