0Pricing
AI Engineering Academy · Lección

Evaluación e implementación de su modelo ajustado

Ejecute evaluaciones cuantitativas que comparen el modelo base y el modelo ajustado con casos de prueba reservados, conviértalo a GGUF para inferencia local y sírvalo mediante llama.cpp o vLLM.

Evaluación e implementación de su modelo ajustado es una lección gratuita de AI Engineering Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Engineering Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Engineering Academy incluye 4 lecciones en total.

Por qué la evaluación debe preceder al despliegue

Un modelo ajustado que funciona bien con los datos de entrenamiento puede rendir peor que el modelo base en su caso de uso real en producción. La única forma de saberlo es realizar una evaluación rigurosa. El ajuste fino puede causar olvido catastrófico (pérdida de capacidades que tenía el modelo base), una especialización excesiva (buen rendimiento en su tarea, pero peor en tareas relacionadas) o regresiones sutiles en los comportamientos de seguridad. Nunca despliegue un modelo ajustado sin evaluarlo frente al modelo base con un conjunto de pruebas representativo.

Creación de un conjunto de pruebas reservado

Su conjunto de pruebas debe estar completamente separado de los datos de entrenamiento y validación: debe contener ejemplos que el modelo no haya visto durante ninguna fase del entrenamiento. El conjunto de pruebas debe representar la distribución completa de las entradas de producción: casos comunes, casos límite y entradas adversarias. En tareas de seguimiento de instrucciones, incluya ejemplos que requieran seguir todos los aspectos de la instrucción, no solo los más habituales. Un conjunto de pruebas de entre 100 y 500 ejemplos suele ser suficiente para una evaluación 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étricas cuantitativas para la evaluación específica de tareas

Elija métricas de evaluación que se ajusten a su tarea. Para la extracción de JSON, mida la tasa de cumplimiento del esquema y la precisión por campo. Para la clasificación, mida la exactitud, la precisión y la exhaustividad por clase. Para la generación de texto, utilice la puntuación de un LLM como juez para evaluar la calidad. Para el cumplimiento del formato, mida la tasa de cumplimiento exacto del formato. Ejecute cada métrica tanto en el modelo base como en el modelo ajustado para medir la diferencia de mejora.

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

Evaluación con un LLM como juez

Para tareas de generación abierta, utilice un LLM como juez para puntuar las salidas del modelo ajustado frente a las salidas esperadas. Indique a GPT-4o que actúe como evaluador, proporciónele la entrada, la salida esperada y la salida del modelo, y pídale que valore la corrección, la exhaustividad y el cumplimiento del formato en una escala del 1 al 5. Calcule la media de las puntuaciones del conjunto de pruebas para obtener una valoración general de la calidad. Compare la puntuación del modelo ajustado con la del modelo base en el mismo conjunto de pruebas.

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)

Ejecución de la evaluación comparativa

Realice una evaluación directa que compare el modelo base, el modelo ajustado y, opcionalmente, una línea base sólida basada en prompting en todos los casos de prueba. Genere salidas de cada modelo para cada caso de prueba y, después, puntúe todas las salidas con sus métricas de evaluación. Elabore una tabla comparativa que muestre las puntuaciones de las métricas, las desviaciones estándar y ejemplos de los casos en los que el modelo ajustado supera o no alcanza la línea base.

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

Pruebas de regresión para detectar el olvido catastrófico

El ajuste fino puede degradar las capacidades generales del modelo, un fenómeno denominado olvido catastrófico. Ejecute una batería de pruebas de regresión tanto en el modelo base como en el ajustado, cubriendo las tareas que le interesen además de la tarea objetivo: respuesta a preguntas generales, razonamiento, generación de código y seguimiento de instrucciones. Si el modelo ajustado obtiene una puntuación significativamente menor en estas tareas, es posible que el rango de LoRA sea demasiado alto o que haya entrenado durante demasiadas épocas.

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

Conversión a GGUF para inferencia local

Para realizar un despliegue local sin una costosa infraestructura de GPU, convierta el modelo combinado al formato GGUF y ejecute la inferencia con llama.cpp. GGUF admite varios niveles de cuantización: Q4_K_M (4 bits, buen equilibrio entre calidad y velocidad), Q8_0 (8 bits, calidad casi completa) y Q2_K (2 bits, muy rápido, pero con menor calidad). Un modelo 7B cuantizado a Q4 requiere solo aproximadamente 4 GB de RAM y puede ejecutarse en CPU a entre 1 y 5 tokens por segundo.

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

Servicio con vLLM para producción

Para ofrecer un modelo ajustado en producción a escala, vLLM es actualmente el estándar. vLLM utiliza PagedAttention para agrupar de forma eficiente varias solicitudes, lo que aumenta considerablemente el rendimiento de la GPU. Admite endpoints de API compatibles con OpenAI, por lo que puede sustituir directamente a la API de OpenAI. Una única GPU A100 que ejecute vLLM con un modelo 7B ajustado puede gestionar cientos de solicitudes por minuto.

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

Prueba A/B del modelo ajustado

Antes de cambiar completamente al modelo ajustado en producción, realice una prueba A/B: dirija un porcentaje del tráfico de producción (comience con un 5-10 %) al modelo ajustado, mientras la mayoría sigue utilizando el modelo base o el enfoque de prompting existente. Supervise las puntuaciones de calidad, la latencia y las métricas de satisfacción de los usuarios de ambos grupos. Aumente la proporción de tráfico del modelo ajustado únicamente si la prueba A/B confirma una mejora después de alcanzar un número estadísticamente significativo de solicitudes.

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

Mantenimiento de los modelos ajustados a lo largo del tiempo

Los modelos ajustados requieren mantenimiento continuo. Cuando se actualiza el modelo base (una nueva versión de GPT-4o, una nueva versión de Mistral), los pesos de su adaptador pueden dejar de ser compatibles y quizá tenga que volver a entrenar. Cuando cambien los requisitos de su tarea, deberá actualizar los datos de entrenamiento y volver a entrenar. Cuando descubra nuevos modos de fallo en producción, añada ejemplos a su conjunto de entrenamiento. Planifique un reentrenamiento periódico como parte de su flujo de operaciones de ML en producción.

Matriz de decisión del despliegue

Elegir una estrategia de implementación para su modelo ajustado depende de su escala y de las restricciones de su infraestructura. Para un volumen bajo (menos de 1000 solicitudes al día), la API de ajuste de OpenAI es la opción más sencilla. Para un volumen medio (entre 1000 y 100 000 al día), considere usar vLLM en una instancia con una sola GPU. Para aplicaciones de volumen alto o críticas en cuanto a latencia, utilice vLLM con varias GPU, considere el paralelismo de tensores y añada una capa de caché delante. Para datos confidenciales, aloje el modelo en su propia infraestructura con GGUF/llama.cpp o vLLM.

Comprobación rápida

Compruebe su comprensión de la evaluación y la implementación de modelos ajustados a partir de esta lección.

Resumen de la lección

En esta lección ha aprendido que la evaluación rigurosa antes de la implementación, comparando el modelo ajustado con el modelo base en casos de prueba reservados, es imprescindible; que las pruebas de regresión detectan el olvido catastrófico de capacidades generales causado por una especialización excesiva; y que las opciones de implementación van desde la API de ajuste administrada de OpenAI, por su sencillez, hasta vLLM, para servir modelos en producción con un alto rendimiento, y GGUF/llama.cpp, para inferencia local basada en CPU. ¡Enhorabuena por completar el itinerario de Ingeniería de IA!

Preguntas frecuentes

¿La lección «Evaluación e implementación de su modelo ajustado» es gratis?

Sí — el texto completo de «Evaluación e implementación de su modelo ajustado» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Engineering Academy, actualiza a CoddyKit PRO. El curso de AI Engineering Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Evaluación e implementación de su modelo ajustado»?

Ejecute evaluaciones cuantitativas que comparen el modelo base y el modelo ajustado con casos de prueba reservados, conviértalo a GGUF para inferencia local y sírvalo mediante llama.cpp o vLLM. Practicas AI Engineering Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar AI Engineering Academy?

No se requiere experiencia previa. AI Engineering Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.

¿Cuánto tiempo toma la lección «Evaluación e implementación de su modelo ajustado»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de AI Engineering Academy?

Sí. Cada lección de AI Engineering Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Cuándo el fine-tuning supera al prompting
  2. Preparación de un conjunto de datos de entrenamiento de alta calidad
  3. Fine-tuning de LoRA con Hugging Face PEFT
  4. Evaluación e implementación de su modelo ajustado
← Volver a AI Engineering Academy