转换 优化 ✅ 并行化数据处理 ✅ 流式加载大型云端数据集 ✅ 创建向量嵌入 ✅ 将训练加速高达20倍 ✅ 运行分布式推理 ✅ 暂停/恢复数据流 ✅ 大规模网页抓取 ✅ 无需本地加载,直接使用远程数据---    [](https://discord.gg/VptPCZkGNa)
Lightning AI • 快速开始 • 优化数据 • 转换数据 • 功能 • 基准测试 • 模板 • 社区
加速模型训练不仅仅是内核调优。数据加载常常是训练过程的瓶颈,因为数据集太大而无法放入磁盘,由数百万个小文件组成,或者从云端流式加载缓慢。
LitData 提供了预处理和优化数据集的工具,将其转换为能够高效地从任何云端或本地源流式加载的格式。它还包含一个用于在优化前进行分布式数据处理的映射算子。这使得数据流水线更快、与云端无关,并可以将训练吞吐量提升高达 20 倍。
超过 340,000 名开发者使用 Lightning Cloud —— 专为 PyTorch 和 PyTorch Lightning 构建。
- GPU 从 $0.19 起。
- 集群:前沿级的训练/推理集群。
- AI Studio(氛围训练):AI 帮助您调试、调优和氛围训练的工作空间。
- AI Studio(氛围部署):AI 帮助您优化和部署模型的工作空间。
- 笔记本:持久的 GPU 工作空间,AI 帮助您编码和分析。
- 推理:将模型部署为推理 API。
首先,安装 LitData:
pip install litdata
选择您的工作流程:
pip install 'litdata[extras]'
直接从云端存储流式加载数据集,无需本地下载。选择适合您工作流程的方法:
直接从云端存储流式加载原始文件——无需预先优化。
from litdata import StreamingRawDataset
from torch.utils.data import DataLoader
# 指向您现有的云端数据
dataset = StreamingRawDataset("s3://my-bucket/raw-data/")
dataloader = DataLoader(dataset, batch_size=32)
for batch in dataloader:
# 即时处理原始字节
pass
主要优势:
✅ 即时访问: 无需预处理,立即开始流式加载。
✅ 零启动时间: 无需数据转换或优化。
✅ 原生格式: 直接使用原始文件格式(图片、文本等)。
✅ 灵活处理: 在流式加载过程中即时应用转换。
✅ 云端原生: 直接从 S3、GCS 或 Azure 存储流式加载。
通过优化数据集以便直接从云端存储流式加载,加速模型训练(速度提升 20 倍)。无需本地下载即可使用远程数据,支持加载数据子集、访问单个样本和可恢复的流式加载。
步骤 1:优化您的数据(一次性设置)
将原始数据转换为优化的分块格式,以获得最大流式加载速度。
此步骤通过将数据写入高效的分块二进制格式来格式化数据集,以实现快速加载。
import numpy as np
from PIL import Image
import litdata as ld
def random_images(index):
# 在此处替换为您的实际图片加载逻辑(例如 .jpg、.png 等)
# 建议:使用像 JPEG 这样的压缩格式以获得更好的存储和优化的流式加载速度
# 您还可以应用调整大小或降低图像质量以进一步提高流式加载速度并节省空间
fake_images = Image.fromarray(np.random.randint(0, 256, (32, 32, 3), dtype=np.uint8))
fake_labels = np.random.randint(10)
# 您可以使用任何键:值对。请注意,样本之间的类型不能改变,Python 列表必须
# 始终包含相同数量的元素且类型相同
data = {"index": index, "image": fake_images, "class": fake_labels}
return data
if __name__ == "__main__":
# optimize 函数将数据写入优化后的格式
ld.optimize(
fn=random_images, # 应用于每个输入的函数
inputs=list(range(1000)), # 函数的输入(这里是一个数字列表)
output_dir="fast_data", # 优化后的数据存储于此
num_workers=4, # 同一台机器上的工作进程数
chunk_bytes="64MB" # 每个数据块的大小
)
步骤 2:将数据上传到云端
将数据上传到 Lightning Studio(由 S3 支持)或您自己的 S3 存储桶:
aws s3 cp --recursive fast_data s3://my-bucket/fast_data
步骤 3:在训练期间流式加载数据
通过将 PyTorch DataSet 和 DataLoader 替换为 StreamingDataset 和 StreamingDataLoader 来加载数据。
import litdata as ld
dataset = ld.StreamingDataset('s3://my-bucket/fast_data', shuffle=True, drop_last=True)
# 自定义 collate 函数以处理批次(可选)
def collate_fn(batch):
return {
"image": [sample["image"] for sample in batch],
"class": [sample["class"] for sample in batch],
}
dataloader = ld.StreamingDataLoader(dataset, collate_fn=collate_fn)
for sample in dataloader:
img, cls = sample["image"], sample["class"]
主要优势:
✅ 加速训练: 优化后的数据集加载速度提升 20 倍。
✅ 流式加载云端数据集: 无需下载即可使用云端数据。
✅ PyTorch 优先: 与 PyTorch 库(如 PyTorch Lightning、Lightning Fabric、Hugging Face)兼容。
✅ 轻松协作: 在云端共享和访问数据集,简化团队项目。
✅ 跨 GPU 扩展: 流式数据自动扩展到所有 GPU。
✅ 灵活存储: 使用 S3、GCS、Azure 或您自己的云账户进行数据存储。
✅ 压缩: 使用高级压缩算法减少数据占用空间。
✅ 本地或云端运行: 在您自己的机器上运行,或通过 Lightning Studios 自动扩展到数千个云端 GPU。
✅ 企业级安全: 通过 Lightning Studios 自行托管或在您的云账户上处理数据。
通过跨多台机器并行化(映射)工作,加速数据处理任务(数据抓取、图像调整大小、嵌入创建、分布式推理)。
以下是一个调整大小和裁剪大型图像数据集的示例:
from PIL import Image
import litdata as ld
# 使用本地或 S3 文件夹
input_dir = "my_large_images" # 或 "s3://my-bucket/my_large_images"
output_dir = "my_resized_images" # 或 "s3://my-bucket/my_resized_images"
inputs = [os.path.join(input_dir, f) for f in os.listdir(input_dir)]
# 调整输入图像大小
def resize_image(image_path, output_dir):
output_image_path = os.path.join(output_dir, os.path.basename(image_path))
Image.open(image_path).resize((224, 224)).save(output_image_path)
ld.map(
fn=resize_image,
inputs=inputs,
output_dir="output_dir",
)
主要优势:
✅ 并行化处理: 通过跨多台机器同时转换数据来减少处理时间。
✅ 扩展到大数据: 增加您可以有效处理的数据集大小。
✅ 灵活用例: 调整图像大小、创建嵌入、抓取互联网等。
✅ 本地或云端: 在您自己的机器上运行,或通过 Lightning Studios 自动扩展到数千个云端 GPU。
✅ 企业级安全: 通过 Lightning Studios 自行托管或在您的云账户上处理数据。
# 对于 aws s3
pip install "litdata[extra]" s3fs
# 对于 gcloud storage
pip install "litdata[extra]" gcsfs
**使用示例:**
from torch.utils.data import DataLoader
from litdata import StreamingRawDataset
dataset = StreamingRawDataset("s3://bucket/files/")
# 配合 PyTorch DataLoader 使用
loader = DataLoader(dataset, batch_size=32)
for batch in loader:
# 每个元素都是原始字节
pass
> 使用 `StreamingRawDataset` 按原样流式加载数据。如果要获得最快的流式加载速度,请在优化数据后使用 `StreamingDataset`。
您还可以通过继承 `StreamingRawDataset` 并重写 `setup` 方法来自定义文件的分组方式。这对于配对相关文件(例如图像和掩码、音频和转录)或任何自定义分组逻辑非常有用。
from typing import Union
from torch.utils.data import DataLoader
from litdata import StreamingRawDataset
from litdata.raw.indexer import FileMetadata
class SegmentationRawDataset(StreamingRawDataset):
def setup(self, files: list[FileMetadata]) -> Union[list[FileMetadata], list[list[FileMetadata]]]:
# TODO:在此处实现您的自定义分组逻辑。
# 例如,按前缀、扩展名或任何您需要的规则对文件进行分组。
# 返回一个组列表,其中每个组都是 FileMetadata 的列表。
# 示例:
# return [[image, mask], ...]
pass
# 初始化自定义数据集
dataset = SegmentationRawDataset("s3://bucket/files/")
loader = DataLoader(dataset, batch_size=32)
for item in loader:
# 批次中的每个项目都是一对:[image_bytes, mask_bytes]
pass
**智能索引缓存**
`StreamingRawDataset` 会自动缓存文件索引以实现即时启动。初始扫描会构建并缓存索引,后续运行即可立即加载。
**两级缓存:**
- **本地:** 存储在您的缓存目录中,以实现即时访问
- **远程:** 自动保存到云端存储(例如 `s3://bucket/files/index.json.zstd`)以备重用
**强制重建:**
# 当数据集文件发生变化时
dataset = StreamingRawDataset("s3://bucket/files/", recompute_index=True)
from litdata import StreamingDataset, StreamingDataLoader
dataset = StreamingDataset('s3://my-bucket/my-data', shuffle=True)
dataloader = StreamingDataLoader(dataset, batch_size=64)
for batch in dataloader:
process(batch) # 替换为您的数据处理逻辑
此外,您可以在初始化数据集时为 [S3](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/session.html#boto3.session.Session.client) 或 GCP 注入客户端连接设置。这对于为每个数据集指定自定义端点和凭据非常有用。
from litdata import StreamingDataset
# 适用于自定义 S3 兼容端点的 boto3 兼容存储选项
storage_options = {
"endpoint_url": "your_endpoint_url",
"aws_access_key_id": "your_access_key_id",
"aws_secret_access_key": "your_secret_access_key",
}
dataset = StreamingDataset('s3://my-bucket/my-data', storage_options=storage_options)
dataset = StreamingDataset('s3://my-bucket/my-data', storage_options=storage_options)
此外,您可以在初始化数据集时指定自定义缓存目录。当您希望将缓存存储在特定位置时,这非常有用。
from litdata import StreamingDataset
# 使用自定义缓存目录初始化 StreamingDataset
dataset = StreamingDataset('s3://my-bucket/my-data', cache_dir="/path/to/cache")
pip install "litdata[extra]" huggingface_hub
# 可选:加速高带宽网络上的下载
pip install hf_transfer
export HF_HUB_ENABLE_HF_TRANSFER=1
**流式加载 Hugging Face 数据集:**
import litdata as ld
# 定义 Hugging Face 数据集 URI
hf_dataset_uri = "hf://datasets/leonardPKU/clevr_cogen_a_train/data"
# 创建流式加载数据集
dataset = ld.StreamingDataset(hf_dataset_uri)
# 打印第一个样本
print("Sample", dataset[0])
# 使用 StreamingDataLoader 流式加载数据集
dataloader = ld.StreamingDataLoader(dataset, batch_size=4)
for sample in dataloader:
pass
您无需担心索引数据集或任何其他设置。**LitData** 将**自动处理所有必要步骤**并`缓存` `index.json` 文件,这样您就不必再次索引。
这确保了下次流式加载数据集时,将跳过索引步骤。
### 索引 HF 数据集(可选)
如果 Hugging Face 数据集尚未索引,您可以先使用 `index_hf_dataset` 方法对其进行索引,然后使用上面的代码进行流式加载。
import litdata as ld
hf_dataset_uri = "hf://datasets/leonardPKU/clevr_cogen_a_train/data"
ld.index_hf_dataset(hf_dataset_uri)
- 提前索引 Hugging Face 数据集将使流式加载稍微快一些,因为它避免了在流式加载期间进行实时索引。
- 要使用 `HF 受限数据集`,请确保设置了 `HF_TOKEN` 环境变量。
**注意**:对于 HuggingFace 数据集,仅支持 **`Parquet 格式`** 的数据集的 `索引` 和 `流式加载`。
### Hugging Face 数据集的完整工作流程
为了完全控制缓存路径(`index.json 文件的存储位置`)和其他配置,请遵循以下步骤:
1. 首先索引 Hugging Face 数据集:
import litdata as ld
hf_dataset_uri = "hf://datasets/open-thoughts/OpenThoughts-114k/data"
ld.index_parquet_dataset(hf_dataset_uri, "hf-index-dir")
2. 现在要流式加载 HF 数据集,将 `HF 数据集 URI`、`index.json` 文件存储的路径以及 `ParquetLoader` 作为 `item_loader` 传递给 **`StreamingDataset`**:
import litdata as ld
from litdata.streaming.item_loader import ParquetLoader
hf_dataset_uri = "hf://datasets/open-thoughts/OpenThoughts-114k/data"
dataset = ld.StreamingDataset(hf_dataset_uri, item_loader=ParquetLoader(), index_path="hf-index-dir")
for batch in ld.StreamingDataLoader(dataset, batch_size=4):
pass
### LitData `Optimize` 与 `Parquet` 对比
以下是 `Imagenet 数据集(155 GB)` 的基准测试,表明 **`使用 LitData 优化数据集比使用原始 Parquet 文件更快,且输出大小更小`**。
| **操作** | **大小 (GB)** | **时间 (秒)** | **吞吐量 (图像/秒)** |
|-----------------------------------|---------------|---------------------|-----------------------------|
| LitData 优化数据集 | 45 | 283.17 | 4000-4700 |
| Parquet 优化数据集 | 51 | 465.96 | 3600-3900 |
| 索引 Parquet 数据集(开销) | N/A | 6 | N/A |
from litdata import StreamingDataset, StreamingDataLoader
# 对于训练数据集,不要忘记启用 shuffle 和 drop_last !!!
train_dataset = StreamingDataset('s3://my-bucket/my-train-data', shuffle=True, drop_last=True)
train_dataloader = StreamingDataLoader(train_dataset, batch_size=64)
for batch in train_dataloader:
process(batch) # 替换为您的数据处理逻辑
val_dataset = StreamingDataset('s3://my-bucket/my-val-data', shuffle=False, drop_last=False)
val_dataloader = StreamingDataLoader(val_dataset, batch_size=64)
for batch in val_dataloader:
process(batch) # 替换为您的数据处理逻辑

import os
import litdata as ld
# 使用 boto3 从 AWS S3 读取数据
aws_storage_options={
"aws_access_key_id": os.environ['AWS_ACCESS_KEY_ID'],
"aws_secret_access_key": os.environ['AWS_SECRET_ACCESS_KEY'],
}
# 您也可以传递会话选项。(仅适用于 boto3)
aws_session_options = {
"profile_name": os.environ['AWS_PROFILE_NAME'], # 仅自定义配置文件需要
"region_name": os.environ['AWS_REGION_NAME'], # 仅自定义区域需要
}
dataset = ld.StreamingDataset("s3://my-bucket/my-data", storage_options=aws_storage_options, session_options=aws_session_options)
# 使用未签名请求从 AWS S3 读取数据(使用 boto3)
aws_storage_options={
"config": botocore.config.Config(
retries={"max_attempts": 1000, "mode": "adaptive"}, # 配置 S3 操作的重试
signature_version=botocore.UNSIGNED, # 使用未签名请求
)
}
dataset = ld.StreamingDataset("s3://my-bucket/my-data", storage_options=aws_storage_options)
aws_storage_options={
"AWS_ACCESS_KEY_ID": os.environ['AWS_ACCESS_KEY_ID'],
"AWS_SECRET_ACCESS_KEY": os.environ['AWS_SECRET_ACCESS_KEY'],
"S3_ENDPOINT_URL": os.environ['AWS_ENDPOINT_URL'], # 仅自定义端点需要
}
dataset = ld.StreamingDataset("s3://my-bucket/my-data", storage_options=aws_storage_options)
dataset = ld.StreamingDataset("s3://my-bucket/my-data", storage_options=aws_storage_options)
# 从 GCS 读取数据
gcp_storage_options={
"project": os.environ['PROJECT_ID'],
}
dataset = ld.StreamingDataset("gs://my-bucket/my-data", storage_options=gcp_storage_options)
# 从 Azure 读取数据
azure_storage_options={
"account_url": f"https://{os.environ['AZURE_ACCOUNT_NAME']}.blob.core.windows.net",
"credential": os.environ['AZURE_ACCOUNT_ACCESS_KEY']
}
dataset = ld.StreamingDataset("azure://my-bucket/my-data", storage_options=azure_storage_options)
import os
import torch
from litdata import StreamingDataset, StreamingDataLoader
dataset = StreamingDataset("s3://my-bucket/my-data", shuffle=True)
dataloader = StreamingDataLoader(dataset, num_workers=os.cpu_count(), batch_size=64)
# 恢复 dataLoader 状态(如果存在)
if os.path.isfile("dataloader_state.pt"):
state_dict = torch.load("dataloader_state.pt")
dataloader.load_state_dict(state_dict)
# 迭代数据
for batch_idx, batch in enumerate(dataloader):
# 每 1000 个批次存储一次状态
if batch_idx % 1000 == 0:
torch.save(dataloader.state_dict(), "dataloader_state.pt")
import numpy as np
from PIL import Image
import litdata as ld
def random_images(index):
fake_images = Image.fromarray(np.random.randint(0, 256, (32, 32, 3), dtype=np.uint8))
fake_labels = np.random.randint(10)
data = {"index": index, "image": fake_images, "class": fake_labels}
return data
if __name__ == "__main__":
# optimize 函数将数据写入优化后的格式。
ld.optimize(
fn=random_images, # 应用于每个输入的函数
inputs=list(range(1000)), # 函数的输入(这里是一个数字列表)
output_dir="fast_data", # 优化后的数据存储于此
num_workers=4, # 同一台机器上的工作进程数
chunk_bytes="64MB", # 每个数据块的大小
keep_data_ordered=False, # 使用共享队列来加速过程
)
### 使用与不使用共享队列的性能差异:
**注意**:以下基准测试是使用 ImageNet 数据集在具有 16 个工作进程的 A10G 机器上收集的。
| 配置 | 优化时间 (秒) | 流式加载 1 (图像/秒) | 流式加载 2 (图像/秒) |
|------------------|---------------------|---------------------|---------------------|
| 共享队列 (`keep_data_ordered=False`) | 1281 | 5392 | 5732 |
| 无共享队列 (`keep_data_ordered=True (默认)`) | 1187 | 5257 | 5746 |
📌 注意:**shared_queue** 选项影响优化时间,而不是流式加载速度。
> 虽然流式加载数字可能看起来略有不同,但这是偶然的,并非由 shared_queue 引起。
>
> 流式加载发生在优化之后,不涉及 shared_queue 所作用的进程间通信。
- 📄 使用共享队列有助于在工作进程之间平衡负载,尽管由于在进程之间发送项目时进行序列化的开销,可能会略微增加优化时间。
- ⚡ 然而,当某些工作进程比其他进程慢时,它可以显著提高优化性能。
from multiprocessing import Process, Queue
from litdata.processing.data_processor import ALL_DONE
import litdata as ld
import time
def yield_numbers():
for i in range(1000):
time.sleep(0.01)
yield (i, i**2)
def data_producer(q: Queue):
for item in yield_numbers():
q.put(item)
q.put(ALL_DONE) # 用于指示完成的哨兵值
def fn(index):
return index # 用于演示的恒等函数
if __name__ == "__main__":
q = Queue(maxsize=100)
producer = Process(target=data_producer, args=(q,))
producer.start()
ld.optimize(
fn=fn, # 处理每个项目的函数
queue=q, # 👈 从此队列流式加载数据
output_dir="fast_data", # 存储优化数据的位置
num_workers=2,
chunk_size=100,
mode="overwrite",
)
producer.join()
📌 注意:使用队列来优化数据集会影响优化时间,而不是流式加载速度。
> 无论工作进程数量如何,您只需要放置一个哨兵值来表示完成。
>
> LitData 会在内部处理它。
import json
from pathlib import Path
import zstandard as zstd
from litdata import optimize, TokensLoader
from tokenizer import Tokenizer
from functools import partial
# 1. 定义一个函数,将 jsonl 文件中的文本转换为 tokens
def tokenize_fn(filepath, tokenizer=None):
with zstd.open(open(filepath, "rb"), "rt", encoding="utf-8") as f:
for row in f:
text = json.loads(row)["text"]
if json.loads(row)["meta"]["redpajama_set_name"] == "RedPajamaGithub":
continue # 排除 GitHub 数据,因为它与 starcoder 重叠
text_ids = tokenizer.encode(text, bos=False, eos=True)
yield text_ids
if __name__ == "__main__":
# 2. 生成输入(我们将优化来自 SlimPajama 数据集的所有压缩 json 文件)
input_dir = "./slimpajama-raw"
inputs = [str(file) for file in Path(f"{input_dir}/SlimPajama-627B/train").rglob("*.zst")]
# 3. 将优化后的数据存储在您想要的任何位置 "/teamspace/datasets" 或 "/teamspace/s3_connections"
outputs = optimize(
fn=partial(tokenize_fn, tokenizer=Tokenizer(f"{input_dir}/checkpoints/Llama-2-7b-hf")), # 注意:您可以使用 HF tokenizer 或任何其他
inputs=inputs,
output_dir="./slimpajama-optimized",
chunk_size=(2049 * 8012),
# 这很重要,用于告知 LitData 我们正在编码连续的 1D 数组(tokens)。
# LitData 会跳过为每个样本存储元数据,例如所有 tokens 连接形成一个大张量。
item_loader=TokensLoader(),
)
import os
from litdata import StreamingDataset, StreamingDataLoader, TokensLoader
from tqdm import tqdm
# 加一,因为我们还需要下一个词
dataset = StreamingDataset(
input_dir=f"./slimpajama-optimized/train",
item_loader=TokensLoader(block_size=2048 + 1),
shuffle=True,
drop_last=True,
)
train_dataloader = StreamingDataLoader(dataset, batch_size=8, pin_memory=True, num_workers=os.cpu_count())
# 迭代 SlimPajama 数据集
for batch in tqdm(train_dataloader):
pass
from litdata import optimize, StreamingDataset
def should_keep(index) -> bool:
# 替换为您自己的逻辑
return index % 2 == 0
def fn(data):
if should_keep(data):
yield data
if __name__ == "__main__":
optimize(
fn=fn,
inputs=list(range(1000)),
output_dir="only_even_index_optimized",
chunk_bytes="64MB",
num_workers=1
)
dataset = StreamingDataset("only_even_index_optimized")
data = list(dataset)
print(data)
# [0, 2, 4, 6, 8, 10, ..., 992, 994, 996, 998]
您甚至可以使用 try/except。
from litdata import optimize, StreamingDataset
def fn(data):
try:
yield 1 / data
except:
pass
if __name__ == "__main__":
optimize(
fn=fn,
inputs=[0, 0, 0, 1, 2, 4, 0],
output_dir="only_defined_ratio_optimized",
chunk_bytes="64MB",
num_workers=1
)
dataset = StreamingDataset("only_defined_ratio_optimized")
data = list(dataset)
# 0 被过滤掉,因为它们会引发除零错误
print(data)
# [1.0, 0.5, 0.25]