mistral-finetune
é uma base de código leve que permite o ajuste fino com eficiência de memória e desempenho dos modelos do Mistral. É baseado no LoRA, um paradigma de treinamento onde a maioria dos pesos são congelados e apenas 1-2% dos pesos adicionais na forma de perturbações de matriz de baixo escalão são treinados.
Para máxima eficiência, é recomendado usar uma GPU A100 ou H100. A base de código é otimizada para configurações de treinamento de nó único com várias GPUs, mas para modelos menores, como o 7B, uma única GPU é suficiente.
Observação
O objetivo deste repositório é fornecer um ponto de entrada simples e guiado para ajustar os modelos Mistral. Como tal, é bastante opinativo (especialmente em relação à formatação de dados) e não pretende ser exaustivo em múltiplas arquiteturas de modelos ou tipos de hardware. Para abordagens mais genéricas, você pode conferir alguns outros projetos excelentes, como o torchtune.
13.08.2024 : Mistral Large v2 agora é compatível com mistral-finetune
!
Recomenda-se usar uma taxa de aprendizagem mais baixa em comparação com outros modelos, por exemplo , lr=1e-6 deve funcionar bem na maioria dos casos.
O ajuste fino do Mistral-Large v2 requer significativamente mais memória devido ao tamanho maior do modelo. Por enquanto defina seq_len
como <= 8192
Baixe o 123B Instruct aqui e defina model_id_or_path
para o diretório do ponto de verificação baixado.
19.07.2024 : Mistral Nemo agora é compatível com mistral-finetune
!
Recomenda-se usar os mesmos hiperparâmetros do 7B v3.
O ajuste fino do Mistral-Nemo requer atualmente muito mais memória devido a um tamanho de vocabulário maior, o que aumenta o pico de exigência de memória da perda de CE (em breve adicionaremos uma perda de CE aprimorada aqui). Por enquanto defina seq_len
como <= 16384
Execute pip install --upgrade mistral-common
para ter uma versão que suporte o Tekkenizer ( >=1.3.1
).
Baixe o 12B Base ou Instruct aqui e defina model_id_or_path
para o diretório do ponto de verificação baixado.
Para começar com o ajuste fino do Mistral LoRA, siga estas etapas:
Clone este repositório:
cd $HOME && git clone https://github.com/mistralai/mistral-finetune.git
Instale todas as dependências necessárias:
cd mistral-finetune pip install -r requirements.txt
Recomendamos o ajuste fino de um dos modelos oficiais do Mistral que você pode baixar aqui:
Modelo | Link | Soma de verificação |
---|---|---|
Base 7B V3 | Base 7B | 0663b293810d7571dad25dae2f2a5806 |
7B Instruir v3 | 7B Instruir v3 | 80b71fcb6416085bcb4efad86dfb4d52 |
Base 8x7B V1 | Base 8x7B | (ligação HF) |
Instrução 8x7B V1 | Instrução 8x7B | 8e2d3930145dc43d3084396f49d38a3f |
8x22 Instrução V3 | Instrução 8x22 | 471a02a6902706a2f1e44a693813855b |
Base 8x22B V3 | Base 8x22B | a2fa75117174f87d1197e3a4eb50371a |
Instrução 12B | Instrução 12B (Mistral-Nemo) | 296fbdf911cb88e6f0be74cd04827fe7 |
Base 12B | Base 12 (Mistral-Nemo) | c5d079ac4b55fc1ae35f51f0a3c0eb83 |
Mistral Grande 2 | Instrução 123B (Grande v2) | fc602155f9e39151fba81fcaab2fa7c4 |
Aviso importante : Para 8x7B Base V1 e 8x7B Instruct V1, é necessário usar nosso tokenizer v3 e estender o tamanho do vocabulário para 32768 antes do ajuste fino. Para obter instruções detalhadas sobre este processo, consulte a seção "Extensão do modelo".
Por exemplo, para baixar o modelo base 7B você pode executar o seguinte comando:
mkdir -p ~/${HOME}/mistral_modelscd ${HOME} && wget https://models.mistralcdn.com/mistral-7b-v0-3/mistral-7B-v0.3.tar tar -xf mistral-7B-v0.3.tar -C mistral_models
Certifique-se de modificar seu script de treinamento e adicionar o caminho à pasta baixada como model_id_or_path
.
Por exemplo, modifique example/7B.yaml para incluir o caminho absoluto para $HOME/mistral_models/7B
:
model_id_or_path: "/Users/johndoe/mistral_models/7B"
Para garantir um treinamento eficaz, mistral-finetune
possui requisitos rígidos sobre como os dados de treinamento devem ser formatados.
Todos os arquivos de dados devem ser armazenados em arquivos no formato jsonl.
Você pode construir dois tipos de arquivos de dados:
Os dados de pré-treinamento correspondem a dados de texto simples armazenados na chave "text"
. Por exemplo:
{"text": "Texto contido no documento n°1"} {"text": "Texto contido no documento n°2"}
Atualmente, são suportados dois tipos diferentes de instruções que seguem dados:
Instruir : dados conversacionais armazenados na chave "messages"
em forma de lista. Cada item da lista é um dicionário contendo as chaves "content"
e "role"
. "role"
é uma string sendo "usuário", "assistente" ou "sistema". A perda só será computada se “role” == “assistente”. Por exemplo:
{ "mensagens": [ { "role": "user", "content": "Interação do usuário nº 1 contida no documento nº 1" }, { "role": "assistente", "content": "Interação do bot nº 1 contida no documento nº 1" }, { "role": "user", "content": "Interação do usuário nº 2 contida no documento nº 1" }, { "role": "assistente", "content": "Interação com bot n°2 contida no documento n°1" } ] } { "mensagens": [ { "role": "user", "content": "Interação do usuário nº 1 contida no documento nº 2" }, { "role": "assistente", "content": "Interação do bot nº 1 contida no documento nº 2" }, { "role": "user", "content": "Interação do usuário nº 2 contida no documento nº 2" }, { "role": "assistente", "content": "Interação do bot n°2 contida no documento n°2", "peso": 0, # não treinar no n°2 }, { "role": "user", "content": "Interação do usuário nº3 contida no documento nº2" }, { "role": "assistente", "content": "Interação com bot n°3 contida no documento n°2" } ] }
Chamada de função : dados conversacionais armazenados na chave "messages"
em forma de lista. Cada item da lista é um dicionário contendo as chaves "role"
e "content"
ou "tool_calls"
. "role"
é uma string sendo "usuário", "assistente", "sistema" ou "ferramenta". A perda só será computada se “role” == “assistente”.
Nota : Na chamada de função, o "id"
de "tool_calls"
e o "tool_call_id"
são strings geradas aleatoriamente de exatamente 9 caracteres. Recomendamos gerar isso automaticamente em um script de preparação de dados como é feito aqui.
Por exemplo:
{ "mensagens": [ { "role": "system", "content": "Você é um assistente prestativo que tem acesso às seguintes funções para ajudar o usuário, você pode usar as funções se necessário" }, { "role": "user", "content": "Você pode me ajudar a gerar um anagrama da palavra "listen"?" }, { "role": "assistente", "tool_calls": [ { "id": "TX92Jm8Zi", "type": "função", "função": { "nome": "generate_anagram", "argumentos": "{"palavra": "ouvir"}" } } ] }, { "role": "ferramenta", "content": "{"anagrama": "silent"}", "tool_call_id": "TX92Jm8Zi" }, { "role": "assistente", "content": "O anagrama da palavra "ouvir" é "silencioso"." }, { "role": "user", "content": "Isso é incrível! Você pode gerar um anagrama para a palavra "raça"?" }, { "role": "assistente", "tool_calls": [ { "id": "3XhQnxLsT", "type": "function", "function": { "name": "generate_anagram", "arguments": "{"word": "race"}" } } ] } ], "ferramentas": [ { "type": "function", "function": { "name": "generate_anagram", "description": "Gerar um anagrama de uma determinada palavra", "parameters": { "type": "object", " propriedades": { "palavra": { "tipo": "string", "descrição": "A palavra para gerar um anagrama de" } }, "obrigatório": [ "palavra" ] } } } ] }
Antes de iniciar uma execução de treinamento, você deve verificar se seu conjunto de dados está formatado corretamente e obter uma estimativa do tempo de treinamento. Você pode fazer isso usando o script ./utils/validate_data.
Observe que esta etapa é crucial para garantir que os dados sejam formatados corretamente.
Vejamos um exemplo simples para treinar um modelo nas instruções a seguir:
Carregue um pedaço de Ultachat_200k
Crie a pasta de dados e navegue até a pasta.
cd $HOME && mkdir -p dados && cd $HOME/dados
Carregue os dados em um Dataframe do Pandas.
Nota : Certifique-se de ter o pandas e o pyarrow instalados ( pip install pandas pyarrow
).
importar pandas como pddf = pd.read_parquet('https://huggingface.co/datasets/HuggingFaceH4/ultrachat_200k/resolve/main/data/test_gen-00000-of-00001-3d4cd8309148a71f.parquet')
Dividir em treinar e avaliar
df_train=df.sample(frac=0,95,random_state=200)df_eval=df.drop(df_train.index)
Salvar dados em jsonl
df_train.to_json("ultrachat_chunk_train.jsonl", orient="records", linhas=True)df_eval.to_json("ultrachat_chunk_eval.jsonl", orient="records", linhas=True)
Modifique seu yaml de treinamento para incluir o conjunto de dados ultrachat e verifique o yaml
Modifique example/7B.yaml para incluir o caminho absoluto para $HOME/data/ultrachat_chunk_train.jsonl
bem como um conjunto de dados que mistura peso para treinamento e $HOME/data/ultrachat_chunk_eval.jsonl
para avaliação, por exemplo
data: instruct_data: "/Users/johndoe/data/ultrachat_chunk_train.jsonl" eval_instruct_data: "/Users/johndoe/data/ultrachat_chunk_eval.jsonl"
Agora você pode verificar seu yaml de treinamento para ter certeza de que os dados estão formatados corretamente e obter uma estimativa do seu tempo de treinamento.
cd $HOME/mistral-finetune python -m utils.validate_data --train_yaml example/7B.yaml
Após a conclusão, você deverá ver um relatório de erros com muitos dos seguintes erros:
The data in line 1412 of dataset /Users/johndoe/data/ultrachat_chunk_eval.jsonl is incorrectly formatted. Expected last role to be one of: [assistant] but got user The data in line 1413 of dataset /Users/johndoe/data/ultrachat_chunk_eval.jsonl is incorrectly formatted. Expected last role to be one of: [assistant] but got user The data in line 1414 of dataset /Users/johndoe/data/ultrachat_chunk_eval.jsonl is incorrectly formatted. Expected last role to be one of: [assistant] but got user The data in line 1415 of dataset /Users/johndoe/data/ultrachat_chunk_eval.jsonl is incorrectly formatted. Expected last role to be one of: [assistant] but got user
Muitas conversas parecem terminar com a função de 'usuário', o que é desnecessário, pois treinamos apenas em mensagens de 'assistentes' e, portanto, processaríamos dados desnecessariamente.
Você pode usar ./utils/reformat_data.py para corrigir os dados:
cd $HOME/mistral-finetune python -m utils.reformat_data $HOME/data/ultrachat_chunk_train.jsonl python -m utils.reformat_data $HOME/data/ultrachat_chunk_eval.jsonl
Você verá que algumas amostras serão ignoradas.
Potencialmente alterar o número de etapas de treinamento
Após a correção do conjunto de dados, execute o script novamente
cd $HOME/mistral-finetune python -m utils.validate_data --train_yaml example/7B.yaml
Você deve obter um resumo da entrada de dados e dos parâmetros de treinamento:
Train States -------------------- { "expected": { "eta": "00:52:44", "data_tokens": 25169147, "train_tokens": 131072000, "epochs": "5.21", "max_steps": 500, "data_tokens_per_dataset": { "/Users/johndoe/data/ultrachat_chunk_train.jsonl": "25169147.0" }, "train_tokens_per_dataset": { "/Users/johndoe/data/ultrachat_chunk_train.jsonl": "131072000.0" }, "epochs_per_dataset": { "/Users/johndoe/data/ultrachat_chunk_train.jsonl": "5.2" } }, }
Ter max_steps
definido como 500 levaria à iteração do conjunto de dados cerca de 5 vezes, o que é razoável, mas pode ser um pouco demais. Uma configuração recomendada é mostrada abaixo e levaria apenas 30 minutos em um cluster 8xH100.
A seguir, vamos examinar um caso de uso mais avançado para ajustar um modelo na chamada de função. A chamada de função requer que os dados estejam no formato explicado acima. Vejamos um exemplo.
Carregue uma versão formatada para bate-papo do conjunto de dados de chamada de função Glaive
Crie a pasta de dados e navegue até a pasta.
cd $HOME && mkdir -p dados && cd $HOME/dados
Carregue os dados em um Dataframe do Pandas.
Nota : Certifique-se de ter o pandas e o pyarrow instalados ( pip install pandas pyarrow
).
importar pandas como pddf = pd.read_parquet('https://huggingface.co/datasets/Locutusque/function-calling-chatml/resolve/main/data/train-00000-of-00001-f0b56c6983b4a78f.parquet')
Dividir em treinar e avaliar
df_train=df.sample(frac=0,95,random_state=200)df_eval=df.drop(df_train.index)
Salvar dados em jsonl
df_train.to_json("glaive_train.jsonl", orient="records", linhas=True)df_eval.to_json("glaive_eval.jsonl", orient="records", linhas=True)
Reformatar conjunto de dados
Como pode-se ver, o conjunto de dados não segue o formato de chamada de função exigido, portanto precisará ser reformatado. Entre outras coisas, "from"
deve ser renomeado para "user"
e caracteres "n"
supérfluos devem ser removidos. Para este conjunto de dados você pode usar ./utils/reformat_data_glaive.py
:
cd $HOME/mistral-finetune python -m utils.reformat_data_glaive $HOME/data/glaive_train.jsonl python -m utils.reformat_data_glaive $HOME/data/glaive_eval.jsonl
A execução deste comando garantirá que a maioria das amostras esteja no formato correto.
Nota : É impossível escrever scripts de reformatação que funcionem para todos os tipos de conjuntos de dados. Se você tiver conjuntos de dados que ainda não seguem o formato exigido acima, provavelmente você mesmo terá que criar um script de reformatação (mistral-chat ou chat-gpt é seu melhor amigo aqui!).
Validar conjunto de dados
Agora você pode validar o conjunto de dados definindo data.instruct_data
e data.eval_instruct_data
como $HOME/data/glaive_train.jsonl
e $HOME/data/glaive_eval.jsonl
em example/7B.yaml
respectivamente.
Os conjuntos de dados reformatados ainda apresentam alguns erros que podem ser removidos com --create_corrected
. Para isso, certifique-se de adicionar --create_corrected
da seguinte forma:
cd $HOME/mistral-finetune python -m utils.validate_data --train_yaml example/7B.yaml --create_corrected
A execução deste comando mostrará alguns erros e salvará dois novos conjuntos de dados $HOME/data/glaive_train.jsonl.corrected
e $HOME/data/glaive_eval.jsonl.corrected
. Certifique-se de usar esses dois conjuntos de dados em example/7B.yaml
e execute o comando novamente. Agora o conjunto de dados deve estar formatado corretamente!
Depois de seguir a seção de verificação do conjunto de dados, podemos agora iniciar o treinamento. Para um treinamento mais rápido, recomendamos definir max_steps para apenas 300. Certifique-se de definir run_dir
para sua pasta de experimento e, opcionalmente, definir wandb_project
para um projeto de Pesos e Vieses para registro, por exemplo :
max_steps: 300 run_dir: "/Users/johndoe/ultra_chat_test" wandb.project: ultra_chat
Opcionalmente, você também pode definir wandb
Salve a configuração do treinamento e comece a treinar! Certifique-se de definir --nproc-per-node
como o número de GPUs disponíveis.
cd $HOME/mistral-finetune torchrun --nproc-per-node 8 --master_port $RANDOM -m train example/7B.yaml
O treinamento no ultra-chat deve levar cerca de 30 minutos em um nó 8xH100 e os pesos resultantes devem dar uma pontuação no MT Bench em torno de 6,3.
O treinamento no glaive deve levar cerca de 1h em um nó 8xH100 e os pesos resultantes devem funcionar bem para chamadas de funções.
O exemplo mistral-finetune/examples/7B
define parâmetros razoáveis para taxa de aprendizagem, redução de peso, etc... mas é aconselhável personalizar essas configurações para seu caso de uso.
Geralmente, uma configuração de treinamento deve preencher os seguintes parâmetros:
model_id_or_path
define o modelo a partir do qual iniciar o treinamento. Pode ser um caminho para um modelo pré-treinado ou um diretório de modelo local.
run_dir
define o diretório onde os pontos de verificação e métricas de treinamento são armazenados.
seq_len
define o comprimento da sequência para treinamento. Este é o comprimento máximo das sequências de entrada que o modelo processará. As amostras são compactadas para atingir um comprimento de seq_len
para máxima eficiência de treinamento.
batch_size
define o número de exemplos de treinamento usados por GPU. Nota : O batch_size efetivo geral (em tokens) em todas as GPUs é igual num_gpus
x batch_size
x seq_len
.
max_steps
define o número máximo de etapas de treinamento. Este é o número total de iterações que o processo de treinamento executará. Pode ser ajustado com base nas necessidades específicas do seu cenário de treinamento. O número total de tokens vistos durante o treinamento é max_steps
x num_gpus
x batch_size
x seq_len
.
optim.lr
define a taxa de aprendizagem. Esta é a taxa de aprendizado inicial do otimizador.
optim.weight_decay
define redução de peso. A redução de peso é uma técnica de regularização usada para evitar overfitting, penalizando pesos grandes. Recomendamos deixá-lo em 0,1.
optim.pct_start
define a porcentagem do total de etapas de treinamento usadas para a fase de aquecimento da taxa de aprendizagem antes que ela comece a diminuir. Corresponde a pct_start do OneCycleLR do PyTorch.
lora.rank
define o tamanho dos adaptadores LoRA (Low-Rank Adaptation). Recomendamos 64 ou menos, o que ajusta a classificação da decomposição de classificação baixa usada no LoRA.
seed
define a semente aleatória para inicialização e embaralhamento/amostragem de dados. Definir uma semente garante a reprodutibilidade dos resultados.
log_freq
define a frequência de registro. Isso especifica com que frequência (em etapas) registrar métricas de treinamento.
data.instruct_data
é o caminho para os dados de instrução usados para treinamento. Este campo deve ser preenchido com uma ou múltiplas fontes de dados no formato explicado acima. Cada fonte de dados deve ser um caminho para um arquivo jsonl ou um caminho para um diretório contendo arquivos jsonl seguido por uma ponderação para definir a importância deste conjunto de dados:
. Por exemplo: data.instruct_data: "/path/to/data1.jsonl:5.,/path/to/data2.jsonl:1.,/path/to/dir_of_jsonls:1."
data.data
é um caminho opcional para dados adicionais de pré-treinamento no formato explicado acima. Observe que este campo pode ser deixado em branco.
data.eval_instruct_data
é um caminho opcional para dados de instruções de avaliação para executar validação cruzada em cada etapa eval_freq
. As métricas de validação cruzada são exibidas como loss
e perplexity
.
eval_freq
define com que frequência (em etapas) avaliar o modelo. Isto especifica o intervalo em que o modelo é avaliado no conjunto de validação.
no_eval
é um sinalizador para ativar ou desativar a avaliação intermediária. Configurá-lo como False permite a avaliação periódica durante o treinamento.
ckpt_freq
define com que frequência (em etapas) salvar os pontos de verificação. Isto especifica o intervalo no qual o estado do modelo é salvo.
save_adapters
define se apenas os pontos de verificação LoRA treinados devem ser salvos ou se o LoRA treinado deve ser mesclado diretamente no modelo base e salvo. Nota : Ao definir save_adapters=False
, certifique-se de ter memória CPU e GPU suficiente para salvar o modelo completo em um único processo (isso geralmente só é possível para o modelo 7B).
wandb.key
é usado para passar sua chave de API Weights & Biases (wandb) para registro. Isso permite registrar métricas de treinamento no painel do wandb.
wandb.project
define o nome do projeto wandb. É aqui que a execução do treinamento será registrada na interface do wandb.
Depois que seu modelo estiver treinado, você deverá testá-lo na inferência. Recomendamos o uso de inferência mistral.
Certifique-se de ter mistral_inference
instalado corretamente:
pip install mistral_inference
Supondo que seu lora.safetensors
esteja salvo em $HOME/ultra_chat_test/checkpoints/checkpoint_000300/consolidated/lora.safetensors
, você pode conversar com o modelo usando mistral_inference
, por exemplo :
mistral-chat /mnt/slow/runs/patrick/mistral-finetune/7B/ --max_tokens 256 --temperatura 1.0 --instruct --lora_path $HOME/ultra_chat_test/checkpoints/checkpoint_000300/consolidated/lora.safetensors
Adicionamos suporte explícito para pesos e preconceitos para ajudá-lo a monitorar e visualizar suas corridas de treinamento. Essa integração permite registrar várias métricas e rastrear experimentos facilmente.
Para usar Pesos e Vieses com mistral-finetune
, siga estas etapas:
Instale pesos e preconceitos:
Certifique-se de ter a biblioteca wandb
instalada. Você pode instalá-lo usando pip:
pip instalar wandb
Assim que o treinamento começar, você poderá monitorar o progresso em tempo real visitando o painel do projeto wandb. Todas as métricas, incluindo perda de treinamento, perda de avaliação, taxa de aprendizagem, etc., serão registradas e visualizadas.
Para obter mais detalhes sobre como usar o wandb, visite a documentação de Pesos e Vieses.
Importante : Observe que só é possível ajustar modelos mistral que sejam compatíveis com o tokenizer v3, o que implica que os modelos tenham um tamanho de vocabulário de 32.768 - não 32.000. No entanto, é possível estender facilmente a versão mais antiga do tamanho de vocabulário 32.000 para ter um tamanho de vocabulário de 32768 usando:
python -m utils.extend_model_vocab --original_model_ckpt /folder/to/old/model --extended_model_ckpt /folder/to/extended/model
Depois que a extensão funcionar, será possível fazer o ajuste fino usando o ponto de verificação do modelo recém-criado em /folder/to/extended/model
.
Qual é a melhor prática para ajustar os MoEs?
Vemos um maior grau de variação de desempenho no ajuste fino dos modelos MoE. Não é incomum descobrir que o ajuste fino de modelos de MoE com sementes diferentes pode levar a uma grande variação no desempenho. Não observamos uma variância tão alta com modelos densos. Portanto, sugerimos executar múltiplas instâncias do mesmo processo de ajuste fino em modelos de MoEs e selecionar aquele que apresenta melhor desempenho.
Como posso determinar o número de tokens usados durante o processo de treinamento do modelo?
Você pode usar o seguinte script para descobrir: https://github.com/mistralai/mistral-finetune/blob/main/utils/validate_data.py. Este script aceita um arquivo de treinamento .yaml como entrada e retorna o número de tokens nos quais o modelo está sendo treinado.
O que devo fazer se encontrar um erro de falta de memória CUDA?
Uma solução possível é reduzir o tamanho do lote por GPU. O tamanho do lote é igual a seq_len
x batch_size
. Tente definir batch_size
como 1 e reduza seq_len
. Você pode definir batch_size
e seq_len
no arquivo .yaml.