Skip to main content

Arrow 与 Parquet 格式详解

Hugging Face datasets 库主要涉及两种核心格式:Apache Arrow(本地缓存/运行时格式)和 Parquet(云端存储/压缩格式)。

它们都是二进制格式,不能像 JSONL 那样直接用记事本或文本编辑器打开阅读(会看到乱码),但通过工具可以非常方便地查看。

1. Hugging Face 的核心格式

Apache Arrow (.arrow) — 本地运行时之王

当你使用 save_to_disk()load_dataset() 时,数据在本地主要是以 Arrow 格式存在的。

  • 本质:一种高性能的内存数据格式
  • 特点
    • 零拷贝 (Zero-copy):读取数据几乎不需要消耗 CPU 去解析(JSON 需要 parse,Arrow 不需要)
    • 内存映射 (Memory Mapping):这是它的杀手锏。它允许你在 16GB 内存的电脑上瞬间"加载" 1TB 的数据集。它并不把数据真的全部读进内存,而是按需从硬盘读取,但操作系统觉得它已经在内存里了
  • 能直接打开吗? ❌ 不能。它是二进制文件

Parquet (.parquet) — 云端存储与压缩之王

你在 Hugging Face 网页上浏览文件列表时,看到的通常是 Parquet 文件。

  • 本质:一种列式存储的二进制文件格式
  • 特点
    • 高压缩率:比 JSONL 小得多(通常只有 JSONL 体积的 40%-60%)
    • 列式读取:如果你只需要数据集里的 "text" 这一列,它不需要扫描整个文件,读取速度极快
  • 能直接打开吗? ❌ 不能。它是二进制文件

2. 与 JSONL 的全方位对比

特性JSONL (.jsonl)Arrow / Parquet (HF 格式)
可读性 (人类可读),可以用记事本、headcat 查看 (二进制乱码),必须用专门工具或代码读取
体积 (纯文本,包含大量重复的 key 字符串) (Parquet 压缩率极高,Arrow 略大但高效)
加载速度,Python 需要逐行解析字符串,消耗 CPU极快,二进制直接映射,几乎无解析开销
内存占用,加载大文件容易内存溢出 (OOM)极低,支持内存映射,不占物理内存
适用场景调试、小数据、数据清洗中间过程大规模预训练数据、生产环境、高性能计算

3. 如何查看这些格式?

虽然不能用记事本,但有很方便的方法查看:

方法一:使用 VS Code 插件(推荐)

如果你用 VS Code 开发,不需要写代码就能看:

  1. Parquet: 安装 Parquet Viewer 插件。点击 .parquet 文件,它会像 Excel 表格一样展示数据
  2. Arrow/Parquet: 安装 Data Wrangler 插件,可以进行高级的数据查看和统计

方法二:使用 Pandas (Python)

这是最通用的方法,把它们当表格读进来:

查看 Parquet:

import pandas as pd

# 就像读 CSV 一样简单
df = pd.read_parquet("output_dir/part-00000.parquet")
print(df.head()) # 查看前5行

查看 Arrow (save_to_disk 的产物):

from datasets import load_from_disk

# 这里传入的是文件夹路径,不是单个文件
dataset = load_from_disk("/root/autodl-fs/cci3_hq_30b")
print(dataset[0]) # 查看第一条数据

4. 为什么云端用 Parquet 而不是 Arrow?

既然 Arrow 在本地跑得那么快(零拷贝、内存映射),为什么云端(如 Hugging Face Hub, S3, HDFS)不直接存 Arrow 格式,反而要多此一举存成 Parquet 呢?

核心原因是:Arrow 是为了"计算"设计的,而 Parquet 是为了"存储和传输"设计的。

宜家家具比喻

  • Arrow (本地运行) = 组装好的成品家具

    • 优点:你坐下就能用(CPU 拿到数据就能算),不需要等待组装
    • 缺点:体积巨大,里面有很多空隙(Padding),极占空间,不适合卡车运输
  • Parquet (云端存储) = 扁平包装的快递箱 (Flat-pack)

    • 优点:体积极小,甚至能压缩到成品的 1/10,非常适合卡车运输(网络传输)和堆在仓库里(云存储)
    • 缺点:你不能直接坐上去,必须先拆包组装(解压并转换为 Arrow 内存格式)才能用

技术原因详解

极致的压缩率 (Parquet 完胜)

云端存储最看重的是体积(省存储费)和传输速度(省带宽费)。

  • Arrow (内存格式):为了让 CPU 处理得快,Arrow 必须保证数据在内存中是对齐的。比如它可能会为了对齐 64 位内存地址,在数据中间留很多空白(Padding)。而且它通常不进行高强度的压缩,因为解压需要消耗 CPU,会拖慢计算速度

  • Parquet (磁盘格式):它使用了很多复杂的编码技术。比如你的某一列全是重复的 "True/False",Parquet 会用游程编码 (Run-Length Encoding),只存"这里有 1000 个 True",而不是存 1000 次 True。加上 Snappy 或 Zstd 压缩算法,Parquet 文件通常比 Arrow 内存镜像小 2倍到 10倍

结论:如果云端存 Arrow,你需要下载 1TB 的数据;存 Parquet,你可能只需要下载 200GB。在公网带宽有限的情况下,下载 Parquet 即使加上本地 CPU 解压的时间,总耗时也比直接下载庞大的 Arrow 要短得多。

IO 瓶颈 vs CPU 瓶颈

  • 云端下载场景:瓶颈通常在网络带宽(Network I/O)或磁盘读取速度(Disk I/O)。因为网络慢,所以我们要尽可能传小文件(Parquet),哪怕牺牲一点本地 CPU 去解压也是划算的

  • 本地训练场景:数据已经到本地了,瓶颈变成了GPU 等待数据的时间。这时候 CPU 哪怕花 1ms 去解压数据都是罪过,所以本地要用 Arrow,让数据直接"流"进 CPU/GPU

兼容性与生态

Parquet 是大数据领域的通用货币:

  • 如果你把数据存成 Parquet,Spark 能读,Hive 能读,Pandas 能读,ClickHouse 也能读
  • Arrow 虽然也在普及,但它更多被视为一种"内存交换协议"。不同版本的 Arrow 库在序列化落盘时,偶尔会有细微的格式差异或向前兼容性问题,不如 Parquet 这种老牌存储格式稳定

5. 典型的数据流转过程

这就是为什么你在使用 Hugging Face 时,会经历以下流程:

云端 (Parquet)  ──下载──>  本地缓存 (~/.cache)  ──转换──>  本地内存 (Arrow)
│ │ │
│ 为了省钱、省带宽 │ HF 库自动把 Parquet │ 训练代码通过内存映射
│ 存的是压缩极好的 │ 解压并转换为 Arrow │ 读取 Arrow 文件
│ "扁平包装" │ 格式 (.arrow) │ 实现极速喂给 GPU

6. 最佳实践建议

在下载几百 GB 的数据时,绝对不要保存为 JSONL,否则硬盘空间会浪费很多,且后续训练加载速度会很慢。

建议

  • 坚持使用 ParquetArrow 格式存储大规模数据
  • 如果需要检查数据,写两行 Python 代码抽取前几条打印出来看即可
  • 流式下载时保存为 Parquet 是最高效的选择
  • 等真正开始训练时,再按需把它加载成 Arrow 格式

总结

  • JSONL 是给看的(方便检查),或者是流式传输的通用协议
  • HuggingFace (Arrow/Parquet) 是给机器GPU看的(为了追求极致的速度和存储效率)