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 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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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