0Pricing
AI Engineering Academy · 课时

评估并部署您的微调模型

运行定量评估,在保留的测试案例上比较基础模型与微调模型,转换为 GGUF 以进行本地推理,并通过 llama.cpp 或 vLLM 提供服务。

评估并部署您的微调模型 是 CoddyKit 上的免费 AI Engineering Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 AI Engineering Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 AI Engineering Academy 课程共包含 4 节课。

为什么必须先评估再部署

在训练数据上表现良好的微调模型,在您的实际生产场景中可能比基础模型表现更差。唯一的确认方法是进行严格评估。微调可能导致灾难性遗忘(丧失基础模型原有的能力)、过度专门化(在您的任务上表现良好,但在相关任务上表现变差),或使安全行为出现细微退化。在使用具有代表性的测试集将微调模型与基础模型进行比较评估之前,绝不要部署微调模型。

构建保留测试集

您的测试集必须与训练数据和验证数据完全分离,其中的示例不能在训练的任何阶段被模型见过。测试集应代表生产输入的完整分布:常见情况、边缘情况和对抗性输入。对于指令遵循任务,请加入要求遵循指令所有方面的示例,而不只是最常见的方面。通常,包含 100–500 个示例的测试集就足以进行可靠评估。

import json
from typing import TypedDict

class TestCase(TypedDict):
    input: str                 # the user message
    expected_output: str       # the ideal response
    category: str              # e.g., 'format', 'accuracy', 'edge_case'
    evaluation_method: str     # 'exact_match', 'json_schema', 'llm_judge'

# Load test set (never used during training)
def load_test_set(path: str) -> list[TestCase]:
    cases = []
    with open(path) as f:
        for line in f:
            data = json.loads(line.strip())
            cases.append({
                'input': data['messages'][-2]['content'],  # user message
                'expected_output': data['messages'][-1]['content'],  # assistant response
                'category': data.get('metadata', {}).get('category', 'general'),
                'evaluation_method': data.get('metadata', {}).get('eval_method', 'llm_judge')
            })
    return cases

test_set = load_test_set('test.jsonl')
print(f'Test set loaded: {len(test_set)} examples')

针对特定任务的量化评估指标

请选择与任务相匹配的评估指标。对于 JSON 提取,请测量模式符合率和字段级准确率。对于分类,请按类别测量准确率、精确率和召回率。对于文本生成,请使用 LLM 评审评分来评估质量。对于格式遵循,请测量完全符合指定格式的比例。在基础模型和微调模型上分别运行每项指标,以便测量改进幅度。

import json

def evaluate_json_extraction(model_output: str, expected: str, schema: dict) -> dict:
    metrics = {'valid_json': False, 'schema_compliant': False, 'field_accuracy': 0.0}
    
    try:
        parsed = json.loads(model_output.strip())
        metrics['valid_json'] = True
        
        # Check schema compliance
        required_fields = schema.get('required', [])
        all_present = all(field in parsed for field in required_fields)
        correct_types = all(
            isinstance(parsed.get(field), schema['properties'][field]['expected_type'])
            for field in required_fields if field in parsed
        )
        metrics['schema_compliant'] = all_present and correct_types
        
        # Field-level accuracy against expected output
        expected_parsed = json.loads(expected)
        correct_fields = sum(1 for k in expected_parsed if parsed.get(k) == expected_parsed[k])
        metrics['field_accuracy'] = correct_fields / len(expected_parsed) if expected_parsed else 0.0
    
    except json.JSONDecodeError:
        pass  # valid_json stays False
    
    return metrics

LLM 评审评估

对于开放式生成任务,请使用 LLM 评审,根据预期输出为微调模型的输出评分。提示 GPT-4o 扮演评估者,提供输入、预期输出和模型输出,并要求它按照 1–5 分制评价正确性、完整性和格式符合度。对整个测试集的分数取平均,得到总体质量评分。使用同一个测试集,将微调模型的分数与基础模型的分数进行比较。

from openai import OpenAI

client = OpenAI()

def llm_judge_score(instruction: str, expected: str, actual: str) -> dict:
    judge_prompt = f'''Evaluate the quality of an AI assistant response.

Instruction given to assistant:
{instruction}

Expected ideal response:
{expected}

Actual response from model being evaluated:
{actual}

Rate the actual response on these criteria (1=poor, 5=excellent):
1. Correctness: Is the information accurate?
2. Format compliance: Does it follow the expected output format?
3. Completeness: Does it address all parts of the instruction?

Return JSON: {{"correctness": N, "format": N, "completeness": N, "overall": N, "reason": "brief explanation"}}'''
    
    response = client.chat.completions.create(
        model='gpt-4o',
        messages=[{'role': 'user', 'content': judge_prompt}],
        response_format={'type': 'json_object'}
    )
    return json.loads(response.choices[0].message.content)

运行对比评估

对基础模型、微调模型(以及可选的高质量提示词基线)进行逐项对比评估,覆盖所有测试用例。让每个模型处理每个测试用例并生成输出,然后使用评估指标为所有输出评分。生成一张对比表,展示各项指标的分数、标准差,以及微调模型相较于基线模型表现更好和更差的示例。

def run_full_evaluation(test_set: list, models: dict, system_prompt: str) -> dict:
    results = {name: {'scores': [], 'errors': 0} for name in models}
    
    for i, test_case in enumerate(test_set):
        print(f'Evaluating test case {i+1}/{len(test_set)}')
        
        for model_name, model_fn in models.items():
            try:
                output = model_fn(test_case['input'], system_prompt)
                score = llm_judge_score(
                    test_case['input'],
                    test_case['expected_output'],
                    output
                )
                results[model_name]['scores'].append(score['overall'])
            except Exception as e:
                results[model_name]['errors'] += 1
                results[model_name]['scores'].append(0)
    
    # Summarize
    summary = {}
    for name, data in results.items():
        scores = data['scores']
        summary[name] = {
            'mean_score': sum(scores) / len(scores),
            'errors': data['errors']
        }
        print(f'{name}: mean={summary[name]["mean_score"]:.2f}, errors={data["errors"]}')
    return summary

针对灾难性遗忘进行回归测试

微调可能削弱模型的通用能力,这种现象称为灾难性遗忘。请在基础模型和微调模型上运行回归测试套件,覆盖目标任务之外您所关心的任务:通用问答、推理、代码生成和指令遵循。如果微调模型在这些任务上的得分明显更低,可能是 LoRA 的秩过高,或者训练轮数过多。

REGRESSION_TEST_CASES = [
    # General QA
    {'input': 'What is the capital of France?', 'expected_substring': 'Paris'},
    {'input': 'What is 17 * 23?', 'expected_substring': '391'},
    # Instruction following
    {'input': 'List 3 planets. Format as: 1. Planet Name', 'expected_pattern': r'^1\. '},
    # Reasoning
    {'input': 'If all A are B and all B are C, are all A also C?', 'expected_substring': 'yes'},
]

def run_regression_tests(model_fn, test_cases: list) -> float:
    passed = 0
    for test in test_cases:
        output = model_fn(test['input'], '')
        if 'expected_substring' in test:
            if test['expected_substring'].lower() in output.lower():
                passed += 1
        elif 'expected_pattern' in test:
            import re
            if re.search(test['expected_pattern'], output):
                passed += 1
    
    rate = passed / len(test_cases)
    print(f'Regression test pass rate: {rate:.1%} ({passed}/{len(test_cases)})')
    return rate

转换为 GGUF 以进行本地推理

如果要在不使用昂贵 GPU 基础设施的情况下进行本地部署,请将合并后的模型转换为 GGUF 格式,并使用 llama.cpp 运行推理。GGUF 支持多种量化级别:Q4_K_M(4 位,质量与速度均衡)、Q8_0(8 位,接近完整质量)和 Q2_K(2 位,速度非常快但质量较低)。经过 Q4 量化的 7B 模型只需约 4GB 的 RAM,并且可以在 CPU 上以每秒 1–5 个令牌的速度运行。

# Step 1: Convert merged HuggingFace model to GGUF
# git clone https://github.com/ggerganov/llama.cpp
# python llama.cpp/convert_hf_to_gguf.py ./merged-model --outtype f16 --outfile model-f16.gguf

# Step 2: Quantize to 4-bit
# ./llama.cpp/llama-quantize model-f16.gguf model-q4.gguf Q4_K_M

# Step 3: Run inference with llama.cpp Python bindings
# pip install llama-cpp-python
from llama_cpp import Llama

llm = Llama(
    model_path='./model-q4.gguf',
    n_ctx=4096,         # context window
    n_threads=8,        # CPU threads
    n_gpu_layers=0      # set > 0 to offload layers to GPU
)

output = llm.create_chat_completion(
    messages=[{'role': 'user', 'content': 'What is the capital of France?'}],
    temperature=0.1
)
print(output['choices'][0]['message']['content'])

使用 vLLM 提供生产服务

对于大规模提供微调模型的生产服务,vLLM 是目前的标准方案。vLLM 使用 PagedAttention 高效地将多个请求批处理在一起,从而大幅提高 GPU 吞吐量。它支持与 OpenAI 兼容的 API 端点,可以直接替代 OpenAI API。单个 A100 GPU 使用 vLLM 运行微调后的 7B 模型时,每分钟可以处理数百个请求。

# Start vLLM server (run from command line)
# pip install vllm
# python -m vllm.entrypoints.openai.api_server \
#     --model ./merged-model \
#     --host 0.0.0.0 \
#     --port 8000 \
#     --max-model-len 4096 \
#     --tensor-parallel-size 1

# Use with OpenAI client (drop-in replacement)
from openai import OpenAI

client = OpenAI(
    base_url='http://localhost:8000/v1',
    api_key='not-needed'  # vLLM doesn't require auth by default
)

response = client.chat.completions.create(
    model='merged-model',  # model name matches the path you passed to vLLM
    messages=[{'role': 'user', 'content': 'Extract JSON from: "Alice, 30, NYC"'}]
)
print(response.choices[0].message.content)

对微调模型进行 A/B 测试

在生产环境中完全切换到微调模型之前,请运行A/B 测试:将一部分生产流量(从 5%–10% 开始)路由到微调模型,同时让大多数流量继续使用基础模型或现有的提示词方案。监控两组的质量评分、延迟和用户满意度指标。只有在 A/B 测试经过具有统计显著性的请求数量并确认效果有所提升后,才增加微调模型的流量占比。

import random

class ModelRouter:
    def __init__(self, fine_tuned_traffic_fraction=0.1):
        self.ft_fraction = fine_tuned_traffic_fraction
        self.metrics = {'base': {'count': 0, 'quality_sum': 0}, 'fine_tuned': {'count': 0, 'quality_sum': 0}}

    def route(self, user_id: str, request: str) -> dict:
        # Deterministic routing by user_id (same user always goes to same model)
        use_fine_tuned = (hash(user_id) % 100) < (self.ft_fraction * 100)
        model_group = 'fine_tuned' if use_fine_tuned else 'base'
        
        response = call_model(request, use_fine_tuned=use_fine_tuned)
        return {'response': response, 'model_group': model_group}

    def record_quality(self, model_group: str, quality_score: float):
        self.metrics[model_group]['count'] += 1
        self.metrics[model_group]['quality_sum'] += quality_score

    def ab_test_summary(self) -> dict:
        summary = {}
        for group, data in self.metrics.items():
            avg = data['quality_sum'] / data['count'] if data['count'] > 0 else 0
            summary[group] = {'avg_quality': avg, 'n': data['count']}
        return summary

长期维护微调模型

微调模型需要持续维护。当基础模型更新时(例如推出新的 GPT-4o 版本或新的 Mistral 版本),您的适配器权重可能不再兼容,您可能需要重新训练。当任务要求发生变化时,您需要更新训练数据并重新训练。当您在生产环境中发现新的失败模式时,请将相关示例添加到训练集中。请将定期重新训练纳入生产机器学习运维工作流程。

部署决策矩阵

为微调模型选择部署策略取决于您的规模和基础设施限制。对于低请求量(每天少于 1000 次请求),OpenAI 的微调 API 最简单。对于中等请求量(每天 1000 至 100,000 次请求),可以考虑在单个 GPU 实例上运行 vLLM。对于高请求量或对延迟敏感的应用,请使用多 GPU 运行 vLLM,考虑采用张量并行,并在前端添加缓存层。对于对隐私敏感的数据,请在您自己的基础设施上使用 GGUF/llama.cpp 或 vLLM 自行托管。

快速检查

测试您对本课中微调模型评估与部署内容的理解。

课程回顾

在本课中,您学到了:对留出的测试用例进行严格的部署前评估,比较微调模型与基础模型的表现,这是不可或缺的;回归测试可以检测过度专业化导致的通用能力灾难性遗忘;部署方案既包括追求简便的OpenAI 托管式微调 API,也包括用于高吞吐量生产服务的vLLM,以及用于基于 CPU 的本地推理的GGUF/llama.cpp。恭喜您完成 AI 工程学习路径!

常见问题解答

「评估并部署您的微调模型」课时是免费的吗?

是的 — 「评估并部署您的微调模型」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 AI Engineering Academy 课程的其余内容,请升级到 CoddyKit PRO。 AI Engineering Academy 课程共包含 4 节课。

「评估并部署您的微调模型」这节课中我会学到什么?

运行定量评估,在保留的测试案例上比较基础模型与微调模型,转换为 GGUF 以进行本地推理,并通过 llama.cpp 或 vLLM 提供服务。 你通过在浏览器中直接运行的动手代码来练习 AI Engineering Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 AI Engineering Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 AI Engineering Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「评估并部署您的微调模型」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 AI Engineering Academy 课中编写并运行代码吗?

能。每节 AI Engineering Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 微调何时胜过提示工程
  2. 准备高质量训练数据集
  3. 使用 Hugging Face PEFT 进行 LoRA 微调
  4. 评估并部署您的微调模型
← 返回 AI Engineering Academy