Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Aplica-se apenas a:Portal Foundry (clássico). Este artigo não está disponível para o novo portal da Foundry.
Saiba mais sobre o novo portal.
Nota
Os links neste artigo podem abrir conteúdo na nova documentação do Microsoft Foundry em vez da documentação do Foundry (clássico) que está a ver agora.
Este tutorial guia-o na afinação de um gpt-4o-mini-2024-07-18 modelo.
Neste tutorial aprende como:
- Crie exemplos de conjuntos de dados de ajuste fino.
- Crie variáveis de ambiente para o endpoint do seu recurso e para a chave da API.
- Prepare os seus conjuntos de dados de treino e validação para afinação.
- Carrega o teu ficheiro de treino e o ficheiro de validação para ajustes finos.
- Crie um processo de ajuste fino para
gpt-4o-mini-2024-07-18. - Implemente um modelo personalizado e ajustado.
Pré-requisitos
- Uma subscrição Azure - Crie uma gratuitamente.
- Python 3.8 ou versão posterior
- As seguintes bibliotecas Python:
json,requests,os,tiktoken,time,openai,numpy. - Cadernos Jupyter
- Um recurso Azure OpenAI numa região onde está disponível
gpt-4o-mini-2024-07-18afinação. Se não tiver um recurso, o processo de criação de um está documentado no nosso guia de implementação de recursos. - O ajuste fino do acesso requer função de Utilizador de IA do Azure.
- Se ainda não tiver acesso para ver quotas e implementar modelos no portal Foundry Microsoft, então precisa de mais permissões.
Importante
Recomendamos que reveja a informação de preços para ajustes finos, de modo a familiarizar-se com os custos associados. A testagem deste tutorial resultou na facturação de 48.000 tokens (4.800 tokens de treino * 10 épocas de formação). Os custos de treino são adicionais aos custos associados ao ajuste fino da inferência e aos custos horários de manutenção de um modelo ajustado implementado. Depois de concluir o tutorial, deve remover a implementação do seu modelo afinado, caso contrário, continuará a incorrer em custos horários de alojamento.
Configuração
Bibliotecas Python
Este tutorial apresenta exemplos de algumas das funcionalidades mais recentes da OpenAI, incluindo seed, eventos e checkpoints. Para tirar partido destas funcionalidades, pode ser necessário correr pip install openai --upgrade para atualizar para a versão mais recente.
pip install openai requests tiktoken numpy
Recuperar chave e endpoint
Para fazer uma chamada com êxito para o Azure OpenAI, precisa de um endpoint e de uma chave.
| Nome da variável | Valor |
|---|---|
ENDPOINT |
O endpoint do serviço pode ser encontrado na secção https://docs-test-001.openai.azure.com/. |
API-KEY |
Este valor pode ser encontrado na secção Keys & Endpoint ao examinar o seu recurso no portal Azure. Pode usar tanto KEY1 como KEY2. |
Aceda ao seu recurso no portal Azure. A secção Chaves e Endpoints pode ser encontrada na secção de Gestão de Recursos . Copie o seu endpoint e a chave de acesso, pois vai precisar de ambos para autenticar as suas chamadas API. Pode usar tanto KEY1 como KEY2. Ter sempre duas chaves permite alternar e regenerar chaves de forma segura sem causar interrupção no serviço.
Variáveis ambientais
Cria e atribui variáveis persistentes de ambiente para a tua chave e endpoint.
Importante
Use as chaves API com cautela. Não incluas a chave API diretamente no teu código e nunca a publiques publicamente. Se usares uma chave API, guarda-a de forma segura no Azure Key Vault. Para mais informações sobre o uso seguro das chaves da API nas suas aplicações, consulte Chaves da API com Azure Key Vault.
Para mais informações sobre a segurança dos serviços de IA, consulte Autenticar pedidos para Serviços de IA do Azure.
setx AZURE_OPENAI_API_KEY "REPLACE_WITH_YOUR_KEY_VALUE_HERE"
Crie um conjunto de dados de exemplo
O ajuste gpt-4o-mini-2024-07-18 fino requer um ficheiro de treino JSONL especialmente formatado. A OpenAI fornece o seguinte exemplo na sua documentação:
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the capital/major city of France?"}, {"role": "assistant", "content": "Paris, as if everyone doesn't know that already."}]}
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who wrote 'Romeo and Juliet'?"}, {"role": "assistant", "content": "Oh, just some guy named William Shakespeare. Ever heard of him?"}]}
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "How far is the Moon from Earth?"}, {"role": "assistant", "content": "Around 384,400 kilometers. Give or take a few, like that really matters."}]}
Neste exemplo, modificamos ligeiramente isto mudando para:
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the capital/major city of France?"}, {"role": "assistant", "content": "Paris, as if everyone doesn't know that already."}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who wrote 'Romeo and Juliet'?"}, {"role": "assistant", "content": "Oh, just some guy named William Shakespeare. Ever heard of him?"}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "How far is the Moon from Earth?"}, {"role": "assistant", "content": "Around 384,400 kilometers. Give or take a few, like that really matters."}]}
Embora estes três exemplos sejam úteis para fornecer o formato geral, se quiser que um modelo personalizado ajustado responda de forma semelhante, precisará de mais exemplos. Geralmente, queres pelo menos 50 exemplos de alta qualidade para começar. No entanto, é perfeitamente possível ter um caso de uso que possa exigir milhares de exemplos de treino de alta qualidade para ter sucesso.
Em geral, duplicar o tamanho do conjunto de dados pode levar a um aumento linear da qualidade do modelo. Mas tenha em mente que exemplos de baixa qualidade podem afetar negativamente o desempenho. Se treinar o modelo com uma grande quantidade de dados internos sem primeiro podar o conjunto de dados apenas para os exemplos de maior qualidade, pode acabar com um modelo que tem um desempenho pior do que o esperado.
Vai precisar de criar dois ficheiros training_set.jsonl e validation_set.jsonl.
Crie os ficheiros no mesmo diretório onde está a executar o Jupyter Notebook e copie o conteúdo dos seguintes blocos de código para os ficheiros correspondentes:
training_set.jsonl
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who discovered Antarctica?"}, {"role": "assistant", "content": "Some chaps named Fabian Gottlieb von Bellingshausen and Mikhail Lazarev, as if they don't teach that in every school!"}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What is the biggest ocean?"}, {"role": "assistant", "content": "The Pacific Ocean. It's not like it's a small pond or anything."}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What is the largest planet?"}, {"role": "assistant", "content": "It's called Jupiter, you might have heard of it...or not."}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who painted the Mona Lisa?"}, {"role": "assistant", "content": "Oh, some obscure artist named Leonardo da Vinci. Not that he is famous or anything."}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the boiling point of water?"}, {"role": "assistant", "content": "Exciting fact of the day: it's 100 degrees Celsius. Life-changing, isn't it?"}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who wrote 'The Odyssey'?"}, {"role": "assistant", "content": "Oh, just some poet named Homer. Nothing special, really."}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What is the largest mammal?"}, {"role": "assistant", "content": "Blue Whale. Quite a big fish isn't it? Oh wait, it's not a fish!"}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the highest mountain peak?"}, {"role": "assistant", "content": "Everest. Brace yourself, it's not a walk in the park."}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who invented the telephone?"}, {"role": "assistant", "content": "A guy named Alexander Graham Bell. Bet you didn't guess that one!"}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What is the distance from Earth to the Sun?"}, {"role": "assistant", "content": "About 93 million miles. Just a quick drive, really."}]}
validation_set.jsonl
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the capital of Australia?"}, {"role": "assistant", "content": "It's Canberra, not Sydney. Shocking, I know!"}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who wrote 'The Great Gatsby'?"}, {"role": "assistant", "content": "Some guy named F. Scott Fitzgerald. Ever heard of him?"}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the deepest part of the ocean?"}, {"role": "assistant", "content": "Mariana Trench, makes your swimming pool seem a bit shallow, doesn't it?"}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who composed the 'Moonlight Sonata'?"}, {"role": "assistant", "content": "Just a little-known guy named Beethoven. No big deal."}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What are the colors of the rainbow?"}, {"role": "assistant", "content": "ROYGBIV. Red, orange, yellow, green, blue, indigo, violet. Not that we learned this in preschool or anything!"}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What is the smallest planet?"}, {"role": "assistant", "content": "Meet Mercury, the runt of the solar system family."}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who wrote 'Pride and Prejudice'?"}, {"role": "assistant", "content": "Do the words Jane Austen ring a bell? No? Well, she wrote it."}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the largest desert?"}, {"role": "assistant", "content": "Antarctica. Surprise, surprise! Deserts aren't just full of sand, you know."}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What is the longest river?"}, {"role": "assistant", "content": "The Nile River. It's not like it's famous or anything."}]}
{"messages": [{"role": "system", "content": "Clippy is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the capital of Germany?"}, {"role": "assistant", "content": "Berlin. Shocking news, right?"}]}
Agora tem de fazer algumas verificações preliminares nos nossos ficheiros de treino e validação.
# Run preliminary checks
import json
# Load the training set
with open('training_set.jsonl', 'r', encoding='utf-8') as f:
training_dataset = [json.loads(line) for line in f]
# Training dataset stats
print("Number of examples in training set:", len(training_dataset))
print("First example in training set:")
for message in training_dataset[0]["messages"]:
print(message)
# Load the validation set
with open('validation_set.jsonl', 'r', encoding='utf-8') as f:
validation_dataset = [json.loads(line) for line in f]
# Validation dataset stats
print("\nNumber of examples in validation set:", len(validation_dataset))
print("First example in validation set:")
for message in validation_dataset[0]["messages"]:
print(message)
Saída:
Number of examples in training set: 10
First example in training set:
{'role': 'system', 'content': 'Clippy is a factual chatbot that is also sarcastic.'}
{'role': 'user', 'content': 'Who discovered America?'}
{'role': 'assistant', 'content': "Some chap named Christopher Columbus, as if they don't teach that in every school!"}
Number of examples in validation set: 10
First example in validation set:
{'role': 'system', 'content': 'Clippy is a factual chatbot that is also sarcastic.'}
{'role': 'user', 'content': "What's the capital of Australia?"}
{'role': 'assistant', 'content': "It's Canberra, not Sydney. Shocking, I know!"}
Neste caso, temos apenas 10 exemplos de treino e 10 de validação, por isso, embora isto demonstre a mecânica básica do ajuste fino de um modelo, é improvável que haja um número suficientemente grande de exemplos para produzir um efeito consistentemente notório.
Agora podes usar a biblioteca tiktoken para validar as contagens de tokens. A contagem de tokens usando este método não vai fornecer as contagens exatas de tokens usadas para afinações, mas deve fornecer uma boa estimativa.
Nota
Os exemplos individuais precisam de permanecer dentro do comprimento de contexto do exemplo de treino atual do modelo de: 64,536 tokens. O limite de tokens de entrada do modelo mantém-se em 128.000 tokens.
# Validate token counts
import json
import tiktoken
import numpy as np
from collections import defaultdict
encoding = tiktoken.get_encoding("o200k_base") # default encoding for gpt-4o models. This requires the latest version of tiktoken to be installed.
def num_tokens_from_messages(messages, tokens_per_message=3, tokens_per_name=1):
num_tokens = 0
for message in messages:
num_tokens += tokens_per_message
for key, value in message.items():
num_tokens += len(encoding.encode(value))
if key == "name":
num_tokens += tokens_per_name
num_tokens += 3
return num_tokens
def num_assistant_tokens_from_messages(messages):
num_tokens = 0
for message in messages:
if message["role"] == "assistant":
num_tokens += len(encoding.encode(message["content"]))
return num_tokens
def print_distribution(values, name):
print(f"\n#### Distribution of {name}:")
print(f"min / max: {min(values)}, {max(values)}")
print(f"mean / median: {np.mean(values)}, {np.median(values)}")
print(f"p5 / p95: {np.quantile(values, 0.1)}, {np.quantile(values, 0.9)}")
files = ['training_set.jsonl', 'validation_set.jsonl']
for file in files:
print(f"Processing file: {file}")
with open(file, 'r', encoding='utf-8') as f:
dataset = [json.loads(line) for line in f]
total_tokens = []
assistant_tokens = []
for ex in dataset:
messages = ex.get("messages", {})
total_tokens.append(num_tokens_from_messages(messages))
assistant_tokens.append(num_assistant_tokens_from_messages(messages))
print_distribution(total_tokens, "total tokens")
print_distribution(assistant_tokens, "assistant tokens")
print('*' * 50)
Saída:
Processing file: training_set.jsonl
#### Distribution of total tokens:
min / max: 46, 59
mean / median: 49.8, 48.5
p5 / p95: 46.0, 53.599999999999994
#### Distribution of assistant tokens:
min / max: 13, 28
mean / median: 16.5, 14.0
p5 / p95: 13.0, 19.9
**************************************************
Processing file: validation_set.jsonl
#### Distribution of total tokens:
min / max: 41, 64
mean / median: 48.9, 47.0
p5 / p95: 43.7, 54.099999999999994
#### Distribution of assistant tokens:
min / max: 8, 29
mean / median: 15.0, 12.5
p5 / p95: 10.7, 19.999999999999996
****************************
Carregar ficheiros de ajuste fino
# Upload fine-tuning files
import os
from openai import OpenAI
client = OpenAI(
api_key = os.getenv("AZURE_OPENAI_API_KEY"),
base_url="https://YOUR-RESOURCE-NAME.openai.azure.com/openai/v1/"
)
training_file_name = 'training_set.jsonl'
validation_file_name = 'validation_set.jsonl'
# Upload the training and validation dataset files to Azure OpenAI with the SDK.
training_response = client.files.create(
file = open(training_file_name, "rb"), purpose="fine-tune"
)
training_file_id = training_response.id
validation_response = client.files.create(
file = open(validation_file_name, "rb"), purpose="fine-tune"
)
validation_file_id = validation_response.id
print("Training file ID:", training_file_id)
print("Validation file ID:", validation_file_id)
Saída:
Training file ID: file-0e3aa3f2e81e49a5b8b96166ea214626
Validation file ID: file-8556c3bb41b7416bb7519b47fcd1dd6b
Iniciar o ajuste fino
Agora que os ficheiros de ajuste fino foram carregados com sucesso, pode submeter a sua tarefa de treino de ajuste fino:
Neste exemplo, também estamos a passar o parâmetro seed. A semente controla a reprodutibilidade do trabalho. Utilizar os mesmos valores para os parâmetros seed e job deve produzir os mesmos resultados, mas pode diferir em casos raros. Se uma semente não for especificada, será gerada uma para você.
# Submit fine-tuning training job
response = client.fine_tuning.jobs.create(
training_file = training_file_id,
validation_file = validation_file_id,
model = "gpt-4o-mini-2024-07-18", # Enter base model name. Note that in Azure OpenAI the model name contains dashes and cannot contain dot/period characters.
seed = 105 # seed parameter controls reproducibility of the fine-tuning job. If no seed is specified one will be generated automatically.
)
job_id = response.id
# You can use the job ID to monitor the status of the fine-tuning job.
# The fine-tuning job will take some time to start and complete.
print("Job ID:", response.id)
print("Status:", response.status)
print(response.model_dump_json(indent=2))
Saída:
Job ID: ftjob-900fcfc7ea1d4360a9f0cb1697b4eaa6
Status: pending
{
"id": "ftjob-900fcfc7ea1d4360a9f0cb1697b4eaa6",
"created_at": 1715824115,
"error": null,
"fine_tuned_model": null,
"finished_at": null,
"hyperparameters": {
"n_epochs": -1,
"batch_size": -1,
"learning_rate_multiplier": 1
},
"model": "gpt-4o-mini-2024-07-18",
"object": "fine_tuning.job",
"organization_id": null,
"result_files": null,
"seed": 105,
"status": "pending",
"trained_tokens": null,
"training_file": "file-0e3aa3f2e81e49a5b8b96166ea214626",
"validation_file": "file-8556c3bb41b7416bb7519b47fcd1dd6b",
"estimated_finish": null,
"integrations": null
}
Acompanhar o estado da tarefa de treino
Se quiser consultar o estado do trabalho de formação até que esteja concluído, pode executar:
# Track training status
from IPython.display import clear_output
import time
start_time = time.time()
# Get the status of our fine-tuning job.
response = client.fine_tuning.jobs.retrieve(job_id)
status = response.status
# If the job isn't done yet, poll it every 10 seconds.
while status not in ["succeeded", "failed"]:
time.sleep(10)
response = client.fine_tuning.jobs.retrieve(job_id)
print(response.model_dump_json(indent=2))
print("Elapsed time: {} minutes {} seconds".format(int((time.time() - start_time) // 60), int((time.time() - start_time) % 60)))
status = response.status
print(f'Status: {status}')
clear_output(wait=True)
print(f'Fine-tuning job {job_id} finished with status: {status}')
# List all fine-tuning jobs for this resource.
print('Checking other fine-tune jobs for this resource.')
response = client.fine_tuning.jobs.list()
print(f'Found {len(response.data)} fine-tune jobs.')
Saída:
Job ID: ftjob-900fcfc7ea1d4360a9f0cb1697b4eaa6
Status: pending
{
"id": "ftjob-900fcfc7ea1d4360a9f0cb1697b4eaa6",
"created_at": 1715824115,
"error": null,
"fine_tuned_model": null,
"finished_at": null,
"hyperparameters": {
"n_epochs": -1,
"batch_size": -1,
"learning_rate_multiplier": 1
},
"model": "gpt-4o-mini-2024-07-18",
"object": "fine_tuning.job",
"organization_id": null,
"result_files": null,
"seed": 105,
"status": "pending",
"trained_tokens": null,
"training_file": "file-0e3aa3f2e81e49a5b8b96166ea214626",
"validation_file": "file-8556c3bb41b7416bb7519b47fcd1dd6b",
"estimated_finish": null,
"integrations": null
}
Não é incomum que o treino demore mais de uma hora a ser concluído. Uma vez concluído o treino, a mensagem de saída muda para algo como:
Fine-tuning job ftjob-900fcfc7ea1d4360a9f0cb1697b4eaa6 finished with status: succeeded
Checking other fine-tune jobs for this resource.
Found 4 fine-tune jobs.
Lista de eventos de ajuste fino
Versão API: 2024-08-01-preview ou posterior é necessária para este comando.
Embora não seja necessário para completar o ajuste fino, pode ser útil examinar os eventos individuais de afinação gerados durante o treino. Os resultados completos da formação também podem ser consultados após a conclusão da formação no ficheiro de resultados.
response = client.fine_tuning.jobs.list_events(fine_tuning_job_id=job_id, limit=10)
print(response.model_dump_json(indent=2))
Saída:
{
"data": [
{
"id": "ftevent-179d02d6178f4a0486516ff8cbcdbfb6",
"created_at": 1715826339,
"level": "info",
"message": "Training hours billed: 0.500",
"object": "fine_tuning.job.event",
"type": "message"
},
{
"id": "ftevent-467bc5e766224e97b5561055dc4c39c0",
"created_at": 1715826339,
"level": "info",
"message": "Completed results file: file-175c81c590074388bdb49e8e0d91bac3",
"object": "fine_tuning.job.event",
"type": "message"
},
{
"id": "ftevent-a30c44da4c304180b327c3be3a7a7e51",
"created_at": 1715826337,
"level": "info",
"message": "Postprocessing started.",
"object": "fine_tuning.job.event",
"type": "message"
},
{
"id": "ftevent-ea10a008f1a045e9914de98b6b47514b",
"created_at": 1715826303,
"level": "info",
"message": "Job succeeded.",
"object": "fine_tuning.job.event",
"type": "message"
},
{
"id": "ftevent-008dc754dc9e61b008dc754dc9e61b00",
"created_at": 1715825614,
"level": "info",
"message": "Step 100: training loss=0.001647822093218565",
"object": "fine_tuning.job.event",
"type": "metrics",
"data": {
"step": 100,
"train_loss": 0.001647822093218565,
"train_mean_token_accuracy": 1,
"valid_loss": 1.5170825719833374,
"valid_mean_token_accuracy": 0.75,
"full_valid_loss": 1.7539110545870624,
"full_valid_mean_token_accuracy": 0.7215189873417721
}
},
{
"id": "ftevent-008dc754dc3f03a008dc754dc3f03a00",
"created_at": 1715825604,
"level": "info",
"message": "Step 90: training loss=0.00971441250294447",
"object": "fine_tuning.job.event",
"type": "metrics",
"data": {
"step": 90,
"train_loss": 0.00971441250294447,
"train_mean_token_accuracy": 1,
"valid_loss": 1.3702410459518433,
"valid_mean_token_accuracy": 0.75,
"full_valid_loss": 1.7371194453179082,
"full_valid_mean_token_accuracy": 0.7278481012658228
}
},
{
"id": "ftevent-008dc754dbdfa59008dc754dbdfa5900",
"created_at": 1715825594,
"level": "info",
"message": "Step 80: training loss=0.0032251903321594",
"object": "fine_tuning.job.event",
"type": "metrics",
"data": {
"step": 80,
"train_loss": 0.0032251903321594,
"train_mean_token_accuracy": 1,
"valid_loss": 1.4242165088653564,
"valid_mean_token_accuracy": 0.75,
"full_valid_loss": 1.6554046099698996,
"full_valid_mean_token_accuracy": 0.7278481012658228
}
},
{
"id": "ftevent-008dc754db80478008dc754db8047800",
"created_at": 1715825584,
"level": "info",
"message": "Step 70: training loss=0.07380199432373047",
"object": "fine_tuning.job.event",
"type": "metrics",
"data": {
"step": 70,
"train_loss": 0.07380199432373047,
"train_mean_token_accuracy": 1,
"valid_loss": 1.2011798620224,
"valid_mean_token_accuracy": 0.75,
"full_valid_loss": 1.508960385865803,
"full_valid_mean_token_accuracy": 0.740506329113924
}
},
{
"id": "ftevent-008dc754db20e97008dc754db20e9700",
"created_at": 1715825574,
"level": "info",
"message": "Step 60: training loss=0.245253324508667",
"object": "fine_tuning.job.event",
"type": "metrics",
"data": {
"step": 60,
"train_loss": 0.245253324508667,
"train_mean_token_accuracy": 0.875,
"valid_loss": 1.0585949420928955,
"valid_mean_token_accuracy": 0.75,
"full_valid_loss": 1.3787144045286541,
"full_valid_mean_token_accuracy": 0.7341772151898734
}
},
{
"id": "ftevent-008dc754dac18b6008dc754dac18b600",
"created_at": 1715825564,
"level": "info",
"message": "Step 50: training loss=0.1696014404296875",
"object": "fine_tuning.job.event",
"type": "metrics",
"data": {
"step": 50,
"train_loss": 0.1696014404296875,
"train_mean_token_accuracy": 0.8999999761581421,
"valid_loss": 0.8862184286117554,
"valid_mean_token_accuracy": 0.8125,
"full_valid_loss": 1.2814022257358213,
"full_valid_mean_token_accuracy": 0.7151898734177216
}
}
],
"has_more": true,
"object": "list"
}
Listar pontos de controlo
Versão API: 2024-08-01-preview ou posterior é necessária para este comando.
Quando cada época de treino termina, é gerado um ponto de controlo. Um checkpoint é uma versão totalmente funcional de um modelo que pode ser tanto implementada como usada como modelo-alvo para trabalhos subsequentes de ajuste fino. Os checkpoints podem ser úteis, pois podem fornecer um instantâneo do seu modelo antes de ocorrer o sobreajuste. Quando um trabalho de afinação é concluído, tens as três versões mais recentes do modelo disponíveis para implementar. A época final será representada pelo teu modelo afinado, as duas épocas anteriores estão disponíveis como pontos de controlo.
response = client.fine_tuning.jobs.checkpoints.list(job_id)
print(response.model_dump_json(indent=2))
Saída:
{
"data": [
{
"id": "ftchkpt-148ab69f0a404cf9ab55a73d51b152de",
"created_at": 1715743077,
"fine_tuned_model_checkpoint": "gpt-4o-mini-2024-07-18.ft-0e208cf33a6a466994aff31a08aba678",
"fine_tuning_job_id": "ftjob-372c72db22c34e6f9ccb62c26ee0fbd9",
"metrics": {
"full_valid_loss": 1.8258173013035255,
"full_valid_mean_token_accuracy": 0.7151898734177216,
"step": 100.0,
"train_loss": 0.004080486483871937,
"train_mean_token_accuracy": 1.0,
"valid_loss": 1.5915886163711548,
"valid_mean_token_accuracy": 0.75
},
"object": "fine_tuning.job.checkpoint",
"step_number": 100
},
{
"id": "ftchkpt-e559c011ecc04fc68eaa339d8227d02d",
"created_at": 1715743013,
"fine_tuned_model_checkpoint": "gpt-4o-mini-2024-07-18.ft-0e208cf33a6a466994aff31a08aba678:ckpt-step-90",
"fine_tuning_job_id": "ftjob-372c72db22c34e6f9ccb62c26ee0fbd9",
"metrics": {
"full_valid_loss": 1.7958603267428241,
"full_valid_mean_token_accuracy": 0.7215189873417721,
"step": 90.0,
"train_loss": 0.0011079151881858706,
"train_mean_token_accuracy": 1.0,
"valid_loss": 1.6084896326065063,
"valid_mean_token_accuracy": 0.75
},
"object": "fine_tuning.job.checkpoint",
"step_number": 90
},
{
"id": "ftchkpt-8ae8beef3dcd4dfbbe9212e79bb53265",
"created_at": 1715742984,
"fine_tuned_model_checkpoint": "gpt-4o-mini-2024-07-18.ft-0e208cf33a6a466994aff31a08aba678:ckpt-step-80",
"fine_tuning_job_id": "ftjob-372c72db22c34e6f9ccb62c26ee0fbd9",
"metrics": {
"full_valid_loss": 1.6909511662736725,
"full_valid_mean_token_accuracy": 0.7088607594936709,
"step": 80.0,
"train_loss": 0.000667572021484375,
"train_mean_token_accuracy": 1.0,
"valid_loss": 1.4677599668502808,
"valid_mean_token_accuracy": 0.75
},
"object": "fine_tuning.job.checkpoint",
"step_number": 80
}
],
"has_more": false,
"object": "list"
}
Resultados finais das corridas de treino
Para obter os resultados finais, execute o seguinte:
# Retrieve fine_tuned_model name
response = client.fine_tuning.jobs.retrieve(job_id)
print(response.model_dump_json(indent=2))
fine_tuned_model = response.fine_tuned_model
Implementar modelo finamente ajustado
Ao contrário dos comandos anteriores do Python SDK neste tutorial, desde a introdução da funcionalidade de quota, a implementação do modelo deve ser feita usando a API REST, que requer autorização separada, um caminho de API diferente e uma versão diferente da API.
Alternativamente, pode implementar o seu modelo ajustado usando qualquer um dos outros métodos comuns de implementação, como Foundry portal, ou CLI do Azure.
| variável | Definição |
|---|---|
| token | Existem várias formas de gerar um token de autorização. O método mais fácil para testes iniciais é lançar o Cloud Shell a partir do portal Azure. Depois executa az account get-access-token. Pode usar este token como token de autorização temporária para testes de API. Recomendamos armazenar isto numa nova variável de ambiente |
| subscrição | O ID de subscrição para o recurso Azure OpenAI associado |
| grupo_de_recursos | O nome do grupo de recursos para o teu recurso Azure OpenAI |
| resource_name | O nome do recurso Azure OpenAI |
| model_deployment_name | O nome personalizado para a sua nova implementação de modelo ajustado. Este é o nome referenciado no teu código ao fazer chamadas de conclusão de chat. |
| modelo afinado | Recupere este valor dos resultados do seu trabalho de afinação na etapa anterior. Parece que gpt-4o-mini-2024-07-18.ft-0e208cf33a6a466994aff31a08aba678. Tens de acrescentar esse valor ao deploy_data json. |
Importante
Depois de implementar um modelo personalizado, se em algum momento a implementação permanecer inativa por mais de 15 dias, a implementação é eliminada. A implementação de um modelo personalizado está inativa se o modelo foi implementado há mais de 15 dias e não foram feitas chamadas de chat ou respostas à API durante um período contínuo de 15 dias.
A eliminação de uma implementação inativa não apaga nem afeta o modelo personalizado subjacente. O modelo personalizado pode ser redistribuído a qualquer momento.
Como descrito em Preços do Azure OpenAI nos Modelos Microsoft Foundry, cada modelo personalizado (ajustado) implementado implica um custo horário de hospedagem, independentemente de serem feitas chamadas de chat ou chamadas à API de resposta ao modelo. Para saber mais sobre planeamento e gestão de custos com Azure OpenAI, consulte Planear e gerir custos para Azure OpenAI.
# Deploy fine-tuned model
import json
import requests
token = os.getenv("TEMP_AUTH_TOKEN")
subscription = "<YOUR_SUBSCRIPTION_ID>"
resource_group = "<YOUR_RESOURCE_GROUP_NAME>"
resource_name = "<YOUR_AZURE_OPENAI_RESOURCE_NAME>"
model_deployment_name = "gpt-4o-mini-2024-07-18-ft" # Custom deployment name you chose for your fine-tuning model
deploy_params = {'api-version': "2024-10-01"} # Control plane API version
deploy_headers = {'Authorization': 'Bearer {}'.format(token), 'Content-Type': 'application/json'}
deploy_data = {
"sku": {"name": "standard", "capacity": 1},
"properties": {
"model": {
"format": "OpenAI",
"name": "<YOUR_FINE_TUNED_MODEL>", #retrieve this value from the previous call, it will look like gpt-4o-mini-2024-07-18.ft-0e208cf33a6a466994aff31a08aba678
"version": "1"
}
}
}
deploy_data = json.dumps(deploy_data)
request_url = f'https://management.azure.com/subscriptions/{subscription}/resourceGroups/{resource_group}/providers/Microsoft.CognitiveServices/accounts/{resource_name}/deployments/{model_deployment_name}'
print('Creating a new deployment...')
r = requests.put(request_url, params=deploy_params, headers=deploy_headers, data=deploy_data)
print(r)
print(r.reason)
print(r.json())
Podes verificar o progresso da tua implementação no portal da Foundry.
Não é incomum que este processo demore algum tempo a ser concluído quando se trata de implementar modelos afinados.
Use um modelo personalizado implementado
Depois de o seu modelo afinado ser implementado, pode usá-lo como qualquer outro modelo implementado, seja no portal Chat Playground do Foundry, ou através da API de conclusão de chat. Por exemplo, pode enviar uma chamada de conclusão de chat para o seu modelo implementado, como mostrado no seguinte exemplo em Python. Pode continuar a usar os mesmos parâmetros com o seu modelo personalizado, como temperatura e max_tokens, tal como pode com outros modelos implementados.
# Use the deployed customized model
import os
from openai import OpenAI
client = OpenAI(
api_key = os.getenv("AZURE_OPENAI_API_KEY"),
base_url="https://YOUR-RESOURCE-NAME.openai.azure.com/openai/v1/"
)
response = client.chat.completions.create(
model = "gpt-4o-mini-2024-07-18-ft", # model = "Custom deployment name you chose for your fine-tuning model"
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Does Azure OpenAI support customer managed keys?"},
{"role": "assistant", "content": "Yes, customer managed keys are supported by Azure OpenAI."},
{"role": "user", "content": "Do other Azure services support this too?"}
]
)
print(response.choices[0].message.content)
Eliminar implementação
Ao contrário de outros tipos de modelos OpenAI Azure, os modelos ajustados/personalizados têm um custo horário de alojamento associado a eles uma vez implementados. É fortemente recomendado que, depois de terminares este tutorial e teres testado algumas chamadas de conclusão de chat no teu modelo afinado, que removas a implementação do modelo.
Eliminar a implementação não afetará o modelo em si, por isso podes voltar a implementar o modelo afinado que treinaste para este tutorial a qualquer momento.
Pode eliminar a implementação no portal Foundry, via REST API, CLI do Azure, ou outros métodos de implementação suportados.
Resolução de problemas
Como posso ativar o ajuste fino? Criar um modelo personalizado está desativado.
Para aceder com sucesso ao ajuste fino, precisa de Azure AI User função atribuída. Mesmo alguém com permissões de Administrador de Serviço de alto nível ainda precisaria desta conta explicitamente definida para aceder a ajustes finos. Para mais informações, por favor consulte as orientações de controlo de acesso baseadas em funções.
Próximos passos
- Saiba mais sobre fine-tuning no Azure OpenAI
- Saiba mais sobre os modelos subjacentes que alimentam Azure OpenAI.