Tous les produits
Search
Centre de documentation

Platform For AI:Model Gallery quick start

Dernière mise à jour :Aug 09, 2026

Model Gallery intègre PAI-DLC et PAI-EAS pour permettre le déploiement et l'entraînement sans code de grands modèles de langage open source. Cette rubrique explique comment déployer, affiner et évaluer le modèle Qwen3-0.6B.

1. Prérequis

Pour activer PAI et créer un espace de travail, connectez-vous à la console PAI avec votre compte Alibaba Cloud, sélectionnez une région dans le coin supérieur gauche, puis activez le service via l'autorisation en un clic.

2. Facturation

Les exemples présentés dans cette rubrique utilisent des ressources publiques pour créer des tâches PAI-DLC et des services PAI-EAS, facturés selon le mode de paiement à l'utilisation. Pour plus d'informations, consultez les rubriques Facturation PAI-DLC et Facturation PAI-EAS.

3. Déploiement du modèle

3.1 Déployer le modèle

  1. Connectez-vous à la console PAI. Dans le volet de navigation de gauche, cliquez sur Model Gallery, recherchez la carte Qwen3-0.6B, puis cliquez sur Deploy.

  2. La page de configuration est préremplie avec les paramètres par défaut. Cliquez sur Deploy > Confirm. Le déploiement prend environ cinq minutes. L'opération est réussie lorsque le statut passe à In operation.

    Par défaut, le modèle est déployé à l'aide de ressources publiques et facturé selon le mode de paiement à l'utilisation.

    La spécification de ressource de déploiement par défaut est ecs.gn7i-c8g1.2xlarge (8 vCPU, 30 GiB, NVIDIA A10 × 1), dont le coût s'élève à environ 10,5 CNY/heure. Après avoir vérifié la configuration, cliquez sur Deploy en bas du panneau.

3.2 Appeler le modèle

  1. Consultez les informations d'appel. Sur la page des détails du service, cliquez sur View Call Information pour obtenir l'Internet Endpoint et le Token.

    Pour consulter ultérieurement les détails de la tâche de déploiement, cliquez dans le volet de navigation de gauche sur Model Gallery > Job Management > Deployment Jobs . Ensuite, cliquez sur le Service name cible.

    Dans la boîte de dialogue invocation information qui s'affiche, consultez respectivement l'Internet Endpoint et le point de terminaison VPC sous les onglets Shared Gateway et VPC High-Speed Direct Connection.

  2. Appelez le modèle en utilisant l'une des méthodes suivantes.

    Débogage en ligne

    Accédez à la page Online Debugging. Le service de grand modèle de langage prend en charge le Conversation Debugging et le API Debugging.

    Client Cherry Studio

    Cherry Studio est un client populaire pour interagir avec les grands modèles de langage. Il intègre la fonctionnalité MCP, qui vous permet de discuter facilement avec les modèles.

    Se connecter au modèle Qwen3 déployé sur PAI

    SDK Python

    from openai import OpenAI
    import os
    # If you have not set the environment variable, you can assign your service Token directly. For example: token = 'YTA1NTEzMzY3ZTY4Z******************'
    token = os.environ.get("Token")
    # Do not remove "/v1" from the end of the endpoint.
    client = OpenAI(
        api_key=token,
        base_url=f'<your_endpoint>/v1',
    )
    if token is None:
        print("Please configure the Token environment variable, or assign the token value directly to the token variable.")
        exit()
    query = 'Hello, who are you?'
    messages = [{'role': 'user', 'content': query}]
    resp = client.chat.completions.create(model='Qwen3-0.6B', messages=messages, max_tokens=512, temperature=0)
    query = messages[0]['content']
    response = resp.choices[0].message.content
    print(f'query: {query}')
    print(f'response: {response}')

3.3 Rappel important

Ce service de modèle utilise des ressources publiques et est facturé selon le mode de paiement à l'utilisation. Pour éviter des frais inutiles, arrêtez ou supprimez le service lorsque vous n'en avez plus besoin.

Vous pouvez effectuer cette opération dans l'onglet Job Management > Deployment Jobs, dans la colonne Actions du service cible.

4. Affinage du modèle

Pour améliorer les performances d'un modèle dans un domaine spécifique, vous pouvez l'affiner à l'aide d'un jeu de données spécialisé. Cette section présente un cas d'usage illustrant l'objectif et les étapes de l'affinage de modèle.

4.1 Cas d'usage

Dans le secteur logistique, il est fréquent d'extraire des informations structurées (telles que le destinataire, l'adresse et le numéro de téléphone) à partir de langage naturel. Les modèles à grand nombre de paramètres, tels que Qwen3-235B-A22B, offrent de bonnes performances pour cette tâche, mais leur coût et leur latence sont élevés. Pour équilibrer performances et coûts, vous pouvez d'abord utiliser un modèle à grand nombre de paramètres pour étiqueter les données, puis utiliser ces données pour affiner un modèle à faible nombre de paramètres, tel que Qwen3-0.6B, afin d'obtenir des performances similaires sur la tâche. Ce processus est également appelé distillation de modèle.

Pour cette tâche, le modèle Qwen3-0.6B original affiche une précision de 50 % . Après affinage, sa précision peut dépasser 90 %.

Exemple d'informations d'adresse du destinataire

Exemple d'informations structurées

Amina Patel - Phone number (474) 598-1543 - 1425 S 5th St, Apt 3B, Allentown, Pennsylvania 18104

{
        "state": "Pennsylvania",
        "city": "Allentown",
        "zip_code": "18104",
        "street_address": "1425 S 5th St, Apt 3B",
        "name": "Amina Patel",
        "phone": "(474) 598-1543"
    }

4,2 Préparation des données

Cette tâche consiste à réaliser une distillation de modèle, du modèle enseignant (Qwen3-235B-A22B) vers le modèle Qwen3-0,6B. Vous devez d'abord utiliser l'API du modèle enseignant pour extraire les informations d'adresse du destinataire sous forme de données JSON structurées. La génération de ces données JSON peut prendre du temps. Par conséquent, cet article met à votre disposition un jeu de données d'entraînement train.json et un jeu de validation eval.json que vous pouvez télécharger et utiliser directement.

Dans le processus de distillation de modèle, le modèle disposant du plus grand nombre de paramètres est appelé modèle enseignant. Les données utilisées dans cet article sont générées de manière synthétique par un grand modèle et ne contiennent aucune information sensible relative aux utilisateurs.

Mise en production

Pour appliquer cette solution à votre activité, nous vous recommandons de préparer les données selon les méthodes suivantes :

Scénarios métier réels (recommandé)

Les données métier réelles reflètent mieux vos scénarios d'activité, ce qui permet au modèle affiné de s'adapter plus efficacement à vos besoins. Une fois que vous avez obtenu les données métier, vous devez les convertir par programmation en un fichier JSON respectant le format suivant.

[
    {
        "instruction": "You are an expert assistant for extracting structured JSON from US shipping information. The JSON keys are name, street_address, city, state, zip_code, and phone.  Name: Isabella Rivera Cruz | 182 Calle Luis Lloréns Torres, Apt 3B, Mayagüez, Puerto Rico 00680 | MOBILE: (640) 486-5927",
        "output": "{\"name\": \"Isabella Rivera Cruz\", \"street_address\": \"182 Calle Luis Lloréns Torres, Apt 3B\", \"city\": \"Mayagüez\", \"state\": \"Puerto Rico\", \"zip_code\": \"00680\", \"phone\": \"(640) 486-5927\"}"
    },
    {
        "instruction": "You are an expert assistant for extracting structured JSON from US shipping information. The JSON keys are name, street_address, city, state, zip_code, and phone.  1245 Broadwater Avenue, Apt 3B, Bozeman, Montana 59715Receiver: Aisha PatelP: (429) 763-9742",
        "output": "{\"name\": \"Aisha Patel\", \"street_address\": \"1245 Broadwater Avenue, Apt 3B\", \"city\": \"Bozeman\", \"state\": \"Montana\", \"zip_code\": \"59715\", \"phone\": \"(429) 763-9742\"}"
    }
]

Le fichier JSON contient plusieurs échantillons d'entraînement. Chaque échantillon comprend deux champs : instruction et output.

  • instruction : Contient l'instruction qui guide le comportement du grand modèle, ainsi que les données d'entrée.

  • output : La réponse standard attendue, généralement générée par des experts humains ou par des modèles plus puissants tels que qwen3-235b-a22b.

Génération par modèle

Lorsque les données métier sont insuffisantes, envisagez d'utiliser un modèle pour l'augmentation des données. Cette approche améliore la diversité et la couverture des données. Afin de préserver la confidentialité des utilisateurs, cette solution utilise un modèle pour générer un lot de données d'adresse virtuelles. Le code de génération ci-dessous est fourni à titre de référence.

Code pour simuler la génération de données métier

Pour exécuter le code suivant, vous devez créer une clé API Alibaba Cloud Model Studio. Le code utilise qwen-plus-latest pour générer les données métier et qwen3-235b-a22b pour l'étiquetage.

# -*- coding: utf-8 -*-
import os
import asyncio
import random
import json
import sys
from typing import List
import platform
from openai import AsyncOpenAI

# Create an asynchronous client instance.
# NOTE: This script uses the DashScope-compatible API endpoint.
# If you are using a different OpenAI-compatible service, change the base_url.
client = AsyncOpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)

# List of US States and Territories.
us_states = [
    "Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware",
    "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky",
    "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi",
    "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico",
    "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania",
    "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont",
    "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming", "District of Columbia",
    "Puerto Rico", "Guam", "American Samoa", "U.S. Virgin Islands", "Northern Mariana Islands"
]

# Recipient templates.
recipient_templates = [
    "To: {name}", "Recipient: {name}", "Deliver to {name}", "For: {name}",
    "ATTN: {name}", "{name}", "Name: {name}", "Contact: {name}", "Receiver: {name}"
]

# Phone number templates.
phone_templates = [
    "Tel: {phone}", "Tel. {phone}", "Mobile: {phone}", "Phone: {phone}",
    "Contact number: {phone}", "Phone number {phone}", "TEL: {phone}", "MOBILE: {phone}",
    "Contact: {phone}", "P: {phone}", "{phone}", "Call: {phone}",
]

# Generate a plausible US-style phone number.
def generate_us_phone():
    """Generates a random 10-digit US phone number in (XXX) XXX-XXXX format."""
    area_code = random.randint(201, 999)  # Avoid 0xx, 1xx area codes.
    exchange = random.randint(200, 999)
    line = random.randint(1000, 9999)
    return f"({area_code}) {exchange}-{line}"

# Use LLM to generate recipient and address information.
async def generate_recipient_and_address_by_llm(state: str):
    """Uses LLM to generate a recipient's name and address details for a given state."""
    prompt = f"""Please generate recipient information for a location in {state}, USA, including the following:
1. A realistic full English name. Aim for diversity.
2. A real city name within that state.
3. A specific street address, such as street number and name, and apartment number. It should be realistic.
4. A corresponding 5-digit ZIP code for that city or area.

Please return only the JSON object in the following format:
{{"name": "Recipient Name", "city": "City Name", "street_address": "Specific Street Address", "zip_code": "ZIP Code"}}

Do not include any other text, just the JSON. Ensure names are diverse, not just John Doe.
"""

    try:
        response = await client.chat.completions.create(
            messages=[{"role": "user", "content": prompt}],
            model="qwen-plus-latest",
            temperature=1.5,  # Increase temperature for more diverse names and addresses.
        )

        result = response.choices[0].message.content.strip()
        # Clean up potential markdown code block markers.
        if result.startswith('```'):
            result = result.split('\n', 1)[1]
        if result.endswith('```'):
            result = result.rsplit('\n', 1)[0]

        # Try to parse JSON.
        info = json.loads(result)
        print(info)
        return info
    except Exception as e:
        print(f"Failed to generate recipient and address: {e}, using fallback.")
        # Fallback mechanism.
        backup_names = ["Michael Johnson", "Emily Williams", "David Brown", "Jessica Jones", "Christopher Davis",
                        "Sarah Miller"]
        return {
            "name": random.choice(backup_names),
            "city": "Anytown",
            "street_address": f"{random.randint(100, 9999)} Main St",
            "zip_code": f"{random.randint(10000, 99999)}"
        }

# Generate a single raw data record.
async def generate_record():
    """Generates one messy, combined string of US address information."""
    # Randomly select a state.
    state = random.choice(us_states)

    # Use LLM to generate recipient and address info.
    info = await generate_recipient_and_address_by_llm(state)

    # Format recipient name.
    recipient = random.choice(recipient_templates).format(name=info['name'])

    # Generate a phone number.
    phone = generate_us_phone()
    phone_info = random.choice(phone_templates).format(phone=phone)

    # Assemble the full address line.
    full_address = f"{info['street_address']}, {info['city']}, {state} {info['zip_code']}"

    # Combine all components.
    components = [recipient, phone_info, full_address]

    # Randomize the order of components.
    random.shuffle(components)

    # Choose a random separator.
    separators = [' ', ', ', '; ', ' | ', '\t', ' - ', ' // ', '', '  ']
    separator = random.choice(separators)

    # Join the components.
    combined_data = separator.join(components)
    return combined_data.strip()

# Generate a batch of data.
async def generate_batch_data(count: int) -> List[str]:
    """Generates a specified number of data records."""
    print(f"Starting to generate {count} records...")

    # Use a semaphore to control concurrency, for example, up to 20 concurrent requests.
    semaphore = asyncio.Semaphore(20)

    async def generate_single_record(index):
        async with semaphore:
            try:
                record = await generate_record()
                print(f"Generated record #{index + 1}: {record}")
                return record
            except Exception as e:
                print(f"Failed to generate record #{index + 1}: {e}")
                return None

    # Concurrently generate data.
    tasks = [generate_single_record(i) for i in range(count)]

    data = await asyncio.gather(*tasks)

    successful_data = [record for record in data if record is not None]

    return successful_data

# Save data to a file.
def save_data(data: List[str], filename: str = "us_recipient_data.json"):
    """Saves the generated data to a JSON file."""
    with open(filename, 'w', encoding='utf-8') as f:
        json.dump(data, f, ensure_ascii=False, indent=2)
    print(f"Data has been saved to {filename}")

# Phase 1: Data Production.
async def produce_data_phase():
    """Handles the generation of raw recipient data."""
    print("=== Phase 1: Starting Raw Recipient Data Generation ===")

    # Generate 2.000 records.
    batch_size = 2000
    data = await generate_batch_data(batch_size)

    # Save the data.
    save_data(data, "us_recipient_data.json")

    print(f"\nTotal records generated: {len(data)}")
    print("\nSample Data:")
    for i, record in enumerate(data[:3]):  # Show first 3 as examples.
        print(f"{i + 1}. Raw Data: {record}\n")

    print("=== Phase 1 Complete ===\n")
    return True

# Define the system prompt for the extraction model.
def get_system_prompt_for_extraction():
    """Returns the system prompt for the information extraction task."""
    return """You are a professional information extraction assistant specializing in parsing US shipping addresses from unstructured text.

## Task Description
Based on the given input text, accurately extract and generate a JSON object containing the following six fields:
- name: The full name of the recipient.
- street_address: The complete street address, including number, street name, and any apartment or suite number.
- city: The city name.
- state: The full state name (e.g., "California", not "CA").
- zip_code: The 5 or 9-digit ZIP code.
- phone: The complete contact phone number.

## Extraction Rules
1.  **Address Handling**:
    -   Accurately identify the components: street, city, state, and ZIP code.
    -   The `state` field must be the full official name (e.g., "New York", not "NY").
    -   The `street_address` should contain all details before the city, such as "123 Apple Lane, Apt 4B".
2.  **Name Identification**:
    -   Extract the full recipient name.
3.  **Phone Number Handling**:
    -   Extract the complete phone number, preserving its original format.
4.  **ZIP Code**:
    -   Extract the 5-digit or 9-digit (ZIP+4) code.

## Output Format
Strictly adhere to the following JSON format. Do not add any explanatory text or markdown.
{
  "name": "Recipient's Full Name",
  "street_address": "Complete Street Address",
  "city": "City Name",
  "state": "Full State Name",
  "zip_code": "ZIP Code",
  "phone": "Contact Phone Number"
}
"""

# Use LLM to predict structured data from raw text.
async def predict_structured_data(raw_data: str):
    """Uses an LLM to predict structured data from a raw string."""
    system_prompt = get_system_prompt_for_extraction()

    try:
        response = await client.chat.completions.create(
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": raw_data}
            ],
            model="qwen3-235b-a22b",  # A powerful model is recommended for this task.
            temperature=0.0,  # Lower temperature for higher accuracy in extraction.
            response_format={"type": "json_object"},
            extra_body={"enable_thinking": False}
        )

        result = response.choices[0].message.content.strip()

        # Clean up potential markdown code block markers.
        if result.startswith('```'):
            lines = result.split('\n')
            for i, line in enumerate(lines):
                if line.strip().startswith('{'):
                    result = '\n'.join(lines[i:])
                    break
        if result.endswith('```'):
            result = result.rsplit('\n```', 1)[0]

        structured_data = json.loads(result)
        return structured_data

    except Exception as e:
        print(f"Failed to predict structured data: {e}, Raw data: {raw_data}")
        # Return an empty structure on failure.
        return {
            "name": "",
            "street_address": "",
            "city": "",
            "state": "",
            "zip_code": "",
            "phone": ""
        }

# Phase 2: Data Conversion.
async def convert_data_phase():
    """Reads raw data, predicts structured format, and saves as SFT data."""
    print("=== Phase 2: Starting Data Conversion to SFT Format ===")

    try:
        print("Reading us_recipient_data.json file...")
        with open('us_recipient_data.json', 'r', encoding='utf-8') as f:
            raw_data_list = json.load(f)

        print(f"Successfully read {len(raw_data_list)} records.")
        print("Starting to predict structured data using the extraction model...")

        # A simple and clear system message can improve training and inference speed.
        system_prompt = "You are an expert assistant for extracting structured JSON from US shipping information. The JSON keys are name, street_address, city, state, zip_code, and phone."
        output_file = 'us_recipient_sft_data.json'

        # Use a semaphore to control concurrency.
        semaphore = asyncio.Semaphore(10)

        async def process_single_item(index, raw_data):
            async with (semaphore):
                structured_data = await predict_structured_data(raw_data)
                print(f"Processing record #{index + 1}: {raw_data}")

                conversation = {
                        "instruction": system_prompt + '  ' + raw_data,
                        "output": json.dumps(structured_data, ensure_ascii=False)
                }

                return conversation

        print(f"Starting conversion to {output_file}...")

        tasks = [process_single_item(i, raw_data) for i, raw_data in enumerate(raw_data_list)]
        conversations = await asyncio.gather(*tasks)

        with open(output_file, 'w', encoding='utf-8') as outfile:
            json.dump(conversations, outfile, ensure_ascii=False, indent=4)

        print(f"Conversion complete! Processed {len(raw_data_list)} records.")
        print(f"Output file: {output_file}")
        print("=== Phase 2 Complete ===")

    except FileNotFoundError:
        print("Error: us_recipient_data.json not found.")
        sys.exit(1)
    except json.JSONDecodeError as e:
        print(f"JSON decoding error: {e}")
        sys.exit(1)
    except Exception as e:
        print(f"An error occurred during conversion: {e}")
        sys.exit(1)

# Main function.
async def main():
    print("Starting the data processing pipeline...")
    print("This program will execute two phases in sequence:")
    print("1. Generate raw US recipient data.")
    print("2. Predict structured data and convert it to SFT format.")
    print("-" * 50)

    # Phase 1: Generate data.
    success = await produce_data_phase()

    if success:
        # Phase 2: Convert data.
        await convert_data_phase()

        print("\n" + "=" * 50)
        print("All processes completed successfully!")
        print("Generated files:")
        print("- us_recipient_data.json: Raw, unstructured data list.")
        print("- us_recipient_sft_data.json: SFT-formatted training data.")
        print("=" * 50)
    else:
        print("Data generation phase failed. Terminating.")

if __name__ == '__main__':
    # Set event loop policy for Windows if needed.
    if platform.system() == 'Windows':
        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

    # Run the main coroutine.
    asyncio.run(main(), debug=False)

4,3 Affiner le modèle

  1. Dans le volet de navigation de gauche, cliquez sur Model Gallery. Recherchez la carte Qwen3-0.6B et cliquez sur Fine-tune.

  2. Configurez les paramètres de la tâche d'entraînement. Définissez uniquement les paramètres clés suivants et conservez les valeurs par défaut pour les autres.

    • Training Mode : la sélection par défaut est SFT (Supervised Fine-Tuning) avec la méthode LoRA.

      LoRA est une technique d'affinage efficace qui économise les ressources d'entraînement en modifiant uniquement un sous-ensemble des paramètres du modèle.
    • Training dataset : téléchargez d'abord l'exemple de jeu de données d'entraînement train.json. Ensuite, sur la page de configuration, sélectionnez OSS file or directory, cliquez sur l'icône image pour choisir un bucket, cliquez sur Upload File afin de charger le jeu de données téléchargé vers Object Storage Service (OSS), puis sélectionnez le fichier.

    • Validate dataset : téléchargez d'abord le jeu de données de validation eval.json. Cliquez ensuite sur Add validation dataset et suivez la même procédure que pour le jeu de données d'entraînement afin de charger et de sélectionner le fichier.

      Le jeu de données de validation évalue les performances du modèle sur des données non vues pendant l'entraînement.
    • Model output path : par défaut, le système enregistre le modèle affiné dans OSS. Si le répertoire OSS cible est vide, cliquez sur Create folder et sélectionnez le répertoire nouvellement créé.

    • Resource Group Type : sélectionnez Public Resource Group. Cette tâche d'affinage nécessite environ 5 Go de mémoire GPU. La console a déjà filtré les types d'instance répondant à cette exigence. Sélectionnez un type d'instance, par exemple ecs.gn7i-c16g1.4xlarge.

    • Hyperparameters :

      • learning_rate : définissez la valeur sur 0,0005

      • num_train_epochs : définissez la valeur sur 4

      • per_device_train_batch_size : définissez la valeur sur 8

      • seq_length : définissez la valeur sur 512

      Cliquez ensuite sur Train > OK. La tâche d'entraînement passe à l'état Creating. Lorsque le statut devient In operation, l'affinage du modèle commence.

  3. Surveillez la tâche d'entraînement jusqu'à son achèvement. Le processus d'affinage dure environ 10 minutes. Pendant ce temps, la page des détails de la tâche affiche les journaux et les courbes de métriques. Une fois la tâche d'entraînement terminée, le système enregistre le modèle affiné dans le répertoire OSS spécifié.

    Pour consulter ultérieurement les détails de la tâche d'entraînement, cliquez dans le volet de navigation de gauche sur Model Gallery > Job Management > Training Jobs , puis cliquez sur le nom de la tâche.

    (Facultatif) Ajuster les hyperparamètres à l'aide des courbes de perte

    Sur la page des détails de la tâche, vous pouvez consulter la courbe train_loss (perte sur l'ensemble d'entraînement) et la courbe eval_loss (perte sur l'ensemble de validation) :

    imageimage

    Vous pouvez utiliser la tendance des valeurs de perte pour évaluer l'efficacité de l'entraînement du modèle :

    • Sous-ajustement : les courbes train_loss et eval_loss continuent de diminuer à la fin de l'entraînement.

      Vous pouvez augmenter la valeur du paramètre num_train_epochs (nombre d'époques d'entraînement, corrélé positivement à la profondeur de l'entraînement) ou la valeur lora_rank (rang de la matrice de bas rang ; un rang plus élevé permet au modèle de gérer des tâches plus complexes, mais augmente le risque de surajustement). Entraînez ensuite à nouveau le modèle pour mieux l'adapter aux données d'entraînement.

    • Surajustement : la courbe train_loss continue de diminuer tandis que la courbe eval_loss commence à augmenter avant la fin de l'entraînement.

      Vous pouvez diminuer la valeur du paramètre num_train_epochs ou la valeur lora_rank, puis entraîner à nouveau le modèle pour éviter le surajustement.

    • Bon ajustement : les courbes train_loss et eval_loss se stabilisent avant la fin de l'entraînement.

      Lorsque le modèle atteint cet état, vous pouvez passer aux étapes suivantes.

4,4 Déployer le modèle affiné

Sur la page des détails de la tâche d'entraînement, cliquez sur Deploy pour ouvrir la page de configuration du déploiement. Pour Resource Type, sélectionnez Public Resources. Le déploiement du modèle 0,6B nécessite environ 5 Go de mémoire GPU. La liste sous Instance Type affiche automatiquement les spécifications compatibles. Sélectionnez-en une, par exemple ecs.gn7i-c8g1.2xlarge. Conservez les valeurs par défaut pour les autres paramètres, puis cliquez sur Deploy > OK.

Le déploiement prend environ 5 minutes et s'achève lorsque le statut passe à Running.

Pour consulter les détails de la tâche d'entraînement, cliquez dans le volet de navigation de gauche sur Model Gallery > Job Management > Training Jobs , puis cliquez sur le nom de la tâche.
Si le bouton Deploy est désactivé après la réussite de la tâche d'entraînement, cela signifie que le modèle de sortie est toujours en cours d'enregistrement. Attendez environ une minute pour que le bouton soit activé.

Les étapes pour appeler le modèle sont identiques à celles décrites dans 3,2 Appeler le modèle.

4,5 Évaluer le modèle affiné

Avant de déployer le modèle affiné en production, évaluez ses performances pour garantir sa stabilité et sa précision. Cette évaluation permet d'éviter les problèmes imprévus après le déploiement.

Préparer les données de test

Préparez un jeu de données de test qui ne chevauche pas vos données d'entraînement afin d'évaluer les performances du modèle. Le code de test de précision ci-dessous télécharge automatiquement un ensemble de test à cet effet.

L'utilisation d'un jeu de données de test distinct des données d'entraînement garantit une évaluation impartiale de la capacité de généralisation du modèle sur des données inédites. Cette pratique évite les scores artificiellement élevés résultant de l'évaluation du modèle sur des données qu'il a déjà rencontrées.

Concevoir les métriques d'évaluation

Les métriques d'évaluation doivent être étroitement alignées sur vos objectifs métier. Pour le cas d'usage de cette solution, outre la validation du JSON généré, vous devez également vérifier l'exactitude des paires clé-valeur.

Définissez les métriques d'évaluation par programmation. Pour l'implémentation dans cet exemple, reportez-vous à la méthode compare_address_info dans le code de test de précision ci-dessous.

Valider le modèle affiné

Exécutez le code de test suivant pour afficher la précision du modèle sur l'ensemble de test.

Tester la précision du modèle

Remarque : remplacez le jeton et l'endpoint par les détails d'appel que vous avez obtenus précédemment.

# pip3 install openai
from openai import AsyncOpenAI
import requests
import json
import asyncio
import os

# If the 'Token' environment variable is not set, replace the following line with your token from the Elastic Algorithm Service (EAS): token = 'YTA1NTEzMzY3ZTY4Z******************'
token = os.environ.get("Token")

# Do not remove the "/v1" suffix from the endpoint.
client = AsyncOpenAI(
    api_key=token,
    base_url=f'YOUR_ENDPOINT/v1',
)

if token is None:
    print("Please set the 'Token' environment variable, or assign your token directly to the 'token' variable.")
    exit()

system_prompt = """You are a professional information extraction assistant specializing in parsing US shipping addresses from unstructured text.

## Task Description
Based on the given input text, accurately extract and generate a JSON object containing the following six fields:
- name: The full name of the recipient.
- street_address: The complete street address, including number, street name, and any apartment or suite number.
- city: The city name.
- state: The full state name (e.g., "California", not "CA").
- zip_code: The 5 or 9-digit ZIP code.
- phone: The complete contact phone number.

## Extraction Rules
1.  **Address Handling**:
    -   Accurately identify the components: street, city, state, and ZIP code.
    -   The `state` field must be the full official name (e.g., "New York", not "NY").
    -   The `street_address` should contain all details before the city, such as "123 Apple Lane, Apt 4B".
2.  **Name Identification**:
    -   Extract the full recipient name.
3.  **Phone Number Handling**:
    -   Extract the complete phone number, preserving its original format.
4.  **ZIP Code**:
    -   Extract the 5-digit or 9-digit (ZIP+4) code.

## Output Format
Strictly adhere to the following JSON format. Do not add any explanatory text or markdown.
{
  "name": "Recipient's Full Name",
  "street_address": "Complete Street Address",
  "city": "City Name",
  "state": "Full State Name",
  "zip_code": "ZIP Code",
  "phone": "Contact Phone Number"
}
"""

def compare_address_info(actual_address_str, predicted_address_str):
    """Compares two JSON strings representing address information to see if they are identical."""
    try:
        # Parse the actual address information
        if actual_address_str:
            actual_address_json = json.loads(actual_address_str)
        else:
            actual_address_json = {}

        # Parse the predicted address information
        if predicted_address_str:
            predicted_address_json = json.loads(predicted_address_str)
        else:
            predicted_address_json = {}

        # Directly compare if the two JSON objects are identical
        is_same = actual_address_json == predicted_address_json

        return {
            "is_same": is_same,
            "actual_address_parsed": actual_address_json,
            "predicted_address_parsed": predicted_address_json,
            "comparison_error": None
        }

    except json.JSONDecodeError as e:
        return {
            "is_same": False,
            "actual_address_parsed": None,
            "predicted_address_parsed": None,
            "comparison_error": f"JSON parsing error: {str(e)}"
        }
    except Exception as e:
        return {
            "is_same": False,
            "actual_address_parsed": None,
            "predicted_address_parsed": None,
            "comparison_error": f"Comparison error: {str(e)}"
        }

async def predict_single_conversation(conversation_data):
    """Predicts the label for a single conversation."""
    try:
        # Extract user content (excluding assistant message)
        messages = conversation_data.get("messages", [])
        user_content = None

        for message in messages:
            if message.get("role") == "user":
                user_content = message.get("content", "")
                break

        if not user_content:
            return {"error": "User message not found"}

        response = client.chat.completions.create(
            model="Qwen3-0.6B",
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": user_content}
            ],
            response_format={"type": "json_object"},
            extra_body={
                "enable_thinking": False
            }
        )

        predicted_labels = response.choices[0].message.content.strip()
        return {"prediction": predicted_labels}

    except Exception as e:
        return {"error": f"Prediction failed: {str(e)}"}

async def process_batch(batch_data, batch_id):
    """Processes a batch of data."""
    print(f"Processing batch {batch_id}, containing {len(batch_data)} items...")

    tasks = []
    for i, conversation in enumerate(batch_data):
        task = predict_single_conversation(conversation)
        tasks.append(task)

    results = await asyncio.gather(*tasks, return_exceptions=True)

    batch_results = []
    for i, result in enumerate(results):
        if isinstance(result, Exception):
            batch_results.append({"error": f"Exception: {str(result)}"})
        else:
            batch_results.append(result)

    return batch_results

async def main():
    output_file = "predicted_labels.jsonl"
    batch_size = 20  # Number of items to process per batch

    # Read test data
    url = 'https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20251015/yghxco/test.jsonl'
    conversations = []

    try:
        response = requests.get(url)
        response.raise_for_status()  # Check if the request was successful
        for line_num, line in enumerate(response.text.splitlines(), 1):
            try:
                data = json.loads(line.strip())
                conversations.append(data)
            except json.JSONDecodeError as e:
                print(f"JSON parsing error on line {line_num}: {e}")
                continue
    except requests.exceptions.RequestException as e:
        print(f"Request error: {e}")
        return

    print(f"Successfully read {len(conversations)} conversations")

    # Process in batches
    all_results = []
    total_batches = (len(conversations) + batch_size - 1) // batch_size

    for batch_id in range(total_batches):
        start_idx = batch_id * batch_size
        end_idx = min((batch_id + 1) * batch_size, len(conversations))
        batch_data = conversations[start_idx:end_idx]

        batch_results = await process_batch(batch_data, batch_id + 1)
        all_results.extend(batch_results)

        print(f"Batch {batch_id + 1}/{total_batches} completed")

        # Add a small delay to avoid making requests too quickly
        if batch_id < total_batches - 1:
            await asyncio.sleep(1)

    # Save results
    same_count = 0
    different_count = 0
    error_count = 0

    with open(output_file, 'w', encoding='utf-8') as f:
        for i, (original_data, prediction_result) in enumerate(zip(conversations, all_results)):
            result_entry = {
                "index": i,
                "original_user_content": None,
                "actual_address": None,
                "predicted_address": None,
                "prediction_error": None,
                "address_comparison": None
            }

            # Extract original user content
            messages = original_data.get("messages", [])
            for message in messages:
                if message.get("role") == "user":
                    result_entry["original_user_content"] = message.get("content", "")
                    break

            # Extract actual address information (if assistant message exists)
            for message in messages:
                if message.get("role") == "assistant":
                    result_entry["actual_address"] = message.get("content", "")
                    break

            # Save prediction result
            if "error" in prediction_result:
                result_entry["prediction_error"] = prediction_result["error"]
                error_count += 1
            else:
                result_entry["predicted_address"] = prediction_result.get("prediction", "")

                # Compare address information
                comparison_result = compare_address_info(
                    result_entry["actual_address"],
                    result_entry["predicted_address"]
                )
                result_entry["address_comparison"] = comparison_result

                # Tally comparison results
                if comparison_result["comparison_error"]:
                    error_count += 1
                elif comparison_result["is_same"]:
                    same_count += 1
                else:
                    different_count += 1

            f.write(json.dumps(result_entry, ensure_ascii=False) + '\n')

    print(f"All predictions complete! Results have been saved to {output_file}")

    # Statistics
    success_count = sum(1 for result in all_results if "error" not in result)
    prediction_error_count = len(all_results) - success_count
    print(f"Number of samples: {success_count}")
    print(f"Correct responses: {same_count}")
    print(f"Incorrect responses: {different_count}")
    print(f"Accuracy: {same_count * 100 / success_count} %")

if __name__ == "__main__":
    asyncio.run(main())

Résultat :

All predictions complete! Results have been saved to predicted_labels.jsonl
Number of samples: 400
Correct responses: 382
Incorrect responses: 18
Accuracy: 95.5 %
En raison de l'utilisation d'un seed aléatoire lors du fine-tuning du modèle et de la nature stochastique des sorties du grand modèle de langage, la précision que vous obtenez peut différer des résultats présentés dans cette solution. Cette variance est normale.

La précision atteint 95,5 %, ce qui constitue une amélioration significative par rapport aux 50 % de précision du modèle Qwen3-0.6B d'origine. Cela démontre que le fine-tuning a considérablement amélioré les performances du modèle pour l'extraction d'informations structurées dans le domaine logistique.

Ce guide utilise uniquement 4 époques d'entraînement afin de réduire le temps de formation, ce qui a permis d'atteindre une précision de 95,5 %. Vous pouvez encore améliorer la précision en augmentant le nombre d'époques d'entraînement.

4.6 Remarque importante

Le service de modèle présenté dans cette rubrique utilise des ressources publiques et fonctionne selon le mode de facturation au paiement à l'utilisation. Lorsque vous n'avez plus besoin du service, arrêtez-le ou supprimez-le pour éviter toute facturation supplémentaire.

Documents connexes

  • Pour en savoir plus sur les fonctionnalités de Model Gallery, telles que l'évaluation et la compression, consultez la section Model Gallery.

  • Pour en savoir plus sur les fonctionnalités d'EAS, telles que Auto Scaling, les tests de charge, ainsi que la surveillance et les alertes, consultez la section Aperçu d'EAS.