Feat: Support newer EAGLE-3 drafters (#24663)
Co-authored-by: hnyls2002 <lsyincs@gmail.com>
This commit is contained in:
co-authored by
hnyls2002
parent
f3a8189e20
commit
a75b79e03b
@@ -50,9 +50,13 @@ class LlamaDecoderLayer(LlamaDecoderLayer):
|
|||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(config, layer_id, quant_config, prefix)
|
super().__init__(config, layer_id, quant_config, prefix)
|
||||||
|
|
||||||
|
# Input layer concats embeds + target_hidden before qkv (input dim 2x).
|
||||||
|
self.is_input_layer = layer_id == 0
|
||||||
|
hidden_size = 2 * self.hidden_size if self.is_input_layer else self.hidden_size
|
||||||
|
|
||||||
# override qkv
|
# override qkv
|
||||||
self.self_attn.qkv_proj = QKVParallelLinear(
|
self.self_attn.qkv_proj = QKVParallelLinear(
|
||||||
2 * self.hidden_size,
|
hidden_size,
|
||||||
self.self_attn.head_dim,
|
self.self_attn.head_dim,
|
||||||
self.self_attn.total_num_heads,
|
self.self_attn.total_num_heads,
|
||||||
self.self_attn.total_num_kv_heads,
|
self.self_attn.total_num_kv_heads,
|
||||||
@@ -81,11 +85,16 @@ class LlamaDecoderLayer(LlamaDecoderLayer):
|
|||||||
residual: Optional[torch.Tensor],
|
residual: Optional[torch.Tensor],
|
||||||
) -> Tuple[torch.Tensor, torch.Tensor]:
|
) -> Tuple[torch.Tensor, torch.Tensor]:
|
||||||
|
|
||||||
residual = hidden_states
|
if self.is_input_layer:
|
||||||
embeds = self.input_layernorm(embeds)
|
# Input layer consumes target hidden states; no carried residual to fuse.
|
||||||
hidden_states = self.hidden_norm(hidden_states)
|
residual = hidden_states
|
||||||
|
hidden_states = self.hidden_norm(hidden_states)
|
||||||
|
embeds = self.input_layernorm(embeds)
|
||||||
|
hidden_states = torch.cat([embeds, hidden_states], dim=-1)
|
||||||
|
else:
|
||||||
|
# Fuse the previous layer's MLP residual add into hidden_norm.
|
||||||
|
hidden_states, residual = self.hidden_norm(hidden_states, residual)
|
||||||
|
|
||||||
hidden_states = torch.cat([embeds, hidden_states], dim=-1)
|
|
||||||
# Self Attention
|
# Self Attention
|
||||||
hidden_states = self.self_attn(
|
hidden_states = self.self_attn(
|
||||||
positions=positions,
|
positions=positions,
|
||||||
@@ -135,24 +144,42 @@ class LlamaModel(nn.Module):
|
|||||||
else:
|
else:
|
||||||
self.hidden_size_in = config.hidden_size
|
self.hidden_size_in = config.hidden_size
|
||||||
|
|
||||||
# Optional per-layer RMSNorm applied to each aux hidden state before
|
# num_aux resolution: explicit attr > eagle_config layer_ids > default 3.
|
||||||
# concatenation, so that all three layers contribute equally regardless
|
self.num_aux_hidden_states = getattr(config, "num_aux_hidden_states", None)
|
||||||
# of their raw scale. Enabled via config "use_aux_norm": true.
|
if self.num_aux_hidden_states is None:
|
||||||
self.use_aux_norm = getattr(config, "use_aux_norm", False)
|
eagle_config = getattr(config, "eagle_config", None) or {}
|
||||||
if self.use_aux_norm:
|
layer_ids = eagle_config.get("eagle_aux_hidden_state_layer_ids")
|
||||||
self.aux_norm_low = RMSNorm(self.hidden_size_in, eps=config.rms_norm_eps)
|
self.num_aux_hidden_states = len(layer_ids) if layer_ids else 3
|
||||||
self.aux_norm_mid = RMSNorm(self.hidden_size_in, eps=config.rms_norm_eps)
|
|
||||||
self.aux_norm_high = RMSNorm(self.hidden_size_in, eps=config.rms_norm_eps)
|
|
||||||
|
|
||||||
self.fc = torch.nn.Linear(
|
self.fc = torch.nn.Linear(
|
||||||
self.hidden_size_in * 3,
|
self.hidden_size_in * self.num_aux_hidden_states,
|
||||||
config.hidden_size,
|
config.hidden_size,
|
||||||
bias=getattr(config, "bias", False),
|
bias=getattr(config, "bias", False),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.midlayer = LlamaDecoderLayer(config, 0, quant_config, prefix)
|
# Per-aux RMSNorm before fc; enabled via `fc_norm` or legacy `use_aux_norm` flag.
|
||||||
|
use_fc_norm = getattr(config, "fc_norm", None) or getattr(
|
||||||
|
config, "use_aux_norm", False
|
||||||
|
)
|
||||||
|
if use_fc_norm:
|
||||||
|
self.fc_norm = nn.ModuleList(
|
||||||
|
[
|
||||||
|
RMSNorm(self.hidden_size_in, eps=config.rms_norm_eps)
|
||||||
|
for _ in range(self.num_aux_hidden_states)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.fc_norm = None
|
||||||
|
|
||||||
|
self.layers = nn.ModuleList(
|
||||||
|
[
|
||||||
|
LlamaDecoderLayer(config, i, quant_config, prefix)
|
||||||
|
for i in range(config.num_hidden_layers)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
|
self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
|
||||||
|
self.norm_output = getattr(config, "norm_output", False)
|
||||||
|
|
||||||
def forward(
|
def forward(
|
||||||
self,
|
self,
|
||||||
@@ -183,13 +210,12 @@ class LlamaModel(nn.Module):
|
|||||||
|
|
||||||
hidden_states = forward_batch.spec_info.hidden_states
|
hidden_states = forward_batch.spec_info.hidden_states
|
||||||
if hidden_states.shape[-1] != embeds.shape[-1]:
|
if hidden_states.shape[-1] != embeds.shape[-1]:
|
||||||
if self.use_aux_norm and hidden_states.shape[-1] == self.hidden_size_in * 3:
|
if self.fc_norm is not None:
|
||||||
# Normalize each aux layer independently before fc projection.
|
chunks = hidden_states.chunk(self.num_aux_hidden_states, dim=-1)
|
||||||
h_low, h_mid, h_high = hidden_states.split(self.hidden_size_in, dim=-1)
|
hidden_states = torch.cat(
|
||||||
h_low = self.aux_norm_low(h_low)
|
[norm(chunk) for norm, chunk in zip(self.fc_norm, chunks)],
|
||||||
h_mid = self.aux_norm_mid(h_mid)
|
dim=-1,
|
||||||
h_high = self.aux_norm_high(h_high)
|
)
|
||||||
hidden_states = torch.cat((h_low, h_mid, h_high), dim=-1)
|
|
||||||
hidden_states = self.fc(hidden_states)
|
hidden_states = self.fc(hidden_states)
|
||||||
|
|
||||||
# idle batch
|
# idle batch
|
||||||
@@ -197,20 +223,22 @@ class LlamaModel(nn.Module):
|
|||||||
return hidden_states, [hidden_states]
|
return hidden_states, [hidden_states]
|
||||||
|
|
||||||
residual = None
|
residual = None
|
||||||
hidden_states, residual = self.midlayer(
|
for layer in self.layers:
|
||||||
positions,
|
hidden_states, residual = layer(
|
||||||
embeds,
|
positions,
|
||||||
hidden_states,
|
embeds,
|
||||||
forward_batch,
|
hidden_states,
|
||||||
residual,
|
forward_batch,
|
||||||
)
|
residual,
|
||||||
|
)
|
||||||
|
|
||||||
hidden_states_to_logits, hidden_states_to_aux = self.norm(
|
hidden_states_to_logits, hidden_states_to_aux = self.norm(
|
||||||
hidden_states, residual
|
hidden_states, residual
|
||||||
)
|
)
|
||||||
|
|
||||||
# For draft decode, we capture the hidden state before norm
|
# Draft decode captures pre-norm hidden by default; `norm_output` opts for normed.
|
||||||
return hidden_states_to_logits, [hidden_states_to_aux]
|
aux = hidden_states_to_logits if self.norm_output else hidden_states_to_aux
|
||||||
|
return hidden_states_to_logits, [aux]
|
||||||
|
|
||||||
|
|
||||||
class LlamaForCausalLMEagle3(LlamaForCausalLM):
|
class LlamaForCausalLMEagle3(LlamaForCausalLM):
|
||||||
@@ -225,9 +253,6 @@ class LlamaForCausalLMEagle3(LlamaForCausalLM):
|
|||||||
self.quant_config = quant_config
|
self.quant_config = quant_config
|
||||||
self.pp_group = get_pp_group()
|
self.pp_group = get_pp_group()
|
||||||
|
|
||||||
if self.config.num_hidden_layers != 1:
|
|
||||||
raise ValueError("EAGLE3 currently only supports 1 layer")
|
|
||||||
|
|
||||||
self.model = LlamaModel(
|
self.model = LlamaModel(
|
||||||
config, quant_config=quant_config, prefix=add_prefix("model", prefix)
|
config, quant_config=quant_config, prefix=add_prefix("model", prefix)
|
||||||
)
|
)
|
||||||
@@ -268,7 +293,19 @@ class LlamaForCausalLMEagle3(LlamaForCausalLM):
|
|||||||
(".gate_up_proj", ".up_proj", 1),
|
(".gate_up_proj", ".up_proj", 1),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Legacy weight names -> new module attribute names (backwards compat).
|
||||||
|
legacy_name_map = {
|
||||||
|
"midlayer": "layers.0",
|
||||||
|
"aux_norm_low": "fc_norm.0",
|
||||||
|
"aux_norm_mid": "fc_norm.1",
|
||||||
|
"aux_norm_high": "fc_norm.2",
|
||||||
|
}
|
||||||
|
|
||||||
for name, loaded_weight in weights:
|
for name, loaded_weight in weights:
|
||||||
|
for legacy, new in legacy_name_map.items():
|
||||||
|
if legacy in name:
|
||||||
|
name = name.replace(legacy, new)
|
||||||
|
|
||||||
if "d2t" in name:
|
if "d2t" in name:
|
||||||
# d2t stores diffs between draft id and target id
|
# d2t stores diffs between draft id and target id
|
||||||
self.hot_token_id = loaded_weight + torch.arange(loaded_weight.shape[0])
|
self.hot_token_id = loaded_weight + torch.arange(loaded_weight.shape[0])
|
||||||
|
|||||||
@@ -846,17 +846,26 @@ class EagleDraftExtendInput(SpecInput):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def hidden_size_for(cls, worker) -> int:
|
def hidden_size_for(cls, worker) -> int:
|
||||||
"""Extend-phase `hidden_states` width: target verify output (EAGLE
|
"""Extend-phase `hidden_states` width: target's `spec_hidden_size`,
|
||||||
paper's "feature"). Widened to `target.hidden_size * 3` for EAGLE-3
|
widened to `num_aux * target_hidden` for EAGLE-3 aux mode."""
|
||||||
aux mode (low/mid/high features fused into a 3k-dim vector, reduced
|
|
||||||
by draft's FC)."""
|
|
||||||
target_cfg = worker.target_worker.model_runner.model_config
|
target_cfg = worker.target_worker.model_runner.model_config
|
||||||
if (
|
if not (
|
||||||
worker.speculative_algorithm.is_eagle3()
|
worker.speculative_algorithm.is_eagle3()
|
||||||
and worker.eagle_use_aux_hidden_state
|
and worker.eagle_use_aux_hidden_state
|
||||||
):
|
):
|
||||||
return target_cfg.hidden_size * 3
|
return target_cfg.spec_hidden_size
|
||||||
return target_cfg.spec_hidden_size
|
|
||||||
|
hf_config = target_cfg.hf_config
|
||||||
|
|
||||||
|
# `num_aux` resolution: explicit attr > eagle_config layer_ids > default 3.
|
||||||
|
num_aux = getattr(hf_config, "num_aux_hidden_states", None)
|
||||||
|
if num_aux is None:
|
||||||
|
eagle_config = getattr(hf_config, "eagle_config", None) or {}
|
||||||
|
layer_ids = eagle_config.get("eagle_aux_hidden_state_layer_ids")
|
||||||
|
num_aux = len(layer_ids) if layer_ids else 3
|
||||||
|
|
||||||
|
target_hidden = getattr(hf_config, "target_hidden_size", target_cfg.hidden_size)
|
||||||
|
return target_hidden * num_aux
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def dtype_for(cls, worker) -> torch.dtype:
|
def dtype_for(cls, worker) -> torch.dtype:
|
||||||
|
|||||||
Reference in New Issue
Block a user