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.
Files changed (75) hide show
  1. {litdata-0.2.64/src/litdata.egg-info → litdata-0.2.66}/PKG-INFO +730 -229
  2. {litdata-0.2.64 → litdata-0.2.66}/README.md +729 -228
  3. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/__about__.py +1 -1
  4. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/constants.py +3 -0
  5. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/processing/data_processor.py +3 -19
  6. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/processing/utilities.py +0 -73
  7. litdata-0.2.66/src/litdata/streaming/async_prefetch.py +256 -0
  8. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/config.py +139 -30
  9. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/dataloader.py +13 -1
  10. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/dataset.py +46 -40
  11. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/downloader.py +149 -27
  12. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/item_loader.py +296 -55
  13. litdata-0.2.66/src/litdata/streaming/reader.py +1090 -0
  14. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/serializers.py +17 -6
  15. litdata-0.2.66/src/litdata/streaming/timing.py +84 -0
  16. {litdata-0.2.64 → litdata-0.2.66/src/litdata.egg-info}/PKG-INFO +730 -229
  17. {litdata-0.2.64 → litdata-0.2.66}/src/litdata.egg-info/SOURCES.txt +2 -0
  18. litdata-0.2.64/src/litdata/streaming/reader.py +0 -643
  19. {litdata-0.2.64 → litdata-0.2.66}/CONTRIBUTING.md +0 -0
  20. {litdata-0.2.64 → litdata-0.2.66}/LICENSE +0 -0
  21. {litdata-0.2.64 → litdata-0.2.66}/MANIFEST.in +0 -0
  22. {litdata-0.2.64 → litdata-0.2.66}/requirements.txt +0 -0
  23. {litdata-0.2.64 → litdata-0.2.66}/setup.cfg +0 -0
  24. {litdata-0.2.64 → litdata-0.2.66}/setup.py +0 -0
  25. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/__init__.py +0 -0
  26. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/__main__.py +0 -0
  27. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/cli/__init__.py +0 -0
  28. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/cli/commands.py +0 -0
  29. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/cli/handler/__init__.py +0 -0
  30. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/cli/handler/cache.py +0 -0
  31. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/cli/handler/optimize.py +0 -0
  32. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/cli/parser.py +0 -0
  33. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/debugger.py +0 -0
  34. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/helpers.py +0 -0
  35. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/imports.py +0 -0
  36. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/processing/__init__.py +0 -0
  37. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/processing/functions.py +0 -0
  38. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/processing/readers.py +0 -0
  39. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/raw/__init__.py +0 -0
  40. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/raw/dataset.py +0 -0
  41. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/raw/indexer.py +0 -0
  42. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/raw/types.py +0 -0
  43. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/requirements.py +0 -0
  44. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/__init__.py +0 -0
  45. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/cache.py +0 -0
  46. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/client.py +0 -0
  47. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/combined.py +0 -0
  48. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/compression.py +0 -0
  49. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/fs_provider.py +0 -0
  50. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/parallel.py +0 -0
  51. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/resolver.py +0 -0
  52. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/sampler.py +0 -0
  53. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/shuffle.py +0 -0
  54. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/streaming/writer.py +0 -0
  55. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/__init__.py +0 -0
  56. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/_pytree.py +0 -0
  57. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/base.py +0 -0
  58. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/breakpoint.py +0 -0
  59. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/broadcast.py +0 -0
  60. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/dataset_utilities.py +0 -0
  61. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/encryption.py +0 -0
  62. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/env.py +0 -0
  63. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/format.py +0 -0
  64. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/hf_dataset.py +0 -0
  65. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/packing.py +0 -0
  66. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/parquet.py +0 -0
  67. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/shuffle.py +0 -0
  68. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/subsample.py +0 -0
  69. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/torch_utils.py +0 -0
  70. {litdata-0.2.64 → litdata-0.2.66}/src/litdata/utilities/train_test_split.py +0 -0
  71. {litdata-0.2.64 → litdata-0.2.66}/src/litdata.egg-info/dependency_links.txt +0 -0
  72. {litdata-0.2.64 → litdata-0.2.66}/src/litdata.egg-info/entry_points.txt +0 -0
  73. {litdata-0.2.64 → litdata-0.2.66}/src/litdata.egg-info/not-zip-safe +0 -0
  74. {litdata-0.2.64 → litdata-0.2.66}/src/litdata.egg-info/requires.txt +0 -0
  75. {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.64
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
  &nbsp;
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 here (e.g., .jpg, .png, etc.)
196
- # Recommended: use compressed formats like JPEG for better storage and optimized streaming speed
197
- # You can also apply resizing or reduce image quality to further increase streaming speed and save space
198
- fake_images = Image.fromarray(np.random.randint(0, 256, (32, 32, 3), dtype=np.uint8))
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
- # You can use any key:value pairs. Note that their types must not change between samples, and Python lists must
202
- # always contain the same number of elements with the same types
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
- # The optimize function writes data in an optimized format
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('s3://my-bucket/fast_data', shuffle=True, drop_last=True)
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> ✅ Stream Hugging Face 🤗 datasets <a id="stream-hf" href="#stream-hf">🔗</a> </summary>
434
-
452
+ <summary> ✅ Optimize images as JPEG (not raw PIL) <a id="optimize-jpeg" href="#optimize-jpeg">🔗</a> </summary>
435
453
  &nbsp;
436
454
 
437
- To use your favorite Hugging Face dataset with LitData, simply pass its URL to `StreamingDataset`.
455
+ How you return images from `optimize` controls storage size and streaming speed.
438
456
 
439
- <details>
440
- <summary>How to get HF dataset URI?</summary>
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
- https://github.com/user-attachments/assets/3ba9e2ef-bf6b-41fc-a578-e4b4113a0e72
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
- **Prerequisites:**
486
+ <details>
487
+ <summary> ✅ Custom serializers <a id="serializers" href="#serializers">🔗</a> </summary>
488
+ &nbsp;
447
489
 
448
- Install the required dependencies to stream Hugging Face datasets:
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
- # Optional: To speed up downloads on high-bandwidth networks
453
- pip install hf_transfer
454
- export HF_HUB_ENABLE_HF_TRANSFER=1
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
- **Stream Hugging Face dataset:**
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
+ &nbsp;
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
- # Define the Hugging Face dataset URI
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
- # Create a streaming dataset
466
- dataset = ld.StreamingDataset(hf_dataset_uri)
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
- pass
549
+ pass
475
550
  ```
476
551
 
477
- You don’t need to worry about indexing the dataset or any other setup. **LitData** will **handle all the necessary steps automatically** and `cache` the `index.json` file, so you won't have to index it again.
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
- This ensures that the next time you stream the dataset, the indexing step is skipped..
569
+ <details>
570
+ <summary> ✅ Stream Hugging Face 🤗 datasets <a id="stream-hf" href="#stream-hf">🔗</a> </summary>
480
571
 
481
572
  &nbsp;
482
573
 
483
- ### Indexing the HF dataset (Optional)
574
+ To use your favorite Hugging Face dataset with LitData, simply pass its URL to `StreamingDataset`.
484
575
 
485
- If the Hugging Face dataset hasn't been indexed yet, you can index it first using the `index_hf_dataset` method, and then stream it using the code above.
576
+ <details>
577
+ <summary>How to get HF dataset URI?</summary>
486
578
 
487
- ```python
488
- import litdata as ld
579
+ https://github.com/user-attachments/assets/3ba9e2ef-bf6b-41fc-a578-e4b4113a0e72
489
580
 
490
- hf_dataset_uri = "hf://datasets/leonardPKU/clevr_cogen_a_train/data"
581
+ </details>
491
582
 
492
- ld.index_hf_dataset(hf_dataset_uri)
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
- - Indexing the Hugging Face dataset ahead of time will make streaming abit faster, as it avoids the need for real-time indexing during streaming.
593
+ **Supported for HF:** datasets stored as **Parquet** only. Gated datasets: set `HF_TOKEN`.
496
594
 
497
- - To use `HF gated dataset`, ensure the `HF_TOKEN` environment variable is set.
595
+ **Stream Hugging Face dataset** (auto-index + auto `ParquetLoader`):
498
596
 
499
- **Note**: For HuggingFace datasets, `indexing` & `streaming` is supported only for datasets in **`Parquet format`**.
597
+ ```python
598
+ import litdata as ld
500
599
 
501
- &nbsp;
600
+ hf_dataset_uri = "hf://datasets/leonardPKU/clevr_cogen_a_train/data"
502
601
 
503
- ### Full Workflow for Hugging Face Datasets
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
- For full control over the cache path(`where index.json file will be stored`) and other configurations, follow these steps:
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
- 1. Index the Hugging Face dataset first:
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
- hf_dataset_uri = "hf://datasets/open-thoughts/OpenThoughts-114k/data"
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
- 2. To stream HF datasets now, pass the `HF dataset URI`, the path where the `index.json` file is stored, and `ParquetLoader` as the `item_loader` to the **`StreamingDataset`**:
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
- hf_dataset_uri = "hf://datasets/open-thoughts/OpenThoughts-114k/data"
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(hf_dataset_uri, item_loader=ParquetLoader(), index_path="hf-index-dir")
526
-
527
- for batch in ld.StreamingDataLoader(dataset, batch_size=4):
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
- &nbsp;
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
+ &nbsp;
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
+ &nbsp;
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(datasets=train_datasets, seed=42, weights=weights, iterate_over_all=False)
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
- **Batching Methods**
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
- The `CombinedStreamingDataset` supports two different batching methods through the `batching_method` parameter:
1141
+ **Batching Methods** (`batching_method`)
950
1142
 
951
- **Stratified Batching (Default)**:
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" # This is the default
1147
+ datasets=[dataset1, dataset2],
1148
+ batching_method="stratified", # default
959
1149
  )
960
1150
  ```
961
1151
 
962
- **Per-Stream Batching**:
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
  &nbsp;
1257
1441
 
1258
- Stream Parquet datasets directly with LitData—no need to convert them into LitData’s optimized binary format! If your dataset is already in Parquet format, you can efficiently index and stream it using `StreamingDataset` and `StreamingDataLoader`.
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
- **Assumption:**
1444
+ ### Stream vs optimize vs map
1261
1445
 
1262
- Your dataset directory contains one or more Parquet files.
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
- **Prerequisites:**
1452
+ Each sample from `ParquetLoader` is a **`dict`** (column name → value).
1265
1453
 
1266
- Install the required dependencies to stream Parquet datasets from cloud storage like **Amazon S3** or **Google Cloud Storage**:
1454
+ ### Prerequisites
1267
1455
 
1268
1456
  ```bash
1269
- # For Amazon S3
1270
- pip install "litdata[extra]" s3fs
1271
-
1272
- # For Google Cloud Storage
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
- **Index Your Dataset**:
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
- # Point to your data stored in the cloud
1284
- pq_dataset_uri = "s3://my-bucket/my-parquet-data" # or "gs://my-bucket/my-parquet-data"
1285
-
1286
- ld.index_parquet_dataset(pq_dataset_uri)
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
- **Stream the Dataset**
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
- Use `StreamingDataset` with `ParquetLoader` to load and stream the dataset efficiently:
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
- # Specify your dataset location in the cloud
1299
- pq_dataset_uri = "s3://my-bucket/my-parquet-data" # or "gs://my-bucket/my-parquet-data"
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
- # Set up the streaming dataset
1302
- dataset = ld.StreamingDataset(pq_dataset_uri, item_loader=ParquetLoader())
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("Sample", dataset[0])
1507
+ print(dataset[0]) # dict of columns
1305
1508
 
1306
- dataloader = ld.StreamingDataLoader(dataset, batch_size=4)
1307
- for sample in dataloader:
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
  &nbsp;
1395
1633
 
1396
- Measure and optimize how fast your data is being loaded, improving efficiency.
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
- The `StreamingDataLoader` supports profiling of your data loading process. Simply use the `profile_batches` argument to specify the number of batches you want to profile:
1644
+ ### Usage
1399
1645
 
1400
1646
  ```python
1401
1647
  from litdata import StreamingDataset, StreamingDataLoader
1402
1648
 
1403
- StreamingDataLoader(..., profile_batches=5)
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
- This generates a Chrome trace called `result.json`. Then, visualize this trace by opening Chrome browser at the `chrome://tracing` URL and load the trace inside.
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
- When processing large files like compressed [parquet files](https://en.wikipedia.org/wiki/Apache_Parquet), use the Python yield keyword to process and store one item at the time, reducing the memory footprint of the entire program.
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
  &nbsp;
1451
1734
 
1452
- Limit the amount of disk space used by temporary files, preventing storage issues.
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
- Adapt the local caching limit of the `StreamingDataset`. This is useful to make sure the downloaded data chunks are deleted when used and the disk usage stays low.
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(..., max_cache_size="10GB")
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
+ &nbsp;
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
  &nbsp;
1467
1808
 
1468
- Specify the directory where cached files should be stored, ensuring efficient data retrieval and management. This is particularly useful for organizing your data storage and improving access times.
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
- cache_dir = "/path/to/your/cache"
1475
- data_dir = "s3://my-bucket/my_optimized_dataset"
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
- dataset = StreamingDataset(input_dir=Dir(path=cache_dir, url=data_dir))
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 dataset in distributed environment <a id="distributed-optimization" href="#distributed-optimization">🔗</a> </summary>
1854
+ <summary> ✅ Optimize / map across multiple machines (Lightning Studios) <a id="distributed-optimization" href="#distributed-optimization">🔗</a> </summary>
1500
1855
  &nbsp;
1501
1856
 
1502
- Lightning can distribute large workloads across hundreds of machines in parallel. This can reduce the time to complete a data processing task from weeks to minutes by scaling to enough machines.
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
- To apply the optimize operator across multiple machines, simply provide the num_nodes and machine arguments to it as follows:
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
- optimize(
1514
- fn=compress,
1515
- inputs=list(range(100)),
1516
- num_workers=2,
1517
- output_dir="my_output",
1518
- chunk_bytes="64MB",
1519
- num_nodes=2,
1520
- machine=Machine.DATA_PREP, # You can select between dozens of optimized machines
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
- If the `output_dir` is a local path, the optimized dataset will be present in: `/teamspace/jobs/{job_name}/nodes-0/my_output`. Otherwise, it will be stored in the specified `output_dir`.
1886
+ **Where outputs land**
1525
1887
 
1526
- Read the optimized dataset:
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
- output_dir = "/teamspace/jobs/litdata-optimize-2024-07-08/nodes.0/my_output"
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
  &nbsp;
1543
1907
 
1544
- Secure data by applying encryption to individual samples or chunks, ensuring sensitive information is protected during storage.
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
- This example shows how to use the `FernetEncryption` class for sample-level encryption with a data optimization function.
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
- # Initialize FernetEncryption with a password for sample-level encryption
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 random_image(index):
1559
- """Generate a random image for demonstration purposes."""
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
- # Load the encryption key
1584
- fernet = FernetEncryption(password="your_secure_password", level="sample")
1585
- fernet.load("fernet.pem")
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
- # Create a streaming dataset for reading the encrypted samples
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
- Implement your own encryption method: Subclass the `Encryption` class and define the necessary methods:
1943
+ **RSA (asymmetric)**
1592
1944
 
1593
1945
  ```python
1594
- from litdata.utilities.encryption import Encryption
1946
+ from litdata.utilities.encryption import RSAEncryption
1595
1947
 
1596
- class CustomEncryption(Encryption):
1597
- def encrypt(self, data):
1598
- # Implement your custom encryption logic here
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
- def decrypt(self, data):
1602
- # Implement your custom decryption logic here
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
- This allows the data to remain secure while maintaining flexibility in the encryption method.
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> ✅ Lightning AI Data Connections - Direct download and upload <a id="lightning-connections" href="#lightning-connections">🔗</a> </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
  &nbsp;
1681
2032
 
1682
- [Lightning Studios](https://lightning.ai/) have special directories for data connections that are available to an entire teamspace. LitData functions that reference those directories will experience a significant performance increase as uploads and downloads will happen directly from the bucket that backs the folder.
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
- For example, output artifacts from this code will be directly uploaded to the `my-data-1` s3 bucket.
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
- Similarly, data will be downloaded directly from the `my-data-1` s3 bucket in this example code.
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
  &nbsp;
@@ -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 can be used to apply a function over a list of inputs.
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
- # Note: Inputs could also refer to files on s3 directly.
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
- output_image_path = os.path.join(output_dir, os.path.basename(image_path))
1753
- Image.open(image_path).resize((224, 224)).save(output_image_path)
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
- map(
1756
- fn=resize_image,
1757
- inputs=inputs,
1758
- output_dir="s3://my-bucket/my_resized_images",
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
+ &nbsp;
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
+ &nbsp;
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
  &nbsp;
@@ -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, # Select between dozens of optimized machines
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
- To scale the number of machines for data optimization, use [Lightning Studios](https://lightning.ai/):
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, # Select between dozens of optimized machines
2379
+ machine=Machine.DATA_PREP,
2380
+ output_dir="/teamspace/s3_connections/my-data/optimized-v1",
1880
2381
  )
1881
2382
  ```
1882
2383