第32讲|Fine-tuning 入门:训练一个懂你的专属 AI

4 阅读1分钟

金句:通用 AI 像一个知识渊博的新员工——什么都懂一点,但不了解你的公司、你的代码风格、你的业务逻辑。Fine-tuning 是让 AI 成为真正"懂你"的老员工。


一、什么时候需要 Fine-tuning?

不需要 Fine-tuning 的情况(用 Prompt 就够了)

  • 需要 AI 遵循特定格式输出
  • 需要 AI 了解最新信息(应该用 RAG)
  • 任务多样、不固定

适合 Fine-tuning 的情况

  • 需要 AI 持续输出特定风格(如公司的代码规范)
  • 有大量重复的固定任务(如特定格式的代码生成)
  • 需要减少提示词的长度(降低 API 成本)
  • 需要提升特定任务的准确率

二、Fine-tuning 的三种方式

方式一:OpenAI Fine-tuning API(最简单)

适合:已有 GPT-3.5 或 GPT-4 的使用场景,预算有限

步骤

1. 准备训练数据(JSONL 格式):

{"messages": [{"role": "system", "content": "你是一个代码审查助手,专注于 TypeScript 最佳实践。"}, {"role": "user", "content": "审查这段代码:const user = await db.query('SELECT * FROM users WHERE id = ' + userId)"}, {"role": "assistant", "content": "🔴 Critical - SQL 注入漏洞\n\n**问题**:直接字符串拼接 SQL 查询,存在严重的 SQL 注入风险。\n\n**修复**:\n```typescript\nconst user = await db.query('SELECT * FROM users WHERE id = $1', [userId]);\n```\n\n**说明**:使用参数化查询,将用户输入作为参数传递,而不是直接拼接到 SQL 字符串中。"}]}
{"messages": [{"role": "system", "content": "你是一个代码审查助手,专注于 TypeScript 最佳实践。"}, {"role": "user", "content": "审查这段代码:async function getUser(id) { return db.findOne(id) }"}, {"role": "assistant", "content": "🟡 Warning - 缺少类型注解和错误处理\n\n**问题1**:参数 `id` 没有类型注解\n**问题2**:函数没有声明返回类型\n**问题3**:没有 try-catch 错误处理\n\n**修复**:\n```typescript\nasync function getUser(id: string): Promise<User | null> {\n  try {\n    return await db.findOne(id);\n  } catch (error) {\n    logger.error('Failed to get user', { id, error });\n    throw new DatabaseException('User retrieval failed');\n  }\n}\n```"}]}

2. 上传数据并开始训练

import openai

client = openai.OpenAI()

# 上传训练数据
with open("training_data.jsonl", "rb") as f:
    response = client.files.create(file=f, purpose="fine-tune")
    file_id = response.id

print(f"文件已上传,ID: {file_id}")

# 开始 Fine-tuning
job = client.fine_tuning.jobs.create(
    training_file=file_id,
    model="gpt-3.5-turbo",  # 或 gpt-4o-mini
    hyperparameters={
        "n_epochs": 3,  # 训练轮数
    }
)

print(f"训练任务已创建,ID: {job.id}")
print(f"状态:{job.status}")

3. 查看训练状态

# 查看训练进度
job_status = client.fine_tuning.jobs.retrieve(job.id)
print(f"状态:{job_status.status}")
print(f"训练模型:{job_status.fine_tuned_model}")  # 完成后显示

4. 使用训练好的模型

# 使用 Fine-tuned 模型
response = client.chat.completions.create(
    model="ft:gpt-3.5-turbo-0125:your-org::xxxxx",  # Fine-tuned 模型 ID
    messages=[
        {"role": "system", "content": "你是一个代码审查助手"},
        {"role": "user", "content": "审查这段代码:..."}
    ]
)

方式二:LoRA/QLoRA 开源模型 Fine-tuning

适合:有 GPU、需要完全控制、不想付 API 费用

# 使用 Hugging Face + PEFT 进行 LoRA Fine-tuning
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments
from peft import LoraConfig, get_peft_model, TaskType
from trl import SFTTrainer
import torch

# 加载基础模型(以 CodeLlama 为例)
model_name = "codellama/CodeLlama-7b-hf"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    load_in_4bit=True,  # QLoRA:4bit 量化,大幅减少显存
    torch_dtype=torch.float16,
    device_map="auto"
)

# LoRA 配置
lora_config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,
    r=16,           # LoRA 秩(越大,表达能力越强,显存越多)
    lora_alpha=32,  # LoRA 缩放系数
    target_modules=["q_proj", "v_proj"],  # 只训练 attention 层
    lora_dropout=0.1,
)

model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# 输出:trainable params: 4,194,304 || all params: 6,742,609,920
# 只训练 0.06% 的参数,节省大量资源

# 训练配置
training_args = TrainingArguments(
    output_dir="./code-review-model",
    num_train_epochs=3,
    per_device_train_batch_size=4,
    learning_rate=2e-4,
    fp16=True,
    logging_steps=100,
    save_steps=500,
)

# 开始训练
trainer = SFTTrainer(
    model=model,
    train_dataset=dataset,  # 你的训练数据
    args=training_args,
    tokenizer=tokenizer,
    dataset_text_field="text",
    max_seq_length=2048,
)

trainer.train()
trainer.save_model()

三、训练数据收集策略

策略一:从人工审查记录中提取

# 从 GitHub PR 评论中提取高质量审查数据
import requests

def get_pr_reviews(repo: str, token: str) -> list:
    """获取 PR 中的代码审查评论"""
    reviews = []
    headers = {"Authorization": f"token {token}"}
    
    # 获取 PR 列表
    prs = requests.get(
        f"https://api.github.com/repos/{repo}/pulls?state=closed&per_page=100",
        headers=headers
    ).json()
    
    for pr in prs:
        # 获取 PR 中的 review 评论
        comments = requests.get(
            f"https://api.github.com/repos/{repo}/pulls/{pr['number']}/comments",
            headers=headers
        ).json()
        
        for comment in comments:
            if len(comment['body']) > 100:  # 只要详细的评论
                reviews.append({
                    "code": comment.get('diff_hunk', ''),
                    "review": comment['body']
                })
    
    return reviews

策略二:数据增强

# 用 GPT-4 生成更多训练样本
def augment_training_data(existing_example: dict) -> list:
    """基于已有样本生成变体"""
    prompt = f"""
基于以下代码审查示例,生成 5 个类似但不同的训练样本:
原始代码:{existing_example['code']}
原始审查:{existing_example['review']}

要求:
1. 保持相同类型的问题(如安全漏洞/性能问题/代码规范)
2. 使用不同的代码语言或场景
3. 保持审查风格一致
"""
    # 调用 GPT-4 生成...

四、Fine-tuning 效果评估

def evaluate_fine_tuned_model(model_id: str, test_cases: list) -> dict:
    """评估 Fine-tuned 模型的性能"""
    results = {
        "format_accuracy": 0,  # 输出格式是否符合规范
        "relevance_score": 0,  # 审查是否抓住关键问题
        "false_positive_rate": 0  # 误报率
    }
    
    for test in test_cases:
        response = client.chat.completions.create(
            model=model_id,
            messages=[{"role": "user", "content": test["input"]}]
        )
        output = response.choices[0].message.content
        
        # 检查输出格式
        if any(marker in output for marker in ["🔴", "🟡", "🟢"]):
            results["format_accuracy"] += 1
        
        # 检查是否发现了预期的问题
        if test["expected_issue"].lower() in output.lower():
            results["relevance_score"] += 1
    
    n = len(test_cases)
    results["format_accuracy"] /= n
    results["relevance_score"] /= n
    
    return results

章节小结:Fine-tuning 是让 AI 从"通用助手"进化为"专业搭档"的技术。OpenAI Fine-tuning API 提供了最低门槛的入口,适合大多数开发者;LoRA/QLoRA 则给予了完全控制权,适合有 GPU 资源的团队。关键是:先用高质量的少量数据(100-1000条)训练,评估效果,再决定是否扩大规模。