Todos os produtos
Search
Central de documentação

Platform For AI:Model Gallery quick start

Última atualização: Sep 22, 2026

O Model Gallery encapsula o PAI-DLC e o PAI-EAS para oferecer suporte à implantação e ao treinamento sem código de modelos de linguagem de grande porte de código aberto. Este tópico demonstra como implantar, ajustar (fine-tune) e avaliar o modelo Qwen3-0.6B.

1. Pré-requisitos

Para ativar o PAI e criar um workspace, faça login no PAI console com sua conta da Alibaba Cloud, escolha uma região no canto superior esquerdo e ative o serviço com a autorização de um clique.

2. Faturamento

Os exemplos deste tópico utilizam recursos públicos para criar tarefas no PAI-DLC e serviços no PAI-EAS, que são faturados conforme o uso (pay-as-you-go). Para mais detalhes, consulte PAI-DLC billing e PAI-EAS billing.

3. Implantação do modelo

3.1 Implantar o modelo

  1. Faça login no PAI console. No painel de navegação à esquerda, clique em Model Gallery, procure o card do Qwen3-0.6B e, em seguida, clique em Deploy.

  2. A página de configuração já vem preenchida com os parâmetros padrão. Clique em Deploy > Confirm. A implantação leva cerca de cinco minutos e estará concluída quando o status mudar para In operation.

    Por padrão, o modelo é implantado usando recursos públicos e é faturado no modelo de pagamento conforme o uso.

    A especificação de recurso padrão para a implantação é ecs.gn7i-c8g1.2xlarge (8 vCPU, 30 GiB, NVIDIA A10 × 1), com custo aproximado de CNY 10,5/hora. Após revisar a configuração, clique em Deploy na parte inferior do painel.

3.2 Invocar o modelo

  1. Visualize as informações de invocação. Na página de detalhes do serviço, clique em View Call Information para obter o Internet Endpoint e o Token.

    Para visualizar os detalhes da tarefa de implantação posteriormente, no painel de navegação à esquerda, clique em Model Gallery > Job Management > Deployment Jobs . Em seguida, clique no Service name de destino.

    Na caixa de diálogo invocation information exibida, visualize o Internet Endpoint e o VPC endpoint nas guias Shared Gateway e VPC High-Speed Direct Connection, respectivamente.

  2. Invoque o modelo utilizando um dos métodos a seguir.

    Depuração online

    Mude para a página Online Debugging. O serviço de modelo de linguagem de grande porte oferece suporte a Conversation Debugging e API Debugging.

    Cliente Cherry Studio

    O Cherry Studio é um cliente popular para interagir com modelos de linguagem de grande porte. Ele integra o recurso MCP, que permite conversar facilmente com os modelos.

    Conectar-se ao modelo Qwen3 implantado no PAI

    Python SDK

    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 Lembrete importante

Este serviço de modelo utiliza recursos públicos e é faturado no modelo de pagamento conforme o uso. Para evitar cobranças desnecessárias, interrompa ou exclua o serviço quando não precisar mais dele.

Realize essa ação na guia Job Management > Deployment Jobs, na coluna Actions do serviço de destino.

4. Ajuste fino do modelo

Para melhorar o desempenho de um modelo em um domínio específico, você pode ajustá-lo com um conjunto de dados especializado. Esta seção apresenta um cenário para demonstrar o objetivo e as etapas do ajuste fino (fine-tuning).

4.1 Caso de uso

No setor de logística, é comum a necessidade de extrair informações estruturadas (como destinatário, endereço e telefone) de linguagem natural. Modelos com muitos parâmetros, como o Qwen3-235B-A22B, apresentam um excelente desempenho nessa tarefa, mas são caros e possuem alta latência. Para equilibrar desempenho e custo, você pode primeiro usar um modelo de grandes parâmetros para rotular os dados e, em seguida, usar esses dados para ajustar um modelo menor, como o Qwen3-0.6B, a fim de obter um desempenho semelhante. Esse processo também é conhecido como destilação de modelo.

Nesta tarefa, o modelo Qwen3-0.6B original possui uma precisão de 50% . Após o ajuste fino, sua precisão pode ultrapassar 90%.

Informações de endereço do destinatário

Informações estruturadas

Amina Patel - Número de telefone (474) 598-1543 - 1425 S 5th St, Apt 3B, Allentown, Pensilvânia 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 Preparação dos dados

Esta tarefa envolve a realização da destilação do modelo professor (Qwen3-235B-A22B) para o modelo Qwen3-0.6B. Primeiro, é necessário usar a API do modelo professor para extrair as informações de endereço do destinatário em dados JSON estruturados. Como a geração desses dados pode ser demorada, este artigo fornece um exemplo de conjunto de dados de treinamento train.json e um conjunto de validação eval.json que você pode baixar e usar diretamente.

Na destilação de modelos, o modelo com mais parâmetros é chamado de modelo professor. Os dados utilizados neste artigo foram gerados sinteticamente por um modelo de grande porte e não contêm informações confidenciais de usuários.

Aquisição de dados para produção

Para aplicar esta solução ao seu negócio, recomendamos preparar os dados utilizando os seguintes métodos:

Cenários de negócios reais (Recomendado)

Dados reais de negócios refletem melhor o seu caso de uso, permitindo que o modelo ajustado se adapte com mais eficácia. Após obter seus dados de negócios, converta-os em um arquivo JSON seguindo o formato de treinamento SFT mostrado abaixo.

[
    {
        "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\"}"
    }
]

O arquivo JSON contém vários exemplos de treinamento. Cada exemplo deve incluir um campo instruction e um campo output:

  • instruction: contém o prompt do sistema, que orienta o comportamento do modelo, e os dados de entrada.

  • output: a resposta correta esperada. Geralmente é gerada por especialistas humanos ou por um modelo de grande porte, como o qwen3-235b-a22b.

Geração por modelos de grande porte

Se você não tiver dados de negócios suficientes, poderá usar um modelo de grande porte para aumento de dados, melhorando a diversidade e a cobertura. Para proteger a privacidade do usuário, esta solução utiliza um modelo grande para gerar um conjunto de dados de endereços sintéticos. O código a seguir é um exemplo.

Exemplo de código: Geração de conjunto de dados sintéticos

Este código de exemplo chama o serviço de modelo de grande porte no Alibaba Cloud Model Studio. Você precisará de uma Model Studio API key. O código utiliza o qwen-plus-latest para gerar dados de negócios e o qwen3-235b-a22b para criar rótulos estruturados para esses dados.

# -*- 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():
    """Generate 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 an LLM to generate recipient and address information.
async def generate_recipient_and_address_by_llm(state: str):
    """Use an 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:
1. A realistic full English name (can be common or less common, aim for diversity).
2. A real city name within that state.
3. A specific street address (e.g., street number + name, apartment number, etc., should be realistic).
4. A corresponding 5-digit ZIP code for that city/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():
    """Generate one messy, combined string of US address information."""
    # Randomly select a state.
    state = random.choice(us_states)

    # Use an 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]:
    """Generate a specified number of data records."""
    print(f"Starting to generate {count} records...")

    # Use a semaphore to control concurrency (e.g., 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"):
    """Save 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():
    """Handle 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():
    """Return 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 an LLM to predict structured data from raw text.
async def predict_structured_data(raw_data: str):
    """Use 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():
    """Read raw data, predict structured data, and save it in SFT format."""
    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 Ajustar o modelo

  1. No painel de navegação à esquerda, clique em Model Gallery. Procure o card do Qwen3-0.6B e clique em Fine-tune.

  2. Configure os parâmetros para a tarefa de treinamento. Defina apenas os seguintes parâmetros principais e mantenha os valores padrão para os demais.

    • Training Mode: por padrão, está selecionado SFT (Supervised Fine-Tuning) utilizando o método LoRA.

      O LoRA é uma técnica de ajuste fino eficiente que economiza recursos de treinamento ao modificar apenas um subconjunto dos parâmetros do modelo.
    • Training dataset: primeiro, baixe o conjunto de dados de treinamento de exemplo train.json. Em seguida, na página de configuração, selecione OSS file or directory, clique no ícone image para selecionar um bucket, clique em Upload File para carregar o conjunto de dados no Object Storage Service (OSS) e selecione o arquivo.

    • Validate dataset: primeiro, baixe o conjunto de validação eval.json. Em seguida, clique em Add validation dataset e siga o mesmo procedimento utilizado para o conjunto de treinamento para carregar e selecionar o arquivo.

      O conjunto de dados de validação avalia o desempenho do modelo em dados que não foram vistos durante o treinamento.
    • Model output path: por padrão, o sistema salva o modelo ajustado no OSS. Se o diretório OSS de destino estiver vazio, clique em Create folder e selecione o novo diretório criado.

    • Resource Group Type: selecione Public Resource Group. Esta tarefa de ajuste fino exige cerca de 5 GB de memória de GPU. O console já filtrou os tipos de instância que atendem a esse requisito. Selecione um tipo, como ecs.gn7i-c16g1.4xlarge.

    • Hyperparameter:

      • learning_rate: defina como 0.0005

      • num_train_epochs: defina como 4

      • per_device_train_batch_size: defina como 8

      • seq_length: defina como 512

      Em seguida, clique em Train > OK. A tarefa de treinamento entra no estado Creating. Quando o status mudar para In operation, o ajuste fino do modelo começa.

  3. Acompanhe a tarefa de treinamento até a sua conclusão. O processo de ajuste fino leva cerca de 10 minutos. Durante esse período, a página de detalhes da tarefa exibe logs e curvas métricas. Após a conclusão, o sistema salva o modelo ajustado no diretório OSS especificado.

    Para visualizar os detalhes da tarefa de treinamento posteriormente, no painel de navegação à esquerda, clique em Model Gallery > Job Management > Training Jobs , e clique no nome da tarefa.

    (Opcional) Ajustar hiperparâmetros usando curvas de perda

    Na página de detalhes da tarefa, você pode visualizar a curva train_loss (perda no conjunto de treinamento) e a curva eval_loss (perda no conjunto de validação):

    imageimage

    Utilize a tendência dos valores de perda para avaliar a eficácia do treinamento do modelo:

    • Subajuste (Underfitting): ambas as curvas train_loss e eval_loss ainda estão diminuindo quando o treinamento termina.

      Você pode aumentar o parâmetro num_train_epochs (número de épocas, correlacionado à profundidade do treinamento) ou o valor lora_rank (o rank da matriz; ranks maiores permitem lidar com tarefas complexas, mas aumentam o risco de sobreajuste). Em seguida, treine novamente o modelo.

    • Sobreajuste (Overfitting): a train_loss continua a diminuir enquanto a eval_loss começa a aumentar antes do término do treinamento.

      Você pode reduzir o parâmetro num_train_epochs ou o valor lora_rank e treinar o modelo novamente para evitar o sobreajuste.

    • Bom ajuste: ambas as curvas train_loss e eval_loss se estabilizam antes do fim do treinamento.

      Quando o modelo atinge este estado, você pode prosseguir para as próximas etapas.

4.4 Implantar o modelo ajustado

Na página de detalhes da tarefa de treinamento, clique em Deploy para abrir a página de configuração de implantação. Em Resource Type, selecione Public Resources. Implantar o modelo de 0.6B exige cerca de 5 GB de memória de GPU. A lista em Instance Type exibe automaticamente as especificações compatíveis. Selecione uma, como ecs.gn7i-c8g1.2xlarge. Mantenha os outros parâmetros com os valores padrão e clique em Deploy > OK.

A implantação leva cerca de 5 minutos e estará concluída quando o status mudar para Running.

Para visualizar os detalhes da tarefa de treinamento, no painel de navegação à esquerda, clique em Model Gallery > Job Management > Training Jobs , e clique no nome da tarefa.
Se o botão Deploy estiver desativado após o sucesso da tarefa de treinamento, significa que o modelo de saída ainda está sendo registrado. Aguarde cerca de um minuto para que o botão seja ativado.

As etapas para invocar o modelo são as mesmas descritas em 3.2 Invoke the model.

4.5 Avaliar o modelo ajustado

Antes de implantar o modelo ajustado em um ambiente de produção, avalie seu desempenho para garantir estabilidade e precisão. Essa avaliação ajuda a prevenir problemas inesperados após a implantação.

Preparar os dados de teste

Prepare um conjunto de dados de teste que não se sobreponha aos dados de treinamento para avaliar o desempenho. O código de teste de precisão abaixo baixa automaticamente um conjunto de testes para essa finalidade.

O uso de um conjunto de dados separado do treinamento garante uma avaliação imparcial da capacidade de generalização do modelo em dados desconhecidos. Essa prática evita pontuações infladas resultantes da avaliação em dados que o modelo já processou.

Projetar métricas de avaliação

As métricas de avaliação devem estar alinhadas aos seus objetivos de negócio. Para o caso de uso desta solução, além de validar o JSON gerado, você deve verificar se os pares chave-valor estão corretos.

Defina as métricas de avaliação programaticamente. Para a implementação neste exemplo, consulte o método compare_address_info no código de teste de precisão abaixo.

Validar o modelo ajustado

Execute o código de teste a seguir para obter a precisão do modelo no conjunto de teste.

Testar precisão do modelo

Nota: substitua o Token e o endpoint pelos detalhes de invocação obtidos anteriormente.

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

Saída:

All predictions complete! Results have been saved to predicted_labels.jsonl
Number of samples: 400
Correct responses: 382
Incorrect responses: 18
Accuracy: 95.5 %
Devido à semente aleatória utilizada no ajuste fino e à natureza estocástica da saída do modelo de linguagem, a precisão alcançada pode diferir dos resultados mostrados nesta solução. Essa variação é normal.

A precisão obtida foi de 95,5%, uma melhoria significativa sobre os 50% de precisão do modelo Qwen3-0.6B original. Isso demonstra que o ajuste fino aumentou consideravelmente o desempenho na extração de informações estruturadas no domínio logístico.

Este guia utilizou apenas 4 épocas para reduzir o tempo de treinamento, elevando a precisão para 95,5%. É possível melhorar ainda mais esse valor aumentando o número de épocas de treinamento.

4.6 Nota importante

O serviço de modelo neste tópico utiliza recursos públicos e é cobrado conforme o uso. Quando não precisar mais do serviço, interrompa-o ou exclua-o para evitar cobranças futuras.

Documentos relacionados

  • Para saber mais sobre os recursos do Model Gallery, como avaliação e compressão, consulte Model Gallery.

  • Para saber mais sobre os recursos do EAS, como Auto Scaling, teste de estresse e monitoramento e alertas, consulte o EAS overview.