litdata 0.2.65__tar.gz → 0.2.67__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {litdata-0.2.65/src/litdata.egg-info → litdata-0.2.67}/PKG-INFO +913 -291
- {litdata-0.2.65 → litdata-0.2.67}/README.md +911 -290
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/__about__.py +1 -1
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/constants.py +3 -2
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/processing/data_processor.py +22 -29
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/processing/functions.py +18 -1
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/processing/utilities.py +0 -73
- litdata-0.2.67/src/litdata/raw/dataset.py +1712 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/raw/indexer.py +63 -19
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/client.py +24 -10
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/downloader.py +46 -27
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/resolver.py +16 -2
- {litdata-0.2.65 → litdata-0.2.67/src/litdata.egg-info}/PKG-INFO +913 -291
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata.egg-info/requires.txt +3 -0
- litdata-0.2.65/src/litdata/raw/dataset.py +0 -223
- {litdata-0.2.65 → litdata-0.2.67}/CONTRIBUTING.md +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/LICENSE +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/MANIFEST.in +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/requirements.txt +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/setup.cfg +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/setup.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/__init__.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/__main__.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/cli/__init__.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/cli/commands.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/cli/handler/__init__.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/cli/handler/cache.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/cli/handler/optimize.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/cli/parser.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/debugger.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/helpers.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/imports.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/processing/__init__.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/processing/readers.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/raw/__init__.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/raw/types.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/requirements.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/__init__.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/async_prefetch.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/cache.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/combined.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/compression.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/config.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/dataloader.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/dataset.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/fs_provider.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/item_loader.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/parallel.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/reader.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/sampler.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/serializers.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/shuffle.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/timing.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/streaming/writer.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/utilities/__init__.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/utilities/_pytree.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/utilities/base.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/utilities/breakpoint.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/utilities/broadcast.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/utilities/dataset_utilities.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/utilities/encryption.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/utilities/env.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/utilities/format.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/utilities/hf_dataset.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/utilities/packing.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/utilities/parquet.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/utilities/shuffle.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/utilities/subsample.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/utilities/torch_utils.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata/utilities/train_test_split.py +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata.egg-info/SOURCES.txt +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata.egg-info/dependency_links.txt +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata.egg-info/entry_points.txt +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata.egg-info/not-zip-safe +0 -0
- {litdata-0.2.65 → litdata-0.2.67}/src/litdata.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: litdata
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.67
|
|
4
4
|
Summary: The Deep Learning framework to train, deploy, and ship AI products Lightning fast.
|
|
5
5
|
Home-page: https://github.com/Lightning-AI/litdata
|
|
6
6
|
Download-URL: https://github.com/Lightning-AI/litdata
|
|
@@ -44,6 +44,7 @@ Requires-Dist: pillow; extra == "extras"
|
|
|
44
44
|
Requires-Dist: polars; extra == "extras"
|
|
45
45
|
Requires-Dist: pyarrow<25.0.0; extra == "extras"
|
|
46
46
|
Requires-Dist: tqdm; extra == "extras"
|
|
47
|
+
Requires-Dist: uvloop; sys_platform != "win32" and extra == "extras"
|
|
47
48
|
Requires-Dist: viztracer; extra == "extras"
|
|
48
49
|
Dynamic: author
|
|
49
50
|
Dynamic: author-email
|
|
@@ -71,12 +72,13 @@ Dynamic: summary
|
|
|
71
72
|
|
|
72
73
|
|
|
73
74
|
<pre>
|
|
74
|
-
Transform Optimize
|
|
75
|
+
Transform Optimize / Stream
|
|
75
76
|
|
|
76
|
-
✅ Parallelize data processing ✅ Stream
|
|
77
|
-
✅ Create vector embeddings ✅
|
|
78
|
-
✅ Run distributed inference ✅
|
|
79
|
-
✅ Scrape websites at scale ✅
|
|
77
|
+
✅ Parallelize data processing ✅ Stream raw files with no prep
|
|
78
|
+
✅ Create vector embeddings ✅ Stream large cloud datasets
|
|
79
|
+
✅ Run distributed inference ✅ Accelerate training by 20x
|
|
80
|
+
✅ Scrape websites at scale ✅ Pause and resume data streaming
|
|
81
|
+
✅ Use remote data without local loading
|
|
80
82
|
</pre>
|
|
81
83
|
|
|
82
84
|
---
|
|
@@ -92,6 +94,8 @@ Transform Optimize
|
|
|
92
94
|
<a href="#speed-up-model-training">Optimize data</a> •
|
|
93
95
|
<a href="#transform-datasets">Transform data</a> •
|
|
94
96
|
<a href="#key-features">Features</a> •
|
|
97
|
+
<a href="#stream-raw">Stream raw files</a> •
|
|
98
|
+
<a href="#resolve-paths">Paths & cloud URLs</a> •
|
|
95
99
|
<a href="#benchmarks">Benchmarks</a> •
|
|
96
100
|
<a href="#start-from-a-template">Templates</a> •
|
|
97
101
|
<a href="#community">Community</a>
|
|
@@ -145,6 +149,21 @@ Install all the extras
|
|
|
145
149
|
pip install 'litdata[extras]'
|
|
146
150
|
```
|
|
147
151
|
|
|
152
|
+
On Linux/macOS, `[extras]` includes optional `uvloop` for a faster asyncio event loop used by `StreamingRawDataset` (stdlib asyncio is the fallback when it is not installed).
|
|
153
|
+
|
|
154
|
+
</details>
|
|
155
|
+
|
|
156
|
+
<details>
|
|
157
|
+
<summary>AI agent skill (Cursor, Claude Code, …)</summary>
|
|
158
|
+
|
|
159
|
+
Install the LitData expert skill so coding agents know the full API, path resolver, optimize/stream recipes, and internals:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npx skills add Lightning-AI/litData
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Source: [`.claude/skills/litdata/`](.claude/skills/litdata/) in this repository ([skills CLI](https://github.com/vercel-labs/skills)).
|
|
166
|
+
|
|
148
167
|
</details>
|
|
149
168
|
|
|
150
169
|
|
|
@@ -154,29 +173,38 @@ pip install 'litdata[extras]'
|
|
|
154
173
|
# Speed up model training
|
|
155
174
|
Stream datasets directly from cloud storage without local downloads. Choose the approach that fits your workflow:
|
|
156
175
|
|
|
157
|
-
## Option 1:
|
|
158
|
-
|
|
176
|
+
## Option 1: Stream existing files as-is ⚡⚡ — `StreamingRawDataset`
|
|
177
|
+
|
|
178
|
+
**No optimize step.** Point LitData at a folder of images, audio, text, or any files (local or cloud) and train with a normal PyTorch `DataLoader`. Downloads are **fully asynchronous** and **batched**; cloud clients include **built-in retries**. You receive **raw `bytes`** — decode, parse, or transform however you want.
|
|
179
|
+
|
|
180
|
+
Details → [Stream raw files](#stream-raw).
|
|
159
181
|
|
|
160
182
|
```python
|
|
161
183
|
from litdata import StreamingRawDataset
|
|
162
184
|
from torch.utils.data import DataLoader
|
|
185
|
+
from PIL import Image
|
|
186
|
+
import io
|
|
163
187
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
188
|
+
dataset = StreamingRawDataset(
|
|
189
|
+
"s3://my-bucket/raw-images/", # or gs://, azure://, /teamspace/s3_connections/..., local path
|
|
190
|
+
transform=lambda b: Image.open(io.BytesIO(b)).convert("RGB"), # optional — default is raw bytes
|
|
191
|
+
)
|
|
192
|
+
loader = DataLoader(dataset, batch_size=32, num_workers=8)
|
|
167
193
|
|
|
168
|
-
for batch in
|
|
169
|
-
|
|
170
|
-
pass
|
|
194
|
+
for batch in loader:
|
|
195
|
+
train_step(batch)
|
|
171
196
|
```
|
|
172
197
|
|
|
173
198
|
**Key benefits:**
|
|
174
199
|
|
|
175
|
-
✅ **
|
|
176
|
-
✅ **
|
|
177
|
-
✅ **
|
|
178
|
-
✅ **
|
|
179
|
-
✅ **Cloud-native:**
|
|
200
|
+
✅ **Zero preprocess:** No chunking job — use the files you already have.
|
|
201
|
+
✅ **Raw bytes, your rules:** Each sample is file `bytes`; decode with PIL, torchaudio, json, or any custom logic (`transform=` optional).
|
|
202
|
+
✅ **Fully async + batched:** Concurrent downloads via `asyncio` / `__getitems__` (not one-file-at-a-time).
|
|
203
|
+
✅ **Built-in retries:** Cloud downloads retry transient failures (adaptive client retries).
|
|
204
|
+
✅ **Cloud-native:** S3 / GCS / Azure / Studio connections; same path resolver as optimized streaming.
|
|
205
|
+
✅ **Grouped samples:** Override `setup()` to yield image+mask, audio+transcript, etc.
|
|
206
|
+
✅ **Indexed once:** `index.json.zstd` cached locally and on the bucket for fast restarts.
|
|
207
|
+
✅ **Upgrade path:** When I/O becomes the bottleneck, `optimize` → `StreamingDataset` for max throughput.
|
|
180
208
|
|
|
181
209
|
## Option 2: Optimize for maximum performance ⚡⚡⚡
|
|
182
210
|
Accelerate model training (20x faster) by optimizing datasets for streaming directly from cloud storage. Work with remote data without local downloads with features like loading data subsets, accessing individual samples, and resumable streaming.
|
|
@@ -187,31 +215,33 @@ Transform raw data into optimized chunks for maximum streaming speed.
|
|
|
187
215
|
This step formats the dataset for fast loading by writing data in an efficient chunked binary format.
|
|
188
216
|
|
|
189
217
|
```python
|
|
218
|
+
import io
|
|
190
219
|
import numpy as np
|
|
191
220
|
from PIL import Image
|
|
192
221
|
import litdata as ld
|
|
193
222
|
|
|
194
223
|
def random_images(index):
|
|
195
|
-
# Replace with your actual image loading
|
|
196
|
-
#
|
|
197
|
-
#
|
|
198
|
-
|
|
224
|
+
# Replace with your actual image loading (e.g. Image.open("photo.jpg")).
|
|
225
|
+
# Prefer JPEG: return a JpegImageFile, or re-encode at quality≈95. Plain
|
|
226
|
+
# Image.fromarray(...) stores uncompressed PIL RAW and can be 10×+ larger.
|
|
227
|
+
img = Image.fromarray(np.random.randint(0, 256, (32, 32, 3), dtype=np.uint8))
|
|
228
|
+
buf = io.BytesIO()
|
|
229
|
+
img.convert("RGB").save(buf, format="JPEG", quality=95)
|
|
230
|
+
buf.seek(0)
|
|
231
|
+
jpeg_image = Image.open(buf) # JpegImageFile → compressed bytes in the chunk
|
|
199
232
|
fake_labels = np.random.randint(10)
|
|
200
233
|
|
|
201
|
-
#
|
|
202
|
-
|
|
203
|
-
data = {"index": index, "image": fake_images, "class": fake_labels}
|
|
204
|
-
|
|
205
|
-
return data
|
|
234
|
+
# Keys/types must stay stable across samples; list lengths/types fixed
|
|
235
|
+
return {"index": index, "image": jpeg_image, "class": fake_labels}
|
|
206
236
|
|
|
207
237
|
if __name__ == "__main__":
|
|
208
|
-
#
|
|
238
|
+
# Exactly one of chunk_bytes or chunk_size
|
|
209
239
|
ld.optimize(
|
|
210
240
|
fn=random_images, # the function applied to each input
|
|
211
241
|
inputs=list(range(1000)), # the inputs to the function (here it's a list of numbers)
|
|
212
242
|
output_dir="fast_data", # optimized data is stored here
|
|
213
243
|
num_workers=4, # the number of workers on the same machine
|
|
214
|
-
chunk_bytes="64MB" #
|
|
244
|
+
chunk_bytes="64MB" # default; see FAQ for larger samples
|
|
215
245
|
)
|
|
216
246
|
```
|
|
217
247
|
|
|
@@ -229,7 +259,12 @@ Load the data by replacing the PyTorch Dataset and DataLoader with the Streaming
|
|
|
229
259
|
```python
|
|
230
260
|
import litdata as ld
|
|
231
261
|
|
|
232
|
-
dataset = ld.StreamingDataset(
|
|
262
|
+
dataset = ld.StreamingDataset(
|
|
263
|
+
's3://my-bucket/fast_data',
|
|
264
|
+
shuffle=True,
|
|
265
|
+
drop_last=True, # important for multi-GPU so every rank sees the same length
|
|
266
|
+
seed=42,
|
|
267
|
+
)
|
|
233
268
|
|
|
234
269
|
# Custom collate function to handle the batch (optional)
|
|
235
270
|
def collate_fn(batch):
|
|
@@ -239,7 +274,7 @@ def collate_fn(batch):
|
|
|
239
274
|
}
|
|
240
275
|
|
|
241
276
|
|
|
242
|
-
dataloader = ld.StreamingDataLoader(dataset, collate_fn=collate_fn)
|
|
277
|
+
dataloader = ld.StreamingDataLoader(dataset, batch_size=64, collate_fn=collate_fn)
|
|
243
278
|
for sample in dataloader:
|
|
244
279
|
img, cls = sample["image"], sample["class"]
|
|
245
280
|
```
|
|
@@ -304,79 +339,158 @@ ld.map(
|
|
|
304
339
|
## Features for optimizing and streaming datasets for model training
|
|
305
340
|
|
|
306
341
|
<details>
|
|
307
|
-
<summary> ✅ Stream raw
|
|
342
|
+
<summary> ✅ Stream raw files as-is (no optimize) — StreamingRawDataset <a id="stream-raw" href="#stream-raw">🔗</a> </summary>
|
|
308
343
|
|
|
309
344
|
|
|
310
|
-
|
|
345
|
+
`StreamingRawDataset` streams **your existing files** from local disk or cloud storage with **no conversion step**. It is a map-style `torch.utils.data.Dataset`: use a standard PyTorch `DataLoader` (not `StreamingDataLoader`).
|
|
311
346
|
|
|
312
|
-
**
|
|
347
|
+
**You get raw `bytes`.** LitData does not impose a sample schema — open images with PIL, parse JSONL, decode audio, run your own tokenizer, or pass a `transform=` if you prefer. Grouped items yield `list[bytes]` (e.g. image + mask).
|
|
313
348
|
|
|
314
|
-
|
|
349
|
+
Downloads are **fully asynchronous** and **batched**: when the DataLoader requests a batch, `__getitems__` fetches those files concurrently with `asyncio.gather`. Cloud clients include **built-in retries** for transient network errors.
|
|
315
350
|
|
|
316
|
-
|
|
317
|
-
# for aws s3
|
|
318
|
-
pip install "litdata[extra]" s3fs
|
|
351
|
+
Use it when you want to train or prototype on JPEGs, masks, audio, JSONL, etc. **immediately**. Switch to [`optimize` → `StreamingDataset`](#speed-up-model-training) later if you need maximum cloud training throughput.
|
|
319
352
|
|
|
320
|
-
|
|
321
|
-
|
|
353
|
+
| | `StreamingRawDataset` | `StreamingDataset` (optimized) |
|
|
354
|
+
|--|----------------------|--------------------------------|
|
|
355
|
+
| Prep | None — point at a folder | One-time `optimize` → `chunk-*.bin` + `index.json` |
|
|
356
|
+
| Item | **Raw file `bytes`** (you decide how to decode) | Deserialized samples (dict/tensor/…) |
|
|
357
|
+
| I/O | Fully async, batched downloads + retries | Chunk prefetch / cache pipeline |
|
|
358
|
+
| Loader | `torch.utils.data.DataLoader` | Prefer `StreamingDataLoader` (shuffle, resume) |
|
|
359
|
+
| Best for | Instant start, full control over bytes | Highest sustained training I/O |
|
|
360
|
+
|
|
361
|
+
### Install (cloud)
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
pip install "litdata[extra]" s3fs # Amazon S3
|
|
365
|
+
pip install "litdata[extra]" gcsfs # Google Cloud Storage
|
|
366
|
+
# Azure / Studio connections: see Paths & cloud URLs
|
|
322
367
|
```
|
|
323
368
|
|
|
324
|
-
|
|
369
|
+
### Quick start
|
|
370
|
+
|
|
325
371
|
```python
|
|
326
372
|
from torch.utils.data import DataLoader
|
|
327
373
|
from litdata import StreamingRawDataset
|
|
374
|
+
from PIL import Image
|
|
375
|
+
import io
|
|
328
376
|
|
|
329
|
-
|
|
377
|
+
def to_image(data: bytes):
|
|
378
|
+
return Image.open(io.BytesIO(data)).convert("RGB")
|
|
379
|
+
|
|
380
|
+
dataset = StreamingRawDataset(
|
|
381
|
+
"s3://my-bucket/images/", # also: gs://, azure://, /teamspace/s3_connections/..., local path
|
|
382
|
+
transform=to_image, # optional; default yields raw bytes
|
|
383
|
+
storage_options={}, # optional cloud credentials / endpoint
|
|
384
|
+
)
|
|
385
|
+
loader = DataLoader(dataset, batch_size=32, num_workers=8)
|
|
330
386
|
|
|
331
|
-
# Use with PyTorch DataLoader
|
|
332
|
-
loader = DataLoader(dataset, batch_size=32)
|
|
333
387
|
for batch in loader:
|
|
334
|
-
|
|
335
|
-
pass
|
|
388
|
+
train_step(batch)
|
|
336
389
|
```
|
|
337
390
|
|
|
338
|
-
|
|
391
|
+
### Constructor knobs
|
|
339
392
|
|
|
393
|
+
| Arg | Default | Purpose |
|
|
394
|
+
|-----|---------|---------|
|
|
395
|
+
| `input_dir` | required | Folder URL/path (same [resolver](#resolve-paths) as optimized streaming) |
|
|
396
|
+
| `cache_dir` | LitData default cache | Where the file index (and optional file cache) live |
|
|
397
|
+
| `cache_files` | `False` | If `True`, keep downloaded files on disk under `cache_dir` (mirror remote layout) |
|
|
398
|
+
| `recompute_index` | `False` | Force re-scan when remote files changed |
|
|
399
|
+
| `transform` | `None` | `fn(bytes) -> Any` or `fn(list[bytes]) -> Any` for grouped items |
|
|
400
|
+
| `storage_options` | `{}` | Cloud client options |
|
|
401
|
+
| `indexer` | `FileIndexer()` | Custom discovery (subclass `BaseIndexer`) |
|
|
402
|
+
| `max_concurrent_downloads` | `None` (adaptive) | Per-worker in-flight downloads. `None` = size-aware budget (bandwidth; Little’s-law only for medians <~8 MiB) split across workers; single-process capped at 128. An explicit `int` is used exactly (no silent clamp) |
|
|
403
|
+
| `max_prefetch` | `16` | Per-worker sequential look-ahead after each batch (default on). When `num_workers > 1`, effective look-ahead is `min(max_prefetch, 64 // num_workers)` so aggregate stays ~64 items. Pass `0` to disable |
|
|
404
|
+
| `prefetch_cache_size` | auto | LRU cap for prefetched items (defaults from `max_prefetch`) |
|
|
405
|
+
| `hedge_delay` | `0` | Seconds before a hedged duplicate GET for a slow download (`0` = off, default; opt-in) |
|
|
406
|
+
| `range_parallel_threshold` | `0` | Objects ≥ this many bytes use parallel ranged GETs (`0` = whole-object only; opt-in) |
|
|
407
|
+
| `item_type` | `"bytes"` | `"bytes"` buffers in RAM; `"path"` returns local cache paths (`cache_files=True` required) |
|
|
340
408
|
|
|
341
|
-
|
|
409
|
+
### Group related files (`setup`)
|
|
410
|
+
|
|
411
|
+
Default: **one file = one sample**. Override `setup` to filter or group (image + mask, audio + transcript, …). Return either a list of `FileMetadata` or a list of groups (`list[list[FileMetadata]]`).
|
|
342
412
|
|
|
343
413
|
```python
|
|
344
|
-
from
|
|
414
|
+
from collections import defaultdict
|
|
345
415
|
from torch.utils.data import DataLoader
|
|
346
416
|
from litdata import StreamingRawDataset
|
|
347
417
|
from litdata.raw.indexer import FileMetadata
|
|
348
418
|
|
|
349
419
|
class SegmentationRawDataset(StreamingRawDataset):
|
|
350
|
-
def setup(self, files: list[FileMetadata]) ->
|
|
351
|
-
#
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
420
|
+
def setup(self, files: list[FileMetadata]) -> list[list[FileMetadata]]:
|
|
421
|
+
# Pair img_001.jpg with img_001.png (mask) by stem
|
|
422
|
+
by_stem: dict[str, dict[str, FileMetadata]] = defaultdict(dict)
|
|
423
|
+
for f in files:
|
|
424
|
+
name = f.path.rsplit("/", 1)[-1]
|
|
425
|
+
stem, _, ext = name.rpartition(".")
|
|
426
|
+
by_stem[stem][ext.lower()] = f
|
|
427
|
+
items = []
|
|
428
|
+
for stem, parts in sorted(by_stem.items()):
|
|
429
|
+
if "jpg" in parts and "png" in parts:
|
|
430
|
+
items.append([parts["jpg"], parts["png"]])
|
|
431
|
+
return items
|
|
432
|
+
|
|
433
|
+
dataset = SegmentationRawDataset(
|
|
434
|
+
"s3://bucket/seg/",
|
|
435
|
+
transform=lambda pair: (pair[0], pair[1]), # list[bytes]: [image, mask]
|
|
436
|
+
)
|
|
437
|
+
loader = DataLoader(dataset, batch_size=16, num_workers=4)
|
|
438
|
+
for images, masks in loader:
|
|
439
|
+
...
|
|
364
440
|
```
|
|
365
441
|
|
|
366
|
-
|
|
442
|
+
### Index caching (`index.json.zstd`)
|
|
367
443
|
|
|
368
|
-
|
|
444
|
+
First open scans the tree and writes a compressed file list:
|
|
369
445
|
|
|
370
|
-
|
|
371
|
-
- **
|
|
372
|
-
- **Remote:** Automatically saved to cloud storage (e.g., `s3://bucket/files/index.json.zstd`) for reuse
|
|
446
|
+
- **Local cache** under your LitData cache dir (fast restart on the same machine)
|
|
447
|
+
- **Remote copy** next to the data when possible (e.g. `s3://bucket/files/index.json.zstd`) so every machine skips the scan
|
|
373
448
|
|
|
374
|
-
**Force Rebuild:**
|
|
375
449
|
```python
|
|
376
|
-
#
|
|
450
|
+
# After adding/removing files on the bucket:
|
|
377
451
|
dataset = StreamingRawDataset("s3://bucket/files/", recompute_index=True)
|
|
378
452
|
```
|
|
379
453
|
|
|
454
|
+
Do **not** confuse this with optimized LitData’s `index.json` (chunk metadata). Raw indexing only lists files.
|
|
455
|
+
|
|
456
|
+
### How downloads work
|
|
457
|
+
|
|
458
|
+
1. DataLoader asks for a batch of indices → `__getitems__`.
|
|
459
|
+
2. LitData **asynchronously** downloads those files **in parallel** (`asyncio.gather` + `adownload_fileobj`).
|
|
460
|
+
3. Cloud SDKs apply **retries** on transient failures (e.g. S3 adaptive retries).
|
|
461
|
+
4. Each item is returned as **`bytes`** (or `list[bytes]` if `setup` grouped files), then optional `transform`.
|
|
462
|
+
|
|
463
|
+
Your training loop stays normal PyTorch — no async/`await` in user code.
|
|
464
|
+
|
|
465
|
+
```python
|
|
466
|
+
# Default: you own the bytes
|
|
467
|
+
dataset = StreamingRawDataset("s3://bucket/files/")
|
|
468
|
+
raw: bytes = dataset[0]
|
|
469
|
+
# e.g. Image.open(io.BytesIO(raw)), json.loads(raw), np.frombuffer(raw), ...
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### Tips
|
|
473
|
+
|
|
474
|
+
- Prefer `num_workers > 0` so worker processes overlap async batch downloads with training. Scale workers toward host vCPUs for network-bound JPEG-sized objects — avoid saturating every vCPU.
|
|
475
|
+
- On Linux, after any parent-process dataset I/O, use `DataLoader(..., multiprocessing_context="spawn", persistent_workers=True)` — default `fork` can hang S3 clients in workers.
|
|
476
|
+
- Default `max_prefetch=16` enables sequential look-ahead **per DataLoader worker**; shuffled access disables it. Pass `0` to turn off. When `num_workers > 1`, look-ahead and download concurrency both scale down with worker count so aggregate in-flight work stays bounded.
|
|
477
|
+
- Prefer an `s3://` / `gs://` URL or `/teamspace/s3_connections/...` so LitData hits the bucket directly ([resolver](#resolve-paths)) — avoid reading through FUSE.
|
|
478
|
+
- Leave `range_parallel_threshold=0` (default) for typical JPEGs; raise it only for large objects where parallel ranged GETs help.
|
|
479
|
+
- Best for medium/large files. Tiny objects (≲100 KB) are request-overhead bound — pack with [`optimize`](#speed-up-model-training) → `StreamingDataset` when I/O plateaus.
|
|
480
|
+
|
|
481
|
+
### Throughput
|
|
482
|
+
|
|
483
|
+
On ImageNet val raw over S3 (50 k JPEGs, batch size 64, spawn workers), throughput gains are clearest at **low worker counts / notebooks** (**+20–80%** at ≤8 workers). At **high workers** (≥16), results are roughly **parity within run-to-run noise**.
|
|
484
|
+
|
|
485
|
+
| workers | before | after | Δ |
|
|
486
|
+
|--------:|-------:|------:|--:|
|
|
487
|
+
| 0 | 543 | 735 | **+35%** |
|
|
488
|
+
| 2 | 816 | 1475 | **+81%** |
|
|
489
|
+
| 8 | 4841 | 5718 | **+18%** |
|
|
490
|
+
| 16+ | ~6k | ~6k | ~parity |
|
|
491
|
+
|
|
492
|
+
Useful knobs: `num_workers`, `max_prefetch` (default 16; worker-aware), `download_timeout` (batch-level hang protection). Ranged parallel downloads stay opt-in (`range_parallel_threshold=0`).
|
|
493
|
+
|
|
380
494
|
</details>
|
|
381
495
|
|
|
382
496
|
<details>
|
|
@@ -412,10 +526,6 @@ storage_options = {
|
|
|
412
526
|
"aws_secret_access_key": "your_secret_access_key",
|
|
413
527
|
}
|
|
414
528
|
|
|
415
|
-
dataset = StreamingDataset('s3://my-bucket/my-data', storage_options=storage_options)
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
529
|
dataset = StreamingDataset('s3://my-bucket/my-data', storage_options=storage_options)
|
|
420
530
|
```
|
|
421
531
|
|
|
@@ -427,108 +537,198 @@ from litdata import StreamingDataset
|
|
|
427
537
|
dataset = StreamingDataset('s3://my-bucket/my-data', cache_dir="/path/to/cache")
|
|
428
538
|
```
|
|
429
539
|
|
|
540
|
+
Any local path, `s3://` / `gs://` / `r2://` / `azure://` / `hf://`, `local:` network drive, or Lightning `/teamspace/...` connection works — see [Resolve any path or cloud URL](#resolve-paths).
|
|
541
|
+
|
|
430
542
|
</details>
|
|
431
543
|
|
|
432
544
|
<details>
|
|
433
|
-
<summary> ✅
|
|
434
|
-
|
|
545
|
+
<summary> ✅ Optimize images as JPEG (not raw PIL) <a id="optimize-jpeg" href="#optimize-jpeg">🔗</a> </summary>
|
|
435
546
|
|
|
436
547
|
|
|
437
|
-
|
|
548
|
+
How you return images from `optimize` controls storage size and streaming speed.
|
|
438
549
|
|
|
439
|
-
|
|
440
|
-
|
|
550
|
+
| What you return | Serializer | Result |
|
|
551
|
+
|-----------------|------------|--------|
|
|
552
|
+
| `PIL.JpegImageFile` (e.g. `Image.open("x.jpg")`) | JPEG | Compressed bytes — **preferred** |
|
|
553
|
+
| Plain `PIL.Image` / `Image.fromarray(...)` | PIL RAW | Uncompressed pixels — often **10×+ larger** |
|
|
441
554
|
|
|
442
|
-
|
|
555
|
+
**Best practice:** store JPEG at **quality ≈ 95** (or keep existing `.jpg` files). Resize when helpful.
|
|
556
|
+
|
|
557
|
+
```python
|
|
558
|
+
import io
|
|
559
|
+
from PIL import Image
|
|
560
|
+
import litdata as ld
|
|
561
|
+
|
|
562
|
+
def load_image(path):
|
|
563
|
+
img = Image.open(path)
|
|
564
|
+
if not str(path).lower().endswith((".jpg", ".jpeg")):
|
|
565
|
+
buf = io.BytesIO()
|
|
566
|
+
img.convert("RGB").save(buf, format="JPEG", quality=95)
|
|
567
|
+
buf.seek(0)
|
|
568
|
+
img = Image.open(buf) # JpegImageFile
|
|
569
|
+
return {"image": img, "path": path}
|
|
570
|
+
|
|
571
|
+
if __name__ == "__main__":
|
|
572
|
+
ld.optimize(fn=load_image, inputs=list_of_paths, output_dir="fast_data", chunk_bytes="64MB", num_workers=8)
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
Ready-made ImageNet optimize/stream scripts: `benchmarks/litdata/` (`--write_mode jpeg --quality 90`).
|
|
443
576
|
|
|
444
577
|
</details>
|
|
445
578
|
|
|
446
|
-
|
|
579
|
+
<details>
|
|
580
|
+
<summary> ✅ Custom serializers <a id="serializers" href="#serializers">🔗</a> </summary>
|
|
581
|
+
|
|
447
582
|
|
|
448
|
-
|
|
449
|
-
```sh
|
|
450
|
-
pip install "litdata[extra]" huggingface_hub
|
|
583
|
+
LitData serializes each leaf of your sample with a pluggable registry. Built-ins (tried in order) include: `str`, `bool`, `int`, `float`, `video`, `tifffile`, `pil`, `jpeg`, `jpeg_array`, `bytes`, `numpy` / `tensor` (and no-header variants), and `pickle` (fallback).
|
|
451
584
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
585
|
+
For images, returning a `JpegImageFile` selects **`jpeg`**; a plain `PIL.Image` selects **`pil`** (raw pixels). See [Optimize images as JPEG](#optimize-jpeg).
|
|
586
|
+
|
|
587
|
+
Pass custom serializers when **streaming** (and when using the lower-level `Cache` writer):
|
|
588
|
+
|
|
589
|
+
```python
|
|
590
|
+
from litdata import StreamingDataset
|
|
591
|
+
from litdata.streaming.serializers import Serializer
|
|
592
|
+
|
|
593
|
+
class MyTypeSerializer(Serializer):
|
|
594
|
+
def serialize(self, item):
|
|
595
|
+
return item.to_bytes(), None # (bytes, optional metadata string)
|
|
596
|
+
|
|
597
|
+
def deserialize(self, data: bytes):
|
|
598
|
+
return MyType.from_bytes(data)
|
|
599
|
+
|
|
600
|
+
def can_serialize(self, item) -> bool:
|
|
601
|
+
return isinstance(item, MyType)
|
|
602
|
+
|
|
603
|
+
dataset = StreamingDataset(
|
|
604
|
+
"s3://bucket/data",
|
|
605
|
+
serializers={"my_type": MyTypeSerializer()}, # merged on top of built-ins
|
|
606
|
+
)
|
|
455
607
|
```
|
|
456
608
|
|
|
457
|
-
|
|
609
|
+
Keys you pass are tried before the defaults (so they win over `pickle`). `optimize()` uses the built-in registry based on the Python types your `fn` returns — prefer JPEG / numpy / tensor leaves for best results.
|
|
610
|
+
|
|
611
|
+
</details>
|
|
612
|
+
|
|
613
|
+
<details>
|
|
614
|
+
<summary> ✅ Stream MosaicML MDS datasets <a id="stream-mds" href="#stream-mds">🔗</a> </summary>
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
If you already have datasets written in [MosaicML Streaming](https://github.com/mosaicml/streaming) MDS (Mosaic Data Shard) format, you can stream them directly with LitData—no re-optimization or conversion required!
|
|
618
|
+
|
|
619
|
+
LitData's default `PyTreeLoader` natively understands the MDS binary layout, so you can read existing MDS shards using the familiar `StreamingDataset` and `StreamingDataLoader` APIs.
|
|
620
|
+
|
|
621
|
+
**Assumption:**
|
|
622
|
+
|
|
623
|
+
Your dataset directory contains MDS shard files (e.g. `shard.00000.mds`, ...) along with an `index.json` describing the shards and their `column_sizes`/`column_names`.
|
|
624
|
+
|
|
625
|
+
**Stream the MDS dataset:**
|
|
458
626
|
|
|
459
627
|
```python
|
|
460
628
|
import litdata as ld
|
|
461
629
|
|
|
462
|
-
#
|
|
463
|
-
|
|
630
|
+
# point to your MDS dataset stored locally or in the cloud
|
|
631
|
+
|
|
632
|
+
mds_dataset_uri = "s3://my-bucket/my-mds-data" # or a local path
|
|
464
633
|
|
|
465
|
-
#
|
|
466
|
-
|
|
634
|
+
# LitData automatically detects and deserializes the MDS format
|
|
635
|
+
|
|
636
|
+
dataset = ld.StreamingDataset(mds_dataset_uri)
|
|
467
637
|
|
|
468
|
-
# Print the first sample
|
|
469
638
|
print("Sample", dataset[0])
|
|
470
639
|
|
|
471
|
-
# Stream the dataset using StreamingDataLoader
|
|
472
640
|
dataloader = ld.StreamingDataLoader(dataset, batch_size=4)
|
|
473
641
|
for sample in dataloader:
|
|
474
|
-
|
|
642
|
+
pass
|
|
475
643
|
```
|
|
476
644
|
|
|
477
|
-
|
|
645
|
+
**How it works:**
|
|
646
|
+
|
|
647
|
+
- LitData reads the `format` field from the dataset config. When it's set to `"mds"`, the item loader uses MDS-aware deserialization (`mds_deserialize`) that respects the per-column sizes stored in each shard.
|
|
648
|
+
- Fixed-size columns are read directly, while variable-size columns are prefixed with a `uint32` length header—exactly as in the MosaicML MDS spec.
|
|
649
|
+
- Each sample is reconstructed into its original Python structure via LitData's `data_spec`.
|
|
650
|
+
|
|
651
|
+
**Key benefits:**
|
|
652
|
+
|
|
653
|
+
✅ **Zero conversion:** Reuse existing MDS shards as-is.
|
|
654
|
+
✅ **Drop-in APIs:** Use the same `StreamingDataset` / `StreamingDataLoader` you already know.
|
|
655
|
+
✅ **Cloud-native:** Stream MDS shards directly from S3, GCS, or Azure.
|
|
656
|
+
✅ **Easy migration:** Move from MosaicML Streaming to LitData without re-optimizing.
|
|
478
657
|
|
|
479
|
-
|
|
658
|
+
> **Note:** Encrypted data loading is not currently supported for the MDS format.
|
|
659
|
+
|
|
660
|
+
</details>
|
|
661
|
+
|
|
662
|
+
<details>
|
|
663
|
+
<summary> ✅ Stream Hugging Face 🤗 datasets <a id="stream-hf" href="#stream-hf">🔗</a> </summary>
|
|
480
664
|
|
|
481
665
|
|
|
482
666
|
|
|
483
|
-
|
|
667
|
+
To use your favorite Hugging Face dataset with LitData, simply pass its URL to `StreamingDataset`.
|
|
484
668
|
|
|
485
|
-
|
|
669
|
+
<details>
|
|
670
|
+
<summary>How to get HF dataset URI?</summary>
|
|
486
671
|
|
|
487
|
-
|
|
488
|
-
import litdata as ld
|
|
672
|
+
https://github.com/user-attachments/assets/3ba9e2ef-bf6b-41fc-a578-e4b4113a0e72
|
|
489
673
|
|
|
490
|
-
|
|
674
|
+
</details>
|
|
675
|
+
|
|
676
|
+
**Prerequisites:**
|
|
491
677
|
|
|
492
|
-
|
|
678
|
+
```sh
|
|
679
|
+
pip install 'litdata[extras]' huggingface_hub
|
|
680
|
+
|
|
681
|
+
# Optional: faster downloads on high-bandwidth networks
|
|
682
|
+
pip install hf_transfer
|
|
683
|
+
export HF_HUB_ENABLE_HF_TRANSFER=1
|
|
493
684
|
```
|
|
494
685
|
|
|
495
|
-
|
|
686
|
+
**Supported for HF:** datasets stored as **Parquet** only. Gated datasets: set `HF_TOKEN`.
|
|
496
687
|
|
|
497
|
-
|
|
688
|
+
**Stream Hugging Face dataset** (auto-index + auto `ParquetLoader`):
|
|
498
689
|
|
|
499
|
-
|
|
690
|
+
```python
|
|
691
|
+
import litdata as ld
|
|
500
692
|
|
|
501
|
-
|
|
693
|
+
hf_dataset_uri = "hf://datasets/leonardPKU/clevr_cogen_a_train/data"
|
|
502
694
|
|
|
503
|
-
|
|
695
|
+
dataset = ld.StreamingDataset(hf_dataset_uri) # indexes on first use; caches index.json locally
|
|
696
|
+
print("Sample", dataset[0]) # dict of columns
|
|
504
697
|
|
|
505
|
-
|
|
698
|
+
# With workers on Linux, use spawn (same as other ParquetLoader usage)
|
|
699
|
+
dataloader = ld.StreamingDataLoader(
|
|
700
|
+
dataset, batch_size=4, num_workers=4, multiprocessing_context="spawn"
|
|
701
|
+
)
|
|
702
|
+
for sample in dataloader:
|
|
703
|
+
pass
|
|
704
|
+
```
|
|
506
705
|
|
|
507
|
-
|
|
706
|
+
Unlike local/S3 parquet ([stream parquet](#stream-parquet)), `hf://` **automatically** indexes (if needed) and selects `ParquetLoader`.
|
|
707
|
+
|
|
708
|
+
### Indexing the HF dataset (optional, faster cold start)
|
|
508
709
|
|
|
509
710
|
```python
|
|
510
711
|
import litdata as ld
|
|
511
712
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
ld.index_parquet_dataset(hf_dataset_uri, "hf-index-dir")
|
|
713
|
+
# Returns the local cache directory that contains index.json
|
|
714
|
+
cache_dir = ld.index_hf_dataset("hf://datasets/leonardPKU/clevr_cogen_a_train/data")
|
|
515
715
|
```
|
|
516
716
|
|
|
517
|
-
|
|
717
|
+
Or control the index path explicitly:
|
|
518
718
|
|
|
519
719
|
```python
|
|
520
720
|
import litdata as ld
|
|
521
721
|
from litdata.streaming.item_loader import ParquetLoader
|
|
522
722
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
dataset = ld.StreamingDataset(hf_dataset_uri, item_loader=ParquetLoader(), index_path="hf-index-dir")
|
|
723
|
+
uri = "hf://datasets/open-thoughts/OpenThoughts-114k/data"
|
|
724
|
+
ld.index_parquet_dataset(uri, "hf-index-dir") # writes index under hf-index-dir
|
|
526
725
|
|
|
527
|
-
|
|
528
|
-
|
|
726
|
+
dataset = ld.StreamingDataset(uri, item_loader=ParquetLoader(), index_path="hf-index-dir")
|
|
727
|
+
for batch in ld.StreamingDataLoader(dataset, batch_size=4, multiprocessing_context="spawn"):
|
|
728
|
+
pass
|
|
529
729
|
```
|
|
530
730
|
|
|
531
|
-
|
|
731
|
+
See also [Stream parquet datasets](#stream-parquet) for `ParquetLoader` knobs, wildcards, and stream-vs-optimize.
|
|
532
732
|
|
|
533
733
|
### LitData `Optimize` v/s `Parquet`
|
|
534
734
|
<!-- TODO: Update benchmark -->
|
|
@@ -574,6 +774,110 @@ for batch in val_dataloader:
|
|
|
574
774
|
|
|
575
775
|
</details>
|
|
576
776
|
|
|
777
|
+
<details>
|
|
778
|
+
<summary> ✅ Shuffle, seed, and drop_last <a id="shuffle" href="#shuffle">🔗</a> </summary>
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
Shuffling is **deterministic** and designed for distributed training:
|
|
782
|
+
|
|
783
|
+
1. Chunks are assigned (and possibly split) across ranks/workers.
|
|
784
|
+
2. Items inside each chunk are permuted.
|
|
785
|
+
|
|
786
|
+
The permutation depends on `seed`, the epoch, and chunk metadata — the same settings always yield the same order (required for resumable `state_dict`).
|
|
787
|
+
|
|
788
|
+
```python
|
|
789
|
+
from litdata import StreamingDataset, StreamingDataLoader
|
|
790
|
+
|
|
791
|
+
train = StreamingDataset(
|
|
792
|
+
"s3://my-bucket/train",
|
|
793
|
+
shuffle=True,
|
|
794
|
+
drop_last=True, # keep every rank/worker at the same length (default True under DDP)
|
|
795
|
+
seed=42, # default is 42; keep stable when resuming
|
|
796
|
+
)
|
|
797
|
+
loader = StreamingDataLoader(train, batch_size=64, num_workers=8)
|
|
798
|
+
|
|
799
|
+
# shuffle=/drop_last= on the loader override the dataset
|
|
800
|
+
loader = StreamingDataLoader(train, batch_size=64, shuffle=True, drop_last=True)
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
**Notes**
|
|
804
|
+
|
|
805
|
+
- Val/test: usually `shuffle=False`, `drop_last=False`.
|
|
806
|
+
- If `drop_last=False` under multi-GPU, LitData warns — collectives can hang when ranks see different lengths.
|
|
807
|
+
- Resume with `loader.state_dict()` / `load_state_dict()`. To deliberately ignore checkpointed shuffle settings, set `force_override_state_dict=True` on the dataset.
|
|
808
|
+
|
|
809
|
+
</details>
|
|
810
|
+
|
|
811
|
+
<details>
|
|
812
|
+
<summary> ✅ FAQ: chunk size & shuffle before optimize <a id="faq-chunk-shuffle" href="#faq-chunk-shuffle">🔗</a> </summary>
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
### What `chunk_bytes` should I use?
|
|
816
|
+
|
|
817
|
+
Default is **64MB** — a good starting point for typical small/medium samples.
|
|
818
|
+
|
|
819
|
+
When each datapoint is large (e.g. a few MB), prefer a **larger chunk** (practical range often **256–512MB**) so each chunk holds more samples and **intra-chunk batch randomization** has a bigger pool. Tradeoff: larger chunks take **longer to download** before they can be used.
|
|
820
|
+
|
|
821
|
+
This is expert guidance (recommended-range mindset), not a published chunk-size sweep.
|
|
822
|
+
|
|
823
|
+
### Is StreamingDataset shuffle enough if my source data is ordered?
|
|
824
|
+
|
|
825
|
+
**Not always.** LitData handles **distributed sampling** and **bucket sampling within chunks** automatically (`shuffle=True` randomizes chunk order and item order inside each chunk). That is **not** a substitute for a fully shuffled file-level DataLoader when the source has strong structure (same subject/set contiguous, class blocks, etc.).
|
|
826
|
+
|
|
827
|
+
If ordered data would make chunked sampling problematic and you cannot embed the grouping as the sample unit:
|
|
828
|
+
|
|
829
|
+
- Shuffle the list of samples **before** `optimize` so chunks mix well, **or**
|
|
830
|
+
- Use [`StreamingRawDataset`](#stream-raw) (per-file random access via a standard PyTorch `DataLoader` with `shuffle=True`) instead of optimize → `StreamingDataset`.
|
|
831
|
+
|
|
832
|
+
### FUSE vs LitData (Lightning Studios)
|
|
833
|
+
|
|
834
|
+
`/teamspace/s3_connections` (and related mounts) are **FUSE** — fine for browsing, not for training I/O. Under load they are very slow and can crash. Pass the same path into LitData (`StreamingRawDataset` / `StreamingDataset` / `optimize`): LitData resolves it and talks **directly** to the bucket ([Resolve any path](#resolve-paths)).
|
|
835
|
+
|
|
836
|
+
Rough ImageNet order-of-magnitude on a Studio (not hard guarantees; right tuning for raw): FUSE hand-read ~**600** images/s · [`StreamingRawDataset`](#stream-raw) ~**6–7k** · optimized [`StreamingDataset`](#speed-up-model-training) (64MB chunks) ~**11k**.
|
|
837
|
+
|
|
838
|
+
</details>
|
|
839
|
+
|
|
840
|
+
<details>
|
|
841
|
+
<summary> ✅ StreamingDataset & StreamingDataLoader knobs <a id="streaming-kwargs" href="#streaming-kwargs">🔗</a> </summary>
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
**`StreamingDataset`**
|
|
845
|
+
|
|
846
|
+
| Argument | Default | Description |
|
|
847
|
+
|----------|---------|-------------|
|
|
848
|
+
| `input_dir` | required | Local path, cloud URI, `Dir`, or parquet path (basename wildcards OK) |
|
|
849
|
+
| `cache_dir` | `LITDATA_CACHE_DIR` or `~/.lightning/chunks` | Where chunks are cached |
|
|
850
|
+
| `item_loader` | from index | `TokensLoader`, `ParquetLoader`, … |
|
|
851
|
+
| `shuffle` | `False` | Deterministic shuffle (see [Shuffle](#shuffle)) |
|
|
852
|
+
| `drop_last` | `True` if distributed else `False` | Equal length across ranks |
|
|
853
|
+
| `seed` | `42` | Shuffle / subsample RNG |
|
|
854
|
+
| `serializers` | built-ins | Custom serialize/deserialize map |
|
|
855
|
+
| `max_cache_size` | `"100GB"` | Evict consumed chunks beyond this size |
|
|
856
|
+
| `max_pre_download` | `2` | Chunks each worker may prefetch (raise for throughput; watch disk) |
|
|
857
|
+
| `subsample` | `1.0` | Fraction of data (`0.01`) or upsample (`2.5`) |
|
|
858
|
+
| `encryption` | `None` | `FernetEncryption` / `RSAEncryption` / custom |
|
|
859
|
+
| `storage_options` | `{}` | Cloud client options |
|
|
860
|
+
| `session_options` | `{}` | boto3 session options (S3) |
|
|
861
|
+
| `index_path` | `None` | Parquet/HF `index.json` file or directory |
|
|
862
|
+
| `force_override_state_dict` | `False` | Local ctor args override loaded checkpoint |
|
|
863
|
+
| `transform` | `None` | Callable or list of callables per sample |
|
|
864
|
+
|
|
865
|
+
Peak disk ≈ `num_workers × max_pre_download × mean_chunk_size`.
|
|
866
|
+
|
|
867
|
+
**`StreamingDataLoader`**
|
|
868
|
+
|
|
869
|
+
| Argument | Description |
|
|
870
|
+
|----------|-------------|
|
|
871
|
+
| All usual `torch.utils.data.DataLoader` kwargs | `batch_size`, `num_workers`, `collate_fn`, `pin_memory`, … |
|
|
872
|
+
| `shuffle` / `drop_last` | Forwarded to the streaming dataset |
|
|
873
|
+
| `profile_batches` | `int` / `True` / `False` — viztracer worker trace (see [Profile data loading](#profile-loading)) |
|
|
874
|
+
| `profile_skip_batches` / `profile_dir` | Warm-up skip count; output dir for `result.json` |
|
|
875
|
+
| `multiprocessing_context` | Use **`"spawn"`** (or `"forkserver"`) with `ParquetLoader` + `num_workers>0` on Linux |
|
|
876
|
+
|
|
877
|
+
Prefer `StreamingDataLoader` over a plain PyTorch `DataLoader` for optimized / combined / parallel datasets (resume + correct batch metadata).
|
|
878
|
+
|
|
879
|
+
</details>
|
|
880
|
+
|
|
577
881
|
<details>
|
|
578
882
|
<summary> ✅ Stream from multiple cloud providers <a id="cloud-providers" href="#cloud-providers">🔗</a> </summary>
|
|
579
883
|
|
|
@@ -911,7 +1215,7 @@ if __name__ == "__main__":
|
|
|
911
1215
|
|
|
912
1216
|
Mix and match different sets of data to experiment and create better models.
|
|
913
1217
|
|
|
914
|
-
Combine datasets with `CombinedStreamingDataset`. As an example, this mixture of [Slimpajama](https://
|
|
1218
|
+
Combine datasets with `CombinedStreamingDataset`. As an example, this mixture of [Slimpajama](https://www.cerebras.ai/blog/slimpajama-a-627b-token-cleaned-and-deduplicated-version-of-redpajama) & [StarCoder](https://huggingface.co/datasets/bigcode/starcoderdata) was used in the [TinyLLAMA](https://github.com/jzhang38/TinyLlama) project to pretrain a 1.1B Llama model on 3 trillion tokens.
|
|
915
1219
|
|
|
916
1220
|
```python
|
|
917
1221
|
from litdata import StreamingDataset, CombinedStreamingDataset, StreamingDataLoader, TokensLoader
|
|
@@ -935,7 +1239,12 @@ train_datasets = [
|
|
|
935
1239
|
|
|
936
1240
|
# Mix SlimPajama data and Starcoder data with these proportions:
|
|
937
1241
|
weights = (0.693584, 0.306416)
|
|
938
|
-
combined_dataset = CombinedStreamingDataset(
|
|
1242
|
+
combined_dataset = CombinedStreamingDataset(
|
|
1243
|
+
datasets=train_datasets,
|
|
1244
|
+
seed=42,
|
|
1245
|
+
weights=weights,
|
|
1246
|
+
iterate_over_all=False, # required when passing weights (see below)
|
|
1247
|
+
)
|
|
939
1248
|
|
|
940
1249
|
train_dataloader = StreamingDataLoader(combined_dataset, batch_size=8, pin_memory=True, num_workers=os.cpu_count())
|
|
941
1250
|
|
|
@@ -944,37 +1253,34 @@ for batch in tqdm(train_dataloader):
|
|
|
944
1253
|
pass
|
|
945
1254
|
```
|
|
946
1255
|
|
|
947
|
-
|
|
1256
|
+
**`iterate_over_all` vs `weights` (important)**
|
|
948
1257
|
|
|
949
|
-
|
|
1258
|
+
| Mode | Behavior |
|
|
1259
|
+
|------|----------|
|
|
1260
|
+
| `iterate_over_all=True` (default) | Iterate until **all** datasets are exhausted. Do **not** pass `weights` — LitData derives them from dataset lengths (raises `ValueError` if you pass both). |
|
|
1261
|
+
| `iterate_over_all=False` | Stop when **any** dataset is exhausted. Pass explicit `weights` for your mixture (e.g. TinyLlama). Length may be `None` (variable). |
|
|
950
1262
|
|
|
951
|
-
**
|
|
952
|
-
|
|
1263
|
+
**Batching Methods** (`batching_method`)
|
|
1264
|
+
|
|
1265
|
+
**Stratified** (default): each batch mixes samples from multiple datasets according to the weights.
|
|
953
1266
|
|
|
954
1267
|
```python
|
|
955
|
-
# Default stratified batching - batches mix samples from all datasets
|
|
956
1268
|
combined_dataset = CombinedStreamingDataset(
|
|
957
|
-
datasets=[dataset1, dataset2],
|
|
958
|
-
batching_method="stratified" #
|
|
1269
|
+
datasets=[dataset1, dataset2],
|
|
1270
|
+
batching_method="stratified", # default
|
|
959
1271
|
)
|
|
960
1272
|
```
|
|
961
1273
|
|
|
962
|
-
**Per-
|
|
963
|
-
With `batching_method="per_stream"`, each batch contains samples exclusively from a single dataset. This is useful when datasets have different shapes or structures:
|
|
1274
|
+
**Per-stream**: each batch comes from only one randomly selected dataset (useful when shapes/dtypes differ).
|
|
964
1275
|
|
|
965
1276
|
```python
|
|
966
|
-
# Per-stream batching - each batch contains samples from only one dataset
|
|
967
1277
|
combined_dataset = CombinedStreamingDataset(
|
|
968
|
-
datasets=[dataset1, dataset2],
|
|
969
|
-
batching_method="per_stream"
|
|
1278
|
+
datasets=[dataset1, dataset2],
|
|
1279
|
+
batching_method="per_stream",
|
|
970
1280
|
)
|
|
971
|
-
|
|
972
|
-
# This ensures each batch has consistent structure, helpful for datasets with varying:
|
|
973
|
-
# - Image sizes
|
|
974
|
-
# - Sequence lengths
|
|
975
|
-
# - Data types
|
|
976
|
-
# - Feature dimensions
|
|
977
1281
|
```
|
|
1282
|
+
|
|
1283
|
+
Other knobs: `seed` (default `42`), `force_override_state_dict=True` to let local ctor args override a loaded checkpoint.
|
|
978
1284
|
</details>
|
|
979
1285
|
|
|
980
1286
|
<details>
|
|
@@ -1255,59 +1561,113 @@ The `overwrite` mode will delete the existing data and start from fresh.
|
|
|
1255
1561
|
<summary> ✅ Stream parquet datasets <a id="stream-parquet" href="#stream-parquet">🔗</a> </summary>
|
|
1256
1562
|
|
|
1257
1563
|
|
|
1258
|
-
Stream Parquet
|
|
1564
|
+
Stream existing Parquet files with LitData **without** converting them to LitData chunks — or convert them when you need LitData’s optimized binary format. Hugging Face parquet datasets are covered in [Stream Hugging Face datasets](#stream-hf).
|
|
1259
1565
|
|
|
1260
|
-
|
|
1566
|
+
### Stream vs optimize vs map
|
|
1261
1567
|
|
|
1262
|
-
|
|
1568
|
+
| Goal | Use |
|
|
1569
|
+
|------|-----|
|
|
1570
|
+
| Train on parquet as-is (no conversion) | `index_parquet_dataset` → `StreamingDataset` + `ParquetLoader` |
|
|
1571
|
+
| Faster I/O / tokenize / custom sample shape | `optimize(fn)` that `yield`s rows from parquet ([reduce memory](#reduce-memory)) |
|
|
1572
|
+
| Reshard huge parquet files while mapping | `map(..., reader=ParquetReader(cache_folder, num_rows=...))` |
|
|
1263
1573
|
|
|
1264
|
-
|
|
1574
|
+
Each sample from `ParquetLoader` is a **`dict`** (column name → value).
|
|
1265
1575
|
|
|
1266
|
-
|
|
1576
|
+
### Prerequisites
|
|
1267
1577
|
|
|
1268
1578
|
```bash
|
|
1269
|
-
#
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
pip install "litdata[extra]" gcsfs
|
|
1579
|
+
pip install 'litdata[extras]' # includes polars + pyarrow
|
|
1580
|
+
# Cloud listing/index extras as needed:
|
|
1581
|
+
pip install s3fs # s3://
|
|
1582
|
+
pip install gcsfs # gs://
|
|
1274
1583
|
```
|
|
1275
1584
|
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
Index your Parquet dataset to create an index file that LitData can use to stream the dataset.
|
|
1585
|
+
### Index a parquet directory
|
|
1279
1586
|
|
|
1280
1587
|
```python
|
|
1281
1588
|
import litdata as ld
|
|
1282
1589
|
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1590
|
+
ld.index_parquet_dataset(
|
|
1591
|
+
"s3://my-bucket/my-parquet-data", # local path, s3://, gs://, or hf://
|
|
1592
|
+
cache_dir=None, # see table below
|
|
1593
|
+
storage_options={}, # cloud credentials / endpoints
|
|
1594
|
+
num_workers=4, # parallel metadata reads
|
|
1595
|
+
)
|
|
1287
1596
|
```
|
|
1288
1597
|
|
|
1289
|
-
|
|
1598
|
+
| Scheme | Where `index.json` is written |
|
|
1599
|
+
|--------|-------------------------------|
|
|
1600
|
+
| Local directory | Next to the files, or under `cache_dir` if set |
|
|
1601
|
+
| `s3://` / `gs://` | **Uploaded to the bucket** at `{url}/index.json` (needs write access) |
|
|
1602
|
+
| `hf://` | **Local** `cache_dir` (required for HF indexing via this helper) |
|
|
1603
|
+
|
|
1604
|
+
**Indexing notes**
|
|
1605
|
+
|
|
1606
|
+
- Lists **top-level** `.parquet` files only (not recursive subfolders).
|
|
1607
|
+
- All files must share the same schema.
|
|
1608
|
+
- Supported for indexing today: local, `s3://`, `gs://`, `hf://` (not `r2://` / `azure://` yet).
|
|
1609
|
+
- For HF, prefer `index_hf_dataset(uri)` (returns a local cache dir) or auto-index via `StreamingDataset("hf://...")` — see [HF section](#stream-hf).
|
|
1290
1610
|
|
|
1291
|
-
|
|
1611
|
+
### Stream with `ParquetLoader`
|
|
1292
1612
|
|
|
1613
|
+
Unlike `hf://`, local/S3/GCS parquet **does not** auto-select the loader — pass `ParquetLoader` explicitly (it must match `index.json`).
|
|
1293
1614
|
|
|
1294
1615
|
```python
|
|
1295
1616
|
import litdata as ld
|
|
1296
1617
|
from litdata.streaming.item_loader import ParquetLoader
|
|
1297
1618
|
|
|
1298
|
-
|
|
1299
|
-
|
|
1619
|
+
uri = "s3://my-bucket/my-parquet-data"
|
|
1620
|
+
dataset = ld.StreamingDataset(
|
|
1621
|
+
uri,
|
|
1622
|
+
item_loader=ParquetLoader(low_memory=True), # default: row-group streaming
|
|
1623
|
+
# index_path="/path/to/index.json", # optional if index lives elsewhere
|
|
1624
|
+
)
|
|
1300
1625
|
|
|
1301
|
-
#
|
|
1302
|
-
dataset = ld.StreamingDataset(
|
|
1626
|
+
# Basename wildcards when the path ends with .parquet:
|
|
1627
|
+
# dataset = ld.StreamingDataset("s3://bucket/data/train-*.parquet", item_loader=ParquetLoader())
|
|
1303
1628
|
|
|
1304
|
-
print(
|
|
1629
|
+
print(dataset[0]) # dict of columns
|
|
1305
1630
|
|
|
1306
|
-
|
|
1307
|
-
|
|
1631
|
+
# Linux + num_workers>0: use spawn (Polars + fork deadlocks)
|
|
1632
|
+
dataloader = ld.StreamingDataLoader(
|
|
1633
|
+
dataset,
|
|
1634
|
+
batch_size=4,
|
|
1635
|
+
num_workers=4,
|
|
1636
|
+
multiprocessing_context="spawn",
|
|
1637
|
+
)
|
|
1638
|
+
for batch in dataloader:
|
|
1308
1639
|
pass
|
|
1309
1640
|
```
|
|
1310
1641
|
|
|
1642
|
+
### `ParquetLoader` knobs
|
|
1643
|
+
|
|
1644
|
+
| Arg | Default | Meaning |
|
|
1645
|
+
|-----|---------|---------|
|
|
1646
|
+
| `low_memory` | `True` | Stream by row group (lower RAM). `False` loads each whole file into memory (warns). |
|
|
1647
|
+
| `pre_load_chunk` | `False` | Prefetch full DataFrame — **only effective when `low_memory=False`**. |
|
|
1648
|
+
|
|
1649
|
+
Import: `from litdata.streaming.item_loader import ParquetLoader` (not re-exported at `litdata` top level).
|
|
1650
|
+
|
|
1651
|
+
### Reshard parquet for `map` / `optimize`
|
|
1652
|
+
|
|
1653
|
+
```python
|
|
1654
|
+
from litdata import map
|
|
1655
|
+
from litdata.processing.readers import ParquetReader
|
|
1656
|
+
|
|
1657
|
+
def process(pq_file, output_dir):
|
|
1658
|
+
# pq_file is a pyarrow.parquet.ParquetFile
|
|
1659
|
+
...
|
|
1660
|
+
|
|
1661
|
+
map(
|
|
1662
|
+
fn=process,
|
|
1663
|
+
inputs=list_of_parquet_paths,
|
|
1664
|
+
output_dir="s3://bucket/out",
|
|
1665
|
+
reader=ParquetReader(cache_folder="/tmp/pq-shards", num_rows=65536),
|
|
1666
|
+
)
|
|
1667
|
+
```
|
|
1668
|
+
|
|
1669
|
+
`ParquetReader` splits inputs that exceed `num_rows` into smaller cached files before your `fn` runs.
|
|
1670
|
+
|
|
1311
1671
|
</details>
|
|
1312
1672
|
|
|
1313
1673
|
<details>
|
|
@@ -1393,17 +1753,60 @@ for batch in dataloader:
|
|
|
1393
1753
|
<summary> ✅ Profile data loading speed <a id="profile-loading" href="#profile-loading">🔗</a> </summary>
|
|
1394
1754
|
|
|
1395
1755
|
|
|
1396
|
-
|
|
1756
|
+
`StreamingDataLoader` can record a **viztracer** Chrome trace of the DataLoader worker loop so you can see where time goes (fetch, deserialize, collate, IPC).
|
|
1757
|
+
|
|
1758
|
+
### Prerequisites
|
|
1759
|
+
|
|
1760
|
+
```bash
|
|
1761
|
+
pip install viztracer
|
|
1762
|
+
```
|
|
1763
|
+
|
|
1764
|
+
Profiling requires **`num_workers >= 1`** (raises otherwise). On multi-GPU, only **global rank 0** installs the worker profiler.
|
|
1397
1765
|
|
|
1398
|
-
|
|
1766
|
+
### Usage
|
|
1399
1767
|
|
|
1400
1768
|
```python
|
|
1401
1769
|
from litdata import StreamingDataset, StreamingDataLoader
|
|
1402
1770
|
|
|
1403
|
-
|
|
1771
|
+
dataset = StreamingDataset("s3://my-bucket/my-data", shuffle=True, drop_last=True)
|
|
1772
|
+
|
|
1773
|
+
loader = StreamingDataLoader(
|
|
1774
|
+
dataset,
|
|
1775
|
+
batch_size=64,
|
|
1776
|
+
num_workers=4,
|
|
1777
|
+
profile_batches=20, # record this many batches (int), or True for the whole run
|
|
1778
|
+
profile_skip_batches=5, # warm up / skip cold-start batches before recording
|
|
1779
|
+
profile_dir="./profiles", # where to write result.json (default: cwd)
|
|
1780
|
+
)
|
|
1781
|
+
|
|
1782
|
+
for batch in loader:
|
|
1783
|
+
train_step(batch)
|
|
1784
|
+
# after profile_batches (+ skip) complete, worker 0 saves the trace and prints the path
|
|
1785
|
+
```
|
|
1786
|
+
|
|
1787
|
+
| Arg | Default | Meaning |
|
|
1788
|
+
|-----|---------|---------|
|
|
1789
|
+
| `profile_batches` | `False` | `int` → stop after that many **recorded** batches; `True` → profile until the iterator ends; `False` → off |
|
|
1790
|
+
| `profile_skip_batches` | `0` | Batches to skip before the tracer starts (useful to skip cache cold-start) |
|
|
1791
|
+
| `profile_dir` | current working directory | Directory for `result.json` (overwrites an existing file) |
|
|
1792
|
+
|
|
1793
|
+
Only **worker 0** is instrumented. When an `int` is used, the tracer wraps `fetcher.fetch` and stops after `profile_skip_batches + profile_batches` fetch calls. When `True`, tracing runs for the lifetime of that worker loop.
|
|
1794
|
+
|
|
1795
|
+
### View the trace
|
|
1796
|
+
|
|
1797
|
+
```bash
|
|
1798
|
+
# Option A — Chrome
|
|
1799
|
+
# open chrome://tracing and load profiles/result.json
|
|
1800
|
+
|
|
1801
|
+
# Option B — Perfetto (often better for large traces)
|
|
1802
|
+
# open https://ui.perfetto.dev and load the same file
|
|
1404
1803
|
```
|
|
1405
1804
|
|
|
1406
|
-
|
|
1805
|
+
### Tips
|
|
1806
|
+
|
|
1807
|
+
- Delete or change `profile_dir` between runs — LitData removes an existing `result.json` before starting.
|
|
1808
|
+
- Pair with a wiped chunk cache if you care about **cold** epoch behavior (`litdata cache clear`).
|
|
1809
|
+
- For deeper LitData internals (download / lock / delete timeline), use `enable_tracer()` + [Litracer](https://github.com/deependujha/litracer) instead — see [Debug & Profile LitData](#debug-profile). That path is complementary: viztracer = DataLoader worker CPU timeline; Litracer = LitData pipeline events.
|
|
1407
1810
|
|
|
1408
1811
|
</details>
|
|
1409
1812
|
|
|
@@ -1413,7 +1816,9 @@ This generates a Chrome trace called `result.json`. Then, visualize this trace b
|
|
|
1413
1816
|
|
|
1414
1817
|
Handle large data files efficiently without using too much of your computer's memory.
|
|
1415
1818
|
|
|
1416
|
-
|
|
1819
|
+
**Optimize from parquet** (convert into LitData chunks) when you need tokenization or LitData’s binary format. To **stream parquet without converting**, see [Stream parquet datasets](#stream-parquet).
|
|
1820
|
+
|
|
1821
|
+
When processing large parquet files, `yield` one item at a time to keep memory low:
|
|
1417
1822
|
|
|
1418
1823
|
```python
|
|
1419
1824
|
from pathlib import Path
|
|
@@ -1449,32 +1854,104 @@ outputs = optimize(
|
|
|
1449
1854
|
<summary> ✅ Limit local cache space <a id="limit-cache" href="#limit-cache">🔗</a> </summary>
|
|
1450
1855
|
|
|
1451
1856
|
|
|
1452
|
-
|
|
1857
|
+
Control how much disk the local chunk cache may use. Downloaded chunks are deleted after use once the cache exceeds the limit.
|
|
1858
|
+
|
|
1859
|
+
Default `max_cache_size` is **`100GB`**. Peak disk in flight is roughly:
|
|
1860
|
+
|
|
1861
|
+
```
|
|
1862
|
+
num_workers × max_pre_download × mean_chunk_size
|
|
1863
|
+
```
|
|
1453
1864
|
|
|
1454
|
-
|
|
1865
|
+
Keep `max_cache_size` comfortably above that peak. For remote datasets, async chunk prefetch often raises `max_pre_download` to **≥4** automatically — see [async prefetch & environment variables](#async-prefetch-env).
|
|
1455
1866
|
|
|
1456
1867
|
```python
|
|
1457
1868
|
from litdata import StreamingDataset
|
|
1458
1869
|
|
|
1459
|
-
dataset = StreamingDataset(
|
|
1870
|
+
dataset = StreamingDataset(
|
|
1871
|
+
"s3://my-bucket/my-data",
|
|
1872
|
+
max_cache_size="10GB",
|
|
1873
|
+
max_pre_download=4, # chunks each worker may prefetch (default 2; async may floor to 4)
|
|
1874
|
+
)
|
|
1460
1875
|
```
|
|
1461
1876
|
|
|
1462
1877
|
</details>
|
|
1463
1878
|
|
|
1879
|
+
<details>
|
|
1880
|
+
<summary> ✅ Async chunk prefetch & environment variables <a id="async-prefetch-env" href="#async-prefetch-env">🔗</a> </summary>
|
|
1881
|
+
|
|
1882
|
+
|
|
1883
|
+
### Async chunk prefetch
|
|
1884
|
+
|
|
1885
|
+
LitData can overlap **remote chunk downloads** with training using `asyncio` inside each DataLoader worker’s prepare thread. This is **not** an async DataLoader — your loop stays:
|
|
1886
|
+
|
|
1887
|
+
```python
|
|
1888
|
+
for batch in StreamingDataLoader(dataset, batch_size=64, num_workers=8):
|
|
1889
|
+
train_step(batch)
|
|
1890
|
+
```
|
|
1891
|
+
|
|
1892
|
+
| Situation | Async prefetch |
|
|
1893
|
+
|-----------|----------------|
|
|
1894
|
+
| Remote dataset (`s3://`, `gs://`, …) | **On** by default |
|
|
1895
|
+
| Local-only dataset | **Off** by default |
|
|
1896
|
+
| `LITDATA_ASYNC_CHUNK_PREFETCH=1` | Force on |
|
|
1897
|
+
| `LITDATA_ASYNC_CHUNK_PREFETCH=0` | Force off |
|
|
1898
|
+
|
|
1899
|
+
When async is on, LitData raises `max_pre_download` to at least **4** so `asyncio.gather` has enough in-flight downloads (override with `LITDATA_ASYNC_MIN_PRE_DOWNLOAD`; set `0` to disable the floor). Peak disk ≈ `num_workers × max_pre_download × chunk_size` — size `max_cache_size` accordingly.
|
|
1900
|
+
|
|
1901
|
+
```bash
|
|
1902
|
+
# Debugging download/delete races — force synchronous downloads
|
|
1903
|
+
export LITDATA_ASYNC_CHUNK_PREFETCH=0
|
|
1904
|
+
|
|
1905
|
+
# Keep max_pre_download=2 even with async enabled
|
|
1906
|
+
export LITDATA_ASYNC_MIN_PRE_DOWNLOAD=0
|
|
1907
|
+
```
|
|
1908
|
+
|
|
1909
|
+
### Common environment variables
|
|
1910
|
+
|
|
1911
|
+
| Variable | Default | Purpose |
|
|
1912
|
+
|----------|---------|---------|
|
|
1913
|
+
| `LITDATA_CACHE_DIR` | `~/.lightning/chunks` | Default chunk cache directory |
|
|
1914
|
+
| `LITDATA_ASYNC_CHUNK_PREFETCH` | on for remote | `0`/`1` force async chunk download overlap |
|
|
1915
|
+
| `LITDATA_ASYNC_MIN_PRE_DOWNLOAD` | `4` | Floor for `max_pre_download` when async is on (`0` = no floor) |
|
|
1916
|
+
| `LITDATA_OBSTORE_STREAM_MIN_CHUNK_MIB` | `8` | S3 obstore stream chunk size (MiB) |
|
|
1917
|
+
| `MAX_WAIT_TIME` | `120` | Seconds to wait for a chunk before error |
|
|
1918
|
+
| `FORCE_DOWNLOAD_TIME` | `30` | Seconds before force re-download of a missing chunk |
|
|
1919
|
+
| `LITDATA_DISABLE_VERSION_CHECK` | `0` | `1` skips the upgrade tip |
|
|
1920
|
+
| `HF_TOKEN` | — | Gated Hugging Face datasets |
|
|
1921
|
+
| `DEBUG_LITDATA` / `PRINT_DEBUG_LOGS` | `0` | Internal debug / stdout logs |
|
|
1922
|
+
|
|
1923
|
+
Multi-node `optimize`/`map` on Studios also uses `DATA_OPTIMIZER_*` (set by the platform). Full catalog (debug logs, Studio injects, torchrun): see the LitData skill `reference/env-vars.md` when using agent skills, or the source modules `constants.py` / `async_prefetch.py`.
|
|
1924
|
+
|
|
1925
|
+
</details>
|
|
1926
|
+
|
|
1464
1927
|
<details>
|
|
1465
1928
|
<summary> ✅ Change cache directory path <a id="cache-directory" href="#cache-directory">🔗</a> </summary>
|
|
1466
1929
|
|
|
1467
1930
|
|
|
1468
|
-
Specify
|
|
1931
|
+
Specify where cached chunk files are stored.
|
|
1469
1932
|
|
|
1470
1933
|
```python
|
|
1471
1934
|
from litdata import StreamingDataset
|
|
1472
1935
|
from litdata.streaming.cache import Dir
|
|
1473
1936
|
|
|
1474
|
-
|
|
1475
|
-
|
|
1937
|
+
# Simple: dedicated cache directory
|
|
1938
|
+
dataset = StreamingDataset("s3://my-bucket/my_optimized_dataset", cache_dir="/path/to/your/cache")
|
|
1939
|
+
|
|
1940
|
+
# Or when cache path and remote URL should differ:
|
|
1941
|
+
dataset = StreamingDataset(input_dir=Dir(path="/path/to/your/cache", url="s3://my-bucket/my_optimized_dataset"))
|
|
1942
|
+
```
|
|
1943
|
+
|
|
1944
|
+
Global default without passing `cache_dir` every time:
|
|
1476
1945
|
|
|
1477
|
-
|
|
1946
|
+
```bash
|
|
1947
|
+
export LITDATA_CACHE_DIR=/path/to/your/cache
|
|
1948
|
+
```
|
|
1949
|
+
|
|
1950
|
+
CLI:
|
|
1951
|
+
|
|
1952
|
+
```bash
|
|
1953
|
+
litdata cache path # print the active cache directory
|
|
1954
|
+
litdata cache clear # delete cached chunks
|
|
1478
1955
|
```
|
|
1479
1956
|
|
|
1480
1957
|
</details>
|
|
@@ -1496,114 +1973,110 @@ dataset = StreamingDataset(input_dir="local:/data/shared-drive/some-data")
|
|
|
1496
1973
|
</details>
|
|
1497
1974
|
|
|
1498
1975
|
<details>
|
|
1499
|
-
<summary> ✅ Optimize
|
|
1976
|
+
<summary> ✅ Optimize / map across multiple machines (Lightning Studios) <a id="distributed-optimization" href="#distributed-optimization">🔗</a> </summary>
|
|
1500
1977
|
|
|
1501
1978
|
|
|
1502
|
-
Lightning
|
|
1979
|
+
On [Lightning Studios](https://lightning.ai/), `num_nodes` and `machine` scale `optimize` / `map` across many machines. This is **not** the same as `num_workers` (processes on one machine).
|
|
1980
|
+
|
|
1981
|
+
**How it works**
|
|
1982
|
+
|
|
1983
|
+
1. You call `optimize(..., num_nodes=N, machine=...)` (or `map`) inside a Studio.
|
|
1984
|
+
2. LitData starts a **data-prep job** that re-runs your script on **N** machines.
|
|
1985
|
+
3. Each machine processes a shard of the inputs (`num_nodes × num_workers` total workers). The last node merges chunk indexes into a single `index.json`.
|
|
1986
|
+
4. Your local call blocks until the job finishes; open the printed Runs URL to monitor.
|
|
1503
1987
|
|
|
1504
|
-
|
|
1988
|
+
Outside Studio, passing `num_nodes` / `machine` raises an error (create a Studio account to use multi-node).
|
|
1505
1989
|
|
|
1506
1990
|
```python
|
|
1507
|
-
import os
|
|
1508
1991
|
from litdata import optimize, Machine
|
|
1509
1992
|
|
|
1510
1993
|
def compress(index):
|
|
1511
1994
|
return (index, index ** 2)
|
|
1512
1995
|
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1996
|
+
if __name__ == "__main__":
|
|
1997
|
+
optimize(
|
|
1998
|
+
fn=compress,
|
|
1999
|
+
inputs=list(range(100)),
|
|
2000
|
+
num_workers=8, # processes per machine
|
|
2001
|
+
output_dir="/teamspace/s3_connections/my-data/optimized-v1", # durable bucket (recommended)
|
|
2002
|
+
chunk_bytes="64MB",
|
|
2003
|
+
num_nodes=32, # machines in the job
|
|
2004
|
+
machine=Machine.DATA_PREP, # or omit to use the current Studio machine type
|
|
2005
|
+
)
|
|
1522
2006
|
```
|
|
1523
2007
|
|
|
1524
|
-
|
|
2008
|
+
**Where outputs land**
|
|
1525
2009
|
|
|
1526
|
-
|
|
2010
|
+
| `output_dir` | Result |
|
|
2011
|
+
|--------------|--------|
|
|
2012
|
+
| `/teamspace/s3_connections/...`, `/teamspace/datasets/...`, `s3://...`, `gs://...` | Written directly to that store (**recommended**) |
|
|
2013
|
+
| Local or `/teamspace/studios/this_studio/...` | Remapped to the job’s **artifacts** storage; the Studio UI may also expose it under `/teamspace/jobs/<job>/...` |
|
|
1527
2014
|
|
|
1528
2015
|
```python
|
|
1529
2016
|
from litdata import StreamingDataset
|
|
1530
2017
|
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
dataset = StreamingDataset(output_dir)
|
|
1534
|
-
|
|
1535
|
-
print(dataset[:])
|
|
2018
|
+
# Prefer the same connection / cloud URL you wrote to:
|
|
2019
|
+
dataset = StreamingDataset("/teamspace/s3_connections/my-data/optimized-v1")
|
|
1536
2020
|
```
|
|
1537
2021
|
|
|
2022
|
+
The same `num_nodes` / `machine` pattern works with `map`. See also [Parallelize transforms and data optimization](#parallelize-transforms-and-data-optimization-on-cloud-machines).
|
|
2023
|
+
|
|
1538
2024
|
</details>
|
|
1539
2025
|
|
|
1540
2026
|
<details>
|
|
1541
2027
|
<summary> ✅ Encrypt, decrypt data at chunk/sample level <a id="encrypt-decrypt" href="#encrypt-decrypt">🔗</a> </summary>
|
|
1542
2028
|
|
|
1543
2029
|
|
|
1544
|
-
|
|
2030
|
+
Encrypt optimized data at **sample** or **chunk** level. Built-ins: `FernetEncryption` and `RSAEncryption` (`litdata.utilities.encryption`). Requires the `cryptography` package. **Not supported for Mosaic MDS.**
|
|
2031
|
+
|
|
2032
|
+
| `level` | Meaning |
|
|
2033
|
+
|---------|---------|
|
|
2034
|
+
| `"sample"` (default) | Encrypt each sample independently |
|
|
2035
|
+
| `"chunk"` | Encrypt whole chunks |
|
|
1545
2036
|
|
|
1546
|
-
|
|
2037
|
+
**Fernet (symmetric)**
|
|
1547
2038
|
|
|
1548
2039
|
```python
|
|
1549
|
-
from litdata import optimize
|
|
2040
|
+
from litdata import optimize, StreamingDataset
|
|
1550
2041
|
from litdata.utilities.encryption import FernetEncryption
|
|
1551
|
-
import numpy as np
|
|
1552
|
-
from PIL import Image
|
|
1553
2042
|
|
|
1554
|
-
|
|
1555
|
-
fernet = FernetEncryption(password="your_secure_password", level="sample")
|
|
2043
|
+
fernet = FernetEncryption(password="your_secure_password", level="sample") # or level="chunk"
|
|
1556
2044
|
data_dir = "s3://my-bucket/optimized_data"
|
|
1557
2045
|
|
|
1558
|
-
def
|
|
1559
|
-
""
|
|
1560
|
-
fake_img = Image.fromarray(np.random.randint(0, 255, (32, 32, 3), dtype=np.uint8))
|
|
1561
|
-
return {"image": fake_img, "class": index}
|
|
1562
|
-
|
|
1563
|
-
# Optimize data while applying encryption
|
|
1564
|
-
optimize(
|
|
1565
|
-
fn=random_image,
|
|
1566
|
-
inputs=list(range(5)), # Example inputs: [0, 1, 2, 3, 4]
|
|
1567
|
-
num_workers=1,
|
|
1568
|
-
output_dir=data_dir,
|
|
1569
|
-
chunk_bytes="64MB",
|
|
1570
|
-
encryption=fernet,
|
|
1571
|
-
)
|
|
1572
|
-
|
|
1573
|
-
# Save the encryption key to a file for later use
|
|
1574
|
-
fernet.save("fernet.pem")
|
|
1575
|
-
```
|
|
1576
|
-
|
|
1577
|
-
Load the encrypted data using the `StreamingDataset` class as follows:
|
|
1578
|
-
|
|
1579
|
-
```python
|
|
1580
|
-
from litdata import StreamingDataset
|
|
1581
|
-
from litdata.utilities.encryption import FernetEncryption
|
|
2046
|
+
def fn(index):
|
|
2047
|
+
return {"index": index, "value": index**2}
|
|
1582
2048
|
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
2049
|
+
if __name__ == "__main__":
|
|
2050
|
+
optimize(
|
|
2051
|
+
fn=fn,
|
|
2052
|
+
inputs=list(range(5)),
|
|
2053
|
+
num_workers=1,
|
|
2054
|
+
output_dir=data_dir,
|
|
2055
|
+
chunk_bytes="64MB",
|
|
2056
|
+
encryption=fernet,
|
|
2057
|
+
)
|
|
2058
|
+
fernet.save("fernet.pem") # persist salt/level; keep the password safe
|
|
1586
2059
|
|
|
1587
|
-
#
|
|
2060
|
+
# Later — load key material with the same password
|
|
2061
|
+
fernet = FernetEncryption.load("fernet.pem", password="your_secure_password")
|
|
1588
2062
|
ds = StreamingDataset(input_dir=data_dir, encryption=fernet)
|
|
1589
2063
|
```
|
|
1590
2064
|
|
|
1591
|
-
|
|
2065
|
+
**RSA (asymmetric)**
|
|
1592
2066
|
|
|
1593
2067
|
```python
|
|
1594
|
-
from litdata.utilities.encryption import
|
|
2068
|
+
from litdata.utilities.encryption import RSAEncryption
|
|
1595
2069
|
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
return data
|
|
2070
|
+
rsa = RSAEncryption(password="your_secure_password", level="sample") # or "chunk"
|
|
2071
|
+
optimize(fn=fn, inputs=list(range(5)), output_dir=data_dir, chunk_bytes="64MB", encryption=rsa)
|
|
2072
|
+
rsa.save("rsa.pem")
|
|
1600
2073
|
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
return data
|
|
2074
|
+
rsa = RSAEncryption.load("rsa.pem", password="your_secure_password")
|
|
2075
|
+
ds = StreamingDataset(input_dir=data_dir, encryption=rsa)
|
|
1604
2076
|
```
|
|
1605
2077
|
|
|
1606
|
-
|
|
2078
|
+
**Custom algorithm** — subclass `Encryption` and implement `encrypt` / `decrypt` / `save` / `load` / `state_dict` / `algorithm`.
|
|
2079
|
+
|
|
1607
2080
|
</details>
|
|
1608
2081
|
|
|
1609
2082
|
<details>
|
|
@@ -1675,17 +2148,99 @@ if __name__ == "__main__":
|
|
|
1675
2148
|
</details>
|
|
1676
2149
|
|
|
1677
2150
|
<details>
|
|
1678
|
-
<summary> ✅
|
|
2151
|
+
<summary> ✅ Resolve any path or cloud URL (local, S3, GCS, R2, Azure, HF, Studio) <a id="resolve-paths" href="#resolve-paths">🔗</a> </summary>
|
|
1679
2152
|
|
|
1680
2153
|
|
|
1681
2154
|
|
|
1682
|
-
|
|
2155
|
+
LitData **resolves** every dataset path you pass to `StreamingDataset`, `StreamingRawDataset`, `optimize`, `map`, and related APIs. You write one path string; LitData figures out whether to read locally, download from object storage, or (inside [Lightning Studios](https://lightning.ai/)) talk **directly to the bucket** behind a `/teamspace/...` mount instead of going through slow FUSE I/O.
|
|
2156
|
+
|
|
2157
|
+
### Supported URI schemes
|
|
1683
2158
|
|
|
1684
|
-
|
|
2159
|
+
| Scheme | Example | Use when |
|
|
2160
|
+
|--------|---------|----------|
|
|
2161
|
+
| Local path | `./data` or `/data/imagenet` | Files on disk |
|
|
2162
|
+
| `s3://` | `s3://my-bucket/optimized` | AWS S3 |
|
|
2163
|
+
| `gs://` | `gs://my-bucket/optimized` | Google Cloud Storage |
|
|
2164
|
+
| `r2://` | `r2://my-bucket/optimized` | Cloudflare R2 |
|
|
2165
|
+
| `azure://` | `azure://container/optimized` | Azure Blob Storage |
|
|
2166
|
+
| `hf://` | `hf://datasets/org/name/data` | Hugging Face datasets (parquet) |
|
|
2167
|
+
| `local:` | `local:/mnt/nfs/dataset` | Network / shared drive (LitData still caches chunks locally to reduce NAS load) |
|
|
1685
2168
|
|
|
1686
2169
|
```python
|
|
1687
|
-
from litdata import optimize
|
|
2170
|
+
from litdata import StreamingDataset, optimize
|
|
2171
|
+
|
|
2172
|
+
# Same APIs — only the path changes
|
|
2173
|
+
StreamingDataset("s3://my-bucket/fast_data", shuffle=True, drop_last=True)
|
|
2174
|
+
StreamingDataset("gs://my-bucket/fast_data")
|
|
2175
|
+
StreamingDataset("r2://my-bucket/fast_data", storage_options={...})
|
|
2176
|
+
StreamingDataset("azure://my-container/fast_data", storage_options={...})
|
|
2177
|
+
StreamingDataset("hf://datasets/org/name/data")
|
|
2178
|
+
StreamingDataset("local:/data/shared-drive/some-data")
|
|
2179
|
+
StreamingDataset("/var/data/fast_data") # plain local directory
|
|
2180
|
+
```
|
|
2181
|
+
|
|
2182
|
+
Pass cloud credentials with `storage_options` (and optional `session_options` for boto3 profiles/regions). See [Stream from multiple cloud providers](#cloud-providers).
|
|
2183
|
+
|
|
2184
|
+
### Cache directory vs remote URL
|
|
2185
|
+
|
|
2186
|
+
By default LitData caches downloaded chunks under `~/.lightning/chunks` (override with `cache_dir=` or `LITDATA_CACHE_DIR`). When the cache location and the dataset URL must differ, use `Dir`:
|
|
2187
|
+
|
|
2188
|
+
```python
|
|
2189
|
+
from litdata import StreamingDataset
|
|
2190
|
+
from litdata.streaming.resolver import Dir
|
|
2191
|
+
|
|
2192
|
+
dataset = StreamingDataset(
|
|
2193
|
+
Dir(path="/fast-ssd/cache/run-1", url="s3://my-bucket/fast_data")
|
|
2194
|
+
)
|
|
2195
|
+
# Equivalent:
|
|
2196
|
+
dataset = StreamingDataset("s3://my-bucket/fast_data", cache_dir="/fast-ssd/cache/run-1")
|
|
2197
|
+
```
|
|
2198
|
+
|
|
2199
|
+
```bash
|
|
2200
|
+
export LITDATA_CACHE_DIR=/fast-ssd/cache
|
|
2201
|
+
litdata cache path # show active cache directory
|
|
2202
|
+
litdata cache clear # wipe cached chunks
|
|
2203
|
+
```
|
|
2204
|
+
|
|
2205
|
+
### Date/time path templates
|
|
2206
|
+
|
|
2207
|
+
Embed a `strftime` pattern in `{...}` and LitData expands it to the current time (useful for versioned `output_dir`s):
|
|
2208
|
+
|
|
2209
|
+
```python
|
|
2210
|
+
# e.g. on 2025-05-05 → ".../run_2025-05-05"
|
|
2211
|
+
optimize(
|
|
2212
|
+
fn=fn,
|
|
2213
|
+
inputs=inputs,
|
|
2214
|
+
output_dir="s3://my-bucket/datasets/run_{%Y-%m-%d}",
|
|
2215
|
+
chunk_bytes="64MB",
|
|
2216
|
+
)
|
|
2217
|
+
```
|
|
2218
|
+
|
|
2219
|
+
### Lightning Studio `/teamspace/...` paths (direct bucket I/O)
|
|
2220
|
+
|
|
2221
|
+
In Lightning Studios, data connections appear under `/teamspace/...`. **Prefer these paths in LitData** — optimize/map uploads and StreamingDataset downloads use the **backing object store URL** (and temporary credentials when needed), which is much faster than reading every file through the FUSE mount.
|
|
2222
|
+
|
|
2223
|
+
| Path prefix | What LitData does |
|
|
2224
|
+
|-------------|-------------------|
|
|
2225
|
+
| `/teamspace/studios/this_studio/...` | Local Studio workspace disk (not a cloud URL) |
|
|
2226
|
+
| `/teamspace/studios/<other_studio>/...` | Resolves to that Studio’s content bucket (`s3://` or `gs://`) |
|
|
2227
|
+
| `/teamspace/s3_connections/<name>/...` | Direct S3 to the connection’s bucket |
|
|
2228
|
+
| `/teamspace/gcs_connections/<name>/...` | Direct GCS |
|
|
2229
|
+
| `/teamspace/s3_folders/<name>/...` | S3 folder connection |
|
|
2230
|
+
| `/teamspace/gcs_folders/<name>/...` | GCS folder connection |
|
|
2231
|
+
| `/teamspace/lightning_storage/<name>/...` | Lightning-managed object storage (R2-style) |
|
|
2232
|
+
| `/teamspace/datasets/...` | Teamspace datasets mount → project datasets bucket |
|
|
2233
|
+
|
|
2234
|
+
```python
|
|
2235
|
+
from litdata import StreamingDataset, StreamingRawDataset, optimize
|
|
2236
|
+
|
|
2237
|
+
# Stream optimized data from an attached S3 connection (direct bucket download)
|
|
2238
|
+
dataset = StreamingDataset("/teamspace/s3_connections/my-data-1/fast_data", shuffle=True, drop_last=True)
|
|
2239
|
+
|
|
2240
|
+
# Stream raw files from a connection
|
|
2241
|
+
raw = StreamingRawDataset("/teamspace/s3_connections/my-bucket-1/raw")
|
|
1688
2242
|
|
|
2243
|
+
# Optimize *into* a connection — chunks upload straight to the bucket
|
|
1689
2244
|
def should_keep(data):
|
|
1690
2245
|
if data % 2 == 0:
|
|
1691
2246
|
yield data
|
|
@@ -1696,31 +2251,16 @@ if __name__ == "__main__":
|
|
|
1696
2251
|
inputs=list(range(1000)),
|
|
1697
2252
|
output_dir="/teamspace/s3_connections/my-data-1/output",
|
|
1698
2253
|
chunk_bytes="64MB",
|
|
1699
|
-
num_workers=1
|
|
2254
|
+
num_workers=1,
|
|
1700
2255
|
)
|
|
1701
2256
|
```
|
|
1702
2257
|
|
|
2258
|
+
**Tips**
|
|
1703
2259
|
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
from litdata import StreamingRawDataset
|
|
1708
|
-
|
|
1709
|
-
if __name__ == "__main__":
|
|
1710
|
-
data_dir = "/teamspace/s3_connections/my-bucket-1/data"
|
|
1711
|
-
|
|
1712
|
-
raw_dataset = StreamingRawDataset(data_dir)
|
|
1713
|
-
|
|
1714
|
-
data = list(raw_dataset)
|
|
1715
|
-
print(data)
|
|
1716
|
-
```
|
|
2260
|
+
- Version remote outputs (`.../v2`, `.../run_{%Y-%m-%d}`). Optimized datasets are immutable unless you pass `mode="append"` or `mode="overwrite"`.
|
|
2261
|
+
- Outside Studio, use `s3://` / `gs://` / … with your own credentials — `/teamspace/...` resolution needs Lightning Studio environment variables.
|
|
2262
|
+
- `optimize` / `map` with `num_nodes` launch a Studio **job** (not local multi-process). Prefer a connection / cloud `output_dir`; local / `this_studio` optimize outputs go to job artifacts (UI may show `/teamspace/jobs/...`). Details: [distributed optimization](#distributed-optimization).
|
|
1717
2263
|
|
|
1718
|
-
References to any of the following directories will work similarly:
|
|
1719
|
-
1. `/teamspace/lightning_storage/...`
|
|
1720
|
-
2. `/teamspace/s3_connections/...`
|
|
1721
|
-
3. `/teamspace/gcs_connections/...`
|
|
1722
|
-
4. `/teamspace/s3_folders/...`
|
|
1723
|
-
5. `/teamspace/gcs_folders/...`
|
|
1724
2264
|
</details>
|
|
1725
2265
|
|
|
1726
2266
|
|
|
@@ -1734,31 +2274,113 @@ References to any of the following directories will work similarly:
|
|
|
1734
2274
|
|
|
1735
2275
|
Apply the same change to different parts of the dataset at once to save time and effort.
|
|
1736
2276
|
|
|
1737
|
-
The `map` operator
|
|
1738
|
-
|
|
1739
|
-
Here is an example where the `map` operator is used to apply a `resize_image` function over a folder of large images.
|
|
2277
|
+
The `map` operator applies a function over a list of inputs. **`fn` must write into `output_dir` and return `None`.** Guard with `if __name__ == "__main__"` when using multiple workers.
|
|
1740
2278
|
|
|
1741
2279
|
```python
|
|
2280
|
+
import os
|
|
1742
2281
|
from litdata import map
|
|
1743
2282
|
from PIL import Image
|
|
1744
2283
|
|
|
1745
|
-
|
|
1746
|
-
input_dir = "my_large_images"
|
|
2284
|
+
input_dir = "my_large_images" # or s3://...
|
|
1747
2285
|
inputs = [os.path.join(input_dir, f) for f in os.listdir(input_dir)]
|
|
1748
2286
|
|
|
1749
|
-
# The resize image takes one of the input (image_path) and the output directory.
|
|
1750
|
-
# Files written to output_dir are persisted.
|
|
1751
2287
|
def resize_image(image_path, output_dir):
|
|
1752
|
-
|
|
1753
|
-
|
|
2288
|
+
output_image_path = os.path.join(output_dir, os.path.basename(image_path))
|
|
2289
|
+
Image.open(image_path).resize((224, 224)).save(output_image_path)
|
|
1754
2290
|
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
2291
|
+
if __name__ == "__main__":
|
|
2292
|
+
map(
|
|
2293
|
+
fn=resize_image,
|
|
2294
|
+
inputs=inputs,
|
|
2295
|
+
output_dir="s3://my-bucket/my_resized_images",
|
|
2296
|
+
num_workers=8,
|
|
2297
|
+
)
|
|
1760
2298
|
```
|
|
1761
2299
|
|
|
2300
|
+
**`map` arguments**
|
|
2301
|
+
|
|
2302
|
+
| Argument | Default | Description |
|
|
2303
|
+
|----------|---------|-------------|
|
|
2304
|
+
| `fn` | required | `fn(input, output_dir) -> None` |
|
|
2305
|
+
| `inputs` | required | Sequence or `StreamingDataLoader` |
|
|
2306
|
+
| `output_dir` | required | Local or cloud path ([resolver](#resolve-paths)) |
|
|
2307
|
+
| `input_dir` | `None` | Root for remote inputs (background download while processing) |
|
|
2308
|
+
| `weights` | `None` | Per-input weights to balance workers |
|
|
2309
|
+
| `num_workers` | CPU count | Local process workers |
|
|
2310
|
+
| `fast_dev_run` | `False` | Process only a few items (`True` → small default, or an int) |
|
|
2311
|
+
| `num_nodes` / `machine` | `None` | Scale out on [Lightning Studios](https://lightning.ai/) |
|
|
2312
|
+
| `num_downloaders` / `num_uploaders` | auto | I/O concurrency per worker |
|
|
2313
|
+
| `reorder_files` | `True` | Pack by file size for balance; `False` preserves order |
|
|
2314
|
+
| `error_when_not_empty` | `False` | Error if `output_dir` already has files |
|
|
2315
|
+
| `reader` | default | Custom reader for inputs |
|
|
2316
|
+
| `batch_size` | `None` | Group inputs into batches for `fn` |
|
|
2317
|
+
| `start_method` | spawn† | Multiprocessing start method (†spawn unless IPython) |
|
|
2318
|
+
| `optimize_dns` | `None` | Optimized DNS (Studio / cloud) |
|
|
2319
|
+
| `storage_options` | `{}` | Cloud credentials / endpoints |
|
|
2320
|
+
| `keep_data_ordered` | `True` | `False` = shared work queue (better for uneven/slow workers) |
|
|
2321
|
+
|
|
2322
|
+
</details>
|
|
2323
|
+
|
|
2324
|
+
<details>
|
|
2325
|
+
<summary> ✅ <code>optimize</code> arguments reference <a id="optimize-kwargs" href="#optimize-kwargs">🔗</a> </summary>
|
|
2326
|
+
|
|
2327
|
+
|
|
2328
|
+
Full knob list for `litdata.optimize` (see Quick start for the minimal recipe). **Exactly one of `chunk_bytes` or `chunk_size`.** Use `if __name__ == "__main__"`.
|
|
2329
|
+
|
|
2330
|
+
| Argument | Default | Description |
|
|
2331
|
+
|----------|---------|-------------|
|
|
2332
|
+
| `fn` | required | Maps each input → sample (or `yield` samples / skip bad ones) |
|
|
2333
|
+
| `inputs` | `None` | Sequence or `StreamingDataLoader` (ignored if `queue` is set) |
|
|
2334
|
+
| `queue` | `None` | `multiprocessing.Queue` of live inputs; send **one** `ALL_DONE` when finished |
|
|
2335
|
+
| `output_dir` | `"optimized_data"` | Local or cloud ([resolver](#resolve-paths)); version remote prefixes |
|
|
2336
|
+
| `input_dir` | `None` | Remote input root for background download |
|
|
2337
|
+
| `weights` | `None` | Per-input weights to balance workers |
|
|
2338
|
+
| `chunk_bytes` | `None` | Max bytes per chunk (e.g. `"64MB"`; see [FAQ](#faq-chunk-shuffle) for larger samples) |
|
|
2339
|
+
| `chunk_size` | `None` | Max items (or tokens with `TokensLoader`) per chunk |
|
|
2340
|
+
| `align_chunking` | `False` | Match single-worker chunk boundaries (needs `chunk_size`; uneven load) |
|
|
2341
|
+
| `compression` | `None` | `"zstd"` today |
|
|
2342
|
+
| `encryption` | `None` | `FernetEncryption` / `RSAEncryption` / custom ([encrypt](#encrypt-decrypt)) |
|
|
2343
|
+
| `num_workers` | CPU count | Local workers |
|
|
2344
|
+
| `fast_dev_run` | `False` | Smoke a subset of inputs |
|
|
2345
|
+
| `num_nodes` / `machine` | `None` | Multi-node on Lightning Studios |
|
|
2346
|
+
| `num_downloaders` / `num_uploaders` | auto | I/O concurrency per worker |
|
|
2347
|
+
| `reorder_files` | `True` | Size-based packing; `False` preserves order |
|
|
2348
|
+
| `reader` | default | Custom input reader |
|
|
2349
|
+
| `batch_size` | `None` | Group inputs for `fn` |
|
|
2350
|
+
| `mode` | `None` | `"append"` or `"overwrite"` existing dataset; default treats data as immutable |
|
|
2351
|
+
| `use_checkpoint` | `False` | Resume an interrupted optimize from `.checkpoints` |
|
|
2352
|
+
| `item_loader` | `None` | e.g. `TokensLoader()` for contiguous tokens |
|
|
2353
|
+
| `start_method` | spawn† | Multiprocessing start method |
|
|
2354
|
+
| `optimize_dns` | `None` | Optimized DNS |
|
|
2355
|
+
| `storage_options` | `{}` | Cloud credentials / endpoints |
|
|
2356
|
+
| `keep_data_ordered` | `True` | `False` = shared queue among workers |
|
|
2357
|
+
| `verbose` | `True` | Progress logging |
|
|
2358
|
+
|
|
2359
|
+
Related features: [shared queue](#shared-queue), [queue input](#queue-input), [append/overwrite](#modify-datasets), [compression](#compression), [TokensLoader / LLM](#llm-training), [filter](#filter-data).
|
|
2360
|
+
|
|
2361
|
+
</details>
|
|
2362
|
+
|
|
2363
|
+
<details>
|
|
2364
|
+
<summary> ✅ Cloud-optimized <code>walk</code> (list files at scale) <a id="walk" href="#walk">🔗</a> </summary>
|
|
2365
|
+
|
|
2366
|
+
|
|
2367
|
+
`litdata.walk` is a threaded, cloud-friendly alternative to `os.walk` for building large `inputs=` lists (especially on Lightning Studios). Yields `(dirpath, dirnames, filenames)` like `os.walk`, but **order is not depth-first**.
|
|
2368
|
+
|
|
2369
|
+
```python
|
|
2370
|
+
from litdata import walk, optimize
|
|
2371
|
+
|
|
2372
|
+
inputs = []
|
|
2373
|
+
for root, dirs, files in walk("/teamspace/s3_connections/my-data/raw", max_workers=32):
|
|
2374
|
+
for name in files:
|
|
2375
|
+
if name.endswith(".jpg"):
|
|
2376
|
+
inputs.append(f"{root}/{name}")
|
|
2377
|
+
|
|
2378
|
+
if __name__ == "__main__":
|
|
2379
|
+
optimize(fn=load_image, inputs=inputs, output_dir="...", chunk_bytes="64MB")
|
|
2380
|
+
```
|
|
2381
|
+
|
|
2382
|
+
Prints a warning outside Lightning Studio — it is optimized for that environment; elsewhere prefer `os.walk` or your cloud SDK’s listing API.
|
|
2383
|
+
|
|
1762
2384
|
</details>
|
|
1763
2385
|
|
|
1764
2386
|
|
|
@@ -1816,8 +2438,7 @@ Speed to stream raw Imagenet 1.2M from different cloud storage providers:
|
|
|
1816
2438
|
| AWS S3 | ~6400 +/- 100 | ~3200 +/- 100 |
|
|
1817
2439
|
| Google Cloud Storage | ~5650 +/- 100 | ~3100 +/- 100 |
|
|
1818
2440
|
|
|
1819
|
-
> **
|
|
1820
|
-
> Use `StreamingRawDataset` if you want to stream your data as-is. Use `StreamingDataset` if you want the fastest streaming and are okay with optimizing your data first.
|
|
2441
|
+
> **Also see:** [`StreamingRawDataset`](#stream-raw) streams existing files with **no optimize step** (great default to start). Use `StreamingDataset` after `optimize` when you need the highest sustained training throughput.
|
|
1821
2442
|
|
|
1822
2443
|
|
|
1823
2444
|
|
|
@@ -1842,7 +2463,7 @@ Time to optimize 1.2 million ImageNet images (Faster is better):
|
|
|
1842
2463
|
|
|
1843
2464
|
## Parallelize data transforms
|
|
1844
2465
|
|
|
1845
|
-
Transformations with LitData are linearly parallelizable across machines.
|
|
2466
|
+
Transformations with LitData are linearly parallelizable across machines on [Lightning Studios](https://lightning.ai/) (see [distributed optimization](#distributed-optimization) for how the job launch works).
|
|
1846
2467
|
|
|
1847
2468
|
For example, let's say that it takes 56 hours to embed a dataset on a single A10G machine. With LitData,
|
|
1848
2469
|
this can be speed up by adding more machines in parallel
|
|
@@ -1855,20 +2476,20 @@ this can be speed up by adding more machines in parallel
|
|
|
1855
2476
|
| ... | ... |
|
|
1856
2477
|
| 64 | 0.875 |
|
|
1857
2478
|
|
|
1858
|
-
To scale the number of machines, run the processing script on [Lightning Studios](https://lightning.ai/):
|
|
1859
|
-
|
|
1860
2479
|
```python
|
|
1861
2480
|
from litdata import map, Machine
|
|
1862
2481
|
|
|
1863
2482
|
map(
|
|
1864
2483
|
...
|
|
1865
2484
|
num_nodes=32,
|
|
1866
|
-
machine=Machine.DATA_PREP,
|
|
2485
|
+
machine=Machine.DATA_PREP, # or omit to inherit the Studio machine
|
|
2486
|
+
# Prefer output_dir on /teamspace/s3_connections/... or s3://...
|
|
1867
2487
|
)
|
|
1868
2488
|
```
|
|
1869
2489
|
|
|
1870
2490
|
## Parallelize data optimization
|
|
1871
|
-
|
|
2491
|
+
|
|
2492
|
+
Same Studio job launch as `map` — `num_nodes` machines × `num_workers` processes; last node merges the index.
|
|
1872
2493
|
|
|
1873
2494
|
```python
|
|
1874
2495
|
from litdata import optimize, Machine
|
|
@@ -1876,7 +2497,8 @@ from litdata import optimize, Machine
|
|
|
1876
2497
|
optimize(
|
|
1877
2498
|
...
|
|
1878
2499
|
num_nodes=32,
|
|
1879
|
-
machine=Machine.DATA_PREP,
|
|
2500
|
+
machine=Machine.DATA_PREP,
|
|
2501
|
+
output_dir="/teamspace/s3_connections/my-data/optimized-v1",
|
|
1880
2502
|
)
|
|
1881
2503
|
```
|
|
1882
2504
|
|
|
@@ -1905,7 +2527,7 @@ Below are templates for real-world applications of LitData at scale.
|
|
|
1905
2527
|
| -------------------------------- | ----------------- | ----------------- | -------------- | -------------- |
|
|
1906
2528
|
| [Benchmark cloud data-loading libraries](https://lightning.ai/lightning-ai/studios/benchmark-cloud-data-loading-libraries) | Image & Label | 10 | 1 | [Imagenet 1M](https://paperswithcode.com/sota/image-classification-on-imagenet?tag_filter=171) |
|
|
1907
2529
|
| [Optimize GeoSpatial data for model training](https://lightning.ai/lightning-ai/studios/convert-spatial-data-to-lightning-streaming) | Image & Mask | 120 | 32 | [Chesapeake Roads Spatial Context](https://github.com/isaaccorley/chesapeakersc) |
|
|
1908
|
-
| [Optimize TinyLlama 1T dataset for training](https://lightning.ai/lightning-ai/studios/prepare-the-tinyllama-1t-token-dataset) | Text | 240 | 32 | [SlimPajama](https://
|
|
2530
|
+
| [Optimize TinyLlama 1T dataset for training](https://lightning.ai/lightning-ai/studios/prepare-the-tinyllama-1t-token-dataset) | Text | 240 | 32 | [SlimPajama](https://www.cerebras.ai/blog/slimpajama-a-627b-token-cleaned-and-deduplicated-version-of-redpajama) & [StarCoder](https://huggingface.co/datasets/bigcode/starcoderdata) |
|
|
1909
2531
|
| [Optimize parquet files for model training](https://lightning.ai/lightning-ai/studios/convert-parquets-to-lightning-streaming) | Parquet Files | 12 | 16 | Randomly Generated data |
|
|
1910
2532
|
|
|
1911
2533
|
|