数据格式与打包流水线

July 21, 2026 · View on GitHub

English Version

概述

训练所需的 JSONL 是打包后的格式:每一行包含多条原始样本拼接而成的单个序列,长度上限为 pack_length 个 token。这种做法通过消除 padding 浪费来最大化 GPU 利用率。

流水线:原始 JSONL → 分词 + 计数 → 打包 → 可用于训练的 JSONL。


1. 原始 OCR JSONL 格式

原始 JSONL 每行是一条训练样本:

{
  "image_path": ["/absolute/path/to/image.png"],
  "conversations": [
    {
      "from": "human",
      "value": "<image>\n提取文档图片中正文的所有信息用markdown格式表示..."
    },
    { "from": "gpt", "value": "# Title\n\nBody text ..." }
  ]
}

字段说明:

字段类型是否必填说明
image_pathlist[str]绝对路径。通常 1 张图,也支持多图。
conversationslist[dict]human / gpt 交替对话。human 值中的 <image> 占位符表示图片插入点。

2. 打包流水线

tools/pipeline_count_and_pack.py 做两件事:

  1. 计数阶段(并行):对每条样本分词,把 token 数写入 count_output_dir/*.jsonl
  2. 打包阶段:按 First-Fit Decreasing 贪心策略,把样本打包到每行 pack_length 个 token 的上限

配置文件:configs/data_list.txt

纯文本文件,每行一个路径:

/path/to/dataset_A.jsonl
/path/to/dataset_B.jsonl
/path/to/dataset_C.jsonl
# 以 # 开头的行会被忽略

运行

MODEL_PATH=/path/to/HunyuanOCR \
INPUT_LIST=./configs/data_list.txt \
PACK_OUTPUT=./data/parsing_packed_20480.jsonl \
    bash scripts/pack_data.sh

可调参数(环境变量)

环境变量默认值含义
MODEL_PATH(必填)HunyuanOCR 基座模型目录(用于 tokenizer + processor)
INPUT_LIST./configs/data_list.txt原始 JSONL 清单文件路径
COUNT_OUTPUT_DIR./data/parsing_jsonl_count计数阶段输出的临时目录
PACK_OUTPUT./data/parsing_packed_{PACK_LEN}.jsonl最终打包好的 JSONL
PACK_LEN20480每行打包序列的最大长度
NUM_PROCESSES32计数阶段的多进程 worker 数
THREADS_PER_PROCESS8每个计数 worker 的线程数
LOG_FILEpack_data.log进度日志路径

输出格式(打包后的 JSONL)

每一行输出格式如下:

{
    "packed_samples": [
        {
            "image_path": ["/abs/path/1.png"],
            "conversations": [...]
        },
        {
            "image_path": ["/abs/path/2.png"],
            "conversations": [...]
        },
        ...
    ],
    "cu_seqlens": [0, 4123, 8567, ..., 20351],
    "total_tokens": 20351
}

字段:

  • packed_samples:本 pack 内拼接的原始样本
  • cu_seqlens:累计 token 边界(供 FlashAttention varlen 使用)
  • total_tokens:合计 ≤ pack_length

性能提示

  • CPU 密集:随 NUM_PROCESSES 近似线性扩展。128 核机器上建议 NUM_PROCESSES=32~64
  • 图片数据不需要预加载,计数阶段只做文本分词;图片在训练时按需加载。
  • 首次运行会生成计数缓存;使用相同的输入清单重跑时,计数阶段会自动跳过。

3. 校验打包结果

对打包后的 JSONL 做健康检查:

python -c "
import json
with open('./data/parsing_packed_20480.jsonl') as f:
    for i, line in enumerate(f):
        r = json.loads(line)
        print(f'pack {i}: {len(r[\"packed_samples\"])} samples, '
              f'{r[\"total_tokens\"]} tokens')
        if i >= 3: break
"

预期输出类似:

pack 0: 7 samples, 20438 tokens
pack 1: 5 samples, 19821 tokens
pack 2: 12 samples, 20301 tokens
pack 3: 3 samples, 18654 tokens

每个 pack 都接近 pack_length=20480,这正是打包的目标。