|
7 | 7 |
|
8 | 8 | from __future__ import annotations |
9 | 9 |
|
10 | | -from dataclasses import asdict, dataclass |
| 10 | +import math |
| 11 | +from dataclasses import asdict, dataclass, field |
11 | 12 | from typing import ( |
12 | 13 | TYPE_CHECKING, |
13 | 14 | Any, |
|
58 | 59 | | EngramBranch |
59 | 60 | | ConceptBlock |
60 | 61 | ) |
| 62 | + |
| 63 | +HYBRID_SIDE_CHANNEL_RESIDUAL_SCALE_FAMILIES = ("platform", "structure", "syntax") |
61 | 64 | PathCActivationProbe = Callable[[Mapping[str, Any]], None] |
62 | 65 |
|
63 | 66 | _ROUTE_SYMBOL_BACKENDS: dict[str, HybridBackend] = { |
@@ -137,6 +140,39 @@ def _path_c_capture_event_metadata(event: Mapping[str, Any]) -> Mapping[str, Any |
137 | 140 | return metadata |
138 | 141 |
|
139 | 142 |
|
| 143 | +def _normalize_side_channel_residual_scale( |
| 144 | + policy: Mapping[str, float], |
| 145 | +) -> dict[str, float]: |
| 146 | + normalized: dict[str, float] = {} |
| 147 | + allowed = set(HYBRID_SIDE_CHANNEL_RESIDUAL_SCALE_FAMILIES) |
| 148 | + for raw_family, raw_scale in policy.items(): |
| 149 | + family = str(raw_family).strip() |
| 150 | + if not family: |
| 151 | + raise ValueError("side_channel_residual_scale family names must be non-empty") |
| 152 | + if family not in allowed: |
| 153 | + allowed_list = ", ".join(HYBRID_SIDE_CHANNEL_RESIDUAL_SCALE_FAMILIES) |
| 154 | + raise ValueError( |
| 155 | + "side_channel_residual_scale only supports model-consumed " |
| 156 | + f"families: {allowed_list}; got {family!r}" |
| 157 | + ) |
| 158 | + scale = float(raw_scale) |
| 159 | + if not math.isfinite(scale) or scale < 0.0: |
| 160 | + raise ValueError( |
| 161 | + f"side_channel_residual_scale for {family!r} must be finite and >= 0" |
| 162 | + ) |
| 163 | + normalized[family] = scale |
| 164 | + if ( |
| 165 | + "structure" in normalized |
| 166 | + and "syntax" in normalized |
| 167 | + and not math.isclose(normalized["structure"], normalized["syntax"]) |
| 168 | + ): |
| 169 | + raise ValueError( |
| 170 | + "structure and syntax currently share a single structure residual; " |
| 171 | + "set one side_channel_residual_scale value or matching values" |
| 172 | + ) |
| 173 | + return dict(sorted(normalized.items())) |
| 174 | + |
| 175 | + |
140 | 176 | @dataclass(frozen=True) |
141 | 177 | class HybridTinyConfig: |
142 | 178 | """Tiny local hybrid-LM config. |
@@ -196,6 +232,7 @@ class HybridTinyConfig: |
196 | 232 | ngram_hash_embed_dim: int = 16 |
197 | 233 | ngram_hash_dropout: float = 0.0 |
198 | 234 | ngram_hash_seed: int | None = None |
| 235 | + side_channel_residual_scale: dict[str, float] = field(default_factory=dict) |
199 | 236 | mhc_enabled: bool = False |
200 | 237 | grad_checkpoint: bool = False |
201 | 238 | # Attention mode default applied to A-layers that did not get DSA routing. |
@@ -257,6 +294,11 @@ def __post_init__(self) -> None: |
257 | 294 | f"attention_mode must be one of 'mla', 'dsa', 'full', 'gqa', " |
258 | 295 | f"got {self.attention_mode!r}" |
259 | 296 | ) |
| 297 | + object.__setattr__( |
| 298 | + self, |
| 299 | + "side_channel_residual_scale", |
| 300 | + _normalize_side_channel_residual_scale(self.side_channel_residual_scale), |
| 301 | + ) |
260 | 302 | self.attention_config(self.attention_mode) |
261 | 303 | # Always also validate the dsa mode contract because dsa_a_layer_ranks |
262 | 304 | # may pin specific A-layers to dsa regardless of attention_mode. |
@@ -371,6 +413,14 @@ def platform_embedding_config(self) -> dict[str, int]: |
371 | 413 | "max_ids": self.platform_max_ids, |
372 | 414 | } |
373 | 415 |
|
| 416 | + def residual_scale_for(self, family: str) -> float: |
| 417 | + if family == "structure": |
| 418 | + return self.side_channel_residual_scale.get( |
| 419 | + "structure", |
| 420 | + self.side_channel_residual_scale.get("syntax", 1.0), |
| 421 | + ) |
| 422 | + return self.side_channel_residual_scale.get(family, 1.0) |
| 423 | + |
374 | 424 | def ngram_hash_config(self) -> dict[str, object] | None: |
375 | 425 | if not self.ngram_hash_enabled: |
376 | 426 | return None |
@@ -1083,37 +1133,56 @@ def decoder_hidden_states( |
1083 | 1133 | if self.ngram_hash_embedding is not None: |
1084 | 1134 | hidden_states = hidden_states + self.ngram_hash_embedding(input_ids) |
1085 | 1135 |
|
1086 | | - structure_embeddings = self.structure_embedding( |
1087 | | - structure_ids=_validate_side_channel_shape( |
| 1136 | + structure_inputs = { |
| 1137 | + "structure_ids": _validate_side_channel_shape( |
1088 | 1138 | "structure_ids", structure_ids, batch_size, seq_length |
1089 | 1139 | ), |
1090 | | - dep_levels=_validate_side_channel_shape( |
| 1140 | + "dep_levels": _validate_side_channel_shape( |
1091 | 1141 | "dep_levels", dep_levels, batch_size, seq_length |
1092 | 1142 | ), |
1093 | | - ast_depth_ids=_validate_side_channel_shape( |
| 1143 | + "ast_depth_ids": _validate_side_channel_shape( |
1094 | 1144 | "ast_depth_ids", ast_depth_ids, batch_size, seq_length |
1095 | 1145 | ), |
1096 | | - sibling_index_ids=_validate_side_channel_shape( |
| 1146 | + "sibling_index_ids": _validate_side_channel_shape( |
1097 | 1147 | "sibling_index_ids", sibling_index_ids, batch_size, seq_length |
1098 | 1148 | ), |
1099 | | - node_type_ids=_validate_side_channel_shape( |
| 1149 | + "node_type_ids": _validate_side_channel_shape( |
1100 | 1150 | "node_type_ids", node_type_ids, batch_size, seq_length |
1101 | 1151 | ), |
1102 | | - target_dtype=hidden_states.dtype, |
1103 | | - ) |
1104 | | - if structure_embeddings.ndim == hidden_states.ndim: |
1105 | | - hidden_states = hidden_states + structure_embeddings |
| 1152 | + } |
| 1153 | + structure_residual_scale = self.config.residual_scale_for("structure") |
| 1154 | + if structure_residual_scale != 0.0: |
| 1155 | + structure_embeddings = self.structure_embedding( |
| 1156 | + **structure_inputs, |
| 1157 | + target_dtype=hidden_states.dtype, |
| 1158 | + ) |
| 1159 | + if structure_embeddings.ndim == hidden_states.ndim: |
| 1160 | + if structure_residual_scale == 1.0: |
| 1161 | + hidden_states = hidden_states + structure_embeddings |
| 1162 | + else: |
| 1163 | + hidden_states = hidden_states + ( |
| 1164 | + structure_embeddings |
| 1165 | + * mx.array(structure_residual_scale, dtype=hidden_states.dtype) |
| 1166 | + ) |
1106 | 1167 |
|
1107 | 1168 | platform_ids = _validate_platform_ids( |
1108 | 1169 | platform_ids, |
1109 | 1170 | batch_size=batch_size, |
1110 | 1171 | seq_length=seq_length, |
1111 | 1172 | ) |
1112 | | - if platform_ids is not None: |
1113 | | - hidden_states = hidden_states + self.platform_embedding( |
| 1173 | + platform_residual_scale = self.config.residual_scale_for("platform") |
| 1174 | + if platform_ids is not None and platform_residual_scale != 0.0: |
| 1175 | + platform_residual = self.platform_embedding( |
1114 | 1176 | platform_ids, |
1115 | 1177 | target_dtype=hidden_states.dtype, |
1116 | 1178 | ) |
| 1179 | + if platform_residual_scale == 1.0: |
| 1180 | + hidden_states = hidden_states + platform_residual |
| 1181 | + else: |
| 1182 | + hidden_states = hidden_states + ( |
| 1183 | + platform_residual |
| 1184 | + * mx.array(platform_residual_scale, dtype=hidden_states.dtype) |
| 1185 | + ) |
1117 | 1186 |
|
1118 | 1187 | document_ids = _validate_document_ids( |
1119 | 1188 | document_ids, |
|
0 commit comments