Agent Platform 模型微调数据准备指南:JSONL 格式、验证集切分与 GCS 上传全解析
【免费下载链接】skillsAgent Skills for Google products and technologies项目地址: https://gitcode.com/GitHub_Trending/skills29/skills
Agent Platform Model Tuning(Agent Platform 模型微调)要求训练数据以JSON Lines(JSONL)格式存放于 Google Cloud Storage(GCS)中。本文以 data_prep.md 为核心,系统讲解受支持的数据格式、数据集硬性要求、验证集切分的数学边界、Bucket 选择策略与格式化最佳实践,并结合本仓库中 prepare_dataset.py 等源码实现,说明如何在提交微调任务前完成数据校验与转换,避免因数据格式问题导致任务被拒。
一、为什么数据准备是微调成败的第一道关口
Agent Platform 的微调服务对输入数据有严格的格式与配额约束。与自建 GPU 集群训练不同,微调任务一旦提交,数据将直接进入云端调度流程,任何格式错误(例如角色字段缺失、内容为空、验证集超限)都会以INVALID_ARGUMENT或类似错误在任务创建阶段被拒绝。本仓库的 SKILL.md 将「数据集是否已按 JSONL 格式化、结构是否有效、是否已上传到 GCS」列为工作流决策树中的关键检查点(Phase 1),并配套提供了 prepare_dataset.py 脚本,在上传之前就完成格式校验与验证集切分,让「提交即失败」的昂贵错误发生在本地。
二、受支持的 JSONL 格式(Open Models)
Agent Platform 为开源模型(Open Models)的微调接受两种 JSONL 行格式,每种格式即一行合法的 JSON 对象。
1. 对话式(Messages)格式
推荐用于聊天类模型(如 Llama 3.1/3.2/3.3 Chat、Gemma 3 IT 等)。每一行是一个包含messages数组的对象,数组元素为带role与content键的消息:
{ "messages": [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What is the capital of France?"}, {"role": "assistant", "content": "The capital of France is Paris."} ] }从源码看,prepare_dataset.py 中的_validate_example对 messages 格式做了严格校验:每条消息必须同时包含role与content两个键,且content不能为空、不能是字符串"nan"(大小写不敏感)。转换脚本_format_row在从 CSV/JSON/Parquet 转换时,会把用户输入列映射为{"role": "user", ...}、把期望输出列映射为{"role": "assistant", ...}。
2. 指令(Prompt/Completion)格式
适用于基础模型(base model)或简单补全任务。每一行是包含prompt与completion两个键的对象:
{ "prompt": "Summarize the following text: [TEXT]", "completion": "[SUMMARY]" }同样的,_validate_example要求两个键都存在、值非空且不为"nan"。这种格式不包含 system 角色,适合无需复杂角色设定的指令学习场景。
关于 Gemini 模型:SKILL.md 的 Phase 1.1 说明,验证 Gemini 数据时使用
--format messages_gemini;Gemini 与开源模型的 schema 存在差异,具体以各自文档为准。本仓库的prepare_dataset.py面向开源模型的messages与prompt两种格式。
三、数据集硬性要求
原文档明确了以下不可妥协的约束:
- 文件类型:必须为
.jsonl; - 编码:UTF-8;
- 存放位置:必须位于 GCS 桶中,例如
gs://my-bucket/train.jsonl; - 验证集(可选但推荐):单独的验证文件,其大小不得超过训练文件按字节计算的 25%,且不得超过 5000 行。
其中「按字节而非按行数」这一约束是本仓库反复强调的核心易错点,详见下一节。
四、验证集切分的数学边界:为什么 80/20 会被拒绝
原文档给出了关键的推导:25% 的上限是相对训练文件而言的,因此切分比例s必须满足s / (1 - s) <= 0.25,即s <= 0.2。
这意味着80/20 切分恰好落在临界线上:只要留出的验证集行平均长度略长于训练集,比值就会突破 25% 而被服务拒绝。原文档记录的实测超限区间为25.03% 到 26.45%——即看起来合规的 80/20 切分在实际写入文件后几乎必然超限。
仓库的做法是:默认使用--validation_split 0.1,这样验证文件约占训练文件的 11%,留出充足的安全余量。
源码在 prepare_dataset.py 中实现了这一检查逻辑:常量MAX_VALIDATION_RATIO = 0.25、DEFAULT_VALIDATION_SPLIT = 0.1,validation_ratio_error函数按写入后的实际字节数计算比值,一旦超限返回明确的错误消息并建议使用更小的--validation_split。由于该检查发生在上传之前,因此校验失败永远不会浪费一次微调任务提交——这正是该脚本被设计为「fail before upload」的原因。
此外,SKILL.md 的 Phase 1.1 明确要求 Agent不要主动提供 80/20 切分方案(微调服务会拒绝),如需从单一训练集切出验证集,必须征得用户同意后使用--validation_split 0.1执行 90/10 切分。
五、Bucket 选择策略:绝不擅自创建
数据与输出产物必须存放在用户指定的桶中。原文档给出了三条铁律:
- 绝不凭空编造桶名;
- 绝不从项目编号推导桶名;
- 绝不未经提示就创建桶——创建桶属于变更操作(mutating action),需要用户显式的 Tier M 确认。
如果用户没有指定桶,应停下并询问用户希望使用哪个桶,并把「为用户创建桶」作为选项之一。只有在用户确认了确切的桶名与位置之后,才允许执行创建命令。
由于开源模型微调的默认位置是global(服务会自动选择有 GPU 容量的区域),新建的桶应使用多区域(multi-region),以保证无论任务落在哪个区域都能访问。推荐US(若数据必须留在欧洲则用EU):
# 仅在用户确认桶名与位置后执行。 # 请替换为已确认的桶名;绝不要带着占位符原样执行。 gcloud storage buckets create gs://CONFIRMED_BUCKET_NAME --location=US只有当微调任务本身被固定到某个单区域时,桶才应固定在单区域。这与 SKILL.md 中「--output_uri为必填、不得凭空指定 GCS 目标」的约束一致——在提交任务前必须与用户确认训练集与输出的存放位置。
六、格式化最佳实践
原文档总结的三条实践准则,与prepare_dataset.py的实现互相印证:
- 质量优先于数量:100 个高质量样本往往胜过 1000 个噪声样本;
- 一致性:system prompt 与指令风格应保持统一格式;
- 禁止空值:每个样本都必须有有效的 prompt/user 消息和 completion/assistant 响应,可使用 prepare_dataset.py 进行校验。
在源码层面,convert_to_jsonl中的is_valid过滤器会剔除 prompt 或 completion 为空、为"nan"或"none"(均大小写不敏感)的行,并在日志中报告被剔除的行数(prepare_dataset.py)。这意味着「清理脏数据」被自动化进了转换流程,而不是依赖人工检查。
七、实战:用 prepare_dataset.py 完成校验与转换
7.1 校验已有 JSONL 文件
仅拥有.jsonl扩展名是不够的,必须验证内容 schema 是否有效(例如 system/user/model 角色是否正确)。使用--validate_only模式:
python3 scripts/prepare_dataset.py \ --input my_data.jsonl \ --format messages \ --validate_only--format的可选值为messages与prompt,默认messages;- 校验通过退出码为 0,存在无效行时退出码为 1,并在日志中逐行报告
Invalid format/empty content at line N或Invalid JSON at line N(见 validate_jsonl)。
7.2 从 CSV / JSON / Parquet 转换并切分验证集
python3 scripts/prepare_dataset.py \ --input my_data.csv \ --output tuning_dataset.jsonl \ --format messages \ --prompt_col question \ --completion_col answer \ --validation_split 0.1参数说明(见脚本 argparse 定义,prepare_dataset.py):
| 参数 | 说明 | 默认值 |
|---|---|---|
--input | 输入 CSV / JSON / Parquet 文件(转换模式必填) | 无 |
--output | 输出 JSONL 文件路径 | tuning_dataset.jsonl |
--format | 目标格式:messages或prompt | messages |
--prompt_col | prompt/用户消息对应列名(仅转换模式必填) | 无 |
--completion_col | completion/助手响应对应列名(仅转换模式必填) | 无 |
--validation_split | 留出用于验证集的比例,保持在 0.1 或更低 | 0.1 |
--validate_only | 仅校验输入 JSONL,不转换 | false |
行为细节:
- 验证集输出到
{output}_validation.jsonl(将.jsonl替换为_validation.jsonl,若替换后与输出同名则追加.validation.jsonl); - 切分使用
datasets库的train_test_split(seed=42, test_size=validation_split),可复现; - 写出后脚本按字节比对训练与验证文件大小,若验证文件超过训练文件的 25% 则直接报错退出(见 convert_to_jsonl),从而把「被服务拒绝」提前为「本地失败」。
7.3 上传到 GCS
上传时应使用带时间戳的唯一目录,避免不同运行互相覆盖输出:
ARTIFACTS="gs://YOUR_BUCKET/tuning_agent_job_<datetime>/dataset.jsonl" gcloud storage cp dataset.jsonl "$ARTIFACTS"八、与微调流水线的衔接
数据准备完成后,后续环节同样可以复用本仓库的脚本资源:
- 成本估算:calculate_cost.py 按
--input中messages的content字符数、模型与微调模式估算费用。其MODEL_DATA表中为每个模型记录了tokens_per_character(每字符折算的计费 token 数,随 PEFT/Full 模式不同而不同)与cost_per_1m_tokens;它接受显示名(Qwen 3 8B)或资源名(qwen/qwen3@qwen3-8b)两种模型写法; - 任务提交:tune_open_model.py 将训练/验证 GCS URI 封装为
TuningDataset/TuningValidationDataset,映射FULL/PEFT_ADAPTER两种模式与 adapter size,并调用genai.Client(enterprise=True, ...).tunings.tune()提交。注意其--output_uri为必填参数——底层后端对开源模型缺失output_uri的任务会以INVALID_ARGUMENT: The output_uri field is required for this model.拒绝; - 模型选择:models.md 提供了每个模型的
--base_model资源名格式{publisher}/{model_id}@{version_id}与基线超参数表,复制资源名而非自行构造,是避免INVALID_ARGUMENT: Invalid open source publisher model resource name的关键; - 任务监控:monitor_tuning_job.py 轮询任务直到终态(
JOB_STATE_SUCCEEDED/FAILED/CANCELLED/PARTIALLY_SUCCEEDED),轮询间隔默认 60 秒。
九、小结
数据准备阶段看似简单,实则集中了 Agent Platform 微调中最容易踩坑的三个点:JSONL 行内 schema 的严格性、验证集按字节计 25% 上限的数学陷阱、以及GCS 桶的命名与区域策略。本仓库将这三条经验固化进了 prepare_dataset.py 的校验与切分逻辑中:默认 10% 验证集切分、写盘后字节级校验、上传前失败,配合 data_prep.md 中的格式规范与最佳实践,可以让数据以合规状态进入微调流水线,将任务被云端拒绝的概率降到最低。
【免费下载链接】skillsAgent Skills for Google products and technologies项目地址: https://gitcode.com/GitHub_Trending/skills29/skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考