0Pricing
AI Engineering Academy · Leçon

Évaluer et déployer votre modèle ajusté

Effectuez des évaluations quantitatives en comparant le modèle de base et le modèle ajusté sur des cas de test mis de côté, convertissez-le au format GGUF pour l’inférence locale et servez-le avec llama.cpp ou vLLM.

Évaluer et déployer votre modèle ajusté est une leçon AI Engineering Academy gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage AI Engineering Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours AI Engineering Academy comprend 4 leçons au total.

Pourquoi l'évaluation doit précéder le déploiement

Un modèle affiné qui obtient de bons résultats sur les données d'entraînement peut être moins performant que le modèle de base dans votre cas d'utilisation réel en production. La seule façon de le savoir est de procéder à une évaluation rigoureuse. L'affinage peut entraîner un oubli catastrophique (perte de capacités présentes dans le modèle de base), une surspécialisation (bons résultats sur votre tâche, mais moins bons sur des tâches connexes) ou de légères régressions des comportements de sécurité. Ne déployez jamais un modèle affiné sans l'évaluer par rapport au modèle de base sur un jeu de tests représentatif.

Création d'un jeu de tests mis de côté

Votre jeu de tests doit être totalement distinct des données d'entraînement et de validation : il doit contenir des exemples que le modèle n'a jamais vus pendant aucune phase d'entraînement. Le jeu de tests doit représenter la distribution complète des entrées de production : cas courants, cas limites et entrées adversariales. Pour les tâches de suivi d'instructions, incluez des exemples qui exigent de suivre tous les aspects de l'instruction, et pas seulement les plus fréquents. Un jeu de tests de 100 à 500 exemples suffit généralement pour obtenir une évaluation fiable.

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')

Métriques quantitatives pour l'évaluation propre à la tâche

Choisissez des métriques d'évaluation adaptées à votre tâche. Pour l'extraction JSON, mesurez le taux de conformité au schéma et la précision au niveau des champs. Pour la classification, mesurez l'exactitude, la précision et le rappel pour chaque classe. Pour la génération de texte, utilisez une notation par un LLM juge pour évaluer la qualité. Pour le respect du format, mesurez le taux exact de conformité au format. Calculez chaque métrique à la fois sur le modèle de base et sur le modèle affiné afin de mesurer l'écart d'amélioration.

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

Évaluation par un LLM juge

Pour les tâches de génération ouverte, utilisez un LLM juge afin d'évaluer les sorties du modèle affiné par rapport aux sorties attendues. Demandez à GPT-4o d'agir comme évaluateur, fournissez-lui l'entrée, la sortie attendue et la sortie du modèle, puis demandez-lui de noter la correction, l'exhaustivité et la conformité au format sur une échelle de 1 à 5. Faites la moyenne des notes sur l'ensemble de tests pour obtenir une évaluation globale de la qualité. Comparez la note du modèle affiné à celle du modèle de base sur le même ensemble de tests.

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)

Exécution de l'évaluation comparative

Effectuez une évaluation comparative directe du modèle de base, du modèle affiné et, éventuellement, d'une référence fondée sur des instructions optimisées, sur tous les cas de test. Générez les sorties de chaque modèle pour chaque cas de test, puis évaluez toutes les sorties avec vos métriques. Produisez un tableau comparatif présentant les scores des métriques, les écarts types et des exemples de situations où le modèle affiné est meilleur ou moins bon que la référence.

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

Tests de régression pour l'oubli catastrophique

L'affinage peut dégrader les capacités générales du modèle, phénomène appelé oubli catastrophique. Exécutez une suite de tests de régression sur le modèle de base et sur le modèle affiné, en couvrant les tâches qui vous importent au-delà de votre tâche cible : réponse générale aux questions, raisonnement, génération de code et suivi d'instructions. Si le modèle affiné obtient des scores nettement inférieurs pour ces tâches, le rang LoRA est peut-être trop élevé ou vous avez entraîné le modèle pendant trop d'époques.

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

Conversion au format GGUF pour l'inférence locale

Pour un déploiement local sans infrastructure GPU coûteuse, convertissez votre modèle fusionné au format GGUF et exécutez l'inférence avec llama.cpp. GGUF prend en charge plusieurs niveaux de quantification : Q4_K_M (4 bits, bon équilibre entre qualité et vitesse), Q8_0 (8 bits, qualité presque complète) et Q2_K (2 bits, très rapide mais de qualité inférieure). Un modèle 7B quantifié en Q4 ne nécessite qu'environ 4 Go de RAM et peut fonctionner sur CPU à raison de 1 à 5 tokens par seconde.

# 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'])

Déploiement avec vLLM en production

Pour servir à grande échelle un modèle affiné en production, vLLM est actuellement la référence. vLLM utilise PagedAttention pour regrouper efficacement plusieurs requêtes, ce qui augmente considérablement le débit du GPU. Il prend en charge des points de terminaison d'API compatibles avec OpenAI et constitue ainsi un remplacement direct de l'API OpenAI. Un seul GPU A100 exécutant vLLM avec un modèle 7B affiné peut traiter des centaines de requêtes par minute.

# 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)

Test A/B du modèle affiné

Avant de basculer complètement vers le modèle affiné en production, effectuez un test A/B : acheminez une partie du trafic de production (commencez par 5 à 10 %) vers le modèle affiné, tandis que la majorité continue d'utiliser le modèle de base ou l'approche existante fondée sur les messages. Surveillez les scores de qualité, la latence et les métriques de satisfaction des utilisateurs pour les deux groupes. N'augmentez la part de trafic du modèle affiné que si le test A/B confirme une amélioration après un nombre statistiquement significatif de requêtes.

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

Maintenance des modèles affinés au fil du temps

Les modèles affinés nécessitent une maintenance continue. Lorsque le modèle de base est mis à jour (nouvelle version de GPT-4o, nouvelle version de Mistral), vos poids d'adaptateur peuvent devenir incompatibles et vous devrez peut-être réentraîner le modèle. Lorsque les exigences de votre tâche changent, vous devez mettre à jour les données d'entraînement et réentraîner le modèle. Lorsque vous découvrez de nouveaux modes d'échec en production, ajoutez des exemples à votre jeu d'entraînement. Prévoyez un réentraînement périodique dans votre flux de travail des opérations de ML en production.

Matrice de décision du déploiement

Le choix d’une stratégie de déploiement pour votre modèle affiné dépend de votre volume et des contraintes de votre infrastructure. Pour un faible volume (moins de 1 000 requêtes par jour), l’API d’affinage d’OpenAI est la solution la plus simple. Pour un volume moyen (1 000 à 100 000 requêtes par jour), envisagez vLLM sur une instance équipée d’un seul GPU. Pour les applications à fort volume ou critiques en matière de latence, utilisez vLLM avec plusieurs GPU, envisagez le parallélisme des tenseurs et ajoutez une couche de mise en cache en amont. Pour les données sensibles en matière de confidentialité, hébergez le modèle sur votre propre infrastructure avec GGUF/llama.cpp ou vLLM.

Vérification rapide

Vérifiez votre compréhension de l’évaluation et du déploiement des modèles affinés présentés dans cette leçon.

Récapitulatif de la leçon

Dans cette leçon, vous avez appris que l’évaluation rigoureuse avant le déploiement, qui compare le modèle affiné au modèle de base sur des cas de test mis de côté, est indispensable, que les tests de régression détectent l’oubli catastrophique des capacités générales causé par une spécialisation excessive, et que les options de déploiement vont de l’API d’affinage gérée par OpenAI, privilégiée pour sa simplicité, à vLLM pour servir des modèles en production avec un débit élevé, en passant par GGUF/llama.cpp pour l’inférence locale basée sur le CPU. Félicitations, vous avez terminé le parcours d’ingénierie de l’IA !

Questions Fréquemment Posées

La leçon « Évaluer et déployer votre modèle ajusté » est-elle gratuite ?

Oui — le texte complet de « Évaluer et déployer votre modèle ajusté » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours AI Engineering Academy, passe à CoddyKit PRO. Le cours AI Engineering Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Évaluer et déployer votre modèle ajusté » ?

Effectuez des évaluations quantitatives en comparant le modèle de base et le modèle ajusté sur des cas de test mis de côté, convertissez-le au format GGUF pour l’inférence locale et servez-le avec ll… Tu pratiques AI Engineering Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer AI Engineering Academy ?

Aucune expérience préalable n'est requise. AI Engineering Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.

Combien de temps prend la leçon « Évaluer et déployer votre modèle ajusté » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon AI Engineering Academy ?

Oui. Chaque leçon AI Engineering Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Quand l’ajustement fin est plus efficace que les invites
  2. Préparer un jeu de données d’entraînement de haute qualité
  3. Ajustement fin LoRA avec Hugging Face PEFT
  4. Évaluer et déployer votre modèle ajusté
← Retour à AI Engineering Academy