khazic's picture
Archive three-epoch run: logs and provenance part 3
e65937c verified
Raw History Blame Contribute Delete
8.05 kB
from collections.abc import Callable
from typing import Any, ClassVar
import torch
from transformers import PretrainedConfig
from speculators.losses import LossConfig, kl_div_loss, resolve_loss_config, tv_loss
from speculators.model import SpeculatorModel
from speculators.models.dflash.config import DFlashSpeculatorConfig
from speculators.models.dflash.core import DFlashDraftModel
from speculators.models.dflash2.config import DFlash2SpeculatorConfig
from speculators.models.dflash2.metrics import (
compute_metrics,
selector_training_candidates,
)
from speculators.models.dflash2.model_definitions import (
CandidateSelector,
Qwen3DFlash2DecoderLayer,
)
from speculators.models.utils import conditional_torch_compile
__all__ = [
"DFlash2DraftModel",
]
_DEFAULT_LOSS_CONFIG: LossConfig = {"kl_div": (kl_div_loss, 1.0)}
@SpeculatorModel.register("dflash2")
class DFlash2DraftModel(DFlashDraftModel):
"""DFlash with local convolution and bilinear candidate reranking."""
config_class: ClassVar[type[DFlash2SpeculatorConfig]] = DFlash2SpeculatorConfig # type: ignore[misc,assignment]
_no_split_modules = ["Qwen3DFlash2DecoderLayer"]
def __init__(self, config: DFlash2SpeculatorConfig) -> None:
target_vocab_size = config.transformer_layer_config.vocab_size
if config.draft_vocab_size != target_vocab_size:
raise ValueError(
"DFlash2 currently requires the full verifier vocabulary: "
f"draft_vocab_size={config.draft_vocab_size}, "
f"verifier_vocab_size={target_vocab_size}."
)
if config.selector_top_k > target_vocab_size:
raise ValueError(
f"selector_top_k ({config.selector_top_k}) cannot exceed "
f"verifier_vocab_size ({target_vocab_size})."
)
super().__init__(config=config)
for layer_ in self.layers:
assert isinstance(layer_, Qwen3DFlash2DecoderLayer) # noqa: S101
layer_.reset_convolutions()
initializer_range = getattr(
config.transformer_layer_config, "initializer_range", 0.02
)
self.candidate_selector = CandidateSelector(
vocab_size=target_vocab_size,
hidden_size=config.transformer_layer_config.hidden_size,
rank=config.selector_rank,
top_k=config.selector_top_k,
initializer_range=initializer_range,
)
def _make_decoder_layer(
self, config: DFlashSpeculatorConfig, layer_idx: int
) -> Qwen3DFlash2DecoderLayer:
assert isinstance(config, DFlash2SpeculatorConfig) # noqa: S101
return Qwen3DFlash2DecoderLayer(
config.transformer_layer_config, # type: ignore[arg-type]
layer_idx,
block_size=config.block_size,
conv_kernel_size=config.conv_kernel_size,
conv_group_size=config.conv_group_size,
)
@classmethod
def from_training_args(
cls,
verifier_config: PretrainedConfig,
t2d: torch.Tensor | None = None,
d2t: torch.Tensor | None = None,
**kwargs,
) -> "DFlash2DraftModel":
"""Create a DFlash2 model from the shared training arguments."""
config = DFlash2SpeculatorConfig(
**cls._build_base_config_kwargs("dflash2", verifier_config, **kwargs),
conv_kernel_size=kwargs.get("conv_kernel_size", 2),
conv_group_size=kwargs.get("conv_group_size", 16),
selector_rank=kwargs.get("selector_rank", 256),
selector_top_k=kwargs.get("selector_top_k", 16),
)
model = cls(config=config)
model.load_vocab_mappings(t2d, d2t)
model.load_verifier_weights()
return model
@staticmethod
def get_trainer_kwargs(**kwargs) -> tuple[dict, dict]:
"""Resolve the unary and selector objectives used during training."""
implementation = kwargs.get("loss_implementation", "fused")
loss_config = resolve_loss_config(kwargs["loss_fn"], implementation)
tv_loss_fn = resolve_loss_config("tv", implementation)["tv"][0]
shared = {
"loss_config": loss_config,
"tv_loss_fn": tv_loss_fn,
"gamma": kwargs.get("dflash_decay_gamma", 4.0),
"max_anchors": kwargs.get("max_anchors", 512),
"per_position_loss_weight": kwargs.get(
"per_position_loss_weight", "fixed-exp-decay"
),
"dpace_alpha": kwargs.get("dpace_alpha", 0.5),
"selector_loss_alpha": kwargs.get("selector_loss_alpha", 1.0),
}
return dict(shared), dict(shared)
def _predecessor_ids(
self,
input_ids: torch.Tensor, # shape: [1, total_seq_len]
anchored_block_indices: torch.Tensor, # shape: [num_anchors*block_size]
) -> torch.Tensor:
block_tokens = input_ids[0, anchored_block_indices].view(
-1, self.block_size
) # shape: [num_anchors, block_size]
if self.config.sample_from_anchor:
return block_tokens
# For token sequence 0 1 2 3 return previous token ids 0 0 1 2
return torch.cat([block_tokens[:, :1], block_tokens[:, :-1]], dim=1)
@conditional_torch_compile
def forward(
self,
hidden_states: torch.Tensor, # shape: [1, total_seq_len,num_hidden*hidden_size]
input_ids: torch.Tensor, # shape: [1, total_seq_len]
loss_mask: torch.Tensor, # shape: [1, total_seq_len]
verifier_last_hidden_states: torch.Tensor, # shape: [1, total_seq_len, hidden_size] # noqa: E501
document_ids: torch.Tensor, # shape: [1, total_seq_len]
position_ids: torch.Tensor | None = None, # shape: [1, total_seq_len]
loss_config: LossConfig | None = None,
tv_loss_fn: Callable[[torch.Tensor, torch.Tensor], torch.Tensor] = tv_loss,
gamma: float = 4.0,
max_anchors: int = 512,
selector_loss_alpha: float = 1.0,
per_position_loss_weight: str = "fixed-exp-decay",
dpace_alpha: float = 0.5,
**kwargs,
) -> tuple[None, torch.Tensor, dict[str, Any]]:
hidden, unary_logits, targets, aligned_loss_mask, block_indices = (
self._backbone_forward(
hidden_states,
input_ids,
loss_mask,
verifier_last_hidden_states,
document_ids,
position_ids,
max_anchors=max_anchors,
**kwargs,
)
)
predecessor_ids = self._predecessor_ids(input_ids, block_indices)
target_ids = targets.argmax(dim=-1)
# shape: [1, num_anchors*block_size]
candidate_ids = unary_logits.topk(self.candidate_selector.top_k, dim=-1).indices
# shape: [1, num_anchors*block_size, top_k]
training_candidate_ids, target_positions, contains_target = (
selector_training_candidates(candidate_ids, target_ids)
)
candidate_logits = self.candidate_selector.score_candidates(
unary_logits,
hidden,
predecessor_ids.reshape(1, -1),
training_candidate_ids,
)
loss, metrics = compute_metrics(
unary_logits=unary_logits,
targets=targets,
training_candidate_ids=training_candidate_ids,
candidate_logits=candidate_logits,
target_positions=target_positions,
contains_target=contains_target,
loss_mask=aligned_loss_mask,
block_size=self.block_size,
top_k=self.candidate_selector.top_k,
sample_from_anchor=self.config.sample_from_anchor,
loss_config=loss_config or _DEFAULT_LOSS_CONFIG,
tv_loss_fn=tv_loss_fn,
gamma=gamma,
selector_loss_alpha=selector_loss_alpha,
per_position_loss_weight=per_position_loss_weight,
dpace_alpha=dpace_alpha,
)
return None, loss, metrics