文本微调
微调允许您根据特定需求,通过在您自己的数据集上训练预训练语言模型来对其进行定制。本指南解释了如何微调文本模型,从准备数据到训练,无论您是想提高特定领域的理解能力,还是适应独特的对话风格。
有关详细的端到端微调示例和常见问题,请查阅我们的微调指南。
您可以直接在平台中进行微调,或通过我们的 API 进行。
数据集格式
数据必须存储在 JSON Lines (.jsonl
) 文件中,这种格式允许存储多个 JSON 对象,每个对象占一行。
数据集应遵循指令遵循格式,表示用户与助手之间的对话。每个 JSON 数据样本应仅包含用户和助手消息(“默认指令”),或包含函数调用逻辑(“函数调用指令”)。
1. 默认指令
用户与助手之间的对话数据,可以是单轮或多轮的。示例
{
"messages": [
{
"role": "user",
"content": "User interaction n°1 contained in document n°2"
},
{
"role": "assistant",
"content": "Bot interaction n°1 contained in document n°2"
},
{
"role": "user",
"content": "User interaction n°2 contained in document n°1"
},
{
"role": "assistant",
"content": "Bot interaction n°2 contained in document n°1"
}
]
}
请注意,文件必须是 JSONL 格式,这意味着每个 JSON 对象必须扁平化为一行,并且每个 JSON 对象位于新的一行。
原始 .jsonl
文件示例。
{"messages": [{"role": "user","content": "..."},{"role": "assistant","content": "..."},...]}
{"messages": [{"role": "user","content": "..."},{"role": "assistant","content": "..."},...]}
{"messages": [{"role": "user","content": "..."},{"role": "assistant","content": "..."},...]}
{"messages": [{"role": "user","content": "..."},{"role": "assistant","content": "..."},...]}
...
- 对话数据必须存储在
"messages"
键下,作为列表。 - 每个列表项是一个字典,包含
"content"
和"role"
键。"role"
是一个字符串:"user"
、"assistant"
或"system"
。 - 损失计算仅在与助手消息对应的标记(
"role" == "assistant"
)上执行。
2. 函数调用指令
包含工具使用的对话数据。示例
{
"messages": [
{
"role": "system",
"content": "You are a helpful assistant with access to the following functions to help the user. You can use the functions if needed."
},
{
"role": "user",
"content": "Can you help me generate an anagram of the word 'listen'?"
},
{
"role": "assistant",
"tool_calls": [
{
"id": "TX92Jm8Zi",
"type": "function",
"function": {
"name": "generate_anagram",
"arguments": "{\"word\": \"listen\"}"
}
}
]
},
{
"role": "tool",
"content": "{\"anagram\": \"silent\"}",
"tool_call_id": "TX92Jm8Zi"
},
{
"role": "assistant",
"content": "The anagram of the word 'listen' is 'silent'."
},
{
"role": "user",
"content": "That's amazing! Can you generate an anagram for the word 'race'?"
},
{
"role": "assistant",
"tool_calls": [
{
"id": "3XhQnxLsT",
"type": "function",
"function": {
"name": "generate_anagram",
"arguments": "{\"word\": \"race\"}"
}
}
]
}
],
"tools": [
{
"type": "function",
"function": {
"name": "generate_anagram",
"description": "Generate an anagram of a given word",
"parameters": {
"type": "object",
"properties": {
"word": {
"type": "string",
"description": "The word to generate an anagram of"
}
},
"required": ["word"]
}
}
}
]
}
- 对话数据必须存储在
"messages"
键下,作为列表。 - 每条消息是一个字典,包含
"role"
和"content"
或"tool_calls"
键。"role"
应是"user"
、"assistant"
、"system"
或"tool"
之一。 - 只有类型为
"assistant"
的消息可以拥有"tool_calls"
键,表示助手正在调用可用的工具。 - 带有
"tool_calls"
键的助手消息不能包含"content"
键,并且其后必须紧跟一条"tool"
消息,而"tool"
消息之后又必须紧跟另一条助手消息。 - 工具消息的
"tool_call_id"
必须匹配前面至少一条助手消息的"id"
。 "id"
和"tool_call_id"
都是精确由 9 个字符组成的随机生成字符串。我们建议在数据准备脚本中自动生成这些 ID,例如在此处所示。"tools"
键必须包含对话中使用的所有工具的定义。- 损失计算仅在与助手消息对应的标记(
"role" == "assistant"
)上执行。
上传文件
一旦您拥有格式正确的数据文件,就可以将其上传到 Mistral Client,使其可用于微调作业。
- python
- typescript
- curl
from mistralai import Mistral
import os
api_key = os.environ["MISTRAL_API_KEY"]
client = Mistral(api_key=api_key)
training_data = client.files.upload(
file={
"file_name": "training_file.jsonl",
"content": open("training_file.jsonl", "rb"),
}
)
validation_data = client.files.upload(
file={
"file_name": "validation_file.jsonl",
"content": open("validation_file.jsonl", "rb"),
}
)
import { Mistral } from '@mistralai/mistralai';
import fs from 'fs';
const apiKey = process.env.MISTRAL_API_KEY;
const client = new Mistral({apiKey: apiKey});
const training_file = fs.readFileSync('training_file.jsonl');
const training_data = await client.files.upload({
file: {
fileName: "training_file.jsonl",
content: training_file,
}
});
const validation_file = fs.readFileSync('validation_file.jsonl');
const validation_data = await client.files.upload({
file: {
fileName: "validation_file.jsonl",
content: validation_file,
}
});
curl https://api.mistral.ai/v1/files \
-H "Authorization: Bearer $MISTRAL_API_KEY" \
-F purpose="fine-tune" \
-F file="@training_file.jsonl"
curl https://api.mistral.ai/v1/files \
-H "Authorization: Bearer $MISTRAL_API_KEY" \
-F purpose="fine-tune" \
-F file="@validation_file.jsonl"
创建微调作业
下一步是创建一个微调作业。
- model:您希望微调的具体模型。可选项包括:
open-mistral-7b
(v0.3)、mistral-small-latest
(mistral-small-2409
)、codestral-latest
(codestral-2405
)、open-mistral-nemo
、mistral-large-latest
(mistral-large-2411
),以及ministral-8b-latest
(ministral-3b-2410
)。 - training_files:训练文件 ID 的集合,可以是一个或多个文件
- validation_files:验证文件 ID 的集合,可以是一个或多个文件
- hyperparameters:用户可以修改的两个可调超参数:“training_steps”和“learning_rate”。
- auto_start
auto_start=True
:您的作业将在验证后立即启动。auto_start=False
(默认):您可以在验证后通过向/fine_tuning/jobs/<uuid>/start
发送 POST 请求来手动启动训练。
- integrations:我们支持的外部集成,例如用于在训练期间跟踪指标的 Weights and Biases。
- python
- typescript
- curl
# create a fine-tuning job
created_jobs = client.fine_tuning.jobs.create(
model="open-mistral-7b",
training_files=[{"file_id": training_data.id, "weight": 1}],
validation_files=[validation_data.id],
hyperparameters={
"training_steps": 10,
"learning_rate":0.0001
},
auto_start=False,
# integrations=[
# {
# "project": "finetuning",
# "api_key": "WANDB_KEY",
# }
# ]
)
创建微调作业后,您可以使用 client.fine_tuning.jobs.get(job_id = created_jobs.id)
查看作业状态。
const createdJob = await client.fineTuning.jobs.create({
model: 'open-mistral-7b',
trainingFiles: [{fileId: training_data.id, weight: 1}],
validationFiles: [validation_data.id],
hyperparameters: {
trainingSteps: 10,
learningRate: 0.0001,
},
autoStart:false,
// integrations=[
// {
// project: "finetuning",
// apiKey: "WANDB_KEY",
// }
// ]
});
创建微调作业后,您可以使用 client.fineTuning.jobs.get({ jobId: createdJob.id })
查看作业状态。
curl https://api.mistral.ai/v1/fine_tuning/jobs \
--header "Authorization: Bearer $MISTRAL_API_KEY" \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
"model": "open-mistral-7b",
"training_files": [
"<uuid>"
],
"validation_files": [
"<uuid>"
],
"hyperparameters": {
"training_steps": 10,
"learning_rate": 0.0001
},
"auto_start": false
}'
创建微调作业后,您可以使用
curl https://api.mistral.ai/v1/fine_tuning/jobs/<jobid> \
--header "Authorization: Bearer $MISTRAL_API_KEY" \
--header 'Content-Type: application/json'
最初,作业状态将是 "QUEUED"
(排队中)。短暂等待后,状态将更新为 "VALIDATED"
(已验证)。此时,您可以继续开始微调作业
- python
- typescript
- curl
# start a fine-tuning job
client.fine_tuning.jobs.start(job_id = created_jobs.id)
created_jobs
await client.fineTuning.jobs.start({jobId: createdJob.id})
curl -X POST https://api.mistral.ai/v1/fine_tuning/jobs/<jobid>/start \
--header "Authorization: Bearer $MISTRAL_API_KEY" \
--header 'Content-Type: application/json'
列出/检索/取消作业
您还可以列出作业、检索作业或取消作业。
您可以使用各种参数过滤和查看作业列表,例如 page
, page_size
, model
, created_after
, created_by_me
, status
, wandb_project
, wandb_name
, 和 suffix
。查阅我们的API 规范了解详情。
- python
- typescript
- curl
# List jobs
jobs = client.fine_tuning.jobs.list()
print(jobs)
# Retrieve a jobs
retrieved_jobs = client.fine_tuning.jobs.get(job_id = created_jobs.id)
print(retrieved_jobs)
# Cancel a jobs
canceled_jobs = client.fine_tuning.jobs.cancel(job_id = created_jobs.id)
print(canceled_jobs)
// List jobs
const jobs = await client.fineTuning.jobs.list();
// Retrieve a job
const retrievedJob = await client.fineTuning.jobs.get({ jobId: createdJob.id })
// Cancel a job
const canceledJob = await client.fineTuning.jobs.cancel({
jobId: createdJob.id,
});
# List jobs
curl https://api.mistral.ai/v1/fine_tuning/jobs \
--header "Authorization: Bearer $MISTRAL_API_KEY" \
--header 'Content-Type: application/json'
# Retrieve a job
curl https://api.mistral.ai/v1/fine_tuning/jobs/<jobid> \
--header "Authorization: Bearer $MISTRAL_API_KEY" \
--header 'Content-Type: application/json'
# Cancel a job
curl -X POST https://api.mistral.ai/v1/fine_tuning/jobs/<jobid>/cancel \
--header "Authorization: Bearer $MISTRAL_API_KEY" \
--header 'Content-Type: application/json'
使用微调模型
微调作业完成后,您可以通过 retrieved_jobs.fine_tuned_model
查看微调模型的名称。然后,您可以使用我们的 chat
端点与微调模型进行聊天
- python
- typescript
- curl
chat_response = client.chat.complete(
model=retrieved_job.fine_tuned_model,
messages = [{"role":'user', "content":'What is the best French cheese?'}]
)
const chatResponse = await client.chat.complete({
model: retrievedJob.fine_tuned_model,
messages: [{role: 'user', content: 'What is the best French cheese?'}],
});
curl "https://api.mistral.ai/v1/chat/completions" \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header "Authorization: Bearer $MISTRAL_API_KEY" \
--data '{
"model": "ft:open-mistral-7b:daf5e488:20240430:c1bed559",
"messages": [{"role": "user", "content": "Who is the most renowned French painter?"}]
}'
删除微调模型
- python
- typescript
- curl
client.models.delete(model_id=retrieved_job.fine_tuned_model)
await client.models.delete({modelId:retrieved_job.fine_tuned_model})
curl --location --request DELETE 'https://api.mistral.ai/v1/models/ft:open-mistral-7b:XXX:20240531:XXX' \
--header 'Accept: application/json' \
--header "Authorization: Bearer $MISTRAL_API_KEY"
常见问题
如何验证数据格式?
-
Mistral API:目前,在您上传数据集时,我们会对每个文件进行验证。
-
mistral-finetune
:您可以运行数据验证脚本来验证数据,并运行数据重新格式化脚本将数据重新格式化为正确格式# download the reformat script
wget https://raw.githubusercontent.com/mistralai/mistral-finetune/main/utils/reformat_data.py
# download the validation script
wget https://raw.githubusercontent.com/mistralai/mistral-finetune/main/utils/validate_data.py
# reformat data
python reformat_data.py data.jsonl
# validate data
python validate_data.py data.jsonl然而,需要注意的是,这些脚本可能无法检测所有问题情况。因此,您可能需要手动验证并更正数据中的任何特殊边缘情况。
训练数据的大小限制是多少?
虽然单个训练数据文件的大小限制为 512MB,但您可以上传的文件数量没有限制。您可以上传多个文件并在创建作业时引用它们。
验证数据的大小限制是多少?
验证数据的大小限制是 1MB。经验法则为
validation_set_max_size = min(1MB, 5% of training data)
如果我尝试创建已存在的作业会发生什么?
创建作业时,如果存在类似作业正在运行/已验证/排队中,您将收到 409 Conflict
错误。此机制有助于避免无意中创建重复作业,从而节省资源并防止冗余。
如果我上传已存在的文件会发生什么?
如果上传的文件在内容和名称上均与现有文件匹配,则会返回现有文件,而不是创建一个新文件。
训练过程中有多少个 epoch?
一个经验法则是:Num epochs = max_steps / file_of_training_jsonls_in_MB。例如,如果您的训练文件是 100MB 且您设置 max_steps=1000,则训练过程将大致执行 10 个 epoch。
我在哪里可以找到关于成本/预计完成时间 (ETA)/标记数量/每个文件的处理次数的信息?
Mistral API:创建微调作业时,使用默认参数 auto_start=False
时会自动看到这些信息。
请注意,参数 dry_run=True
将于九月移除。
mistral-finetune
:您可以使用以下脚本来查找:https://github.com/mistralai/mistral-finetune/blob/main/utils/validate_data.py。此脚本接受 .yaml 训练文件作为输入,并返回模型正在训练所用的标记数量。
如何估算微调作业的成本?
对于 Mistral API,您可以使用上一问题中提到的 auto_start=False
参数。
推荐的学习率是多少?
对于 LoRA 微调,我们推荐 1e-4(默认)或 1e-5。
请注意,我们定义的学习率是峰值学习率,而不是固定学习率。学习率遵循线性预热和余弦衰减策略。在预热阶段,学习率在一定数量的训练步骤内从较小的初始值线性增加到较大的值。预热阶段后,学习率使用余弦函数进行衰减。
微调 API 是否兼容 OpenAI 数据格式?
是的,我们支持 OpenAI 格式。
如果我的文件大小超过 500MB 并收到错误消息 413 Request Entity Too Large
怎么办?
您可以将数据文件分割成块。此处有一个示例
详情
import json
from datasets import load_dataset
# get data from hugging face
ds = load_dataset("HuggingFaceH4/ultrachat_200k",split="train_gen")
# save data into .jsonl. This file is about 1.3GB
with open('train.jsonl', 'w') as f:
for line in ds:
json.dump(line, f)
f.write('\n')
# reformat data
!wget https://raw.githubusercontent.com/mistralai/mistral-finetune/main/utils/reformat_data.py
!python reformat_data.py train.jsonl
# Split file into three chunks
input_file = "train.jsonl"
output_files = ["train_1.jsonl", "train_2.jsonl", "train_3.jsonl"]
# open the output files
output_file_objects = [open(file, "w") for file in output_files]
# counter for output files
counter = 0
with open(input_file, "r") as f_in:
# read the input file line by line
for line in f_in:
# parse the line as JSON
data = json.loads(line)
# write the data to the current output file
output_file_objects[counter].write(json.dumps(data) + "\n")
# increment the counter
counter = (counter + 1) % 3
# close the output files
for file in output_file_objects:
file.close()
# now you should see three jsonl files under 500MB