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.
- efficio-0.1.0/LICENSE +21 -0
- efficio-0.1.0/MANIFEST.in +9 -0
- efficio-0.1.0/PKG-INFO +225 -0
- efficio-0.1.0/README.md +186 -0
- efficio-0.1.0/efficio/__init__.py +43 -0
- efficio-0.1.0/efficio/_planned.py +16 -0
- efficio-0.1.0/efficio/adapters/__init__.py +1 -0
- efficio-0.1.0/efficio/adapters/hf_trainer.py +8 -0
- efficio-0.1.0/efficio/adapters/lightning.py +8 -0
- efficio-0.1.0/efficio/budget/__init__.py +2 -0
- efficio-0.1.0/efficio/budget/planner.py +9 -0
- efficio-0.1.0/efficio/budget/shutdown.py +8 -0
- efficio-0.1.0/efficio/checkpoint/__init__.py +5 -0
- efficio-0.1.0/efficio/checkpoint/light.py +9 -0
- efficio-0.1.0/efficio/checkpoint/manager.py +96 -0
- efficio-0.1.0/efficio/checkpoint/storage.py +9 -0
- efficio-0.1.0/efficio/data/__init__.py +5 -0
- efficio-0.1.0/efficio/data/image_cache.py +386 -0
- efficio-0.1.0/efficio/env/__init__.py +6 -0
- efficio-0.1.0/efficio/env/detect.py +35 -0
- efficio-0.1.0/efficio/env/hardware.py +39 -0
- efficio-0.1.0/efficio/interfaces.py +88 -0
- efficio-0.1.0/efficio/preflight.py +9 -0
- efficio-0.1.0/efficio/report/__init__.py +1 -0
- efficio-0.1.0/efficio/report/notify.py +8 -0
- efficio-0.1.0/efficio/report/profiler.py +161 -0
- efficio-0.1.0/efficio/run.py +375 -0
- efficio-0.1.0/efficio/speed/__init__.py +1 -0
- efficio-0.1.0/efficio/speed/amp.py +8 -0
- efficio-0.1.0/efficio/speed/batch_size.py +8 -0
- efficio-0.1.0/efficio/speed/cache.py +8 -0
- efficio-0.1.0/efficio/speed/dataloader.py +129 -0
- efficio-0.1.0/efficio/speed/gpu_augment.py +259 -0
- efficio-0.1.0/efficio/speed/gpu_data.py +154 -0
- efficio-0.1.0/efficio/speed/memory_format.py +25 -0
- efficio-0.1.0/efficio/speed/multigpu.py +8 -0
- efficio-0.1.0/efficio.egg-info/PKG-INFO +225 -0
- efficio-0.1.0/efficio.egg-info/SOURCES.txt +41 -0
- efficio-0.1.0/efficio.egg-info/dependency_links.txt +1 -0
- efficio-0.1.0/efficio.egg-info/requires.txt +18 -0
- efficio-0.1.0/efficio.egg-info/top_level.txt +1 -0
- efficio-0.1.0/pyproject.toml +62 -0
- 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).
|
efficio-0.1.0/README.md
ADDED
|
@@ -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,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,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__)
|