dp-vit 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.
- dp_vit-0.1.0/LICENSE +21 -0
- dp_vit-0.1.0/PKG-INFO +238 -0
- dp_vit-0.1.0/README.md +226 -0
- dp_vit-0.1.0/pyproject.toml +18 -0
- dp_vit-0.1.0/setup.cfg +4 -0
- dp_vit-0.1.0/src/dp_vit/__init__.py +34 -0
- dp_vit-0.1.0/src/dp_vit/checkpoint.py +147 -0
- dp_vit-0.1.0/src/dp_vit/config.py +116 -0
- dp_vit-0.1.0/src/dp_vit/ics.py +215 -0
- dp_vit-0.1.0/src/dp_vit/inference.py +132 -0
- dp_vit-0.1.0/src/dp_vit/model.py +80 -0
- dp_vit-0.1.0/src/dp_vit/patching.py +283 -0
- dp_vit-0.1.0/src/dp_vit/pruning.py +171 -0
- dp_vit-0.1.0/src/dp_vit.egg-info/PKG-INFO +238 -0
- dp_vit-0.1.0/src/dp_vit.egg-info/SOURCES.txt +16 -0
- dp_vit-0.1.0/src/dp_vit.egg-info/dependency_links.txt +1 -0
- dp_vit-0.1.0/src/dp_vit.egg-info/requires.txt +3 -0
- dp_vit-0.1.0/src/dp_vit.egg-info/top_level.txt +1 -0
dp_vit-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Quepi14
|
|
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.
|
dp_vit-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dp-vit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Dynamic Patch Vision Transformer with ICS-based patch selection
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: torch
|
|
9
|
+
Requires-Dist: numpy
|
|
10
|
+
Requires-Dist: scipy
|
|
11
|
+
Dynamic: license-file
|
|
12
|
+
|
|
13
|
+
# DP-ViT
|
|
14
|
+
|
|
15
|
+
DP-ViT is a PyTorch library for image classification with ICS-guided adaptive
|
|
16
|
+
patch sizes. It also provides optional structured magnitude-based pruning for
|
|
17
|
+
the Transformer MLP layers.
|
|
18
|
+
|
|
19
|
+
The library provides:
|
|
20
|
+
|
|
21
|
+
- Image Complexity Score (ICS) calculation and calibration
|
|
22
|
+
- Adaptive patch-size mapping and patch embedding
|
|
23
|
+
- A Vision Transformer classification model
|
|
24
|
+
- Optional structured MLP pruning
|
|
25
|
+
- Checkpoint save and load utilities
|
|
26
|
+
- Single-image inference
|
|
27
|
+
|
|
28
|
+
Dataset loading, image cleaning, augmentation, normalization, and training
|
|
29
|
+
loops are handled by the user.
|
|
30
|
+
|
|
31
|
+
> **Status:** DP-ViT is under development. Its API may change before a stable
|
|
32
|
+
> release.
|
|
33
|
+
|
|
34
|
+
## How it works
|
|
35
|
+
|
|
36
|
+
DP-ViT divides an image into regions and assigns a patch size to each region
|
|
37
|
+
using its ICS value. Regions with higher complexity receive smaller patches;
|
|
38
|
+
regions with lower complexity receive larger patches.
|
|
39
|
+
|
|
40
|
+
The patches are projected into tokens and passed to a Transformer encoder.
|
|
41
|
+
After training, users can optionally apply structured magnitude-based pruning
|
|
42
|
+
to the MLP layers. Pruning does not automatically fine-tune the model.
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
Clone the repository, then run this command from its root directory:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install -e .
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Basic workflow
|
|
53
|
+
|
|
54
|
+
1. Prepare RGB images with your own data pipeline.
|
|
55
|
+
2. Fit ICS calibration values using representative images.
|
|
56
|
+
3. Calculate a patch-size map for each image before channel normalization.
|
|
57
|
+
4. Normalize the image for the model, if needed.
|
|
58
|
+
5. Use `adaptive_collate_fn` to create batches.
|
|
59
|
+
6. Train `DPViT` with your own training loop.
|
|
60
|
+
7. Optionally prune the trained model.
|
|
61
|
+
8. Save the checkpoint and use it for inference.
|
|
62
|
+
|
|
63
|
+
## Example
|
|
64
|
+
|
|
65
|
+
This example shows how to create the model, prepare a batch, run a forward
|
|
66
|
+
pass, and optionally prune and save the model. It does not include a training
|
|
67
|
+
loop or dataset implementation.
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from functools import partial
|
|
71
|
+
|
|
72
|
+
import torch
|
|
73
|
+
from torch.utils.data import DataLoader
|
|
74
|
+
|
|
75
|
+
from dp_vit import (
|
|
76
|
+
DPViT,
|
|
77
|
+
DPViTConfig,
|
|
78
|
+
ICSConfig,
|
|
79
|
+
PruningConfig,
|
|
80
|
+
adaptive_collate_fn,
|
|
81
|
+
compute_patch_size_map_for_image,
|
|
82
|
+
fit_ics_calibration,
|
|
83
|
+
prune_mlp_by_magnitude,
|
|
84
|
+
save_checkpoint,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Each calibration image must be an RGB NumPy array with values in [0, 1].
|
|
88
|
+
calibration_images = [
|
|
89
|
+
# image_1_rgb_array,
|
|
90
|
+
# image_2_rgb_array,
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
config = DPViTConfig(
|
|
94
|
+
num_classes=5,
|
|
95
|
+
image_size=224,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
ics_calibration = fit_ics_calibration(
|
|
99
|
+
calibration_images=calibration_images,
|
|
100
|
+
region_size=config.region_size,
|
|
101
|
+
config=ICSConfig(),
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# For each dataset image, compute its patch map from the RGB image
|
|
105
|
+
# before channel normalization.
|
|
106
|
+
image_rgb_tensor = ... # Tensor with shape (3, 224, 224), values in [0, 1]
|
|
107
|
+
label = 0
|
|
108
|
+
|
|
109
|
+
patch_size_map = compute_patch_size_map_for_image(
|
|
110
|
+
image=image_rgb_tensor.permute(1, 2, 0).cpu().numpy(),
|
|
111
|
+
calibration=ics_calibration,
|
|
112
|
+
region_size=config.region_size,
|
|
113
|
+
patch_sizes=config.patch_sizes,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Apply your chosen normalization after computing the patch map.
|
|
117
|
+
image_for_model = image_rgb_tensor
|
|
118
|
+
|
|
119
|
+
dataset = [
|
|
120
|
+
(image_for_model, patch_size_map, label),
|
|
121
|
+
# Add more (image_tensor, patch_size_map, label) samples here.
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
loader = DataLoader(
|
|
125
|
+
dataset,
|
|
126
|
+
batch_size=2,
|
|
127
|
+
collate_fn=partial(
|
|
128
|
+
adaptive_collate_fn,
|
|
129
|
+
region_size=config.region_size,
|
|
130
|
+
patch_sizes=config.patch_sizes,
|
|
131
|
+
),
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
135
|
+
model = DPViT(config).to(device)
|
|
136
|
+
|
|
137
|
+
# Example forward pass. Use your own loss, optimizer, and training loop.
|
|
138
|
+
batch = next(iter(loader))
|
|
139
|
+
batch = {
|
|
140
|
+
key: value.to(device) if isinstance(value, torch.Tensor) else value
|
|
141
|
+
for key, value in batch.items()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
logits = model(batch)
|
|
145
|
+
targets = batch["labels"]
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The dataset samples passed to `adaptive_collate_fn` must have this structure:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
(image_tensor, patch_size_map, label)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
`image_tensor` must have shape `(3, height, width)`. The patch map must be
|
|
155
|
+
computed from the RGB image before channel normalization.
|
|
156
|
+
|
|
157
|
+
## Optional pruning and checkpoint saving
|
|
158
|
+
|
|
159
|
+
Apply pruning after training. Evaluate the pruned model on your validation or
|
|
160
|
+
test data to measure its effect.
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
model, pruning_report = prune_mlp_by_magnitude(
|
|
164
|
+
model,
|
|
165
|
+
PruningConfig(target_ratio=0.2),
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
save_checkpoint(
|
|
169
|
+
path="checkpoints/dp_vit.pth",
|
|
170
|
+
model=model,
|
|
171
|
+
ics_calibration=ics_calibration,
|
|
172
|
+
class_names=["class_a", "class_b", "class_c", "class_d", "class_e"],
|
|
173
|
+
pruning_report=pruning_report,
|
|
174
|
+
)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Loading a checkpoint and inference
|
|
178
|
+
|
|
179
|
+
The inference input must be an RGB tensor with shape `(3, image_size, image_size)`
|
|
180
|
+
and values in `[0, 1]`. Apply the same resize and normalization conventions used
|
|
181
|
+
during training.
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
from dp_vit import load_checkpoint, predict_tensor
|
|
185
|
+
|
|
186
|
+
model, ics_calibration, metadata = load_checkpoint(
|
|
187
|
+
"checkpoints/dp_vit.pth",
|
|
188
|
+
map_location=device,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
result = predict_tensor(
|
|
192
|
+
model=model,
|
|
193
|
+
image_before_normalization=image_rgb_tensor,
|
|
194
|
+
ics_calibration=ics_calibration,
|
|
195
|
+
class_names=metadata["class_names"],
|
|
196
|
+
normalize_fn=None,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
print(result["class_name"])
|
|
200
|
+
print(result["confidence"])
|
|
201
|
+
print(result["patch_size_map"])
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
If training used channel normalization, pass the same normalization function
|
|
205
|
+
through `normalize_fn` during inference.
|
|
206
|
+
|
|
207
|
+
## Project structure
|
|
208
|
+
|
|
209
|
+
```text
|
|
210
|
+
DP-ViT/
|
|
211
|
+
├── LICENSE
|
|
212
|
+
├── README.md
|
|
213
|
+
├── pyproject.toml
|
|
214
|
+
└── src/
|
|
215
|
+
└── dp_vit/
|
|
216
|
+
├── __init__.py
|
|
217
|
+
├── config.py
|
|
218
|
+
├── ics.py
|
|
219
|
+
├── patching.py
|
|
220
|
+
├── model.py
|
|
221
|
+
├── pruning.py
|
|
222
|
+
├── checkpoint.py
|
|
223
|
+
└── inference.py
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Scope
|
|
227
|
+
|
|
228
|
+
DP-ViT provides model components. It does not provide:
|
|
229
|
+
|
|
230
|
+
- Dataset-specific loaders or downloads
|
|
231
|
+
- Image cleaning or augmentation pipelines
|
|
232
|
+
- A training loop, optimizer, or scheduler
|
|
233
|
+
- A baseline Vision Transformer implementation
|
|
234
|
+
- A guarantee of faster training or inference
|
|
235
|
+
|
|
236
|
+
## License
|
|
237
|
+
|
|
238
|
+
This project is licensed under the MIT License. See [LICENSE](LICENSE).
|
dp_vit-0.1.0/README.md
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# DP-ViT
|
|
2
|
+
|
|
3
|
+
DP-ViT is a PyTorch library for image classification with ICS-guided adaptive
|
|
4
|
+
patch sizes. It also provides optional structured magnitude-based pruning for
|
|
5
|
+
the Transformer MLP layers.
|
|
6
|
+
|
|
7
|
+
The library provides:
|
|
8
|
+
|
|
9
|
+
- Image Complexity Score (ICS) calculation and calibration
|
|
10
|
+
- Adaptive patch-size mapping and patch embedding
|
|
11
|
+
- A Vision Transformer classification model
|
|
12
|
+
- Optional structured MLP pruning
|
|
13
|
+
- Checkpoint save and load utilities
|
|
14
|
+
- Single-image inference
|
|
15
|
+
|
|
16
|
+
Dataset loading, image cleaning, augmentation, normalization, and training
|
|
17
|
+
loops are handled by the user.
|
|
18
|
+
|
|
19
|
+
> **Status:** DP-ViT is under development. Its API may change before a stable
|
|
20
|
+
> release.
|
|
21
|
+
|
|
22
|
+
## How it works
|
|
23
|
+
|
|
24
|
+
DP-ViT divides an image into regions and assigns a patch size to each region
|
|
25
|
+
using its ICS value. Regions with higher complexity receive smaller patches;
|
|
26
|
+
regions with lower complexity receive larger patches.
|
|
27
|
+
|
|
28
|
+
The patches are projected into tokens and passed to a Transformer encoder.
|
|
29
|
+
After training, users can optionally apply structured magnitude-based pruning
|
|
30
|
+
to the MLP layers. Pruning does not automatically fine-tune the model.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
Clone the repository, then run this command from its root directory:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install -e .
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Basic workflow
|
|
41
|
+
|
|
42
|
+
1. Prepare RGB images with your own data pipeline.
|
|
43
|
+
2. Fit ICS calibration values using representative images.
|
|
44
|
+
3. Calculate a patch-size map for each image before channel normalization.
|
|
45
|
+
4. Normalize the image for the model, if needed.
|
|
46
|
+
5. Use `adaptive_collate_fn` to create batches.
|
|
47
|
+
6. Train `DPViT` with your own training loop.
|
|
48
|
+
7. Optionally prune the trained model.
|
|
49
|
+
8. Save the checkpoint and use it for inference.
|
|
50
|
+
|
|
51
|
+
## Example
|
|
52
|
+
|
|
53
|
+
This example shows how to create the model, prepare a batch, run a forward
|
|
54
|
+
pass, and optionally prune and save the model. It does not include a training
|
|
55
|
+
loop or dataset implementation.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from functools import partial
|
|
59
|
+
|
|
60
|
+
import torch
|
|
61
|
+
from torch.utils.data import DataLoader
|
|
62
|
+
|
|
63
|
+
from dp_vit import (
|
|
64
|
+
DPViT,
|
|
65
|
+
DPViTConfig,
|
|
66
|
+
ICSConfig,
|
|
67
|
+
PruningConfig,
|
|
68
|
+
adaptive_collate_fn,
|
|
69
|
+
compute_patch_size_map_for_image,
|
|
70
|
+
fit_ics_calibration,
|
|
71
|
+
prune_mlp_by_magnitude,
|
|
72
|
+
save_checkpoint,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Each calibration image must be an RGB NumPy array with values in [0, 1].
|
|
76
|
+
calibration_images = [
|
|
77
|
+
# image_1_rgb_array,
|
|
78
|
+
# image_2_rgb_array,
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
config = DPViTConfig(
|
|
82
|
+
num_classes=5,
|
|
83
|
+
image_size=224,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
ics_calibration = fit_ics_calibration(
|
|
87
|
+
calibration_images=calibration_images,
|
|
88
|
+
region_size=config.region_size,
|
|
89
|
+
config=ICSConfig(),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# For each dataset image, compute its patch map from the RGB image
|
|
93
|
+
# before channel normalization.
|
|
94
|
+
image_rgb_tensor = ... # Tensor with shape (3, 224, 224), values in [0, 1]
|
|
95
|
+
label = 0
|
|
96
|
+
|
|
97
|
+
patch_size_map = compute_patch_size_map_for_image(
|
|
98
|
+
image=image_rgb_tensor.permute(1, 2, 0).cpu().numpy(),
|
|
99
|
+
calibration=ics_calibration,
|
|
100
|
+
region_size=config.region_size,
|
|
101
|
+
patch_sizes=config.patch_sizes,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Apply your chosen normalization after computing the patch map.
|
|
105
|
+
image_for_model = image_rgb_tensor
|
|
106
|
+
|
|
107
|
+
dataset = [
|
|
108
|
+
(image_for_model, patch_size_map, label),
|
|
109
|
+
# Add more (image_tensor, patch_size_map, label) samples here.
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
loader = DataLoader(
|
|
113
|
+
dataset,
|
|
114
|
+
batch_size=2,
|
|
115
|
+
collate_fn=partial(
|
|
116
|
+
adaptive_collate_fn,
|
|
117
|
+
region_size=config.region_size,
|
|
118
|
+
patch_sizes=config.patch_sizes,
|
|
119
|
+
),
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
123
|
+
model = DPViT(config).to(device)
|
|
124
|
+
|
|
125
|
+
# Example forward pass. Use your own loss, optimizer, and training loop.
|
|
126
|
+
batch = next(iter(loader))
|
|
127
|
+
batch = {
|
|
128
|
+
key: value.to(device) if isinstance(value, torch.Tensor) else value
|
|
129
|
+
for key, value in batch.items()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
logits = model(batch)
|
|
133
|
+
targets = batch["labels"]
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The dataset samples passed to `adaptive_collate_fn` must have this structure:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
(image_tensor, patch_size_map, label)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`image_tensor` must have shape `(3, height, width)`. The patch map must be
|
|
143
|
+
computed from the RGB image before channel normalization.
|
|
144
|
+
|
|
145
|
+
## Optional pruning and checkpoint saving
|
|
146
|
+
|
|
147
|
+
Apply pruning after training. Evaluate the pruned model on your validation or
|
|
148
|
+
test data to measure its effect.
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
model, pruning_report = prune_mlp_by_magnitude(
|
|
152
|
+
model,
|
|
153
|
+
PruningConfig(target_ratio=0.2),
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
save_checkpoint(
|
|
157
|
+
path="checkpoints/dp_vit.pth",
|
|
158
|
+
model=model,
|
|
159
|
+
ics_calibration=ics_calibration,
|
|
160
|
+
class_names=["class_a", "class_b", "class_c", "class_d", "class_e"],
|
|
161
|
+
pruning_report=pruning_report,
|
|
162
|
+
)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Loading a checkpoint and inference
|
|
166
|
+
|
|
167
|
+
The inference input must be an RGB tensor with shape `(3, image_size, image_size)`
|
|
168
|
+
and values in `[0, 1]`. Apply the same resize and normalization conventions used
|
|
169
|
+
during training.
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from dp_vit import load_checkpoint, predict_tensor
|
|
173
|
+
|
|
174
|
+
model, ics_calibration, metadata = load_checkpoint(
|
|
175
|
+
"checkpoints/dp_vit.pth",
|
|
176
|
+
map_location=device,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
result = predict_tensor(
|
|
180
|
+
model=model,
|
|
181
|
+
image_before_normalization=image_rgb_tensor,
|
|
182
|
+
ics_calibration=ics_calibration,
|
|
183
|
+
class_names=metadata["class_names"],
|
|
184
|
+
normalize_fn=None,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
print(result["class_name"])
|
|
188
|
+
print(result["confidence"])
|
|
189
|
+
print(result["patch_size_map"])
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
If training used channel normalization, pass the same normalization function
|
|
193
|
+
through `normalize_fn` during inference.
|
|
194
|
+
|
|
195
|
+
## Project structure
|
|
196
|
+
|
|
197
|
+
```text
|
|
198
|
+
DP-ViT/
|
|
199
|
+
├── LICENSE
|
|
200
|
+
├── README.md
|
|
201
|
+
├── pyproject.toml
|
|
202
|
+
└── src/
|
|
203
|
+
└── dp_vit/
|
|
204
|
+
├── __init__.py
|
|
205
|
+
├── config.py
|
|
206
|
+
├── ics.py
|
|
207
|
+
├── patching.py
|
|
208
|
+
├── model.py
|
|
209
|
+
├── pruning.py
|
|
210
|
+
├── checkpoint.py
|
|
211
|
+
└── inference.py
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Scope
|
|
215
|
+
|
|
216
|
+
DP-ViT provides model components. It does not provide:
|
|
217
|
+
|
|
218
|
+
- Dataset-specific loaders or downloads
|
|
219
|
+
- Image cleaning or augmentation pipelines
|
|
220
|
+
- A training loop, optimizer, or scheduler
|
|
221
|
+
- A baseline Vision Transformer implementation
|
|
222
|
+
- A guarantee of faster training or inference
|
|
223
|
+
|
|
224
|
+
## License
|
|
225
|
+
|
|
226
|
+
This project is licensed under the MIT License. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "dp-vit"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Dynamic Patch Vision Transformer with ICS-based patch selection"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"torch",
|
|
13
|
+
"numpy",
|
|
14
|
+
"scipy",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[tool.setuptools.packages.find]
|
|
18
|
+
where = ["src"]
|
dp_vit-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from .checkpoint import load_checkpoint, save_checkpoint
|
|
2
|
+
from .config import DPViTConfig, ICSCalibration, ICSConfig, PruningConfig
|
|
3
|
+
from .ics import (
|
|
4
|
+
compute_gradient_magnitude,
|
|
5
|
+
compute_ics,
|
|
6
|
+
compute_local_variance,
|
|
7
|
+
compute_patch_size_map_for_image,
|
|
8
|
+
determine_patch_size_map,
|
|
9
|
+
fit_ics_calibration,
|
|
10
|
+
)
|
|
11
|
+
from .inference import predict_tensor
|
|
12
|
+
from .model import DPViT
|
|
13
|
+
from .patching import AdaptivePatchEmbedding, adaptive_collate_fn
|
|
14
|
+
from .pruning import prune_mlp_by_magnitude
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"AdaptivePatchEmbedding",
|
|
18
|
+
"DPViT",
|
|
19
|
+
"DPViTConfig",
|
|
20
|
+
"ICSCalibration",
|
|
21
|
+
"ICSConfig",
|
|
22
|
+
"PruningConfig",
|
|
23
|
+
"adaptive_collate_fn",
|
|
24
|
+
"compute_gradient_magnitude",
|
|
25
|
+
"compute_ics",
|
|
26
|
+
"compute_local_variance",
|
|
27
|
+
"compute_patch_size_map_for_image",
|
|
28
|
+
"determine_patch_size_map",
|
|
29
|
+
"fit_ics_calibration",
|
|
30
|
+
"load_checkpoint",
|
|
31
|
+
"predict_tensor",
|
|
32
|
+
"prune_mlp_by_magnitude",
|
|
33
|
+
"save_checkpoint",
|
|
34
|
+
]
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""Checkpoint saving and loading for DP-ViT."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import asdict
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, cast
|
|
6
|
+
|
|
7
|
+
import torch
|
|
8
|
+
from torch import nn
|
|
9
|
+
|
|
10
|
+
from .config import DPViTConfig, ICSCalibration
|
|
11
|
+
from .model import DPViT
|
|
12
|
+
|
|
13
|
+
CHECKPOINT_FORMAT_VERSION = 1
|
|
14
|
+
|
|
15
|
+
def _get_mlp_widths(model: DPViT) -> list[int]:
|
|
16
|
+
"""Return the hidden width of every encoder MLP."""
|
|
17
|
+
return [
|
|
18
|
+
int(getattr(getattr(layer, "linear1"), "out_features"))
|
|
19
|
+
for layer in model.encoder.layers
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
def _resize_mlp_layer(
|
|
23
|
+
layer: nn.TransformerEncoderLayer,
|
|
24
|
+
hidden_width: int,
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Create MLP layers with a target width before leading checkpoint weights"""
|
|
27
|
+
if hidden_width <= 0:
|
|
28
|
+
raise ValueError("Each MLP hidden width must be greater than zero.")
|
|
29
|
+
|
|
30
|
+
old_linear1 = layer.linear1
|
|
31
|
+
old_linear2 = layer.linear2
|
|
32
|
+
|
|
33
|
+
new_linear1 = nn.Linear(
|
|
34
|
+
in_features=old_linear1.in_features,
|
|
35
|
+
out_features=hidden_width,
|
|
36
|
+
bias=old_linear1.bias is not None,
|
|
37
|
+
device=old_linear1.weight.device,
|
|
38
|
+
dtype=old_linear1.weight.dtype,
|
|
39
|
+
)
|
|
40
|
+
new_linear2 = nn.Linear(
|
|
41
|
+
in_features=hidden_width,
|
|
42
|
+
out_features=old_linear2.out_features,
|
|
43
|
+
bias=old_linear2.bias is not None,
|
|
44
|
+
device=old_linear2.weight.device,
|
|
45
|
+
dtype=old_linear2.weight.dtype,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
layer.linear1 = new_linear1
|
|
49
|
+
layer.linear2 = new_linear2
|
|
50
|
+
|
|
51
|
+
def save_checkpoint(
|
|
52
|
+
path: str | Path,
|
|
53
|
+
model: DPViT,
|
|
54
|
+
ics_calibration: ICSCalibration,
|
|
55
|
+
class_names: list[str] | None = None,
|
|
56
|
+
pruning_report: dict[str, Any] | None = None,
|
|
57
|
+
) -> Path:
|
|
58
|
+
"""Save a DP-ViT checkpoint and the metadata needed to rebuild it."""
|
|
59
|
+
if not isinstance(model, DPViT):
|
|
60
|
+
raise TypeError("model must be an instance of DPViT")
|
|
61
|
+
|
|
62
|
+
if not isinstance(ics_calibration, ICSCalibration):
|
|
63
|
+
raise TypeError("ics_calibration must be an ICSCalibration instance.")
|
|
64
|
+
|
|
65
|
+
if class_names is not None:
|
|
66
|
+
if len(class_names) != model.config.num_classes:
|
|
67
|
+
raise ValueError(
|
|
68
|
+
"The number of class names must match num_classes."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
checkpoint_path = Path(path).expanduser()
|
|
72
|
+
checkpoint_path.parent.mkdir(parents=True, exist_ok=True)
|
|
73
|
+
|
|
74
|
+
payload = {
|
|
75
|
+
"format_version": CHECKPOINT_FORMAT_VERSION,
|
|
76
|
+
"model_config": asdict(model.config),
|
|
77
|
+
"ics_calibration": asdict(ics_calibration),
|
|
78
|
+
"class_names": class_names,
|
|
79
|
+
"mlp_widths": _get_mlp_widths(model),
|
|
80
|
+
"pruning_report": pruning_report,
|
|
81
|
+
"model_state_dict": {
|
|
82
|
+
name: value.detach().cpu()
|
|
83
|
+
for name, value in model.state_dict().items()
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
temporary_path = checkpoint_path.with_suffix(
|
|
88
|
+
checkpoint_path.suffix + ".tmp"
|
|
89
|
+
)
|
|
90
|
+
torch.save(payload, temporary_path)
|
|
91
|
+
temporary_path.replace(checkpoint_path)
|
|
92
|
+
|
|
93
|
+
return checkpoint_path
|
|
94
|
+
|
|
95
|
+
def load_checkpoint(
|
|
96
|
+
path: str | Path,
|
|
97
|
+
map_location: str | torch.device = "cpu",
|
|
98
|
+
) -> tuple[DPViT, ICSCalibration, dict[str, Any]]:
|
|
99
|
+
"""
|
|
100
|
+
Load a DP-ViT checkpoint and reconstruct its architecture.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
model: The reconstructured DP-ViT model.
|
|
104
|
+
ics_calibration: The ICS parameters saved with the model.
|
|
105
|
+
metadata: Class names, MLP widths, and pruning report.
|
|
106
|
+
"""
|
|
107
|
+
checkpoint_path = Path(path).expanduser()
|
|
108
|
+
|
|
109
|
+
if not checkpoint_path.is_file():
|
|
110
|
+
raise FileNotFoundError(f"Checkpoint file does not exist: {checkpoint_path}")
|
|
111
|
+
|
|
112
|
+
payload = torch.load(
|
|
113
|
+
checkpoint_path,
|
|
114
|
+
map_location=map_location,
|
|
115
|
+
weights_only=True,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if payload.get("format_version") != CHECKPOINT_FORMAT_VERSION:
|
|
119
|
+
raise ValueError("Unsupported or missing DP-ViT checkpoint format version.")
|
|
120
|
+
|
|
121
|
+
config = DPViTConfig(**payload["model_config"])
|
|
122
|
+
calibration = ICSCalibration(**payload["ics_calibration"])
|
|
123
|
+
|
|
124
|
+
model = DPViT(config)
|
|
125
|
+
|
|
126
|
+
mlp_widths = payload["mlp_widths"]
|
|
127
|
+
encoder_layers = list(model.encoder.layers)
|
|
128
|
+
|
|
129
|
+
if len(mlp_widths) != len(encoder_layers):
|
|
130
|
+
raise ValueError("Checkpoint MLP widths do not match the encoder depth.")
|
|
131
|
+
|
|
132
|
+
for layer, hidden_width in zip(encoder_layers, mlp_widths):
|
|
133
|
+
_resize_mlp_layer(
|
|
134
|
+
cast(nn.TransformerEncoderLayer, layer),
|
|
135
|
+
int(hidden_width),
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
model.load_state_dict(payload["model_state_dict"])
|
|
139
|
+
model.to(map_location)
|
|
140
|
+
|
|
141
|
+
metadata = {
|
|
142
|
+
"class_names": payload.get("class_names"),
|
|
143
|
+
"mlp_widths": mlp_widths,
|
|
144
|
+
"pruning_report": payload.get("pruning_report"),
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return model, calibration, metadata
|