litdata 0.2.65__tar.gz → 0.2.67__tar.gz

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