efficio 0.1.0__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 (43) hide show
  1. efficio-0.1.0/LICENSE +21 -0
  2. efficio-0.1.0/MANIFEST.in +9 -0
  3. efficio-0.1.0/PKG-INFO +225 -0
  4. efficio-0.1.0/README.md +186 -0
  5. efficio-0.1.0/efficio/__init__.py +43 -0
  6. efficio-0.1.0/efficio/_planned.py +16 -0
  7. efficio-0.1.0/efficio/adapters/__init__.py +1 -0
  8. efficio-0.1.0/efficio/adapters/hf_trainer.py +8 -0
  9. efficio-0.1.0/efficio/adapters/lightning.py +8 -0
  10. efficio-0.1.0/efficio/budget/__init__.py +2 -0
  11. efficio-0.1.0/efficio/budget/planner.py +9 -0
  12. efficio-0.1.0/efficio/budget/shutdown.py +8 -0
  13. efficio-0.1.0/efficio/checkpoint/__init__.py +5 -0
  14. efficio-0.1.0/efficio/checkpoint/light.py +9 -0
  15. efficio-0.1.0/efficio/checkpoint/manager.py +96 -0
  16. efficio-0.1.0/efficio/checkpoint/storage.py +9 -0
  17. efficio-0.1.0/efficio/data/__init__.py +5 -0
  18. efficio-0.1.0/efficio/data/image_cache.py +386 -0
  19. efficio-0.1.0/efficio/env/__init__.py +6 -0
  20. efficio-0.1.0/efficio/env/detect.py +35 -0
  21. efficio-0.1.0/efficio/env/hardware.py +39 -0
  22. efficio-0.1.0/efficio/interfaces.py +88 -0
  23. efficio-0.1.0/efficio/preflight.py +9 -0
  24. efficio-0.1.0/efficio/report/__init__.py +1 -0
  25. efficio-0.1.0/efficio/report/notify.py +8 -0
  26. efficio-0.1.0/efficio/report/profiler.py +161 -0
  27. efficio-0.1.0/efficio/run.py +375 -0
  28. efficio-0.1.0/efficio/speed/__init__.py +1 -0
  29. efficio-0.1.0/efficio/speed/amp.py +8 -0
  30. efficio-0.1.0/efficio/speed/batch_size.py +8 -0
  31. efficio-0.1.0/efficio/speed/cache.py +8 -0
  32. efficio-0.1.0/efficio/speed/dataloader.py +129 -0
  33. efficio-0.1.0/efficio/speed/gpu_augment.py +259 -0
  34. efficio-0.1.0/efficio/speed/gpu_data.py +154 -0
  35. efficio-0.1.0/efficio/speed/memory_format.py +25 -0
  36. efficio-0.1.0/efficio/speed/multigpu.py +8 -0
  37. efficio-0.1.0/efficio.egg-info/PKG-INFO +225 -0
  38. efficio-0.1.0/efficio.egg-info/SOURCES.txt +41 -0
  39. efficio-0.1.0/efficio.egg-info/dependency_links.txt +1 -0
  40. efficio-0.1.0/efficio.egg-info/requires.txt +18 -0
  41. efficio-0.1.0/efficio.egg-info/top_level.txt +1 -0
  42. efficio-0.1.0/pyproject.toml +62 -0
  43. efficio-0.1.0/setup.cfg +4 -0
efficio-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hassaan Tole and Durvesh Bhadgaonkar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,9 @@
1
+ # The source distribution holds only the efficio package plus the files needed to build it.
2
+ include LICENSE README.md pyproject.toml
3
+ graft efficio
4
+ prune tests
5
+ prune benchmarks
6
+ prune examples
7
+ prune data
8
+ global-exclude *.py[cod] __pycache__ *.ipynb_checkpoints
9
+ prune .github
efficio-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,225 @@
1
+ Metadata-Version: 2.4
2
+ Name: efficio
3
+ Version: 0.1.0
4
+ Summary: Get more training out of every GPU hour on Colab, Kaggle and local GPUs: mixed precision, faster data loading, time budgets and exact resume for PyTorch.
5
+ Author: Hassaan Tole, Durvesh Bhadgaonkar
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/HuNTer8272/Efficio
8
+ Project-URL: Repository, https://github.com/HuNTer8272/Efficio
9
+ Project-URL: Issues, https://github.com/HuNTer8272/Efficio/issues
10
+ Keywords: pytorch,deep-learning,training,gpu,colab,kaggle,mixed-precision,checkpointing,resume,dataloader
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: torch>=2.0
25
+ Requires-Dist: numpy
26
+ Requires-Dist: pillow
27
+ Provides-Extra: hub
28
+ Requires-Dist: huggingface_hub; extra == "hub"
29
+ Provides-Extra: light
30
+ Requires-Dist: bitsandbytes; extra == "light"
31
+ Provides-Extra: monitor
32
+ Requires-Dist: nvidia-ml-py; extra == "monitor"
33
+ Provides-Extra: dev
34
+ Requires-Dist: pytest; extra == "dev"
35
+ Requires-Dist: ruff; extra == "dev"
36
+ Requires-Dist: build; extra == "dev"
37
+ Requires-Dist: twine; extra == "dev"
38
+ Dynamic: license-file
39
+
40
+ # Efficio
41
+
42
+ **Get more training out of every GPU hour on Colab, Kaggle and your own GPU.** Efficio is a small PyTorch library that picks the right mixed precision, keeps the GPU fed with data, stops cleanly before your time runs out and resumes exactly where it stopped.
43
+
44
+ ```bash
45
+ pip install efficio
46
+ ```
47
+
48
+ ## Quick start on Colab
49
+
50
+ ```python
51
+ !pip install -q efficio
52
+ import efficio, torch
53
+
54
+ run = efficio.Run(budget="2h") # save and stop 10 minutes before 2 h
55
+ model, optimizer = run.prepare(model, optimizer) # resumes from the last checkpoint
56
+ for x, y in run.loop(train_loader, epochs=20): # batches arrive on the GPU
57
+ with run.autocast(): # fp16 on a T4, bf16 on newer GPUs
58
+ loss = torch.nn.functional.cross_entropy(model(x), y)
59
+ run.backward(loss)
60
+ run.step()
61
+ run.report() # time split, GPU use and tips
62
+ ```
63
+
64
+ Run the same cell again after a disconnect or in a new session and training continues from the last checkpoint. A complete CIFAR-10 example is in the [Colab quick-start notebook](https://github.com/HuNTer8272/Efficio/blob/main/examples/colab_quickstart.ipynb).
65
+
66
+ ## What it does for you
67
+
68
+ Free GPUs on Colab and Kaggle have strict time limits, and much of that time is wasted: slow data loading, full-precision math and runs lost when a session dies.
69
+
70
+ - **On Colab and Kaggle:** faster training, less wasted quota, and progress saved before the limit hits, so you can continue after the reset or on the other platform.
71
+ - **On your own GPU:** faster training and crash safety (power cuts, restarts, out-of-memory crashes). Local GPUs have no quota, so there the benefit is speed and safety, not extra time.
72
+
73
+ ## Features
74
+
75
+ ### Speed
76
+
77
+ - **GPU-aware mixed precision:** fp16 with gradient scaling on a T4 and older GPUs, bf16 on Ampere and newer, fp32 on CPU (`run.autocast()`).
78
+ - **Automatic DataLoader tuning:** more workers, pinned memory, persistent workers and prefetching, applied by `run.loop()` on Linux (Colab, Kaggle).
79
+ - **channels_last** memory format for convolutional models on the GPU, plus `cudnn.benchmark`.
80
+ - **GPULoader:** keeps a small dataset (CIFAR, MNIST, FashionMNIST) on the GPU and builds and augments batches there, with no CPU work per batch.
81
+ - **ImageCache:** decodes and resizes a large image dataset once, using all CPU cores and parallel file reads, into one uint8 array that later sessions reuse. The build is resumable.
82
+ - **GPU augmentation** for any DataLoader: crop, resized crop, affine, flip, brightness and contrast, normalize (`efficio.gpu_augment`, used through `run.loop(..., gpu_transform=...)`).
83
+
84
+ ### Time budget and checkpoints
85
+
86
+ - **Budget stop:** `Run(budget="2h")` saves and stops before the time runs out. A shutdown signal (SIGTERM) also triggers a save.
87
+ - **Full checkpoints:** model, optimizer, scheduler, gradient scaler, RNG states and the exact position in the epoch.
88
+ - **Atomic saves**, so a crash in the middle of a save never corrupts the last good checkpoint. Periodic and emergency saves.
89
+ - **Exact mid-epoch resume:** the same step count, and the same data order with GPULoader or unshuffled data.
90
+ - **Gradient accumulation** (`Run(grad_accum_steps=...)`) and a validation helper (`run.evaluate_mode()`: eval mode, no gradients, mixed precision).
91
+
92
+ ### Visibility
93
+
94
+ - **End-of-run report:** setup vs training time, time spent waiting for data, average GPU utilization, GPU minutes used, the optimizations applied, and plain-language tips (for example: use GPULoader, use ImageCache, or try a larger batch).
95
+
96
+ ### Available, needs GPU verification
97
+
98
+ These work and are covered by CPU tests, but haven't been confirmed on a GPU yet:
99
+
100
+ - **Same data order after a resume with a shuffled DataLoader** (per-epoch seeded shuffle).
101
+ - **Gradient clipping that works with fp16 and gradient accumulation** (`run.clip_grad_norm(max_norm)`). It has run on a Kaggle T4 without errors; its values with the fp16 scaler are only checked on CPU so far.
102
+ - **Report tip for image datasets decoded from files every epoch** (suggests ImageCache).
103
+
104
+ The large-image features (ImageCache, CachedImageDataset, GPU augmentation through `gpu_transform`, `evaluate_mode`) have been verified on Kaggle (Tesla T4) but not yet on Colab.
105
+
106
+ ## Usage
107
+
108
+ ### Keep a small dataset on the GPU
109
+
110
+ ```python
111
+ from efficio import GPULoader, gpu_augment as A
112
+
113
+ loader = GPULoader.from_torchvision(train_set, batch_size=128, shuffle=True, transform=A.Compose(
114
+ [A.RandomCrop(32, padding=4), A.RandomHorizontalFlip(), A.Normalize(mean, std)]))
115
+ for x, y in run.loop(loader, epochs=20):
116
+ ...
117
+ ```
118
+
119
+ `from_torchvision` reads `train_set.data` and `train_set.targets` and ignores the dataset's own CPU transform, so pass the GPU version as `transform`. For other data use `GPULoader.from_tensors(x, y, batch_size)`. GPULoader refuses datasets that would need more than 30% of free GPU memory; use a normal DataLoader for those.
120
+
121
+ ### Large image datasets
122
+
123
+ ```python
124
+ from torch.utils.data import DataLoader
125
+ import efficio
126
+ from efficio import CachedImageDataset, ImageCache, gpu_augment as A
127
+
128
+ ImageCache.build(paths, labels, "/content/cache/train", size=(224, 224), grayscale=True)
129
+ loader = DataLoader(CachedImageDataset("/content/cache/train"), batch_size=64, shuffle=True,
130
+ num_workers=2)
131
+ augment = A.Compose([A.ToFloat(), A.RepeatChannels(3), A.RandomResizedCrop(224, scale=(0.8, 1.0)),
132
+ A.RandomHorizontalFlip(), A.Normalize(mean, std)])
133
+ for x, y in run.loop(loader, epochs=10, gpu_transform=efficio.on_inputs(augment)):
134
+ ...
135
+ ```
136
+
137
+ `labels` can be class ids or a multi-hot array. Building the cache needs no GPU: to save GPU quota, build it in a CPU-only session, keep the folder (Google Drive on Colab; notebook output or a private dataset on Kaggle) and train in a GPU session. With `workers > 1` on Windows or macOS, call `ImageCache.build` under `if __name__ == "__main__":`.
138
+
139
+ ### Gradient clipping and validation
140
+
141
+ ```python
142
+ run.backward(loss)
143
+ run.clip_grad_norm(1.0) # between backward and step; real units, also with fp16
144
+ run.step()
145
+
146
+ with run.evaluate_mode(): # eval mode, no gradients, mixed precision
147
+ val_loss = loss_fn(model(x_val), y_val)
148
+ ```
149
+
150
+ ## Results
151
+
152
+ These are single runs on free notebook GPUs, so read them as examples, not guarantees: speedups depend on the model, the data pipeline and how many CPU cores you get. The baselines are plain PyTorch in fp32 without any Efficio feature. Epoch-time averages leave out the first (warm-up) epoch. The raw results are in [`benchmarks/results/`](https://github.com/HuNTer8272/Efficio/tree/main/benchmarks/results) and the scripts in [`benchmarks/`](https://github.com/HuNTer8272/Efficio/tree/main/benchmarks).
153
+
154
+ ### ResNet-18 on CIFAR-10, Colab, 1x Tesla T4
155
+
156
+ 5 epochs, batch size 128.
157
+
158
+ | Run | Avg epoch | Epochs per hour | GPU utilization | Test accuracy | Speedup |
159
+ |---|---|---|---|---|---|
160
+ | Plain PyTorch (fp32) | 47.6 s | 75.6 | 98% | 85.37% | 1.00x |
161
+ | Efficio (fp16, DataLoader tuning, channels_last) | 20.7 s | 173.6 | 54% | 85.57% | 2.30x |
162
+ | Efficio + GPULoader | 13.3 s | 270.4 | 96% | 85.59% | 3.58x |
163
+
164
+ With the normal DataLoader the GPU waited for Colab's 2 CPU cores to augment images (22% data-wait). GPULoader keeps the data and augmentation on the GPU.
165
+
166
+ ### ResNet-18 on CIFAR-10, Kaggle, Tesla T4 (one of the two GPUs)
167
+
168
+ Same settings as on Colab.
169
+
170
+ | Run | Avg epoch | Epochs per hour | GPU utilization | Test accuracy | Speedup |
171
+ |---|---|---|---|---|---|
172
+ | Plain PyTorch (fp32) | 44.8 s | 80.3 | 98% | 85.98% | 1.00x |
173
+ | Efficio (fp16, DataLoader tuning, channels_last) | 14.7 s | 244.3 | 89% | 85.55% | 3.04x |
174
+ | Efficio + GPULoader | 14.7 s | 244.3 | 91% | 85.52% | 3.04x |
175
+
176
+ Kaggle gives the notebook 4 CPU cores, so the tuned DataLoader already keeps the GPU busy (2% data-wait) and GPULoader adds no speed there.
177
+
178
+ ### Large images from disk: NIH Chest X-rays, Kaggle, Tesla T4
179
+
180
+ 5,000 randomly sampled 1024x1024 PNGs from `/kaggle/input` (4,500 train / 500 validation), 14 findings as a multi-label target, ResNet-18 at 224x224, batch size 64, 4 epochs. The baseline decodes, resizes and augments every PNG on the CPU in each epoch with 4 DataLoader workers; Efficio builds an ImageCache once and augments on the GPU. Both ran in the same Kaggle session.
181
+
182
+ | Run | Avg epoch | Epochs per hour | GPU utilization | Data-wait | Validation AUROC |
183
+ |---|---|---|---|---|---|
184
+ | Plain PyTorch (fp32, CPU decoding, 4 workers) | 34.9 s | 103.0 | 37% | 74% | 0.615 |
185
+ | Efficio (fp16, ImageCache + GPU augmentation) | 5.8 s | 625.6 | 78% | 0.3% | 0.673 |
186
+
187
+ Building the cache took about 36 s on cold files (139 images/s); later runs reuse it. Caveats:
188
+
189
+ - **Warm file cache:** the baseline ran after the cache build, so the PNGs were already in RAM, which makes its file reads faster than on a cold start.
190
+ - **Single runs:** each row is one 4-epoch run. Efficio's epoch time varied by about 10-15% between sessions (5.1 to 5.8 s).
191
+ - **AUROC, not accuracy:** most labels are 0, so per-label accuracy is about 95% for every run. Across the NIH runs, AUROC ranged from 0.615 to 0.673 for both pipelines, so 4 epochs on 4,500 images can't show an accuracy difference either way.
192
+
193
+ ## Works with
194
+
195
+ - PyTorch 2.0 or newer, Python 3.10 or newer
196
+ - Google Colab and Kaggle notebooks
197
+ - A local NVIDIA GPU, or CPU (for development and testing)
198
+
199
+ ## Known limitations
200
+
201
+ - **PyTorch only.** TensorFlow/Keras support is planned.
202
+ - **Single GPU.** On Kaggle's 2x T4 machines, Efficio trains on one GPU.
203
+ - **GPULoader** needs a dataset with `.data` and `.targets` (`from_torchvision`), or tensors passed to `GPULoader.from_tensors`.
204
+ - **Windows and macOS:** Efficio doesn't add DataLoader workers automatically there, because worker processes need a picklable dataset and an `if __name__ == "__main__":` guard. Set `num_workers` yourself to use them.
205
+
206
+ ## Roadmap
207
+
208
+ Planned for future releases:
209
+
210
+ - Light checkpoints (about 3x smaller)
211
+ - Hugging Face Hub and Google Drive storage for moving runs between platforms
212
+ - Automatic batch size with out-of-memory recovery
213
+ - A CPU pre-flight check, a budget planner and auto-shutdown when training ends
214
+ - Both Kaggle T4 GPUs
215
+ - Hugging Face Trainer, PyTorch Lightning and TensorFlow/Keras support
216
+ - Telegram and email notifications
217
+ - More benchmarks: a DistilBERT fine-tune and a small LoRA run
218
+
219
+ ## Contributing
220
+
221
+ See [CONTRIBUTING.md](https://github.com/HuNTer8272/Efficio/blob/main/CONTRIBUTING.md) for the development setup, tests and pull request rules. Changes are listed in the [changelog](https://github.com/HuNTer8272/Efficio/blob/main/CHANGELOG.md).
222
+
223
+ ## License
224
+
225
+ MIT. See [LICENSE](https://github.com/HuNTer8272/Efficio/blob/main/LICENSE).
@@ -0,0 +1,186 @@
1
+ # Efficio
2
+
3
+ **Get more training out of every GPU hour on Colab, Kaggle and your own GPU.** Efficio is a small PyTorch library that picks the right mixed precision, keeps the GPU fed with data, stops cleanly before your time runs out and resumes exactly where it stopped.
4
+
5
+ ```bash
6
+ pip install efficio
7
+ ```
8
+
9
+ ## Quick start on Colab
10
+
11
+ ```python
12
+ !pip install -q efficio
13
+ import efficio, torch
14
+
15
+ run = efficio.Run(budget="2h") # save and stop 10 minutes before 2 h
16
+ model, optimizer = run.prepare(model, optimizer) # resumes from the last checkpoint
17
+ for x, y in run.loop(train_loader, epochs=20): # batches arrive on the GPU
18
+ with run.autocast(): # fp16 on a T4, bf16 on newer GPUs
19
+ loss = torch.nn.functional.cross_entropy(model(x), y)
20
+ run.backward(loss)
21
+ run.step()
22
+ run.report() # time split, GPU use and tips
23
+ ```
24
+
25
+ Run the same cell again after a disconnect or in a new session and training continues from the last checkpoint. A complete CIFAR-10 example is in the [Colab quick-start notebook](https://github.com/HuNTer8272/Efficio/blob/main/examples/colab_quickstart.ipynb).
26
+
27
+ ## What it does for you
28
+
29
+ Free GPUs on Colab and Kaggle have strict time limits, and much of that time is wasted: slow data loading, full-precision math and runs lost when a session dies.
30
+
31
+ - **On Colab and Kaggle:** faster training, less wasted quota, and progress saved before the limit hits, so you can continue after the reset or on the other platform.
32
+ - **On your own GPU:** faster training and crash safety (power cuts, restarts, out-of-memory crashes). Local GPUs have no quota, so there the benefit is speed and safety, not extra time.
33
+
34
+ ## Features
35
+
36
+ ### Speed
37
+
38
+ - **GPU-aware mixed precision:** fp16 with gradient scaling on a T4 and older GPUs, bf16 on Ampere and newer, fp32 on CPU (`run.autocast()`).
39
+ - **Automatic DataLoader tuning:** more workers, pinned memory, persistent workers and prefetching, applied by `run.loop()` on Linux (Colab, Kaggle).
40
+ - **channels_last** memory format for convolutional models on the GPU, plus `cudnn.benchmark`.
41
+ - **GPULoader:** keeps a small dataset (CIFAR, MNIST, FashionMNIST) on the GPU and builds and augments batches there, with no CPU work per batch.
42
+ - **ImageCache:** decodes and resizes a large image dataset once, using all CPU cores and parallel file reads, into one uint8 array that later sessions reuse. The build is resumable.
43
+ - **GPU augmentation** for any DataLoader: crop, resized crop, affine, flip, brightness and contrast, normalize (`efficio.gpu_augment`, used through `run.loop(..., gpu_transform=...)`).
44
+
45
+ ### Time budget and checkpoints
46
+
47
+ - **Budget stop:** `Run(budget="2h")` saves and stops before the time runs out. A shutdown signal (SIGTERM) also triggers a save.
48
+ - **Full checkpoints:** model, optimizer, scheduler, gradient scaler, RNG states and the exact position in the epoch.
49
+ - **Atomic saves**, so a crash in the middle of a save never corrupts the last good checkpoint. Periodic and emergency saves.
50
+ - **Exact mid-epoch resume:** the same step count, and the same data order with GPULoader or unshuffled data.
51
+ - **Gradient accumulation** (`Run(grad_accum_steps=...)`) and a validation helper (`run.evaluate_mode()`: eval mode, no gradients, mixed precision).
52
+
53
+ ### Visibility
54
+
55
+ - **End-of-run report:** setup vs training time, time spent waiting for data, average GPU utilization, GPU minutes used, the optimizations applied, and plain-language tips (for example: use GPULoader, use ImageCache, or try a larger batch).
56
+
57
+ ### Available, needs GPU verification
58
+
59
+ These work and are covered by CPU tests, but haven't been confirmed on a GPU yet:
60
+
61
+ - **Same data order after a resume with a shuffled DataLoader** (per-epoch seeded shuffle).
62
+ - **Gradient clipping that works with fp16 and gradient accumulation** (`run.clip_grad_norm(max_norm)`). It has run on a Kaggle T4 without errors; its values with the fp16 scaler are only checked on CPU so far.
63
+ - **Report tip for image datasets decoded from files every epoch** (suggests ImageCache).
64
+
65
+ The large-image features (ImageCache, CachedImageDataset, GPU augmentation through `gpu_transform`, `evaluate_mode`) have been verified on Kaggle (Tesla T4) but not yet on Colab.
66
+
67
+ ## Usage
68
+
69
+ ### Keep a small dataset on the GPU
70
+
71
+ ```python
72
+ from efficio import GPULoader, gpu_augment as A
73
+
74
+ loader = GPULoader.from_torchvision(train_set, batch_size=128, shuffle=True, transform=A.Compose(
75
+ [A.RandomCrop(32, padding=4), A.RandomHorizontalFlip(), A.Normalize(mean, std)]))
76
+ for x, y in run.loop(loader, epochs=20):
77
+ ...
78
+ ```
79
+
80
+ `from_torchvision` reads `train_set.data` and `train_set.targets` and ignores the dataset's own CPU transform, so pass the GPU version as `transform`. For other data use `GPULoader.from_tensors(x, y, batch_size)`. GPULoader refuses datasets that would need more than 30% of free GPU memory; use a normal DataLoader for those.
81
+
82
+ ### Large image datasets
83
+
84
+ ```python
85
+ from torch.utils.data import DataLoader
86
+ import efficio
87
+ from efficio import CachedImageDataset, ImageCache, gpu_augment as A
88
+
89
+ ImageCache.build(paths, labels, "/content/cache/train", size=(224, 224), grayscale=True)
90
+ loader = DataLoader(CachedImageDataset("/content/cache/train"), batch_size=64, shuffle=True,
91
+ num_workers=2)
92
+ augment = A.Compose([A.ToFloat(), A.RepeatChannels(3), A.RandomResizedCrop(224, scale=(0.8, 1.0)),
93
+ A.RandomHorizontalFlip(), A.Normalize(mean, std)])
94
+ for x, y in run.loop(loader, epochs=10, gpu_transform=efficio.on_inputs(augment)):
95
+ ...
96
+ ```
97
+
98
+ `labels` can be class ids or a multi-hot array. Building the cache needs no GPU: to save GPU quota, build it in a CPU-only session, keep the folder (Google Drive on Colab; notebook output or a private dataset on Kaggle) and train in a GPU session. With `workers > 1` on Windows or macOS, call `ImageCache.build` under `if __name__ == "__main__":`.
99
+
100
+ ### Gradient clipping and validation
101
+
102
+ ```python
103
+ run.backward(loss)
104
+ run.clip_grad_norm(1.0) # between backward and step; real units, also with fp16
105
+ run.step()
106
+
107
+ with run.evaluate_mode(): # eval mode, no gradients, mixed precision
108
+ val_loss = loss_fn(model(x_val), y_val)
109
+ ```
110
+
111
+ ## Results
112
+
113
+ These are single runs on free notebook GPUs, so read them as examples, not guarantees: speedups depend on the model, the data pipeline and how many CPU cores you get. The baselines are plain PyTorch in fp32 without any Efficio feature. Epoch-time averages leave out the first (warm-up) epoch. The raw results are in [`benchmarks/results/`](https://github.com/HuNTer8272/Efficio/tree/main/benchmarks/results) and the scripts in [`benchmarks/`](https://github.com/HuNTer8272/Efficio/tree/main/benchmarks).
114
+
115
+ ### ResNet-18 on CIFAR-10, Colab, 1x Tesla T4
116
+
117
+ 5 epochs, batch size 128.
118
+
119
+ | Run | Avg epoch | Epochs per hour | GPU utilization | Test accuracy | Speedup |
120
+ |---|---|---|---|---|---|
121
+ | Plain PyTorch (fp32) | 47.6 s | 75.6 | 98% | 85.37% | 1.00x |
122
+ | Efficio (fp16, DataLoader tuning, channels_last) | 20.7 s | 173.6 | 54% | 85.57% | 2.30x |
123
+ | Efficio + GPULoader | 13.3 s | 270.4 | 96% | 85.59% | 3.58x |
124
+
125
+ With the normal DataLoader the GPU waited for Colab's 2 CPU cores to augment images (22% data-wait). GPULoader keeps the data and augmentation on the GPU.
126
+
127
+ ### ResNet-18 on CIFAR-10, Kaggle, Tesla T4 (one of the two GPUs)
128
+
129
+ Same settings as on Colab.
130
+
131
+ | Run | Avg epoch | Epochs per hour | GPU utilization | Test accuracy | Speedup |
132
+ |---|---|---|---|---|---|
133
+ | Plain PyTorch (fp32) | 44.8 s | 80.3 | 98% | 85.98% | 1.00x |
134
+ | Efficio (fp16, DataLoader tuning, channels_last) | 14.7 s | 244.3 | 89% | 85.55% | 3.04x |
135
+ | Efficio + GPULoader | 14.7 s | 244.3 | 91% | 85.52% | 3.04x |
136
+
137
+ Kaggle gives the notebook 4 CPU cores, so the tuned DataLoader already keeps the GPU busy (2% data-wait) and GPULoader adds no speed there.
138
+
139
+ ### Large images from disk: NIH Chest X-rays, Kaggle, Tesla T4
140
+
141
+ 5,000 randomly sampled 1024x1024 PNGs from `/kaggle/input` (4,500 train / 500 validation), 14 findings as a multi-label target, ResNet-18 at 224x224, batch size 64, 4 epochs. The baseline decodes, resizes and augments every PNG on the CPU in each epoch with 4 DataLoader workers; Efficio builds an ImageCache once and augments on the GPU. Both ran in the same Kaggle session.
142
+
143
+ | Run | Avg epoch | Epochs per hour | GPU utilization | Data-wait | Validation AUROC |
144
+ |---|---|---|---|---|---|
145
+ | Plain PyTorch (fp32, CPU decoding, 4 workers) | 34.9 s | 103.0 | 37% | 74% | 0.615 |
146
+ | Efficio (fp16, ImageCache + GPU augmentation) | 5.8 s | 625.6 | 78% | 0.3% | 0.673 |
147
+
148
+ Building the cache took about 36 s on cold files (139 images/s); later runs reuse it. Caveats:
149
+
150
+ - **Warm file cache:** the baseline ran after the cache build, so the PNGs were already in RAM, which makes its file reads faster than on a cold start.
151
+ - **Single runs:** each row is one 4-epoch run. Efficio's epoch time varied by about 10-15% between sessions (5.1 to 5.8 s).
152
+ - **AUROC, not accuracy:** most labels are 0, so per-label accuracy is about 95% for every run. Across the NIH runs, AUROC ranged from 0.615 to 0.673 for both pipelines, so 4 epochs on 4,500 images can't show an accuracy difference either way.
153
+
154
+ ## Works with
155
+
156
+ - PyTorch 2.0 or newer, Python 3.10 or newer
157
+ - Google Colab and Kaggle notebooks
158
+ - A local NVIDIA GPU, or CPU (for development and testing)
159
+
160
+ ## Known limitations
161
+
162
+ - **PyTorch only.** TensorFlow/Keras support is planned.
163
+ - **Single GPU.** On Kaggle's 2x T4 machines, Efficio trains on one GPU.
164
+ - **GPULoader** needs a dataset with `.data` and `.targets` (`from_torchvision`), or tensors passed to `GPULoader.from_tensors`.
165
+ - **Windows and macOS:** Efficio doesn't add DataLoader workers automatically there, because worker processes need a picklable dataset and an `if __name__ == "__main__":` guard. Set `num_workers` yourself to use them.
166
+
167
+ ## Roadmap
168
+
169
+ Planned for future releases:
170
+
171
+ - Light checkpoints (about 3x smaller)
172
+ - Hugging Face Hub and Google Drive storage for moving runs between platforms
173
+ - Automatic batch size with out-of-memory recovery
174
+ - A CPU pre-flight check, a budget planner and auto-shutdown when training ends
175
+ - Both Kaggle T4 GPUs
176
+ - Hugging Face Trainer, PyTorch Lightning and TensorFlow/Keras support
177
+ - Telegram and email notifications
178
+ - More benchmarks: a DistilBERT fine-tune and a small LoRA run
179
+
180
+ ## Contributing
181
+
182
+ See [CONTRIBUTING.md](https://github.com/HuNTer8272/Efficio/blob/main/CONTRIBUTING.md) for the development setup, tests and pull request rules. Changes are listed in the [changelog](https://github.com/HuNTer8272/Efficio/blob/main/CHANGELOG.md).
183
+
184
+ ## License
185
+
186
+ MIT. See [LICENSE](https://github.com/HuNTer8272/Efficio/blob/main/LICENSE).
@@ -0,0 +1,43 @@
1
+ """Efficio: get more training out of every GPU hour on Colab, Kaggle and local GPUs.
2
+
3
+ import efficio
4
+ run = efficio.Run(budget="2h")
5
+ model, optimizer = run.prepare(model, optimizer)
6
+ for x, y in run.loop(loader, epochs=20):
7
+ with run.autocast():
8
+ loss = loss_fn(model(x), y)
9
+ run.backward(loss)
10
+ run.step()
11
+ run.report()
12
+ """
13
+
14
+ from importlib.metadata import PackageNotFoundError
15
+ from importlib.metadata import version as _version
16
+
17
+ from .data import CachedImageDataset, ImageCache
18
+ from .env import detect_hardware, detect_platform
19
+ from .interfaces import Budget, HardwareInfo, TrainState
20
+ from .run import Run
21
+ from .speed import gpu_augment
22
+ from .speed.gpu_augment import on_inputs
23
+ from .speed.gpu_data import GPULoader
24
+
25
+ try:
26
+ __version__ = _version("efficio") # the version in pyproject.toml, once installed
27
+ except PackageNotFoundError: # running from a source tree that isn't installed
28
+ __version__ = "0+unknown"
29
+
30
+ __all__ = [
31
+ "Run",
32
+ "GPULoader",
33
+ "ImageCache",
34
+ "CachedImageDataset",
35
+ "on_inputs",
36
+ "gpu_augment",
37
+ "detect_hardware",
38
+ "detect_platform",
39
+ "Budget",
40
+ "HardwareInfo",
41
+ "TrainState",
42
+ "__version__",
43
+ ]
@@ -0,0 +1,16 @@
1
+ """Support for modules whose features are planned but not released yet.
2
+
3
+ A planned module sets `__getattr__ = planned(__name__)`. Looking up any public name in it
4
+ raises NotImplementedError, so nothing in it can be mistaken for a working feature.
5
+ """
6
+
7
+ MESSAGE = "planned for a future release"
8
+
9
+
10
+ def planned(module_name):
11
+ """Return a module-level __getattr__ that rejects every public name."""
12
+ def __getattr__(name):
13
+ if name.startswith("__"): # let Python and tools probe dunder names safely
14
+ raise AttributeError(f"module {module_name!r} has no attribute {name!r}")
15
+ raise NotImplementedError(f"{module_name}.{name}: {MESSAGE}")
16
+ return __getattr__
@@ -0,0 +1 @@
1
+ """Planned for a future release: adapters for the Hugging Face Trainer and PyTorch Lightning."""
@@ -0,0 +1,8 @@
1
+ """Planned for a future release: a callback for the Hugging Face Trainer.
2
+
3
+ Nothing in this module is usable yet; any name looked up here raises NotImplementedError.
4
+ """
5
+
6
+ from .._planned import planned
7
+
8
+ __getattr__ = planned(__name__)
@@ -0,0 +1,8 @@
1
+ """Planned for a future release: a callback for PyTorch Lightning.
2
+
3
+ Nothing in this module is usable yet; any name looked up here raises NotImplementedError.
4
+ """
5
+
6
+ from .._planned import planned
7
+
8
+ __getattr__ = planned(__name__)
@@ -0,0 +1,2 @@
1
+ """Time budget features. The budget stop itself is part of efficio.Run; the planner and
2
+ auto-shutdown here are planned for a future release."""
@@ -0,0 +1,9 @@
1
+ """Planned for a future release: a budget planner that times the first steps and fits the number
2
+ of epochs and the learning-rate schedule to the time budget.
3
+
4
+ Nothing in this module is usable yet; any name looked up here raises NotImplementedError.
5
+ """
6
+
7
+ from .._planned import planned
8
+
9
+ __getattr__ = planned(__name__)
@@ -0,0 +1,8 @@
1
+ """Planned for a future release: releasing the runtime when training ends (for example on Colab).
2
+
3
+ Nothing in this module is usable yet; any name looked up here raises NotImplementedError.
4
+ """
5
+
6
+ from .._planned import planned
7
+
8
+ __getattr__ = planned(__name__)
@@ -0,0 +1,5 @@
1
+ """Atomic checkpoint save and exact resume."""
2
+
3
+ from .manager import CheckpointManager
4
+
5
+ __all__ = ["CheckpointManager"]
@@ -0,0 +1,9 @@
1
+ """Planned for a future release: light checkpoints (trainable parameters only, fp16 weights,
2
+ 8-bit optimizer state).
3
+
4
+ Nothing in this module is usable yet; any name looked up here raises NotImplementedError.
5
+ """
6
+
7
+ from .._planned import planned
8
+
9
+ __getattr__ = planned(__name__)