feat: implement hardware-adaptive compute bounding and dynamic entropy routing (Eqs. 3-4)
#2
by dataopsnick - opened
train.py
CHANGED
|
@@ -158,16 +158,40 @@ class StackedLDMHeads(nn.Module):
|
|
| 158 |
logits = self.head(forecast)
|
| 159 |
return logits
|
| 160 |
|
| 161 |
-
class
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
def compute_entropy(self, logits: torch.Tensor) -> torch.Tensor:
|
| 163 |
probs = F.softmax(logits.float(), dim=-1)
|
| 164 |
entropy = -torch.sum(probs * torch.log(probs + 1e-9), dim=-1)
|
| 165 |
return entropy
|
| 166 |
|
| 167 |
-
def forward(self, logits: torch.Tensor,
|
| 168 |
entropy = self.compute_entropy(logits)
|
| 169 |
-
|
| 170 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
|
| 172 |
class ActorCriticPruner:
|
| 173 |
def __init__(self, lm_head, lambda_reg=0.1):
|
|
@@ -216,12 +240,12 @@ class ActorCriticPruner:
|
|
| 216 |
|
| 217 |
|
| 218 |
class ADAPTDIFFPipeline(nn.Module):
|
| 219 |
-
def __init__(self, base_lm_model, block_size=12,
|
| 220 |
super().__init__()
|
| 221 |
self.base_model = base_lm_model.model
|
| 222 |
self.lm_head = base_lm_model.lm_head
|
| 223 |
self.block_size = block_size
|
| 224 |
-
self.
|
| 225 |
|
| 226 |
self.ldm_heads = StackedLDMHeads(
|
| 227 |
hidden_size=base_lm_model.config.hidden_size,
|
|
@@ -229,10 +253,13 @@ class ADAPTDIFFPipeline(nn.Module):
|
|
| 229 |
block_size=block_size
|
| 230 |
).to(DEVICE)
|
| 231 |
|
| 232 |
-
self.router =
|
| 233 |
self.pruner = ActorCriticPruner(self.lm_head)
|
| 234 |
|
| 235 |
-
def generate_adapt_diff(self, input_ids, max_new_tokens=128):
|
|
|
|
|
|
|
|
|
|
| 236 |
current_seq = input_ids.clone()
|
| 237 |
generated_count = 0
|
| 238 |
total_full_transformer_evals = 0
|
|
@@ -245,7 +272,7 @@ class ADAPTDIFFPipeline(nn.Module):
|
|
| 245 |
block_logits = self.ldm_heads(last_hidden).squeeze(0).squeeze(0)
|
| 246 |
draft_tokens = torch.argmax(block_logits, dim=-1)
|
| 247 |
|
| 248 |
-
mask, entropy = self.router(block_logits,
|
| 249 |
|
| 250 |
if not mask.any():
|
| 251 |
final_block = draft_tokens
|
|
@@ -277,7 +304,7 @@ a2d_model = AutoModelForCausalLM.from_pretrained(
|
|
| 277 |
device_map=DEVICE
|
| 278 |
)
|
| 279 |
|
| 280 |
-
pipeline = ADAPTDIFFPipeline(a2d_model, block_size=12,
|
| 281 |
print("Downloading LDM head projection weights for calibration baseline...")
|
| 282 |
ldm_weights_path = hf_hub_download(repo_id=ADAPT_DIFF_ID, filename="ldm_heads.pt")
|
| 283 |
pipeline.ldm_heads.load_state_dict(torch.load(ldm_weights_path, map_location=DEVICE))
|
|
|
|
| 158 |
logits = self.head(forecast)
|
| 159 |
return logits
|
| 160 |
|
| 161 |
+
class HardwareAdaptiveRouter(nn.Module):
|
| 162 |
+
def __init__(self, c_base=1.0, c_bf16=5.0):
|
| 163 |
+
super().__init__()
|
| 164 |
+
# c_base: FLOP cost proxy for the LDM block projection
|
| 165 |
+
# c_bf16: FLOP cost proxy for a single token bfloat16 refinement
|
| 166 |
+
self.c_base = c_base
|
| 167 |
+
self.c_bf16 = c_bf16
|
| 168 |
+
|
| 169 |
def compute_entropy(self, logits: torch.Tensor) -> torch.Tensor:
|
| 170 |
probs = F.softmax(logits.float(), dim=-1)
|
| 171 |
entropy = -torch.sum(probs * torch.log(probs + 1e-9), dim=-1)
|
| 172 |
return entropy
|
| 173 |
|
| 174 |
+
def forward(self, logits: torch.Tensor, c_target: float):
|
| 175 |
entropy = self.compute_entropy(logits)
|
| 176 |
+
|
| 177 |
+
# Equation (3): C_step = C_base + sum(M_i) * C_BF16 <= c_target
|
| 178 |
+
# Calculate the maximum number of bfloat16 refinements we can afford
|
| 179 |
+
max_refinements = max(0, int((c_target - self.c_base) / self.c_bf16))
|
| 180 |
+
L = entropy.shape[-1]
|
| 181 |
+
allowed_refinements = min(max_refinements, L)
|
| 182 |
+
|
| 183 |
+
mask = torch.zeros_like(entropy, dtype=torch.bool)
|
| 184 |
+
dynamic_tau = float('inf')
|
| 185 |
+
|
| 186 |
+
if allowed_refinements > 0:
|
| 187 |
+
# Equation (4): tau = inf { t | C_step(t) <= C_target }
|
| 188 |
+
sorted_entropy, indices = torch.sort(entropy, descending=True)
|
| 189 |
+
dynamic_tau = sorted_entropy[allowed_refinements - 1].item()
|
| 190 |
+
|
| 191 |
+
# Apply the computed hardware-bounded threshold
|
| 192 |
+
mask[indices[:allowed_refinements]] = True
|
| 193 |
+
|
| 194 |
+
return mask, entropy, dynamic_tau
|
| 195 |
|
| 196 |
class ActorCriticPruner:
|
| 197 |
def __init__(self, lm_head, lambda_reg=0.1):
|
|
|
|
| 240 |
|
| 241 |
|
| 242 |
class ADAPTDIFFPipeline(nn.Module):
|
| 243 |
+
def __init__(self, base_lm_model, block_size=12, target_budget=15.0):
|
| 244 |
super().__init__()
|
| 245 |
self.base_model = base_lm_model.model
|
| 246 |
self.lm_head = base_lm_model.lm_head
|
| 247 |
self.block_size = block_size
|
| 248 |
+
self.target_budget = target_budget
|
| 249 |
|
| 250 |
self.ldm_heads = StackedLDMHeads(
|
| 251 |
hidden_size=base_lm_model.config.hidden_size,
|
|
|
|
| 253 |
block_size=block_size
|
| 254 |
).to(DEVICE)
|
| 255 |
|
| 256 |
+
self.router = HardwareAdaptiveRouter(c_base=1.0, c_bf16=5.0)
|
| 257 |
self.pruner = ActorCriticPruner(self.lm_head)
|
| 258 |
|
| 259 |
+
def generate_adapt_diff(self, input_ids, max_new_tokens=128, c_target=None):
|
| 260 |
+
if c_target is None:
|
| 261 |
+
c_target = self.target_budget
|
| 262 |
+
|
| 263 |
current_seq = input_ids.clone()
|
| 264 |
generated_count = 0
|
| 265 |
total_full_transformer_evals = 0
|
|
|
|
| 272 |
block_logits = self.ldm_heads(last_hidden).squeeze(0).squeeze(0)
|
| 273 |
draft_tokens = torch.argmax(block_logits, dim=-1)
|
| 274 |
|
| 275 |
+
mask, entropy, dynamic_tau = self.router(block_logits, c_target)
|
| 276 |
|
| 277 |
if not mask.any():
|
| 278 |
final_block = draft_tokens
|
|
|
|
| 304 |
device_map=DEVICE
|
| 305 |
)
|
| 306 |
|
| 307 |
+
pipeline = ADAPTDIFFPipeline(a2d_model, block_size=12, target_budget=15.0)
|
| 308 |
print("Downloading LDM head projection weights for calibration baseline...")
|
| 309 |
ldm_weights_path = hf_hub_download(repo_id=ADAPT_DIFF_ID, filename="ldm_heads.pt")
|
| 310 |
pipeline.ldm_heads.load_state_dict(torch.load(ldm_weights_path, map_location=DEVICE))
|