|
16 | 16 | """Write each decoder layer's quantized checkpoint shard as soon as it is calibrated.""" |
17 | 17 |
|
18 | 18 | import contextlib |
| 19 | +import contextvars |
19 | 20 | import json |
20 | 21 | import warnings |
21 | 22 | from pathlib import Path |
@@ -146,26 +147,36 @@ def assert_layerwise_export_supported(model: nn.Module) -> None: |
146 | 147 | ) |
147 | 148 |
|
148 | 149 |
|
149 | | -#: Set by the caller on the calibrated submodel when the checkpoint must describe a larger |
150 | | -#: model. Multimodal pipelines calibrate the extracted language model, but the shards and |
151 | | -#: config have to describe the whole VLM. |
152 | | -EXPORT_PARENT_ATTR = "_layerwise_export_parent" |
| 150 | +_export_parent: contextvars.ContextVar[nn.Module | None] = contextvars.ContextVar( |
| 151 | + "layerwise_export_parent", default=None |
| 152 | +) |
153 | 153 |
|
154 | 154 |
|
155 | | -def resolve_export_parent(model: nn.Module) -> nn.Module: |
156 | | - """Return the model the checkpoint should describe: the marked parent, or ``model``. |
| 155 | +@contextlib.contextmanager |
| 156 | +def export_parent(parent: nn.Module): |
| 157 | + """Export the checkpoint for ``parent`` while calibration runs on one of its submodules. |
157 | 158 |
|
158 | | - The decoder layers are the same objects either way, so walking the parent yields |
159 | | - parent-namespace tensor names, the full config and the untouched towers with no |
160 | | - prefixing. Membership is checked by identity, not by name. |
| 159 | + Multimodal pipelines calibrate the extracted language model, but the shards and config |
| 160 | + have to describe the whole VLM. The decoder layers are the same objects either way, so |
| 161 | + walking the parent yields parent-namespace tensor names, the full config and the |
| 162 | + untouched towers with no prefixing. |
161 | 163 | """ |
162 | | - parent = getattr(model, EXPORT_PARENT_ATTR, None) |
| 164 | + token = _export_parent.set(parent) |
| 165 | + try: |
| 166 | + yield |
| 167 | + finally: |
| 168 | + _export_parent.reset(token) |
| 169 | + |
| 170 | + |
| 171 | +def _resolve_export_parent(model: nn.Module) -> nn.Module: |
| 172 | + """Return the model the checkpoint should describe. Membership is by identity, not name.""" |
| 173 | + parent = _export_parent.get() |
163 | 174 | if parent is None or parent is model: |
164 | 175 | return model |
165 | 176 | if all(m is not model for m in parent.modules()): |
166 | 177 | raise ValueError( |
167 | | - f"{EXPORT_PARENT_ATTR} was set to a {type(parent).__name__} that does not " |
168 | | - "contain the calibrated model." |
| 178 | + f"export_parent() was given a {type(parent).__name__} that does not contain the " |
| 179 | + "calibrated model." |
169 | 180 | ) |
170 | 181 | return parent |
171 | 182 |
|
@@ -220,7 +231,7 @@ def __init__( |
220 | 231 |
|
221 | 232 | Runs before calibration, so nothing amax-dependent exists yet. |
222 | 233 | """ |
223 | | - model = resolve_export_parent(model) |
| 234 | + model = _resolve_export_parent(model) |
224 | 235 | assert_layerwise_export_supported(model) |
225 | 236 | # Splits regroup tensors across the whole state dict; no per-layer pass reverses that. |
226 | 237 | _assert_no_split_rules(model) |
@@ -275,16 +286,6 @@ def __init__( |
275 | 286 | "match the original HF hub checkpoint." |
276 | 287 | ) |
277 | 288 |
|
278 | | - # Tied aliases the whole-model export omits and lets the loader re-tie. |
279 | | - raw_tied = ( |
280 | | - set(getattr(model, "_tied_weights_keys", None) or []) |
281 | | - if getattr(model.config, "tie_word_embeddings", False) |
282 | | - else set() |
283 | | - ) |
284 | | - self._tied_alias_keys = ( |
285 | | - {self._name_mapper(k) for k in raw_tied} if self._name_mapper else raw_tied |
286 | | - ) |
287 | | - |
288 | 289 | def export_layer( |
289 | 290 | self, |
290 | 291 | layer_idx: int, |
@@ -480,8 +481,6 @@ def _collect(self, out: dict[str, torch.Tensor], full_key: str, tensor: torch.Te |
480 | 481 | return |
481 | 482 | if self._name_mapper is not None: |
482 | 483 | new_key = self._name_mapper(new_key) |
483 | | - if new_key in self._tied_alias_keys: |
484 | | - return |
485 | 484 | out[new_key] = new_value.detach().contiguous().cpu() |
486 | 485 |
|
487 | 486 | def _write_index(self) -> None: |
|
0 commit comments