litdata 0.2.64__tar.gz → 0.2.66__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.64/src/litdata.egg-info → litdata-0.2.66}/PKG-INFO +730 -229
- {litdata-0.2.64 → litdata-0.2.66}/README.md +729 -228
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/__about__.py +1 -1
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/constants.py +3 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/processing/data_processor.py +3 -19
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/processing/utilities.py +0 -73
- litdata-0.2.66/src/litdata/streaming/async_prefetch.py +256 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/config.py +139 -30
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/dataloader.py +13 -1
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/dataset.py +46 -40
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/downloader.py +149 -27
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/item_loader.py +296 -55
- litdata-0.2.66/src/litdata/streaming/reader.py +1090 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/serializers.py +17 -6
- litdata-0.2.66/src/litdata/streaming/timing.py +84 -0
- {litdata-0.2.64 → litdata-0.2.66/src/litdata.egg-info}/PKG-INFO +730 -229
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata.egg-info/SOURCES.txt +2 -0
- litdata-0.2.64/src/litdata/streaming/reader.py +0 -643
- {litdata-0.2.64 → litdata-0.2.66}/CONTRIBUTING.md +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/LICENSE +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/MANIFEST.in +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/requirements.txt +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/setup.cfg +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/setup.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/__init__.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/__main__.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/cli/__init__.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/cli/commands.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/cli/handler/__init__.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/cli/handler/cache.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/cli/handler/optimize.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/cli/parser.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/debugger.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/helpers.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/imports.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/processing/__init__.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/processing/functions.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/processing/readers.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/raw/__init__.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/raw/dataset.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/raw/indexer.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/raw/types.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/requirements.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/__init__.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/cache.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/client.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/combined.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/compression.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/fs_provider.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/parallel.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/resolver.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/sampler.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/shuffle.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/writer.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/__init__.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/_pytree.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/base.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/breakpoint.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/broadcast.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/dataset_utilities.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/encryption.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/env.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/format.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/hf_dataset.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/packing.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/parquet.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/shuffle.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/subsample.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/torch_utils.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/train_test_split.py +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata.egg-info/dependency_links.txt +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata.egg-info/entry_points.txt +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata.egg-info/not-zip-safe +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/src/litdata.egg-info/requires.txt +0 -0
- {litdata-0.2.64 → litdata-0.2.66}/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.66
|
|
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
|
|
@@ -92,6 +92,7 @@ Transform Optimize
|
|
|
92
92
|
<a href="#speed-up-model-training">Optimize data</a> •
|
|
93
93
|
<a href="#transform-datasets">Transform data</a> •
|
|
94
94
|
<a href="#key-features">Features</a> •
|
|
95
|
+
<a href="#resolve-paths">Paths & cloud URLs</a> •
|
|
95
96
|
<a href="#benchmarks">Benchmarks</a> •
|
|
96
97
|
<a href="#start-from-a-template">Templates</a> •
|
|
97
98
|
<a href="#community">Community</a>
|
|
@@ -147,6 +148,19 @@ pip install 'litdata[extras]'
|
|
|
147
148
|
|
|
148
149
|
</details>
|
|
149
150
|
|
|
151
|
+
<details>
|
|
152
|
+
<summary>AI agent skill (Cursor, Claude Code, …)</summary>
|
|
153
|
+
|
|
154
|
+
Install the LitData expert skill so coding agents know the full API, path resolver, optimize/stream recipes, and internals:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
npx skills add Lightning-AI/litData
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Source: [`.claude/skills/litdata/`](.claude/skills/litdata/) in this repository ([skills CLI](https://github.com/vercel-labs/skills)).
|
|
161
|
+
|
|
162
|
+
</details>
|
|
163
|
+
|
|
150
164
|
|
|
151
165
|
|
|
152
166
|
----
|
|
@@ -187,25 +201,27 @@ Transform raw data into optimized chunks for maximum streaming speed.
|
|
|
187
201
|
This step formats the dataset for fast loading by writing data in an efficient chunked binary format.
|
|
188
202
|
|
|
189
203
|
```python
|
|
204
|
+
import io
|
|
190
205
|
import numpy as np
|
|
191
206
|
from PIL import Image
|
|
192
207
|
import litdata as ld
|
|
193
208
|
|
|
194
209
|
def random_images(index):
|
|
195
|
-
# Replace with your actual image loading
|
|
196
|
-
#
|
|
197
|
-
#
|
|
198
|
-
|
|
210
|
+
# Replace with your actual image loading (e.g. Image.open("photo.jpg")).
|
|
211
|
+
# Prefer JPEG: return a JpegImageFile, or re-encode at quality≈95. Plain
|
|
212
|
+
# Image.fromarray(...) stores uncompressed PIL RAW and can be 10×+ larger.
|
|
213
|
+
img = Image.fromarray(np.random.randint(0, 256, (32, 32, 3), dtype=np.uint8))
|
|
214
|
+
buf = io.BytesIO()
|
|
215
|
+
img.convert("RGB").save(buf, format="JPEG", quality=95)
|
|
216
|
+
buf.seek(0)
|
|
217
|
+
jpeg_image = Image.open(buf) # JpegImageFile → compressed bytes in the chunk
|
|
199
218
|
fake_labels = np.random.randint(10)
|
|
200
219
|
|
|
201
|
-
#
|
|
202
|
-
|
|
203
|
-
data = {"index": index, "image": fake_images, "class": fake_labels}
|
|
204
|
-
|
|
205
|
-
return data
|
|
220
|
+
# Keys/types must stay stable across samples; list lengths/types fixed
|
|
221
|
+
return {"index": index, "image": jpeg_image, "class": fake_labels}
|
|
206
222
|
|
|
207
223
|
if __name__ == "__main__":
|
|
208
|
-
#
|
|
224
|
+
# Exactly one of chunk_bytes or chunk_size
|
|
209
225
|
ld.optimize(
|
|
210
226
|
fn=random_images, # the function applied to each input
|
|
211
227
|
inputs=list(range(1000)), # the inputs to the function (here it's a list of numbers)
|
|
@@ -229,7 +245,12 @@ Load the data by replacing the PyTorch Dataset and DataLoader with the Streaming
|
|
|
229
245
|
```python
|
|
230
246
|
import litdata as ld
|
|
231
247
|
|
|
232
|
-
dataset = ld.StreamingDataset(
|
|
248
|
+
dataset = ld.StreamingDataset(
|
|
249
|
+
's3://my-bucket/fast_data',
|
|
250
|
+
shuffle=True,
|
|
251
|
+
drop_last=True, # important for multi-GPU so every rank sees the same length
|
|
252
|
+
seed=42,
|
|
253
|
+
)
|
|
233
254
|
|
|
234
255
|
# Custom collate function to handle the batch (optional)
|
|
235
256
|
def collate_fn(batch):
|
|
@@ -239,7 +260,7 @@ def collate_fn(batch):
|
|
|
239
260
|
}
|
|
240
261
|
|
|
241
262
|
|
|
242
|
-
dataloader = ld.StreamingDataLoader(dataset, collate_fn=collate_fn)
|
|
263
|
+
dataloader = ld.StreamingDataLoader(dataset, batch_size=64, collate_fn=collate_fn)
|
|
243
264
|
for sample in dataloader:
|
|
244
265
|
img, cls = sample["image"], sample["class"]
|
|
245
266
|
```
|
|
@@ -412,10 +433,6 @@ storage_options = {
|
|
|
412
433
|
"aws_secret_access_key": "your_secret_access_key",
|
|
413
434
|
}
|
|
414
435
|
|
|
415
|
-
dataset = StreamingDataset('s3://my-bucket/my-data', storage_options=storage_options)
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
436
|
dataset = StreamingDataset('s3://my-bucket/my-data', storage_options=storage_options)
|
|
420
437
|
```
|
|
421
438
|
|
|
@@ -427,108 +444,198 @@ from litdata import StreamingDataset
|
|
|
427
444
|
dataset = StreamingDataset('s3://my-bucket/my-data', cache_dir="/path/to/cache")
|
|
428
445
|
```
|
|
429
446
|
|
|
447
|
+
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).
|
|
448
|
+
|
|
430
449
|
</details>
|
|
431
450
|
|
|
432
451
|
<details>
|
|
433
|
-
<summary> ✅
|
|
434
|
-
|
|
452
|
+
<summary> ✅ Optimize images as JPEG (not raw PIL) <a id="optimize-jpeg" href="#optimize-jpeg">🔗</a> </summary>
|
|
435
453
|
|
|
436
454
|
|
|
437
|
-
|
|
455
|
+
How you return images from `optimize` controls storage size and streaming speed.
|
|
438
456
|
|
|
439
|
-
|
|
440
|
-
|
|
457
|
+
| What you return | Serializer | Result |
|
|
458
|
+
|-----------------|------------|--------|
|
|
459
|
+
| `PIL.JpegImageFile` (e.g. `Image.open("x.jpg")`) | JPEG | Compressed bytes — **preferred** |
|
|
460
|
+
| Plain `PIL.Image` / `Image.fromarray(...)` | PIL RAW | Uncompressed pixels — often **10×+ larger** |
|
|
441
461
|
|
|
442
|
-
|
|
462
|
+
**Best practice:** store JPEG at **quality ≈ 95** (or keep existing `.jpg` files). Resize when helpful.
|
|
463
|
+
|
|
464
|
+
```python
|
|
465
|
+
import io
|
|
466
|
+
from PIL import Image
|
|
467
|
+
import litdata as ld
|
|
468
|
+
|
|
469
|
+
def load_image(path):
|
|
470
|
+
img = Image.open(path)
|
|
471
|
+
if not str(path).lower().endswith((".jpg", ".jpeg")):
|
|
472
|
+
buf = io.BytesIO()
|
|
473
|
+
img.convert("RGB").save(buf, format="JPEG", quality=95)
|
|
474
|
+
buf.seek(0)
|
|
475
|
+
img = Image.open(buf) # JpegImageFile
|
|
476
|
+
return {"image": img, "path": path}
|
|
477
|
+
|
|
478
|
+
if __name__ == "__main__":
|
|
479
|
+
ld.optimize(fn=load_image, inputs=list_of_paths, output_dir="fast_data", chunk_bytes="64MB", num_workers=8)
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
Ready-made ImageNet optimize/stream scripts: `benchmarks/litdata/` (`--write_mode jpeg --quality 90`).
|
|
443
483
|
|
|
444
484
|
</details>
|
|
445
485
|
|
|
446
|
-
|
|
486
|
+
<details>
|
|
487
|
+
<summary> ✅ Custom serializers <a id="serializers" href="#serializers">🔗</a> </summary>
|
|
488
|
+
|
|
447
489
|
|
|
448
|
-
|
|
449
|
-
```sh
|
|
450
|
-
pip install "litdata[extra]" huggingface_hub
|
|
490
|
+
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
491
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
492
|
+
For images, returning a `JpegImageFile` selects **`jpeg`**; a plain `PIL.Image` selects **`pil`** (raw pixels). See [Optimize images as JPEG](#optimize-jpeg).
|
|
493
|
+
|
|
494
|
+
Pass custom serializers when **streaming** (and when using the lower-level `Cache` writer):
|
|
495
|
+
|
|
496
|
+
```python
|
|
497
|
+
from litdata import StreamingDataset
|
|
498
|
+
from litdata.streaming.serializers import Serializer
|
|
499
|
+
|
|
500
|
+
class MyTypeSerializer(Serializer):
|
|
501
|
+
def serialize(self, item):
|
|
502
|
+
return item.to_bytes(), None # (bytes, optional metadata string)
|
|
503
|
+
|
|
504
|
+
def deserialize(self, data: bytes):
|
|
505
|
+
return MyType.from_bytes(data)
|
|
506
|
+
|
|
507
|
+
def can_serialize(self, item) -> bool:
|
|
508
|
+
return isinstance(item, MyType)
|
|
509
|
+
|
|
510
|
+
dataset = StreamingDataset(
|
|
511
|
+
"s3://bucket/data",
|
|
512
|
+
serializers={"my_type": MyTypeSerializer()}, # merged on top of built-ins
|
|
513
|
+
)
|
|
455
514
|
```
|
|
456
515
|
|
|
457
|
-
|
|
516
|
+
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.
|
|
517
|
+
|
|
518
|
+
</details>
|
|
519
|
+
|
|
520
|
+
<details>
|
|
521
|
+
<summary> ✅ Stream MosaicML MDS datasets <a id="stream-mds" href="#stream-mds">🔗</a> </summary>
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
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!
|
|
525
|
+
|
|
526
|
+
LitData's default `PyTreeLoader` natively understands the MDS binary layout, so you can read existing MDS shards using the familiar `StreamingDataset` and `StreamingDataLoader` APIs.
|
|
527
|
+
|
|
528
|
+
**Assumption:**
|
|
529
|
+
|
|
530
|
+
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`.
|
|
531
|
+
|
|
532
|
+
**Stream the MDS dataset:**
|
|
458
533
|
|
|
459
534
|
```python
|
|
460
535
|
import litdata as ld
|
|
461
536
|
|
|
462
|
-
#
|
|
463
|
-
hf_dataset_uri = "hf://datasets/leonardPKU/clevr_cogen_a_train/data"
|
|
537
|
+
# point to your MDS dataset stored locally or in the cloud
|
|
464
538
|
|
|
465
|
-
#
|
|
466
|
-
|
|
539
|
+
mds_dataset_uri = "s3://my-bucket/my-mds-data" # or a local path
|
|
540
|
+
|
|
541
|
+
# LitData automatically detects and deserializes the MDS format
|
|
542
|
+
|
|
543
|
+
dataset = ld.StreamingDataset(mds_dataset_uri)
|
|
467
544
|
|
|
468
|
-
# Print the first sample
|
|
469
545
|
print("Sample", dataset[0])
|
|
470
546
|
|
|
471
|
-
# Stream the dataset using StreamingDataLoader
|
|
472
547
|
dataloader = ld.StreamingDataLoader(dataset, batch_size=4)
|
|
473
548
|
for sample in dataloader:
|
|
474
|
-
|
|
549
|
+
pass
|
|
475
550
|
```
|
|
476
551
|
|
|
477
|
-
|
|
552
|
+
**How it works:**
|
|
553
|
+
|
|
554
|
+
- 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.
|
|
555
|
+
- Fixed-size columns are read directly, while variable-size columns are prefixed with a `uint32` length header—exactly as in the MosaicML MDS spec.
|
|
556
|
+
- Each sample is reconstructed into its original Python structure via LitData's `data_spec`.
|
|
557
|
+
|
|
558
|
+
**Key benefits:**
|
|
559
|
+
|
|
560
|
+
✅ **Zero conversion:** Reuse existing MDS shards as-is.
|
|
561
|
+
✅ **Drop-in APIs:** Use the same `StreamingDataset` / `StreamingDataLoader` you already know.
|
|
562
|
+
✅ **Cloud-native:** Stream MDS shards directly from S3, GCS, or Azure.
|
|
563
|
+
✅ **Easy migration:** Move from MosaicML Streaming to LitData without re-optimizing.
|
|
564
|
+
|
|
565
|
+
> **Note:** Encrypted data loading is not currently supported for the MDS format.
|
|
566
|
+
|
|
567
|
+
</details>
|
|
478
568
|
|
|
479
|
-
|
|
569
|
+
<details>
|
|
570
|
+
<summary> ✅ Stream Hugging Face 🤗 datasets <a id="stream-hf" href="#stream-hf">🔗</a> </summary>
|
|
480
571
|
|
|
481
572
|
|
|
482
573
|
|
|
483
|
-
|
|
574
|
+
To use your favorite Hugging Face dataset with LitData, simply pass its URL to `StreamingDataset`.
|
|
484
575
|
|
|
485
|
-
|
|
576
|
+
<details>
|
|
577
|
+
<summary>How to get HF dataset URI?</summary>
|
|
486
578
|
|
|
487
|
-
|
|
488
|
-
import litdata as ld
|
|
579
|
+
https://github.com/user-attachments/assets/3ba9e2ef-bf6b-41fc-a578-e4b4113a0e72
|
|
489
580
|
|
|
490
|
-
|
|
581
|
+
</details>
|
|
491
582
|
|
|
492
|
-
|
|
583
|
+
**Prerequisites:**
|
|
584
|
+
|
|
585
|
+
```sh
|
|
586
|
+
pip install 'litdata[extras]' huggingface_hub
|
|
587
|
+
|
|
588
|
+
# Optional: faster downloads on high-bandwidth networks
|
|
589
|
+
pip install hf_transfer
|
|
590
|
+
export HF_HUB_ENABLE_HF_TRANSFER=1
|
|
493
591
|
```
|
|
494
592
|
|
|
495
|
-
|
|
593
|
+
**Supported for HF:** datasets stored as **Parquet** only. Gated datasets: set `HF_TOKEN`.
|
|
496
594
|
|
|
497
|
-
|
|
595
|
+
**Stream Hugging Face dataset** (auto-index + auto `ParquetLoader`):
|
|
498
596
|
|
|
499
|
-
|
|
597
|
+
```python
|
|
598
|
+
import litdata as ld
|
|
500
599
|
|
|
501
|
-
|
|
600
|
+
hf_dataset_uri = "hf://datasets/leonardPKU/clevr_cogen_a_train/data"
|
|
502
601
|
|
|
503
|
-
|
|
602
|
+
dataset = ld.StreamingDataset(hf_dataset_uri) # indexes on first use; caches index.json locally
|
|
603
|
+
print("Sample", dataset[0]) # dict of columns
|
|
504
604
|
|
|
505
|
-
|
|
605
|
+
# With workers on Linux, use spawn (same as other ParquetLoader usage)
|
|
606
|
+
dataloader = ld.StreamingDataLoader(
|
|
607
|
+
dataset, batch_size=4, num_workers=4, multiprocessing_context="spawn"
|
|
608
|
+
)
|
|
609
|
+
for sample in dataloader:
|
|
610
|
+
pass
|
|
611
|
+
```
|
|
506
612
|
|
|
507
|
-
|
|
613
|
+
Unlike local/S3 parquet ([stream parquet](#stream-parquet)), `hf://` **automatically** indexes (if needed) and selects `ParquetLoader`.
|
|
614
|
+
|
|
615
|
+
### Indexing the HF dataset (optional, faster cold start)
|
|
508
616
|
|
|
509
617
|
```python
|
|
510
618
|
import litdata as ld
|
|
511
619
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
ld.index_parquet_dataset(hf_dataset_uri, "hf-index-dir")
|
|
620
|
+
# Returns the local cache directory that contains index.json
|
|
621
|
+
cache_dir = ld.index_hf_dataset("hf://datasets/leonardPKU/clevr_cogen_a_train/data")
|
|
515
622
|
```
|
|
516
623
|
|
|
517
|
-
|
|
624
|
+
Or control the index path explicitly:
|
|
518
625
|
|
|
519
626
|
```python
|
|
520
627
|
import litdata as ld
|
|
521
628
|
from litdata.streaming.item_loader import ParquetLoader
|
|
522
629
|
|
|
523
|
-
|
|
630
|
+
uri = "hf://datasets/open-thoughts/OpenThoughts-114k/data"
|
|
631
|
+
ld.index_parquet_dataset(uri, "hf-index-dir") # writes index under hf-index-dir
|
|
524
632
|
|
|
525
|
-
dataset = ld.StreamingDataset(
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
pass
|
|
633
|
+
dataset = ld.StreamingDataset(uri, item_loader=ParquetLoader(), index_path="hf-index-dir")
|
|
634
|
+
for batch in ld.StreamingDataLoader(dataset, batch_size=4, multiprocessing_context="spawn"):
|
|
635
|
+
pass
|
|
529
636
|
```
|
|
530
637
|
|
|
531
|
-
|
|
638
|
+
See also [Stream parquet datasets](#stream-parquet) for `ParquetLoader` knobs, wildcards, and stream-vs-optimize.
|
|
532
639
|
|
|
533
640
|
### LitData `Optimize` v/s `Parquet`
|
|
534
641
|
<!-- TODO: Update benchmark -->
|
|
@@ -574,6 +681,81 @@ for batch in val_dataloader:
|
|
|
574
681
|
|
|
575
682
|
</details>
|
|
576
683
|
|
|
684
|
+
<details>
|
|
685
|
+
<summary> ✅ Shuffle, seed, and drop_last <a id="shuffle" href="#shuffle">🔗</a> </summary>
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
Shuffling is **deterministic** and designed for distributed training:
|
|
689
|
+
|
|
690
|
+
1. Chunks are assigned (and possibly split) across ranks/workers.
|
|
691
|
+
2. Items inside each chunk are permuted.
|
|
692
|
+
|
|
693
|
+
The permutation depends on `seed`, the epoch, and chunk metadata — the same settings always yield the same order (required for resumable `state_dict`).
|
|
694
|
+
|
|
695
|
+
```python
|
|
696
|
+
from litdata import StreamingDataset, StreamingDataLoader
|
|
697
|
+
|
|
698
|
+
train = StreamingDataset(
|
|
699
|
+
"s3://my-bucket/train",
|
|
700
|
+
shuffle=True,
|
|
701
|
+
drop_last=True, # keep every rank/worker at the same length (default True under DDP)
|
|
702
|
+
seed=42, # default is 42; keep stable when resuming
|
|
703
|
+
)
|
|
704
|
+
loader = StreamingDataLoader(train, batch_size=64, num_workers=8)
|
|
705
|
+
|
|
706
|
+
# shuffle=/drop_last= on the loader override the dataset
|
|
707
|
+
loader = StreamingDataLoader(train, batch_size=64, shuffle=True, drop_last=True)
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
**Notes**
|
|
711
|
+
|
|
712
|
+
- Val/test: usually `shuffle=False`, `drop_last=False`.
|
|
713
|
+
- If `drop_last=False` under multi-GPU, LitData warns — collectives can hang when ranks see different lengths.
|
|
714
|
+
- Resume with `loader.state_dict()` / `load_state_dict()`. To deliberately ignore checkpointed shuffle settings, set `force_override_state_dict=True` on the dataset.
|
|
715
|
+
|
|
716
|
+
</details>
|
|
717
|
+
|
|
718
|
+
<details>
|
|
719
|
+
<summary> ✅ StreamingDataset & StreamingDataLoader knobs <a id="streaming-kwargs" href="#streaming-kwargs">🔗</a> </summary>
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
**`StreamingDataset`**
|
|
723
|
+
|
|
724
|
+
| Argument | Default | Description |
|
|
725
|
+
|----------|---------|-------------|
|
|
726
|
+
| `input_dir` | required | Local path, cloud URI, `Dir`, or parquet path (basename wildcards OK) |
|
|
727
|
+
| `cache_dir` | `LITDATA_CACHE_DIR` or `~/.lightning/chunks` | Where chunks are cached |
|
|
728
|
+
| `item_loader` | from index | `TokensLoader`, `ParquetLoader`, … |
|
|
729
|
+
| `shuffle` | `False` | Deterministic shuffle (see [Shuffle](#shuffle)) |
|
|
730
|
+
| `drop_last` | `True` if distributed else `False` | Equal length across ranks |
|
|
731
|
+
| `seed` | `42` | Shuffle / subsample RNG |
|
|
732
|
+
| `serializers` | built-ins | Custom serialize/deserialize map |
|
|
733
|
+
| `max_cache_size` | `"100GB"` | Evict consumed chunks beyond this size |
|
|
734
|
+
| `max_pre_download` | `2` | Chunks each worker may prefetch (raise for throughput; watch disk) |
|
|
735
|
+
| `subsample` | `1.0` | Fraction of data (`0.01`) or upsample (`2.5`) |
|
|
736
|
+
| `encryption` | `None` | `FernetEncryption` / `RSAEncryption` / custom |
|
|
737
|
+
| `storage_options` | `{}` | Cloud client options |
|
|
738
|
+
| `session_options` | `{}` | boto3 session options (S3) |
|
|
739
|
+
| `index_path` | `None` | Parquet/HF `index.json` file or directory |
|
|
740
|
+
| `force_override_state_dict` | `False` | Local ctor args override loaded checkpoint |
|
|
741
|
+
| `transform` | `None` | Callable or list of callables per sample |
|
|
742
|
+
|
|
743
|
+
Peak disk ≈ `num_workers × max_pre_download × mean_chunk_size`.
|
|
744
|
+
|
|
745
|
+
**`StreamingDataLoader`**
|
|
746
|
+
|
|
747
|
+
| Argument | Description |
|
|
748
|
+
|----------|-------------|
|
|
749
|
+
| All usual `torch.utils.data.DataLoader` kwargs | `batch_size`, `num_workers`, `collate_fn`, `pin_memory`, … |
|
|
750
|
+
| `shuffle` / `drop_last` | Forwarded to the streaming dataset |
|
|
751
|
+
| `profile_batches` | `int` / `True` / `False` — viztracer worker trace (see [Profile data loading](#profile-loading)) |
|
|
752
|
+
| `profile_skip_batches` / `profile_dir` | Warm-up skip count; output dir for `result.json` |
|
|
753
|
+
| `multiprocessing_context` | Use **`"spawn"`** (or `"forkserver"`) with `ParquetLoader` + `num_workers>0` on Linux |
|
|
754
|
+
|
|
755
|
+
Prefer `StreamingDataLoader` over a plain PyTorch `DataLoader` for optimized / combined / parallel datasets (resume + correct batch metadata).
|
|
756
|
+
|
|
757
|
+
</details>
|
|
758
|
+
|
|
577
759
|
<details>
|
|
578
760
|
<summary> ✅ Stream from multiple cloud providers <a id="cloud-providers" href="#cloud-providers">🔗</a> </summary>
|
|
579
761
|
|
|
@@ -935,7 +1117,12 @@ train_datasets = [
|
|
|
935
1117
|
|
|
936
1118
|
# Mix SlimPajama data and Starcoder data with these proportions:
|
|
937
1119
|
weights = (0.693584, 0.306416)
|
|
938
|
-
combined_dataset = CombinedStreamingDataset(
|
|
1120
|
+
combined_dataset = CombinedStreamingDataset(
|
|
1121
|
+
datasets=train_datasets,
|
|
1122
|
+
seed=42,
|
|
1123
|
+
weights=weights,
|
|
1124
|
+
iterate_over_all=False, # required when passing weights (see below)
|
|
1125
|
+
)
|
|
939
1126
|
|
|
940
1127
|
train_dataloader = StreamingDataLoader(combined_dataset, batch_size=8, pin_memory=True, num_workers=os.cpu_count())
|
|
941
1128
|
|
|
@@ -944,37 +1131,34 @@ for batch in tqdm(train_dataloader):
|
|
|
944
1131
|
pass
|
|
945
1132
|
```
|
|
946
1133
|
|
|
947
|
-
|
|
1134
|
+
**`iterate_over_all` vs `weights` (important)**
|
|
1135
|
+
|
|
1136
|
+
| Mode | Behavior |
|
|
1137
|
+
|------|----------|
|
|
1138
|
+
| `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). |
|
|
1139
|
+
| `iterate_over_all=False` | Stop when **any** dataset is exhausted. Pass explicit `weights` for your mixture (e.g. TinyLlama). Length may be `None` (variable). |
|
|
948
1140
|
|
|
949
|
-
|
|
1141
|
+
**Batching Methods** (`batching_method`)
|
|
950
1142
|
|
|
951
|
-
**Stratified
|
|
952
|
-
With `batching_method="stratified"` (the default), each batch contains samples from multiple datasets according to the specified weights:
|
|
1143
|
+
**Stratified** (default): each batch mixes samples from multiple datasets according to the weights.
|
|
953
1144
|
|
|
954
1145
|
```python
|
|
955
|
-
# Default stratified batching - batches mix samples from all datasets
|
|
956
1146
|
combined_dataset = CombinedStreamingDataset(
|
|
957
|
-
datasets=[dataset1, dataset2],
|
|
958
|
-
batching_method="stratified" #
|
|
1147
|
+
datasets=[dataset1, dataset2],
|
|
1148
|
+
batching_method="stratified", # default
|
|
959
1149
|
)
|
|
960
1150
|
```
|
|
961
1151
|
|
|
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:
|
|
1152
|
+
**Per-stream**: each batch comes from only one randomly selected dataset (useful when shapes/dtypes differ).
|
|
964
1153
|
|
|
965
1154
|
```python
|
|
966
|
-
# Per-stream batching - each batch contains samples from only one dataset
|
|
967
1155
|
combined_dataset = CombinedStreamingDataset(
|
|
968
|
-
datasets=[dataset1, dataset2],
|
|
969
|
-
batching_method="per_stream"
|
|
1156
|
+
datasets=[dataset1, dataset2],
|
|
1157
|
+
batching_method="per_stream",
|
|
970
1158
|
)
|
|
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
1159
|
```
|
|
1160
|
+
|
|
1161
|
+
Other knobs: `seed` (default `42`), `force_override_state_dict=True` to let local ctor args override a loaded checkpoint.
|
|
978
1162
|
</details>
|
|
979
1163
|
|
|
980
1164
|
<details>
|
|
@@ -1255,59 +1439,113 @@ The `overwrite` mode will delete the existing data and start from fresh.
|
|
|
1255
1439
|
<summary> ✅ Stream parquet datasets <a id="stream-parquet" href="#stream-parquet">🔗</a> </summary>
|
|
1256
1440
|
|
|
1257
1441
|
|
|
1258
|
-
Stream Parquet
|
|
1442
|
+
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
1443
|
|
|
1260
|
-
|
|
1444
|
+
### Stream vs optimize vs map
|
|
1261
1445
|
|
|
1262
|
-
|
|
1446
|
+
| Goal | Use |
|
|
1447
|
+
|------|-----|
|
|
1448
|
+
| Train on parquet as-is (no conversion) | `index_parquet_dataset` → `StreamingDataset` + `ParquetLoader` |
|
|
1449
|
+
| Faster I/O / tokenize / custom sample shape | `optimize(fn)` that `yield`s rows from parquet ([reduce memory](#reduce-memory)) |
|
|
1450
|
+
| Reshard huge parquet files while mapping | `map(..., reader=ParquetReader(cache_folder, num_rows=...))` |
|
|
1263
1451
|
|
|
1264
|
-
|
|
1452
|
+
Each sample from `ParquetLoader` is a **`dict`** (column name → value).
|
|
1265
1453
|
|
|
1266
|
-
|
|
1454
|
+
### Prerequisites
|
|
1267
1455
|
|
|
1268
1456
|
```bash
|
|
1269
|
-
#
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
pip install "litdata[extra]" gcsfs
|
|
1457
|
+
pip install 'litdata[extras]' # includes polars + pyarrow
|
|
1458
|
+
# Cloud listing/index extras as needed:
|
|
1459
|
+
pip install s3fs # s3://
|
|
1460
|
+
pip install gcsfs # gs://
|
|
1274
1461
|
```
|
|
1275
1462
|
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
Index your Parquet dataset to create an index file that LitData can use to stream the dataset.
|
|
1463
|
+
### Index a parquet directory
|
|
1279
1464
|
|
|
1280
1465
|
```python
|
|
1281
1466
|
import litdata as ld
|
|
1282
1467
|
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1468
|
+
ld.index_parquet_dataset(
|
|
1469
|
+
"s3://my-bucket/my-parquet-data", # local path, s3://, gs://, or hf://
|
|
1470
|
+
cache_dir=None, # see table below
|
|
1471
|
+
storage_options={}, # cloud credentials / endpoints
|
|
1472
|
+
num_workers=4, # parallel metadata reads
|
|
1473
|
+
)
|
|
1287
1474
|
```
|
|
1288
1475
|
|
|
1289
|
-
|
|
1476
|
+
| Scheme | Where `index.json` is written |
|
|
1477
|
+
|--------|-------------------------------|
|
|
1478
|
+
| Local directory | Next to the files, or under `cache_dir` if set |
|
|
1479
|
+
| `s3://` / `gs://` | **Uploaded to the bucket** at `{url}/index.json` (needs write access) |
|
|
1480
|
+
| `hf://` | **Local** `cache_dir` (required for HF indexing via this helper) |
|
|
1290
1481
|
|
|
1291
|
-
|
|
1482
|
+
**Indexing notes**
|
|
1292
1483
|
|
|
1484
|
+
- Lists **top-level** `.parquet` files only (not recursive subfolders).
|
|
1485
|
+
- All files must share the same schema.
|
|
1486
|
+
- Supported for indexing today: local, `s3://`, `gs://`, `hf://` (not `r2://` / `azure://` yet).
|
|
1487
|
+
- For HF, prefer `index_hf_dataset(uri)` (returns a local cache dir) or auto-index via `StreamingDataset("hf://...")` — see [HF section](#stream-hf).
|
|
1488
|
+
|
|
1489
|
+
### Stream with `ParquetLoader`
|
|
1490
|
+
|
|
1491
|
+
Unlike `hf://`, local/S3/GCS parquet **does not** auto-select the loader — pass `ParquetLoader` explicitly (it must match `index.json`).
|
|
1293
1492
|
|
|
1294
1493
|
```python
|
|
1295
1494
|
import litdata as ld
|
|
1296
1495
|
from litdata.streaming.item_loader import ParquetLoader
|
|
1297
1496
|
|
|
1298
|
-
|
|
1299
|
-
|
|
1497
|
+
uri = "s3://my-bucket/my-parquet-data"
|
|
1498
|
+
dataset = ld.StreamingDataset(
|
|
1499
|
+
uri,
|
|
1500
|
+
item_loader=ParquetLoader(low_memory=True), # default: row-group streaming
|
|
1501
|
+
# index_path="/path/to/index.json", # optional if index lives elsewhere
|
|
1502
|
+
)
|
|
1300
1503
|
|
|
1301
|
-
#
|
|
1302
|
-
dataset = ld.StreamingDataset(
|
|
1504
|
+
# Basename wildcards when the path ends with .parquet:
|
|
1505
|
+
# dataset = ld.StreamingDataset("s3://bucket/data/train-*.parquet", item_loader=ParquetLoader())
|
|
1303
1506
|
|
|
1304
|
-
print(
|
|
1507
|
+
print(dataset[0]) # dict of columns
|
|
1305
1508
|
|
|
1306
|
-
|
|
1307
|
-
|
|
1509
|
+
# Linux + num_workers>0: use spawn (Polars + fork deadlocks)
|
|
1510
|
+
dataloader = ld.StreamingDataLoader(
|
|
1511
|
+
dataset,
|
|
1512
|
+
batch_size=4,
|
|
1513
|
+
num_workers=4,
|
|
1514
|
+
multiprocessing_context="spawn",
|
|
1515
|
+
)
|
|
1516
|
+
for batch in dataloader:
|
|
1308
1517
|
pass
|
|
1309
1518
|
```
|
|
1310
1519
|
|
|
1520
|
+
### `ParquetLoader` knobs
|
|
1521
|
+
|
|
1522
|
+
| Arg | Default | Meaning |
|
|
1523
|
+
|-----|---------|---------|
|
|
1524
|
+
| `low_memory` | `True` | Stream by row group (lower RAM). `False` loads each whole file into memory (warns). |
|
|
1525
|
+
| `pre_load_chunk` | `False` | Prefetch full DataFrame — **only effective when `low_memory=False`**. |
|
|
1526
|
+
|
|
1527
|
+
Import: `from litdata.streaming.item_loader import ParquetLoader` (not re-exported at `litdata` top level).
|
|
1528
|
+
|
|
1529
|
+
### Reshard parquet for `map` / `optimize`
|
|
1530
|
+
|
|
1531
|
+
```python
|
|
1532
|
+
from litdata import map
|
|
1533
|
+
from litdata.processing.readers import ParquetReader
|
|
1534
|
+
|
|
1535
|
+
def process(pq_file, output_dir):
|
|
1536
|
+
# pq_file is a pyarrow.parquet.ParquetFile
|
|
1537
|
+
...
|
|
1538
|
+
|
|
1539
|
+
map(
|
|
1540
|
+
fn=process,
|
|
1541
|
+
inputs=list_of_parquet_paths,
|
|
1542
|
+
output_dir="s3://bucket/out",
|
|
1543
|
+
reader=ParquetReader(cache_folder="/tmp/pq-shards", num_rows=65536),
|
|
1544
|
+
)
|
|
1545
|
+
```
|
|
1546
|
+
|
|
1547
|
+
`ParquetReader` splits inputs that exceed `num_rows` into smaller cached files before your `fn` runs.
|
|
1548
|
+
|
|
1311
1549
|
</details>
|
|
1312
1550
|
|
|
1313
1551
|
<details>
|
|
@@ -1393,17 +1631,60 @@ for batch in dataloader:
|
|
|
1393
1631
|
<summary> ✅ Profile data loading speed <a id="profile-loading" href="#profile-loading">🔗</a> </summary>
|
|
1394
1632
|
|
|
1395
1633
|
|
|
1396
|
-
|
|
1634
|
+
`StreamingDataLoader` can record a **viztracer** Chrome trace of the DataLoader worker loop so you can see where time goes (fetch, deserialize, collate, IPC).
|
|
1635
|
+
|
|
1636
|
+
### Prerequisites
|
|
1637
|
+
|
|
1638
|
+
```bash
|
|
1639
|
+
pip install viztracer
|
|
1640
|
+
```
|
|
1641
|
+
|
|
1642
|
+
Profiling requires **`num_workers >= 1`** (raises otherwise). On multi-GPU, only **global rank 0** installs the worker profiler.
|
|
1397
1643
|
|
|
1398
|
-
|
|
1644
|
+
### Usage
|
|
1399
1645
|
|
|
1400
1646
|
```python
|
|
1401
1647
|
from litdata import StreamingDataset, StreamingDataLoader
|
|
1402
1648
|
|
|
1403
|
-
|
|
1649
|
+
dataset = StreamingDataset("s3://my-bucket/my-data", shuffle=True, drop_last=True)
|
|
1650
|
+
|
|
1651
|
+
loader = StreamingDataLoader(
|
|
1652
|
+
dataset,
|
|
1653
|
+
batch_size=64,
|
|
1654
|
+
num_workers=4,
|
|
1655
|
+
profile_batches=20, # record this many batches (int), or True for the whole run
|
|
1656
|
+
profile_skip_batches=5, # warm up / skip cold-start batches before recording
|
|
1657
|
+
profile_dir="./profiles", # where to write result.json (default: cwd)
|
|
1658
|
+
)
|
|
1659
|
+
|
|
1660
|
+
for batch in loader:
|
|
1661
|
+
train_step(batch)
|
|
1662
|
+
# after profile_batches (+ skip) complete, worker 0 saves the trace and prints the path
|
|
1404
1663
|
```
|
|
1405
1664
|
|
|
1406
|
-
|
|
1665
|
+
| Arg | Default | Meaning |
|
|
1666
|
+
|-----|---------|---------|
|
|
1667
|
+
| `profile_batches` | `False` | `int` → stop after that many **recorded** batches; `True` → profile until the iterator ends; `False` → off |
|
|
1668
|
+
| `profile_skip_batches` | `0` | Batches to skip before the tracer starts (useful to skip cache cold-start) |
|
|
1669
|
+
| `profile_dir` | current working directory | Directory for `result.json` (overwrites an existing file) |
|
|
1670
|
+
|
|
1671
|
+
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.
|
|
1672
|
+
|
|
1673
|
+
### View the trace
|
|
1674
|
+
|
|
1675
|
+
```bash
|
|
1676
|
+
# Option A — Chrome
|
|
1677
|
+
# open chrome://tracing and load profiles/result.json
|
|
1678
|
+
|
|
1679
|
+
# Option B — Perfetto (often better for large traces)
|
|
1680
|
+
# open https://ui.perfetto.dev and load the same file
|
|
1681
|
+
```
|
|
1682
|
+
|
|
1683
|
+
### Tips
|
|
1684
|
+
|
|
1685
|
+
- Delete or change `profile_dir` between runs — LitData removes an existing `result.json` before starting.
|
|
1686
|
+
- Pair with a wiped chunk cache if you care about **cold** epoch behavior (`litdata cache clear`).
|
|
1687
|
+
- 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
1688
|
|
|
1408
1689
|
</details>
|
|
1409
1690
|
|
|
@@ -1413,7 +1694,9 @@ This generates a Chrome trace called `result.json`. Then, visualize this trace b
|
|
|
1413
1694
|
|
|
1414
1695
|
Handle large data files efficiently without using too much of your computer's memory.
|
|
1415
1696
|
|
|
1416
|
-
|
|
1697
|
+
**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).
|
|
1698
|
+
|
|
1699
|
+
When processing large parquet files, `yield` one item at a time to keep memory low:
|
|
1417
1700
|
|
|
1418
1701
|
```python
|
|
1419
1702
|
from pathlib import Path
|
|
@@ -1449,32 +1732,104 @@ outputs = optimize(
|
|
|
1449
1732
|
<summary> ✅ Limit local cache space <a id="limit-cache" href="#limit-cache">🔗</a> </summary>
|
|
1450
1733
|
|
|
1451
1734
|
|
|
1452
|
-
|
|
1735
|
+
Control how much disk the local chunk cache may use. Downloaded chunks are deleted after use once the cache exceeds the limit.
|
|
1736
|
+
|
|
1737
|
+
Default `max_cache_size` is **`100GB`**. Peak disk in flight is roughly:
|
|
1738
|
+
|
|
1739
|
+
```
|
|
1740
|
+
num_workers × max_pre_download × mean_chunk_size
|
|
1741
|
+
```
|
|
1453
1742
|
|
|
1454
|
-
|
|
1743
|
+
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
1744
|
|
|
1456
1745
|
```python
|
|
1457
1746
|
from litdata import StreamingDataset
|
|
1458
1747
|
|
|
1459
|
-
dataset = StreamingDataset(
|
|
1748
|
+
dataset = StreamingDataset(
|
|
1749
|
+
"s3://my-bucket/my-data",
|
|
1750
|
+
max_cache_size="10GB",
|
|
1751
|
+
max_pre_download=4, # chunks each worker may prefetch (default 2; async may floor to 4)
|
|
1752
|
+
)
|
|
1460
1753
|
```
|
|
1461
1754
|
|
|
1462
1755
|
</details>
|
|
1463
1756
|
|
|
1757
|
+
<details>
|
|
1758
|
+
<summary> ✅ Async chunk prefetch & environment variables <a id="async-prefetch-env" href="#async-prefetch-env">🔗</a> </summary>
|
|
1759
|
+
|
|
1760
|
+
|
|
1761
|
+
### Async chunk prefetch
|
|
1762
|
+
|
|
1763
|
+
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:
|
|
1764
|
+
|
|
1765
|
+
```python
|
|
1766
|
+
for batch in StreamingDataLoader(dataset, batch_size=64, num_workers=8):
|
|
1767
|
+
train_step(batch)
|
|
1768
|
+
```
|
|
1769
|
+
|
|
1770
|
+
| Situation | Async prefetch |
|
|
1771
|
+
|-----------|----------------|
|
|
1772
|
+
| Remote dataset (`s3://`, `gs://`, …) | **On** by default |
|
|
1773
|
+
| Local-only dataset | **Off** by default |
|
|
1774
|
+
| `LITDATA_ASYNC_CHUNK_PREFETCH=1` | Force on |
|
|
1775
|
+
| `LITDATA_ASYNC_CHUNK_PREFETCH=0` | Force off |
|
|
1776
|
+
|
|
1777
|
+
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.
|
|
1778
|
+
|
|
1779
|
+
```bash
|
|
1780
|
+
# Debugging download/delete races — force synchronous downloads
|
|
1781
|
+
export LITDATA_ASYNC_CHUNK_PREFETCH=0
|
|
1782
|
+
|
|
1783
|
+
# Keep max_pre_download=2 even with async enabled
|
|
1784
|
+
export LITDATA_ASYNC_MIN_PRE_DOWNLOAD=0
|
|
1785
|
+
```
|
|
1786
|
+
|
|
1787
|
+
### Common environment variables
|
|
1788
|
+
|
|
1789
|
+
| Variable | Default | Purpose |
|
|
1790
|
+
|----------|---------|---------|
|
|
1791
|
+
| `LITDATA_CACHE_DIR` | `~/.lightning/chunks` | Default chunk cache directory |
|
|
1792
|
+
| `LITDATA_ASYNC_CHUNK_PREFETCH` | on for remote | `0`/`1` force async chunk download overlap |
|
|
1793
|
+
| `LITDATA_ASYNC_MIN_PRE_DOWNLOAD` | `4` | Floor for `max_pre_download` when async is on (`0` = no floor) |
|
|
1794
|
+
| `LITDATA_OBSTORE_STREAM_MIN_CHUNK_MIB` | `8` | S3 obstore stream chunk size (MiB) |
|
|
1795
|
+
| `MAX_WAIT_TIME` | `120` | Seconds to wait for a chunk before error |
|
|
1796
|
+
| `FORCE_DOWNLOAD_TIME` | `30` | Seconds before force re-download of a missing chunk |
|
|
1797
|
+
| `LITDATA_DISABLE_VERSION_CHECK` | `0` | `1` skips the upgrade tip |
|
|
1798
|
+
| `HF_TOKEN` | — | Gated Hugging Face datasets |
|
|
1799
|
+
| `DEBUG_LITDATA` / `PRINT_DEBUG_LOGS` | `0` | Internal debug / stdout logs |
|
|
1800
|
+
|
|
1801
|
+
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`.
|
|
1802
|
+
|
|
1803
|
+
</details>
|
|
1804
|
+
|
|
1464
1805
|
<details>
|
|
1465
1806
|
<summary> ✅ Change cache directory path <a id="cache-directory" href="#cache-directory">🔗</a> </summary>
|
|
1466
1807
|
|
|
1467
1808
|
|
|
1468
|
-
Specify
|
|
1809
|
+
Specify where cached chunk files are stored.
|
|
1469
1810
|
|
|
1470
1811
|
```python
|
|
1471
1812
|
from litdata import StreamingDataset
|
|
1472
1813
|
from litdata.streaming.cache import Dir
|
|
1473
1814
|
|
|
1474
|
-
|
|
1475
|
-
|
|
1815
|
+
# Simple: dedicated cache directory
|
|
1816
|
+
dataset = StreamingDataset("s3://my-bucket/my_optimized_dataset", cache_dir="/path/to/your/cache")
|
|
1817
|
+
|
|
1818
|
+
# Or when cache path and remote URL should differ:
|
|
1819
|
+
dataset = StreamingDataset(input_dir=Dir(path="/path/to/your/cache", url="s3://my-bucket/my_optimized_dataset"))
|
|
1820
|
+
```
|
|
1821
|
+
|
|
1822
|
+
Global default without passing `cache_dir` every time:
|
|
1823
|
+
|
|
1824
|
+
```bash
|
|
1825
|
+
export LITDATA_CACHE_DIR=/path/to/your/cache
|
|
1826
|
+
```
|
|
1827
|
+
|
|
1828
|
+
CLI:
|
|
1476
1829
|
|
|
1477
|
-
|
|
1830
|
+
```bash
|
|
1831
|
+
litdata cache path # print the active cache directory
|
|
1832
|
+
litdata cache clear # delete cached chunks
|
|
1478
1833
|
```
|
|
1479
1834
|
|
|
1480
1835
|
</details>
|
|
@@ -1496,114 +1851,110 @@ dataset = StreamingDataset(input_dir="local:/data/shared-drive/some-data")
|
|
|
1496
1851
|
</details>
|
|
1497
1852
|
|
|
1498
1853
|
<details>
|
|
1499
|
-
<summary> ✅ Optimize
|
|
1854
|
+
<summary> ✅ Optimize / map across multiple machines (Lightning Studios) <a id="distributed-optimization" href="#distributed-optimization">🔗</a> </summary>
|
|
1500
1855
|
|
|
1501
1856
|
|
|
1502
|
-
Lightning
|
|
1857
|
+
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).
|
|
1503
1858
|
|
|
1504
|
-
|
|
1859
|
+
**How it works**
|
|
1860
|
+
|
|
1861
|
+
1. You call `optimize(..., num_nodes=N, machine=...)` (or `map`) inside a Studio.
|
|
1862
|
+
2. LitData starts a **data-prep job** that re-runs your script on **N** machines.
|
|
1863
|
+
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`.
|
|
1864
|
+
4. Your local call blocks until the job finishes; open the printed Runs URL to monitor.
|
|
1865
|
+
|
|
1866
|
+
Outside Studio, passing `num_nodes` / `machine` raises an error (create a Studio account to use multi-node).
|
|
1505
1867
|
|
|
1506
1868
|
```python
|
|
1507
|
-
import os
|
|
1508
1869
|
from litdata import optimize, Machine
|
|
1509
1870
|
|
|
1510
1871
|
def compress(index):
|
|
1511
1872
|
return (index, index ** 2)
|
|
1512
1873
|
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1874
|
+
if __name__ == "__main__":
|
|
1875
|
+
optimize(
|
|
1876
|
+
fn=compress,
|
|
1877
|
+
inputs=list(range(100)),
|
|
1878
|
+
num_workers=8, # processes per machine
|
|
1879
|
+
output_dir="/teamspace/s3_connections/my-data/optimized-v1", # durable bucket (recommended)
|
|
1880
|
+
chunk_bytes="64MB",
|
|
1881
|
+
num_nodes=32, # machines in the job
|
|
1882
|
+
machine=Machine.DATA_PREP, # or omit to use the current Studio machine type
|
|
1883
|
+
)
|
|
1522
1884
|
```
|
|
1523
1885
|
|
|
1524
|
-
|
|
1886
|
+
**Where outputs land**
|
|
1525
1887
|
|
|
1526
|
-
|
|
1888
|
+
| `output_dir` | Result |
|
|
1889
|
+
|--------------|--------|
|
|
1890
|
+
| `/teamspace/s3_connections/...`, `/teamspace/datasets/...`, `s3://...`, `gs://...` | Written directly to that store (**recommended**) |
|
|
1891
|
+
| 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
1892
|
|
|
1528
1893
|
```python
|
|
1529
1894
|
from litdata import StreamingDataset
|
|
1530
1895
|
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
dataset = StreamingDataset(output_dir)
|
|
1534
|
-
|
|
1535
|
-
print(dataset[:])
|
|
1896
|
+
# Prefer the same connection / cloud URL you wrote to:
|
|
1897
|
+
dataset = StreamingDataset("/teamspace/s3_connections/my-data/optimized-v1")
|
|
1536
1898
|
```
|
|
1537
1899
|
|
|
1900
|
+
The same `num_nodes` / `machine` pattern works with `map`. See also [Parallelize transforms and data optimization](#parallelize-transforms-and-data-optimization-on-cloud-machines).
|
|
1901
|
+
|
|
1538
1902
|
</details>
|
|
1539
1903
|
|
|
1540
1904
|
<details>
|
|
1541
1905
|
<summary> ✅ Encrypt, decrypt data at chunk/sample level <a id="encrypt-decrypt" href="#encrypt-decrypt">🔗</a> </summary>
|
|
1542
1906
|
|
|
1543
1907
|
|
|
1544
|
-
|
|
1908
|
+
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.**
|
|
1545
1909
|
|
|
1546
|
-
|
|
1910
|
+
| `level` | Meaning |
|
|
1911
|
+
|---------|---------|
|
|
1912
|
+
| `"sample"` (default) | Encrypt each sample independently |
|
|
1913
|
+
| `"chunk"` | Encrypt whole chunks |
|
|
1914
|
+
|
|
1915
|
+
**Fernet (symmetric)**
|
|
1547
1916
|
|
|
1548
1917
|
```python
|
|
1549
|
-
from litdata import optimize
|
|
1918
|
+
from litdata import optimize, StreamingDataset
|
|
1550
1919
|
from litdata.utilities.encryption import FernetEncryption
|
|
1551
|
-
import numpy as np
|
|
1552
|
-
from PIL import Image
|
|
1553
1920
|
|
|
1554
|
-
|
|
1555
|
-
fernet = FernetEncryption(password="your_secure_password", level="sample")
|
|
1921
|
+
fernet = FernetEncryption(password="your_secure_password", level="sample") # or level="chunk"
|
|
1556
1922
|
data_dir = "s3://my-bucket/optimized_data"
|
|
1557
1923
|
|
|
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
|
|
1924
|
+
def fn(index):
|
|
1925
|
+
return {"index": index, "value": index**2}
|
|
1582
1926
|
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1927
|
+
if __name__ == "__main__":
|
|
1928
|
+
optimize(
|
|
1929
|
+
fn=fn,
|
|
1930
|
+
inputs=list(range(5)),
|
|
1931
|
+
num_workers=1,
|
|
1932
|
+
output_dir=data_dir,
|
|
1933
|
+
chunk_bytes="64MB",
|
|
1934
|
+
encryption=fernet,
|
|
1935
|
+
)
|
|
1936
|
+
fernet.save("fernet.pem") # persist salt/level; keep the password safe
|
|
1586
1937
|
|
|
1587
|
-
#
|
|
1938
|
+
# Later — load key material with the same password
|
|
1939
|
+
fernet = FernetEncryption.load("fernet.pem", password="your_secure_password")
|
|
1588
1940
|
ds = StreamingDataset(input_dir=data_dir, encryption=fernet)
|
|
1589
1941
|
```
|
|
1590
1942
|
|
|
1591
|
-
|
|
1943
|
+
**RSA (asymmetric)**
|
|
1592
1944
|
|
|
1593
1945
|
```python
|
|
1594
|
-
from litdata.utilities.encryption import
|
|
1946
|
+
from litdata.utilities.encryption import RSAEncryption
|
|
1595
1947
|
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
return data
|
|
1948
|
+
rsa = RSAEncryption(password="your_secure_password", level="sample") # or "chunk"
|
|
1949
|
+
optimize(fn=fn, inputs=list(range(5)), output_dir=data_dir, chunk_bytes="64MB", encryption=rsa)
|
|
1950
|
+
rsa.save("rsa.pem")
|
|
1600
1951
|
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
return data
|
|
1952
|
+
rsa = RSAEncryption.load("rsa.pem", password="your_secure_password")
|
|
1953
|
+
ds = StreamingDataset(input_dir=data_dir, encryption=rsa)
|
|
1604
1954
|
```
|
|
1605
1955
|
|
|
1606
|
-
|
|
1956
|
+
**Custom algorithm** — subclass `Encryption` and implement `encrypt` / `decrypt` / `save` / `load` / `state_dict` / `algorithm`.
|
|
1957
|
+
|
|
1607
1958
|
</details>
|
|
1608
1959
|
|
|
1609
1960
|
<details>
|
|
@@ -1675,17 +2026,99 @@ if __name__ == "__main__":
|
|
|
1675
2026
|
</details>
|
|
1676
2027
|
|
|
1677
2028
|
<details>
|
|
1678
|
-
<summary> ✅
|
|
2029
|
+
<summary> ✅ Resolve any path or cloud URL (local, S3, GCS, R2, Azure, HF, Studio) <a id="resolve-paths" href="#resolve-paths">🔗</a> </summary>
|
|
1679
2030
|
|
|
1680
2031
|
|
|
1681
2032
|
|
|
1682
|
-
|
|
2033
|
+
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.
|
|
2034
|
+
|
|
2035
|
+
### Supported URI schemes
|
|
1683
2036
|
|
|
1684
|
-
|
|
2037
|
+
| Scheme | Example | Use when |
|
|
2038
|
+
|--------|---------|----------|
|
|
2039
|
+
| Local path | `./data` or `/data/imagenet` | Files on disk |
|
|
2040
|
+
| `s3://` | `s3://my-bucket/optimized` | AWS S3 |
|
|
2041
|
+
| `gs://` | `gs://my-bucket/optimized` | Google Cloud Storage |
|
|
2042
|
+
| `r2://` | `r2://my-bucket/optimized` | Cloudflare R2 |
|
|
2043
|
+
| `azure://` | `azure://container/optimized` | Azure Blob Storage |
|
|
2044
|
+
| `hf://` | `hf://datasets/org/name/data` | Hugging Face datasets (parquet) |
|
|
2045
|
+
| `local:` | `local:/mnt/nfs/dataset` | Network / shared drive (LitData still caches chunks locally to reduce NAS load) |
|
|
1685
2046
|
|
|
1686
2047
|
```python
|
|
1687
|
-
from litdata import optimize
|
|
2048
|
+
from litdata import StreamingDataset, optimize
|
|
2049
|
+
|
|
2050
|
+
# Same APIs — only the path changes
|
|
2051
|
+
StreamingDataset("s3://my-bucket/fast_data", shuffle=True, drop_last=True)
|
|
2052
|
+
StreamingDataset("gs://my-bucket/fast_data")
|
|
2053
|
+
StreamingDataset("r2://my-bucket/fast_data", storage_options={...})
|
|
2054
|
+
StreamingDataset("azure://my-container/fast_data", storage_options={...})
|
|
2055
|
+
StreamingDataset("hf://datasets/org/name/data")
|
|
2056
|
+
StreamingDataset("local:/data/shared-drive/some-data")
|
|
2057
|
+
StreamingDataset("/var/data/fast_data") # plain local directory
|
|
2058
|
+
```
|
|
2059
|
+
|
|
2060
|
+
Pass cloud credentials with `storage_options` (and optional `session_options` for boto3 profiles/regions). See [Stream from multiple cloud providers](#cloud-providers).
|
|
2061
|
+
|
|
2062
|
+
### Cache directory vs remote URL
|
|
2063
|
+
|
|
2064
|
+
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`:
|
|
2065
|
+
|
|
2066
|
+
```python
|
|
2067
|
+
from litdata import StreamingDataset
|
|
2068
|
+
from litdata.streaming.resolver import Dir
|
|
2069
|
+
|
|
2070
|
+
dataset = StreamingDataset(
|
|
2071
|
+
Dir(path="/fast-ssd/cache/run-1", url="s3://my-bucket/fast_data")
|
|
2072
|
+
)
|
|
2073
|
+
# Equivalent:
|
|
2074
|
+
dataset = StreamingDataset("s3://my-bucket/fast_data", cache_dir="/fast-ssd/cache/run-1")
|
|
2075
|
+
```
|
|
2076
|
+
|
|
2077
|
+
```bash
|
|
2078
|
+
export LITDATA_CACHE_DIR=/fast-ssd/cache
|
|
2079
|
+
litdata cache path # show active cache directory
|
|
2080
|
+
litdata cache clear # wipe cached chunks
|
|
2081
|
+
```
|
|
1688
2082
|
|
|
2083
|
+
### Date/time path templates
|
|
2084
|
+
|
|
2085
|
+
Embed a `strftime` pattern in `{...}` and LitData expands it to the current time (useful for versioned `output_dir`s):
|
|
2086
|
+
|
|
2087
|
+
```python
|
|
2088
|
+
# e.g. on 2025-05-05 → ".../run_2025-05-05"
|
|
2089
|
+
optimize(
|
|
2090
|
+
fn=fn,
|
|
2091
|
+
inputs=inputs,
|
|
2092
|
+
output_dir="s3://my-bucket/datasets/run_{%Y-%m-%d}",
|
|
2093
|
+
chunk_bytes="64MB",
|
|
2094
|
+
)
|
|
2095
|
+
```
|
|
2096
|
+
|
|
2097
|
+
### Lightning Studio `/teamspace/...` paths (direct bucket I/O)
|
|
2098
|
+
|
|
2099
|
+
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.
|
|
2100
|
+
|
|
2101
|
+
| Path prefix | What LitData does |
|
|
2102
|
+
|-------------|-------------------|
|
|
2103
|
+
| `/teamspace/studios/this_studio/...` | Local Studio workspace disk (not a cloud URL) |
|
|
2104
|
+
| `/teamspace/studios/<other_studio>/...` | Resolves to that Studio’s content bucket (`s3://` or `gs://`) |
|
|
2105
|
+
| `/teamspace/s3_connections/<name>/...` | Direct S3 to the connection’s bucket |
|
|
2106
|
+
| `/teamspace/gcs_connections/<name>/...` | Direct GCS |
|
|
2107
|
+
| `/teamspace/s3_folders/<name>/...` | S3 folder connection |
|
|
2108
|
+
| `/teamspace/gcs_folders/<name>/...` | GCS folder connection |
|
|
2109
|
+
| `/teamspace/lightning_storage/<name>/...` | Lightning-managed object storage (R2-style) |
|
|
2110
|
+
| `/teamspace/datasets/...` | Teamspace datasets mount → project datasets bucket |
|
|
2111
|
+
|
|
2112
|
+
```python
|
|
2113
|
+
from litdata import StreamingDataset, StreamingRawDataset, optimize
|
|
2114
|
+
|
|
2115
|
+
# Stream optimized data from an attached S3 connection (direct bucket download)
|
|
2116
|
+
dataset = StreamingDataset("/teamspace/s3_connections/my-data-1/fast_data", shuffle=True, drop_last=True)
|
|
2117
|
+
|
|
2118
|
+
# Stream raw files from a connection
|
|
2119
|
+
raw = StreamingRawDataset("/teamspace/s3_connections/my-bucket-1/raw")
|
|
2120
|
+
|
|
2121
|
+
# Optimize *into* a connection — chunks upload straight to the bucket
|
|
1689
2122
|
def should_keep(data):
|
|
1690
2123
|
if data % 2 == 0:
|
|
1691
2124
|
yield data
|
|
@@ -1696,31 +2129,16 @@ if __name__ == "__main__":
|
|
|
1696
2129
|
inputs=list(range(1000)),
|
|
1697
2130
|
output_dir="/teamspace/s3_connections/my-data-1/output",
|
|
1698
2131
|
chunk_bytes="64MB",
|
|
1699
|
-
num_workers=1
|
|
2132
|
+
num_workers=1,
|
|
1700
2133
|
)
|
|
1701
2134
|
```
|
|
1702
2135
|
|
|
2136
|
+
**Tips**
|
|
1703
2137
|
|
|
1704
|
-
|
|
2138
|
+
- Version remote outputs (`.../v2`, `.../run_{%Y-%m-%d}`). Optimized datasets are immutable unless you pass `mode="append"` or `mode="overwrite"`.
|
|
2139
|
+
- Outside Studio, use `s3://` / `gs://` / … with your own credentials — `/teamspace/...` resolution needs Lightning Studio environment variables.
|
|
2140
|
+
- `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).
|
|
1705
2141
|
|
|
1706
|
-
```python
|
|
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
|
-
```
|
|
1717
|
-
|
|
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
2142
|
</details>
|
|
1725
2143
|
|
|
1726
2144
|
|
|
@@ -1734,31 +2152,113 @@ References to any of the following directories will work similarly:
|
|
|
1734
2152
|
|
|
1735
2153
|
Apply the same change to different parts of the dataset at once to save time and effort.
|
|
1736
2154
|
|
|
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.
|
|
2155
|
+
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
2156
|
|
|
1741
2157
|
```python
|
|
2158
|
+
import os
|
|
1742
2159
|
from litdata import map
|
|
1743
2160
|
from PIL import Image
|
|
1744
2161
|
|
|
1745
|
-
|
|
1746
|
-
input_dir = "my_large_images"
|
|
2162
|
+
input_dir = "my_large_images" # or s3://...
|
|
1747
2163
|
inputs = [os.path.join(input_dir, f) for f in os.listdir(input_dir)]
|
|
1748
2164
|
|
|
1749
|
-
# The resize image takes one of the input (image_path) and the output directory.
|
|
1750
|
-
# Files written to output_dir are persisted.
|
|
1751
2165
|
def resize_image(image_path, output_dir):
|
|
1752
|
-
|
|
1753
|
-
|
|
2166
|
+
output_image_path = os.path.join(output_dir, os.path.basename(image_path))
|
|
2167
|
+
Image.open(image_path).resize((224, 224)).save(output_image_path)
|
|
1754
2168
|
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
2169
|
+
if __name__ == "__main__":
|
|
2170
|
+
map(
|
|
2171
|
+
fn=resize_image,
|
|
2172
|
+
inputs=inputs,
|
|
2173
|
+
output_dir="s3://my-bucket/my_resized_images",
|
|
2174
|
+
num_workers=8,
|
|
2175
|
+
)
|
|
2176
|
+
```
|
|
2177
|
+
|
|
2178
|
+
**`map` arguments**
|
|
2179
|
+
|
|
2180
|
+
| Argument | Default | Description |
|
|
2181
|
+
|----------|---------|-------------|
|
|
2182
|
+
| `fn` | required | `fn(input, output_dir) -> None` |
|
|
2183
|
+
| `inputs` | required | Sequence or `StreamingDataLoader` |
|
|
2184
|
+
| `output_dir` | required | Local or cloud path ([resolver](#resolve-paths)) |
|
|
2185
|
+
| `input_dir` | `None` | Root for remote inputs (background download while processing) |
|
|
2186
|
+
| `weights` | `None` | Per-input weights to balance workers |
|
|
2187
|
+
| `num_workers` | CPU count | Local process workers |
|
|
2188
|
+
| `fast_dev_run` | `False` | Process only a few items (`True` → small default, or an int) |
|
|
2189
|
+
| `num_nodes` / `machine` | `None` | Scale out on [Lightning Studios](https://lightning.ai/) |
|
|
2190
|
+
| `num_downloaders` / `num_uploaders` | auto | I/O concurrency per worker |
|
|
2191
|
+
| `reorder_files` | `True` | Pack by file size for balance; `False` preserves order |
|
|
2192
|
+
| `error_when_not_empty` | `False` | Error if `output_dir` already has files |
|
|
2193
|
+
| `reader` | default | Custom reader for inputs |
|
|
2194
|
+
| `batch_size` | `None` | Group inputs into batches for `fn` |
|
|
2195
|
+
| `start_method` | spawn† | Multiprocessing start method (†spawn unless IPython) |
|
|
2196
|
+
| `optimize_dns` | `None` | Optimized DNS (Studio / cloud) |
|
|
2197
|
+
| `storage_options` | `{}` | Cloud credentials / endpoints |
|
|
2198
|
+
| `keep_data_ordered` | `True` | `False` = shared work queue (better for uneven/slow workers) |
|
|
2199
|
+
|
|
2200
|
+
</details>
|
|
2201
|
+
|
|
2202
|
+
<details>
|
|
2203
|
+
<summary> ✅ <code>optimize</code> arguments reference <a id="optimize-kwargs" href="#optimize-kwargs">🔗</a> </summary>
|
|
2204
|
+
|
|
2205
|
+
|
|
2206
|
+
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__"`.
|
|
2207
|
+
|
|
2208
|
+
| Argument | Default | Description |
|
|
2209
|
+
|----------|---------|-------------|
|
|
2210
|
+
| `fn` | required | Maps each input → sample (or `yield` samples / skip bad ones) |
|
|
2211
|
+
| `inputs` | `None` | Sequence or `StreamingDataLoader` (ignored if `queue` is set) |
|
|
2212
|
+
| `queue` | `None` | `multiprocessing.Queue` of live inputs; send **one** `ALL_DONE` when finished |
|
|
2213
|
+
| `output_dir` | `"optimized_data"` | Local or cloud ([resolver](#resolve-paths)); version remote prefixes |
|
|
2214
|
+
| `input_dir` | `None` | Remote input root for background download |
|
|
2215
|
+
| `weights` | `None` | Per-input weights to balance workers |
|
|
2216
|
+
| `chunk_bytes` | `None` | Max bytes per chunk (e.g. `"64MB"`) |
|
|
2217
|
+
| `chunk_size` | `None` | Max items (or tokens with `TokensLoader`) per chunk |
|
|
2218
|
+
| `align_chunking` | `False` | Match single-worker chunk boundaries (needs `chunk_size`; uneven load) |
|
|
2219
|
+
| `compression` | `None` | `"zstd"` today |
|
|
2220
|
+
| `encryption` | `None` | `FernetEncryption` / `RSAEncryption` / custom ([encrypt](#encrypt-decrypt)) |
|
|
2221
|
+
| `num_workers` | CPU count | Local workers |
|
|
2222
|
+
| `fast_dev_run` | `False` | Smoke a subset of inputs |
|
|
2223
|
+
| `num_nodes` / `machine` | `None` | Multi-node on Lightning Studios |
|
|
2224
|
+
| `num_downloaders` / `num_uploaders` | auto | I/O concurrency per worker |
|
|
2225
|
+
| `reorder_files` | `True` | Size-based packing; `False` preserves order |
|
|
2226
|
+
| `reader` | default | Custom input reader |
|
|
2227
|
+
| `batch_size` | `None` | Group inputs for `fn` |
|
|
2228
|
+
| `mode` | `None` | `"append"` or `"overwrite"` existing dataset; default treats data as immutable |
|
|
2229
|
+
| `use_checkpoint` | `False` | Resume an interrupted optimize from `.checkpoints` |
|
|
2230
|
+
| `item_loader` | `None` | e.g. `TokensLoader()` for contiguous tokens |
|
|
2231
|
+
| `start_method` | spawn† | Multiprocessing start method |
|
|
2232
|
+
| `optimize_dns` | `None` | Optimized DNS |
|
|
2233
|
+
| `storage_options` | `{}` | Cloud credentials / endpoints |
|
|
2234
|
+
| `keep_data_ordered` | `True` | `False` = shared queue among workers |
|
|
2235
|
+
| `verbose` | `True` | Progress logging |
|
|
2236
|
+
|
|
2237
|
+
Related features: [shared queue](#shared-queue), [queue input](#queue-input), [append/overwrite](#modify-datasets), [compression](#compression), [TokensLoader / LLM](#llm-training), [filter](#filter-data).
|
|
2238
|
+
|
|
2239
|
+
</details>
|
|
2240
|
+
|
|
2241
|
+
<details>
|
|
2242
|
+
<summary> ✅ Cloud-optimized <code>walk</code> (list files at scale) <a id="walk" href="#walk">🔗</a> </summary>
|
|
2243
|
+
|
|
2244
|
+
|
|
2245
|
+
`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**.
|
|
2246
|
+
|
|
2247
|
+
```python
|
|
2248
|
+
from litdata import walk, optimize
|
|
2249
|
+
|
|
2250
|
+
inputs = []
|
|
2251
|
+
for root, dirs, files in walk("/teamspace/s3_connections/my-data/raw", max_workers=32):
|
|
2252
|
+
for name in files:
|
|
2253
|
+
if name.endswith(".jpg"):
|
|
2254
|
+
inputs.append(f"{root}/{name}")
|
|
2255
|
+
|
|
2256
|
+
if __name__ == "__main__":
|
|
2257
|
+
optimize(fn=load_image, inputs=inputs, output_dir="...", chunk_bytes="64MB")
|
|
1760
2258
|
```
|
|
1761
2259
|
|
|
2260
|
+
Prints a warning outside Lightning Studio — it is optimized for that environment; elsewhere prefer `os.walk` or your cloud SDK’s listing API.
|
|
2261
|
+
|
|
1762
2262
|
</details>
|
|
1763
2263
|
|
|
1764
2264
|
|
|
@@ -1842,7 +2342,7 @@ Time to optimize 1.2 million ImageNet images (Faster is better):
|
|
|
1842
2342
|
|
|
1843
2343
|
## Parallelize data transforms
|
|
1844
2344
|
|
|
1845
|
-
Transformations with LitData are linearly parallelizable across machines.
|
|
2345
|
+
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
2346
|
|
|
1847
2347
|
For example, let's say that it takes 56 hours to embed a dataset on a single A10G machine. With LitData,
|
|
1848
2348
|
this can be speed up by adding more machines in parallel
|
|
@@ -1855,20 +2355,20 @@ this can be speed up by adding more machines in parallel
|
|
|
1855
2355
|
| ... | ... |
|
|
1856
2356
|
| 64 | 0.875 |
|
|
1857
2357
|
|
|
1858
|
-
To scale the number of machines, run the processing script on [Lightning Studios](https://lightning.ai/):
|
|
1859
|
-
|
|
1860
2358
|
```python
|
|
1861
2359
|
from litdata import map, Machine
|
|
1862
2360
|
|
|
1863
2361
|
map(
|
|
1864
2362
|
...
|
|
1865
2363
|
num_nodes=32,
|
|
1866
|
-
machine=Machine.DATA_PREP,
|
|
2364
|
+
machine=Machine.DATA_PREP, # or omit to inherit the Studio machine
|
|
2365
|
+
# Prefer output_dir on /teamspace/s3_connections/... or s3://...
|
|
1867
2366
|
)
|
|
1868
2367
|
```
|
|
1869
2368
|
|
|
1870
2369
|
## Parallelize data optimization
|
|
1871
|
-
|
|
2370
|
+
|
|
2371
|
+
Same Studio job launch as `map` — `num_nodes` machines × `num_workers` processes; last node merges the index.
|
|
1872
2372
|
|
|
1873
2373
|
```python
|
|
1874
2374
|
from litdata import optimize, Machine
|
|
@@ -1876,7 +2376,8 @@ from litdata import optimize, Machine
|
|
|
1876
2376
|
optimize(
|
|
1877
2377
|
...
|
|
1878
2378
|
num_nodes=32,
|
|
1879
|
-
machine=Machine.DATA_PREP,
|
|
2379
|
+
machine=Machine.DATA_PREP,
|
|
2380
|
+
output_dir="/teamspace/s3_connections/my-data/optimized-v1",
|
|
1880
2381
|
)
|
|
1881
2382
|
```
|
|
1882
2383
|
|