趋近智
大型语言模型需要海量文本语料库,这些语料库在训练期间必须高效存储并快速访问。此类语料通常会经过抓取、清洗和预处理。选择合适的存储格式是一个根本性决定,它会影响存储成本、I/O性能以及数据加载流程的整体效率。虽然简单格式容易开始使用,但随着数据集扩展到TB或PB级别,专门格式的益处将越来越明显。
我们来考察常见选择:纯文本文件、Apache Arrow和Apache Parquet。
.txt、.jsonl)最直接的方法通常是将文本数据存储在纯文本文件中。每行可能代表一个文档,或者您可以使用JSON Lines(.jsonl)等格式,其中每行都是一个表示数据记录的有效JSON对象(例如,包含文本和元数据)。
优点:
grep、sed、awk)可直接使用。缺点:
一个典型的.jsonl文件可能如下所示:
{
"text": "The quick brown fox jumps over the lazy dog.",
"source": "wikipedia",
"id": "doc_001"
}
{
"text": "Large language models require immense amounts of text data.",
"source": "common_crawl",
"id": "cc_abc"
}
{
"text": "...",
"source": "...",
"id": "..."
}
在Python中读取这个文件很简单,但对于非常大的文件来说可能很慢,特别是当反序列化复杂的JSON时:
import json
import gzip
def read_jsonl_gz(filepath):
"""从一个gzipped JSON Lines文件中读取记录。"""
with gzip.open(filepath, 'rt', encoding='utf-8') as f:
for line in f:
try:
yield json.loads(line)
except json.JSONDecodeError:
# 处理或记录格式错误行
print(f"正在跳过格式错误的行:{line.strip()}")
# 使用示例
# 数据加载器会迭代这个生成器
# data_generator = read_jsonl_gz("my_large_dataset.jsonl.gz")
# for record in data_generator:
# process(record['text'])
对于较小的数据集或初步原型来说尚可接受,但逐行解析文本的CPU开销会成为瓶颈,当向多个GPU提供高速消耗数据的LLM训练时。
Apache Arrow是一个内存列式数据格式标准。它旨在提高分析查询性能,并以最小的序列化/反序列化开销(通常是零拷贝读取)在系统和语言之间高效交换数据。
理念:
优点:
缺点:
.arrow或.feather),但它主要针对内存表示进行优化。Parquet通常更适合磁盘上的持久性、压缩存储。Arrow在Hugging Face datasets等库的底层被大量使用。当您使用datasets加载或映射数据集时,它通常在内部使用Arrow表进行缓存和快速访问。
import pyarrow as pa
import pyarrow.feather as feather
import time
# 示例:创建和写入一个Arrow表
# 假设'data'是一个字典列表,例如[{'text': '...', 'id': '...'}, ...]
# 将Python对象转换为Arrow数组
texts = pa.array([d['text'] for d in data], type=pa.string())
ids = pa.array([d['id'] for d in data], type=pa.string())
# 创建一个Arrow表
table = pa.Table.from_arrays([texts, ids], names=['text', 'id'])
# 写入到Feather文件(Arrow文件格式)
feather.write_feather(table, 'my_dataset.arrow')
# 读取通常非常快
start_time = time.time()
read_table = feather.read_table('my_dataset.arrow')
end_time = time.time()
print(f"Arrow读取时间:{end_time - start_time:.4f} 秒")
# 访问列是高效的
text_column = read_table['text']
# print(text_column[0].as_py()) # 访问第一个文本条目
当您流程的多个阶段都使用Arrow时,才能体现其真正的效用,从而避免昂贵的序列化步骤。在Spark上运行的数据预处理任务可以直接将Arrow文件输出到云存储,然后PyTorch DataLoader可以使用pyarrow高效读取这些文件。
Apache Parquet是一个广泛采用的列式存储格式,针对大规模数据仓库和分析进行了优化,对于在磁盘上存储LLM数据集也非常有效。
理念:
优点:
pandas、pyarrow)中都有出色的支持。缺点:
Parquet通常是最终处理数据集的首选格式,该数据集将在训练期间重复读取。
import pyarrow as pa
import pyarrow.parquet as pq
import pandas as pd # 通常用作中间件
# 直接使用PyArrow的示例(类似于Arrow Feather示例)
# 假设“table”是Arrow示例中创建的pyarrow.Table
# 写入一个Parquet文件(默认使用Snappy压缩)
pq.write_table(table, 'my_dataset.parquet', compression='snappy')
# 读取Parquet文件
start_time = time.time()
# 可以只读取特定列,减少I/O
read_parquet_table = pq.read_table('my_dataset.parquet', columns=['text'])
end_time = time.time()
print(
f"Parquet读取时间(仅文本列):" # Parquet读取时间(仅文本列):
f"{end_time - start_time:.4f} 秒" # {end_time - start_time:.4f} 秒
)
# 使用Pandas的示例(常见工作流)
# df = pd.DataFrame(data) # data是一个字典列表
# df.to_parquet('my_dataset_pd.parquet', engine='pyarrow', compression='snappy')
# read_df = pd.read_parquet('my_dataset_pd.parquet', engine='pyarrow', columns=['text'])
以下是权衡的汇总:
| 特点 | 文本 (.txt, .jsonl) | Apache Arrow | Apache Parquet |
|---|---|---|---|
| 类型 | 基于行 | 列式(内存中) | 列式(磁盘上) |
| 主要用途 | 简单性,调试 | 快速内存分析,进程间通信 | 高效存储,分析 |
| 可读性 | 是 | 否 | 否 |
| 压缩 | 外部(Gzip, Zstd) | 有限(进程间通信通常是原始数据) | 优秀(内置) |
| 读取速度 | 慢(解析开销) | 非常快(零拷贝) | 快(列式扫描) |
| 写入速度 | 快 | 中等 | 中等-慢(编码) |
| CPU使用(读取) | 高(解析) | 低 | 低-中等(解码) |
| 随机访问 | 差 | 中等(内存中) | 中等(行组) |
| 生态系统 | 通用 | 增长中(Pandas, Spark) | 优秀(大数据) |
大型文本数据集常见数据存储格式的比较。
对LLM数据集的建议:
datasets等库大量使用Arrow进行缓存和内存映射。为存储在云存储或HDFS上的大规模训练数据选择Parquet,并通过使用Arrow进行内存表示的库(如datasets或使用pyarrow.parquet的自定义DataLoader)进行读取,为将数据馈送给您的分布式训练任务提供了高效的根本。这最大限度地减少了I/O瓶颈和存储占用,使您能够将计算资源集中在模型训练本身上。
这部分内容有帮助吗?
datasets库的官方文档,提供了用于管理和加载大型文本数据集以进行LLM训练的工具,内部常使用Apache Arrow来提高性能。© 2026 ApX Machine Learning用心打造