dragon-3b-annealing-389010 / modeling_dragon.py
alexandretl's picture
Upload folder using huggingface_hub
badd78a verified
Raw
History Blame Contribute Delete
55.9 kB
# coding=utf-8
"""PyTorch Dragon model."""
from typing import Any, Dict, Optional, Tuple, Union
from dataclasses import dataclass
import inspect
import math
from einops import rearrange
import torch
import torch.nn.functional as F
from torch import nn
from transformers.modeling_utils import PreTrainedModel
from transformers.modeling_layers import GradientCheckpointingLayer
from transformers.cache_utils import DynamicCache
from transformers.generation import GenerationMixin
from transformers.utils import ModelOutput, logging
from .configuration_dragon import DragonConfig
logger = logging.get_logger(__name__)
ATTN_IMPL = "eager"
try:
from flash_attn import flash_attn_func # FA2
ATTN_IMPL = "fa2"
except ImportError:
try:
import flash_attn_interface # FA3
flash_attn_func = flash_attn_interface.flash_attn_func
_flash_supports_window_size = "window_size" in list(inspect.signature(flash_attn_func).parameters)
if not _flash_supports_window_size:
raise ImportError("flash_attn_func does not support window_size parameter. Please update to more recent flash_attn version")
ATTN_IMPL = "fa3"
except ImportError:
logger.warning_once(
"Flash attention is not installed, using eager attention implementation. "
"For better performance, consider installing flash_attn."
)
print(f"Using attention implementation: {ATTN_IMPL}")
DIFF_ATTN_IMPL = None
try:
import flex_head_fa
DIFF_ATTN_IMPL = "flex_head"
except ImportError:
DIFF_ATTN_IMPL = ATTN_IMPL # if we don't have flex_head_fa, fallback to the best attention impl we have
print(f"Using differential attention implementation: {DIFF_ATTN_IMPL}")
# Gated DeltaNet
try:
from fla.ops.gated_delta_rule import chunk_gated_delta_rule, fused_recurrent_gated_delta_rule
except ImportError:
logger.warning_once("Falling back to Torch implementation for Gated DeltaNet as flash-linear-attention module was not found.")
chunk_gated_delta_rule, fused_recurrent_gated_delta_rule = None, None
# 1D short convolution
try:
from causal_conv1d import causal_conv1d_fn, causal_conv1d_update
except ImportError:
logger.warning_once("Falling back to Torch implementation for the short convolution as causal-conv1d module was not found.")
causal_conv1d_fn, causal_conv1d_update = None, None
class DragonHeadWiseRMSNorm(nn.Module):
def __init__(self, n_heads, d_head, eps=1e-6):
super().__init__()
self.rms = nn.RMSNorm(d_head, eps=eps, elementwise_affine=False)
self.weight = nn.Parameter(torch.ones(n_heads, d_head))
def forward(self, hidden_states):
B, L, H, D = hidden_states.shape
y = self.rms(hidden_states) * self.weight.view(1, 1, H, D)
return y.view(B, L, H, D)
class DragonRMSNorm(nn.RMSNorm):
def __init__(self, hidden_size, eps=1e-6):
"""
DragonRMSNorm is equivalent to RMSNorm
"""
super().__init__(normalized_shape=hidden_size, eps=eps)
class _ScaleFB(torch.autograd.Function):
@staticmethod
def forward(ctx, x, alpha_fwd: torch.Tensor, alpha_bwd: torch.Tensor):
ctx.save_for_backward(alpha_bwd)
return x * alpha_fwd
@staticmethod
def backward(ctx, grad_output):
(alpha_bwd,) = ctx.saved_tensors
return grad_output * alpha_bwd, None, None
class _ScaledLinearFB(torch.autograd.Function):
@staticmethod
def forward(ctx, x, weight, bias, alpha_fwd, alpha_bwd_x, alpha_bwd_w):
ctx.save_for_backward(x, weight, bias)
ctx.alpha_bwd_x = alpha_bwd_x
ctx.alpha_bwd_w = alpha_bwd_w
return F.linear(x, weight, bias) * alpha_fwd
@staticmethod
def backward(ctx, grad_out):
x, weight, bias = ctx.saved_tensors
# -------- grads ----------
grad_x = torch.matmul(grad_out * ctx.alpha_bwd_x, weight)
go_flat = (grad_out * ctx.alpha_bwd_w).reshape(-1, grad_out.shape[-1])
x_flat = x.reshape(-1, x.shape[-1])
grad_weight = go_flat.t() @ x_flat
grad_bias = go_flat.sum(0) if bias is not None else None
return grad_x, grad_weight, grad_bias, None, None, None
class DragonLinear(nn.Linear):
"""Linear layer with different forward/backward scalings."""
def __init__(self, config: DragonConfig, in_features, out_features, bias=False, alpha_fwd=None, alpha_bwd=None):
super().__init__(in_features, out_features, bias)
if alpha_fwd is None:
alpha_fwd = 1.0 / math.sqrt(in_features)
if not config.use_uscaling:
alpha_fwd, alpha_bwd = 1, 1
self.register_buffer("alpha_fwd", torch.tensor(float(alpha_fwd)), persistent=False)
self.register_buffer("alpha_bwd", torch.tensor(float(alpha_bwd if alpha_bwd is not None else alpha_fwd)), persistent=False)
def forward(self, x):
return _ScaledLinearFB.apply(x, self.weight, self.bias, self.alpha_fwd, self.alpha_bwd, self.alpha_bwd)
# heavily adapted from flash-linear-attention
def prepare_lens(cu_seqlens: torch.LongTensor) -> torch.LongTensor:
return cu_seqlens[1:] - cu_seqlens[:-1]
def prepare_position_ids(cu_seqlens: torch.LongTensor) -> torch.LongTensor:
return torch.cat([
torch.arange(n, dtype=cu_seqlens.dtype, device=cu_seqlens.device)
for n in prepare_lens(cu_seqlens).unbind()
])
def prepare_sequence_ids(cu_seqlens: torch.LongTensor) -> torch.LongTensor:
return prepare_position_ids(cu_seqlens).eq(0).cumsum(0) - 1
class DragonConv1D(nn.Conv1d):
"""Wrapper around nn.Conv1d (for definition) and causal_conv1d (for forward)"""
def __init__(
self,
hidden_size: int,
kernel_size: int,
bias: bool = False,
device: Optional[torch.device] = None,
dtype: Optional[torch.dtype] = None,
):
super().__init__(
in_channels=hidden_size,
out_channels=hidden_size,
kernel_size=kernel_size,
groups=hidden_size,
bias=bias,
padding=kernel_size - 1,
device=device,
dtype=dtype,
)
self.hidden_size = hidden_size
def forward(
self,
x: torch.Tensor,
mask: Optional[torch.Tensor] = None,
cache: Optional[torch.Tensor] = None,
output_final_state: bool = False,
**kwargs,
) -> Tuple[torch.Tensor, torch.Tensor]:
"""
Args:
x (`torch.Tensor`):
Tensor of shape `[B, T, D]`.
If `seq_idx` is provided, `B` must be 1.
mask (`Optional[torch.Tensor]`):
Attention mask dealing with padded positions.
cache (`Optional[torch.Tensor]`):
Previous cache tensor of shape `[N, D, W]`, where `W` is the kernel size.
If provided, the cache is updated **inplace**.
output_final_state (Optional[bool]):
Whether to output the final state of shape `[N, D, W]`. Default: `False`.
Returns:
Tensor of shape `[B, T, D]`.
"""
B, T, D, W = *x.shape, self.kernel_size[0]
N = B
if mask is not None:
x = x.mul_(mask.unsqueeze(-1))
if output_final_state and cache is None:
cache = x.new_zeros(N, D, W)
# during the decoding phase, we assume the batch is composed of sequences of length 1
if cache is not None and T == 1:
return self.step(x, cache)
if cache is not None:
cache[:, :, -min(W, T):].copy_(rearrange(x[..., -min(W, T):, :], 'n w d -> n d w'))
x = rearrange(x, 'b t d -> b d t')
if causal_conv1d_fn is not None:
# Sequence index for each token. Used for varlen.
# Suppose a batch consists of two sequences with lengths 3 and 4,
# seq_idx=[0, 0, 0, 1, 1, 1, 1] for this batch.
# NOTE: No need to provide this arg if `cu_seqlens` is passed.
# This arg is just for BC, and will be removed in the future.
# [B, T]
seq_idx = kwargs.get('seq_idx', None)
x = causal_conv1d_fn(
x=x.contiguous(),
weight=rearrange(self.weight, "d 1 w -> d w"),
bias=self.bias,
activation="silu",
seq_idx=seq_idx,
)
else:
x = self._conv_forward(x, self.weight, self.bias)[..., :x.shape[-1]]
x = F.silu(x)
return rearrange(x, "b d t -> b t d"), cache
def step(
self,
x: torch.Tensor,
cache: torch.Tensor,
cu_seqlens: Optional[torch.LongTensor] = None
):
shape = x.shape
x = x.squeeze(0) if cu_seqlens is not None else x.squeeze(1)
if causal_conv1d_update is not None:
x = causal_conv1d_update(
x=x,
conv_state=cache,
weight=rearrange(self.weight, "d 1 w -> d w"),
bias=self.bias,
activation="silu",
)
else:
# we follow the fast mode that updates the cache in-place
cache.copy_(cache.roll(shifts=-1, dims=-1))
cache[:, :, -1] = x
x = torch.sum(cache * rearrange(self.weight, "d 1 w -> d w"), dim=-1)
if self.bias is not None:
x = x + self.bias
x = F.silu(x)
return x.view(shape), cache
class HybridDragonAttentionDynamicCache(DynamicCache):
"""
A dynamic cache that handle both the attention cache (which has a seq_len dimension) and the GDN cache
(which has a constant shape regardless of seq_len).
This cache has two sets of lists of tensors: `key_cache` and `value_cache` for attention cache and `conv_states`
and `ssm_states` for GDN cache. The expected shape for each tensor is as follows:
For each layers, `key_cache` and `value_cache` have a shape of `(batch_size, num_heads, seq_len, head_dim)`,
if local attention produce k and v
while `conv_states` represents the convolution state and has a shape of `(batch_size, d_inner, d_conv)`,
and `ssm_states` represents the ssm state and has a shape of `(batch_size, d_inner, d_state)`.
"""
def __init__(self, config: DragonConfig, dtype=torch.bfloat16):
super().__init__()
self.config = config
self.dtype = dtype
self.q_conv_states = []
self.k_conv_states = []
self.v_conv_states = []
self.ssm_states = []
self._key_cache = {}
self._value_cache = {}
for idx, layer_type in enumerate(config.layers_config):
if layer_type in ['l', 'd']:
self._key_cache[idx] = None
self._value_cache[idx] = None
self.q_conv_states.append(None)
self.k_conv_states.append(None)
self.v_conv_states.append(None)
self.ssm_states.append(None)
self.window_size = config.sliding_window_size
self.layers_config = config.layers_config
self.past_length = [0 for _ in range(len(config.layers_config))]
def update(
self,
k: torch.Tensor, # (B, L, h, D)
v: torch.Tensor, # (B, L, h, D)
layer_idx: int,
):
added_len = k.size(1)
# grab cache
k_cache = self._key_cache[layer_idx]
v_cache = self._value_cache[layer_idx]
if k_cache is None:
k_cache = k
v_cache = v
else:
k_cache = torch.cat([k_cache, k], dim=1)
v_cache = torch.cat([v_cache, v], dim=1)
# save cache
self._key_cache[layer_idx] = k_cache
self._value_cache[layer_idx] = v_cache
# update cache length
self.past_length[layer_idx] += added_len
return k_cache, v_cache
def trim(self, layer_idx: int):
# discard old keys/values
window_size = min(self.window_size, self.config.slw_wsize) if self.config.slw_wsize > 0 else self.window_size
if self.layers_config[layer_idx] == 'l':
if self._key_cache[layer_idx].size(1) > window_size:
self._key_cache[layer_idx] = self._key_cache[layer_idx][:, -window_size:, ...].contiguous()
self._value_cache[layer_idx] = self._value_cache[layer_idx][:, -window_size:, ...].contiguous()
def update_ssm_cache(
self,
q_conv_states: torch.Tensor,
k_conv_states: torch.Tensor,
v_conv_states: torch.Tensor,
ssm_states: torch.Tensor,
layer_idx: int,
) -> None:
# Update the SSM cache
self.q_conv_states[layer_idx] = q_conv_states
self.k_conv_states[layer_idx] = k_conv_states
self.v_conv_states[layer_idx] = v_conv_states
self.ssm_states[layer_idx] = ssm_states
def get_ssm_cache(self, layer_idx: int) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
# Get the SSM cache for the specified layer
return (
self.q_conv_states[layer_idx],
self.k_conv_states[layer_idx],
self.v_conv_states[layer_idx],
self.ssm_states[layer_idx],
)
def get_total_seen(self, layer_idx: int) -> int:
return self.past_length[layer_idx]
def to_legacy_cache(self) -> Tuple[Tuple[torch.Tensor], Tuple[torch.Tensor]]:
raise NotImplementedError("HybridDragonAttentionDynamicCache does not have a legacy cache equivalent.")
@classmethod
def from_legacy_cache(cls, cache_params: Optional[Tuple[Tuple[torch.FloatTensor]]] = None) -> "DynamicCache":
raise NotImplementedError("HybridDragonAttentionDynamicCache does not have a legacy cache equivalent.")
class DragonRotaryEmbedding(torch.nn.Module):
def __init__(self, config: DragonConfig, head_dim: int):
super().__init__()
self.config = config
inv_freq = 1.0 / (config.rope_theta ** (torch.arange(0, head_dim, 2).float() / head_dim))
self.register_buffer("inv_freq", inv_freq, persistent=False)
self.seq_len_cached = 0
self.cos_cached = None
self.sin_cached = None
def forward(self, x, position_ids):
max_pos = self.config.max_position_embeddings
if max_pos > self.seq_len_cached:
self.seq_len_cached = max(2 * max_pos, 16)
t = torch.arange(self.seq_len_cached, device=x.device, dtype=self.inv_freq.dtype)
freqs = torch.outer(t, self.inv_freq)
self.cos_cached = freqs.cos().to(torch.bfloat16)
self.sin_cached = freqs.sin().to(torch.bfloat16)
cos = self.cos_cached[position_ids] # (B, T, head_dim/2)
sin = self.sin_cached[position_ids]
cos = cos[..., None, :] # (B, T, 1, head_dim/2), broadcasts over heads
sin = sin[..., None, :]
return cos, sin
def apply_rotary_emb(x, cos, sin):
assert x.ndim == 4 # multihead attention
d = x.shape[3]//2 # head dim
x1 = x[..., :d]
x2 = x[..., d:]
y1 = x1 * cos + x2 * sin
y2 = x1 * (-sin) + x2 * cos
return torch.cat([y1, y2], 3).type_as(x)
# heavily adapated from Gemma3
def eager_attention_forward(
module: nn.Module, # TODO: remove module
query: torch.Tensor,
key: torch.Tensor,
value: torch.Tensor,
causal: bool = True,
window_size: Optional[Tuple[int, int]] = None,
softcap: Optional[float] = None,
softmax_scale: Optional[float] = None,
**kwargs,
) -> torch.Tensor:
if softmax_scale is None:
softmax_scale = module.head_dim**-0.5
query = query.transpose(1, 2) # (B, H, L, D)
key = key.transpose(1, 2) # (B, H, L, D)
value = value.transpose(1, 2) # (B, H, L, D)
key = key.repeat_interleave(module.num_heads // module.num_key_value_heads, dim=1)
value = value.repeat_interleave(module.num_heads // module.num_key_value_heads, dim=1)
attn_weights = torch.matmul(query, key.transpose(2, 3)) * softmax_scale
if softcap is not None:
attn_weights = torch.tanh(attn_weights / softcap) * softcap
if causal or (window_size is not None):
Lq = query.size(2)
Lk = key.size(2)
past = max(Lk - Lq, 0)
i = torch.arange(Lq, device=attn_weights.device).unsqueeze(1) + past # [Lq,1]
j = torch.arange(Lk, device=attn_weights.device).unsqueeze(0) # [1,Lk]
allowed = torch.ones((Lq, Lk), dtype=torch.bool, device=attn_weights.device)
if causal:
allowed &= (j <= i) # prevent attending to future positions
if window_size is not None:
w_left, w_right = window_size
# treat None as "no limit" on that side
if w_left is None:
w_left = Lk
if w_right is None:
w_right = Lk
allowed &= (j >= i - w_left) & (j <= i + w_right)
# broadcast [Lq,Lk] -> [B, H, Lq, Lk]
attn_weights = attn_weights.masked_fill(~allowed, float("-inf"))
# upcast attention to fp32
attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
attn_output = torch.matmul(attn_weights, value)
attn_output = attn_output.transpose(1, 2).contiguous()
return attn_output
def get_query_key_value_tensors(module: nn.Module, hidden_states: torch.Tensor):
"""
Derives `query`, `key` and `value` tensors from `hidden_states`.
"""
# (B, L, D) -> (B, L, ng * (np/ng + 2) * hn))
mixed_qkv = module.linear_qkv(hidden_states)
if getattr(module, "reuse_kv", False):
# reshape to [..., num_query_groups, heads_per_group * d]
q_dim = (module.num_heads // module.num_key_value_heads) * module.head_dim
new_shape = mixed_qkv.size()[:-1] + (module.num_key_value_heads, q_dim)
query = mixed_qkv.view(*new_shape)
# final shape (B, L, H, d)
query = query.reshape(query.size(0), query.size(1), -1, module.head_dim)
return query
# (B, L, hp) -> (B, L, ng, (np/ng + 2) * hn)
new_tensor_shape = mixed_qkv.size()[:-1] + (
module.num_key_value_heads,
(
(module.num_heads // module.num_key_value_heads + 2)
* module.head_dim
),
)
mixed_qkv = mixed_qkv.view(*new_tensor_shape)
split_arg_list = [
(
module.num_heads
// module.num_key_value_heads
* module.head_dim
),
module.head_dim,
module.head_dim,
]
# [B, L, ng, (np/ng + 2) * hn] -> [B, L, ng, np/ng * hn], [B, L, ng, hn], [B, L, ng, hn]
(query, key, value) = torch.split(mixed_qkv, split_arg_list, dim=3)
# [B, L, ng, np/ng * hn] -> [B, L, np, hn]
query = query.reshape(query.size(0), query.size(1), -1, module.head_dim)
return query, key, value
class DragonAttention(nn.Module):
"""
Multi-headed attention from 'Attention Is All You Need' paper.
Modified to use sliding window attention: Longformer and "Generating Long Sequences with Sparse Transformers".
Doesn't include output projection: output is (B, L, H, D).
"""
def __init__(self, config: DragonConfig, reuse_kv: bool, layer_idx: Optional[int], **kwargs):
super().__init__()
self.config = config
self.layer_idx = layer_idx
if layer_idx is None:
logger.warning_once(
f"Instantiating {self.__class__.__name__} without passing a `layer_idx` is not recommended and will "
"lead to errors during the forward call if caching is used. Please make sure to provide a `layer_idx` "
"when creating this class."
)
self.num_heads = config.num_attention_heads
self.num_key_value_heads = config.num_key_value_heads
self.hidden_size = config.hidden_size
self.projection_dim = config.hidden_size * config.expand_factor
self.head_dim = self.projection_dim // self.num_heads
self.rope_theta = config.rope_theta
self.qk_norm = config.qk_norm
self.window_size = config.sliding_window_size
self.reuse_kv = reuse_kv
projection_dim = self.head_dim * (self.num_heads + 2 * (0 if reuse_kv else self.num_key_value_heads))
self.linear_qkv = DragonLinear(config, config.hidden_size, projection_dim, bias=False)
if self.qk_norm:
self.q_norm = DragonRMSNorm(self.head_dim, eps=config.norm_epsilon)
if not reuse_kv:
self.k_norm = DragonRMSNorm(self.head_dim, eps=config.norm_epsilon)
def forward(
self,
hidden_states: torch.Tensor,
position_embeddings: tuple[torch.Tensor, torch.Tensor],
position_ids: Optional[torch.LongTensor] = None,
cache_params: Optional[HybridDragonAttentionDynamicCache] = None,
key_value_last_layer: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
**kwargs,
):
# Q, K, V projections.
if not self.reuse_kv:
query_states, key_states, value_states = get_query_key_value_tensors(self, hidden_states)
else:
query_states = get_query_key_value_tensors(self, hidden_states)
key_states, value_states = key_value_last_layer
last_key_states, last_value_states = None, None
# QK-norm.
if self.qk_norm:
query_states = self.q_norm(query_states)
if not self.reuse_kv:
key_states = self.k_norm(key_states)
# RoPE.
cos, sin = position_embeddings
query_states = apply_rotary_emb(query_states, cos, sin)
if not self.reuse_kv:
key_states = apply_rotary_emb(key_states, cos, sin)
# KV-cache.
if not self.reuse_kv and cache_params is not None:
key_states, value_states = cache_params.update(key_states, value_states, self.layer_idx)
# save k,v for next layer (*after* norm and RoPE and kv-cache update)
if not self.reuse_kv:
last_key_states, last_value_states = key_states, value_states
# attention computation. # TODO: do that in init ?
if ATTN_IMPL == "eager":
attention_interface = lambda q, k, v, **kw: eager_attention_forward(self, q, k, v, **kw)
elif ATTN_IMPL == "fa2":
attention_interface = lambda q, k, v, **kw: flash_attn_func(q, k, v, **kw)
elif ATTN_IMPL == "fa3":
attention_interface = lambda q, k, v, **kw: flash_attn_func(q, k, v, **kw)[0]
else:
raise ValueError(f"Unknown ATTN_IMPL: {ATTN_IMPL}")
attn_output = attention_interface(
query_states.bfloat16(),
key_states.bfloat16(),
value_states.bfloat16(),
causal=True,
window_size=(min(self.window_size, self.config.slw_wsize) if self.config.slw_wsize > 0 else self.window_size, 0),
softcap=self.config.softcap_local_attn,
softmax_scale=None if not self.config.use_uscaling else 1/self.head_dim,
**kwargs,
)
if cache_params is not None and not self.reuse_kv:
cache_params.trim(self.layer_idx)
return attn_output, last_key_states, last_value_states
# heavily adapted from official differential attention implementation
"""def eager_differential_attention_forward(
module: nn.Module,
query: torch.Tensor,
key: torch.Tensor,
value: torch.Tensor,
causal: bool = True,
window_size: Optional[Tuple[int, int]] = None,
softcap: Optional[float] = None,
softmax_scale: Optional[float] = None,
**kwargs,
) -> torch.Tensor:
if softmax_scale is None:
softmax_scale = module.head_dim ** -0.5
B, H2, Lq, Dh = query.shape # H2 = 2 * H
H = module.num_heads
Hkv = module.num_key_value_heads
assert H2 == 2 * H, "query must have 2*num_heads heads"
assert key.shape[-1] == Dh, "key head_dim must match query"
assert value.shape[-1] == 2 * Dh, "value must have 2*head_dim"
# repeat K to 2H (for the two "channels") and V to H (final combined heads)
n_rep = H // Hkv
k_2H = repeat_kv(key, 2 * n_rep) # [B, 2H, Lk, Dh]
v_H = repeat_kv(value, n_rep) # [B, H, Lk, 2Dh]
# raw attention logits for the 2 channels
attn_weights = torch.matmul(query, k_2H.transpose(2, 3)) * softmax_scale # [B, 2H, Lq, Lk]
if softcap is not None:
attn_weights = torch.tanh(attn_weights / softcap) * softcap
# masking (causal and/or sliding window)
if causal or (window_size is not None):
Lk = k_2H.size(2)
i = torch.arange(Lq, device=attn_weights.device).unsqueeze(1) # [Lq,1]
j = torch.arange(Lk, device=attn_weights.device).unsqueeze(0) # [1,Lk]
allowed = torch.ones((Lq, Lk), dtype=torch.bool, device=attn_weights.device)
if causal:
allowed &= (j <= i)
if window_size is not None:
w_left, w_right = window_size
if w_left is None: w_left = Lk
if w_right is None: w_right = Lk
allowed &= (j >= i - w_left) & (j <= i + w_right)
attn_weights = attn_weights.masked_fill(~allowed, float("-inf"))
# softmax in fp32 then cast back
attn_probs = torch.nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype) # [B,2H,Lq,Lk]
# reshape to [B, H, 2, Lq, Lk] and combine the two channels with learned lambda
attn_probs = attn_probs.view(B, H, 2, Lq, -1) # -1 = Lk
# per-head scalar lambdas: exp(<λ_q1,λ_k1>) - exp(<λ_q2,λ_k2>) + λ_init
lambda_1 = torch.exp(torch.sum(module.lambda_q1 * module.lambda_k1, dim=-1).float()).to(query.dtype) # [H]
lambda_2 = torch.exp(torch.sum(module.lambda_q2 * module.lambda_k2, dim=-1).float()).to(query.dtype) # [H]
lambda_full = (lambda_1 - lambda_2 + module.lambda_init).view(1, H, 1, 1) # [1,H,1,1] for broadcast
combined_probs = attn_probs[:, :, 0] - lambda_full * attn_probs[:, :, 1] # [B,H,Lq,Lk]
# weighted sum over V (note: V has 2*Dh per head)
attn = torch.matmul(combined_probs, v_H) # [B,H,Lq,2Dh]
# sub-layer norm (or similar) then final scaling
attn = module.subln(attn)
attn = attn * (1 - module.lambda_init)
# (B,Lq,H*2Dh)
attn = attn.transpose(1, 2).contiguous().view(B, Lq, H * 2 * Dh)
return attn"""
class DragonDifferentialAttention(nn.Module):
"""
Multi-headed differential attention (https://arxiv.org/abs/2410.05258)
"""
def __init__(self, config: DragonConfig, layer_idx: Optional[int], **kwargs):
super().__init__()
self.config = config
self.layer_idx = layer_idx
if layer_idx is None:
logger.warning_once(
f"Instantiating {self.__class__.__name__} without passing a `layer_idx` is not recommended and will "
"lead to errors during the forward call if caching is used. Please make sure to provide a `layer_idx` "
"when creating this class."
)
self.num_heads = config.num_attention_heads
self.num_key_value_heads = config.num_key_value_heads
self.hidden_size = config.hidden_size
self.head_dim = config.hidden_size * config.expand_factor // self.num_heads
self.qk_norm = config.qk_norm
self.softcap = config.softcap_global_attn
self.scalable_softmax = config.scalable_softmax
projection_dim = self.head_dim * (self.num_heads + 2 * self.num_key_value_heads)
self.linear_qkv = DragonLinear(config, config.hidden_size, projection_dim, bias=False)
if self.qk_norm:
self.q_norm = DragonRMSNorm(self.head_dim, eps=config.norm_epsilon)
self.k_norm = DragonRMSNorm(self.head_dim, eps=config.norm_epsilon)
if self.scalable_softmax:
self.softmax_scaler = nn.Parameter(torch.ones(self.num_heads, dtype=torch.float32))
self.register_buffer("lambda_init", torch.tensor(0.8 - 0.6 * math.exp(-0.3 * (layer_idx+1))), persistent=False)
self.lambda_q1 = torch.nn.Parameter(torch.zeros(self.head_dim//2, dtype=torch.float32).normal_(mean=0,std=0.1))
self.lambda_k1 = torch.nn.Parameter(torch.zeros(self.head_dim//2, dtype=torch.float32).normal_(mean=0,std=0.1))
self.lambda_q2 = torch.nn.Parameter(torch.zeros(self.head_dim//2, dtype=torch.float32).normal_(mean=0,std=0.1))
self.lambda_k2 = torch.nn.Parameter(torch.zeros(self.head_dim//2, dtype=torch.float32).normal_(mean=0,std=0.1))
def forward(
self,
hidden_states: torch.Tensor,
position_ids: Optional[torch.LongTensor] = None,
cache_params: Optional[HybridDragonAttentionDynamicCache] = None,
**kwargs,
):
# Q, K, V projections.
query_states, key_states, value_states = get_query_key_value_tensors(self, hidden_states)
value_states = value_states.reshape(value_states.size(0), value_states.size(1), value_states.size(2)//2, 2*value_states.size(3))
# QK-norm.
if self.qk_norm:
query_states = self.q_norm(query_states)
key_states = self.k_norm(key_states)
# scalable softmax.
if self.scalable_softmax:
# scalable-softmax (https://arxiv.org/abs/2501.19399): multiply q by s*log(n)
T = query_states.size(1)
pos = (position_ids.to(torch.float32).view(position_ids.size(0), T, 1, 1) + 1.)
log_pos = pos.log() if self.config.slw_wsize <= 0 else torch.clamp_max(pos, self.config.slw_wsize).log()
query_states = (self.softmax_scaler.view(1, 1, -1, 1) * log_pos) * query_states
# TODO: caching mechanism for log_pos
# KV-cache.
if cache_params is not None:
key_states, value_states = cache_params.update(key_states, value_states, self.layer_idx)
# attention computation.
# split q,k heads into two groups
query1_states, query2_states = query_states[:, :, torch.arange(0, self.num_heads, 2)].contiguous(), query_states[:, :, torch.arange(1, self.num_heads, 2)].contiguous()
key1_states, key2_states = key_states[:, :, torch.arange(0, self.num_key_value_heads, 2)].contiguous(), key_states[:, :, torch.arange(1, self.num_key_value_heads, 2)].contiguous()
# compute
# TODO: do that in init ?
if DIFF_ATTN_IMPL == "flex_head":
def diff_attention_interface(q, k, v, **kw):
return flex_head_fa.flash_attn_func(q, k, v, **kw)
elif DIFF_ATTN_IMPL == "fa2":
def diff_attention_interface(q, k, v, **kw):
D = v.size(3)
v1 = v[:, :, :, :D//2]#.contiguous()
v2 = v[:, :, :, D//2:]#.contiguous()
o1 = flash_attn_func(q, k, v1, **kw)
o2 = flash_attn_func(q, k, v2, **kw)
o = torch.cat([o1, o2], dim=-1)
return o
elif DIFF_ATTN_IMPL == "fa3":
def diff_attention_interface(q, k, v, **kw):
D = v.size(3)
v1 = v[:, :, :, :D//2]#.contiguous()
v2 = v[:, :, :, D//2:]#.contiguous()
o1 = flash_attn_func(q, k, v1, **kw)[0]
o2 = flash_attn_func(q, k, v2, **kw)[0]
o = torch.cat([o1, o2], dim=-1)
return o
elif DIFF_ATTN_IMPL == "eager":
def diff_attention_interface(q, k, v, **kw):
D = v.size(3)
v1 = v[:, :, :, :D//2]#.contiguous()
v2 = v[:, :, :, D//2:]#.contiguous()
o1 = eager_attention_forward(self, q, k, v1, **kw)
o2 = eager_attention_forward(self, q, k, v2, **kw)
o = torch.cat([o1, o2], dim=-1)
return o
y1 = diff_attention_interface(
query1_states.bfloat16(),
key1_states.bfloat16(),
value_states.bfloat16(),
causal=True,
window_size=(self.config.slw_wsize, 0),
softcap=self.softcap,
softmax_scale=None if not self.config.use_uscaling else 1/self.head_dim)
y2 = diff_attention_interface(
query2_states.bfloat16(),
key2_states.bfloat16(),
value_states.bfloat16(),
causal=True,
window_size=(self.config.slw_wsize, 0),
softcap=self.softcap,
softmax_scale=None if not self.config.use_uscaling else 1/self.head_dim)
lambda_1 = torch.exp((self.lambda_q1 * self.lambda_k1).sum(-1).float()) # (H/2)
lambda_2 = torch.exp((self.lambda_q2 * self.lambda_k2).sum(-1).float()) # (H/2)
lambda_full = (lambda_1 - lambda_2 + self.lambda_init).view(1, 1, -1, 1).type_as(y1)
attn_output = (y1 - lambda_full * y2).contiguous()
if cache_params is not None:
cache_params.trim(self.layer_idx)
return attn_output, None, None
class DragonGatedDeltaNet(nn.Module):
def __init__(self, config: DragonConfig, layer_idx: Optional[int], **kwargs):
super().__init__()
self.config = config
self.layer_idx = layer_idx
if layer_idx is None:
logger.warning_once(
f"Instantiating {self.__class__.__name__} without passing a `layer_idx` is not recommended and will "
"lead to errors during the forward call if caching is used. Please make sure to provide a `layer_idx` "
"when creating this class."
)
self.conv_size = config.conv_kernel
self.conv_bias = config.use_bias
self.n_heads = config.num_attention_heads
self.n_heads_local = self.n_heads // 1
self.d_head = int(config.hidden_size * (config.expand_factor/2)) // self.n_heads
self.key_dim = int(self.n_heads * self.d_head)
self.value_dim = int(2*self.key_dim) # todo refactor
self.head_k_dim = self.d_head
self.head_v_dim = int(2*self.d_head)
self.silu = nn.SiLU()
self.dk = self.head_k_dim
self.dv = self.head_v_dim # todo : duplicate variables
self.per_head_proj = 2*self.dk + self.dv + 2 + self.dv # [q k v b a g] per head
in_proj_dim_global = self.n_heads * self.per_head_proj
# todo: rename d_head => head_dim (for consistency with other classes)
self.in_gate_proj = DragonLinear(config, config.hidden_size, in_proj_dim_global, bias=False)
dt_min = config.time_step_min
dt_max = config.time_step_max
dt_init_floor = config.time_step_floor
A_init_range = config.A_init_range
# Initialize dt bias so that F.softplus(dt_bias) is between dt_min and dt_max
dt = torch.exp(
torch.rand(self.n_heads_local) * (math.log(dt_max) - math.log(dt_min))
+ math.log(dt_min)
)
dt = torch.clamp(dt, min=dt_init_floor)
# Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
inv_dt = dt + torch.log(-torch.expm1(-dt))
with torch.no_grad():
self.dt_bias = nn.Parameter(inv_dt)
assert A_init_range[0] > 0 and A_init_range[1] >= A_init_range[0]
A = torch.empty(
self.n_heads_local, dtype=torch.float32, device=torch.cuda.current_device()
).uniform_(*A_init_range)
A_log = torch.log(A) # Keep A_log in fp32
self.A_log = nn.Parameter(A_log)
self.q_conv1d = DragonConv1D(
hidden_size=self.key_dim,
kernel_size=self.conv_size,
)
self.k_conv1d = DragonConv1D(
hidden_size=self.key_dim,
kernel_size=self.conv_size,
)
self.v_conv1d = DragonConv1D(
hidden_size=self.value_dim,
kernel_size=self.conv_size,
)
self.act_func_gate = F.silu
def forward(self,
hidden_states: torch.Tensor,
cache_params: Optional[HybridDragonAttentionDynamicCache] = None,
):
_, q_len, _ = hidden_states.shape
mode = 'fused_recurrent' if q_len <= 64 else 'chunk'
if self.training:
assert mode == 'chunk', "Only chunk mode is supported in training."
# input projection (TP-aware)
qkvbag = self.in_gate_proj(hidden_states) # (l, b, H_local * per_head_proj)
# [L,B,(H*P)] -> [B,L,H,P]
qkvbag = rearrange(qkvbag, "b l (h p) -> b l h p", h=self.n_heads_local).contiguous()
# split per head: [B,L,H,dk/dk/dv/1/1]
q_proj = qkvbag[..., 0:self.dk]
k_proj = qkvbag[..., self.dk:2*self.dk]
v_proj = qkvbag[..., 2*self.dk:2*self.dk+self.dv]
b_proj = qkvbag[..., 2*self.dk+self.dv:2*self.dk+self.dv+1]
a_proj = qkvbag[..., 2*self.dk+self.dv+1:2*self.dk+self.dv+2]
g_proj = qkvbag[..., 2*self.dk+self.dv+2:]
# concat for conv
q_proj = rearrange(q_proj, "b l h d -> b l (h d)")
k_proj = rearrange(k_proj, "b l h d -> b l (h d)")
v_proj = rearrange(v_proj, "b l h d -> b l (h d)")
b_proj = rearrange(b_proj, "b l h d -> b l (h d)") # d=1
a_proj = rearrange(a_proj, "b l h d -> b l (h d)")
q_conv_cache, k_conv_cache, v_conv_cache, ssm_cache = (None, None, None, None)
if cache_params is not None:
q_conv_cache, k_conv_cache, v_conv_cache, ssm_cache = cache_params.get_ssm_cache(self.layer_idx)
q, q_conv_cache = self.q_conv1d(
x=q_proj,
mask=None,
cache=q_conv_cache,
output_final_state=(cache_params is not None))
k, k_conv_cache = self.k_conv1d(
x=k_proj,
mask=None,
cache=k_conv_cache,
output_final_state=(cache_params is not None))
v, v_conv_cache = self.v_conv1d(
x=v_proj,
mask=None,
cache=v_conv_cache,
output_final_state=(cache_params is not None))
# back to per-head for kernels
q = rearrange(q, "b l (h d) -> b l h d", d=self.dk)
k = rearrange(k, "b l (h d) -> b l h d", d=self.dk)
v = rearrange(v, "b l (h d) -> b l h d", d=self.dv)
beta = b_proj.sigmoid()
g = -self.A_log.float().exp() * F.softplus(a_proj.float() + self.dt_bias)
if mode == 'chunk':
if chunk_gated_delta_rule is not None:
o, ssm_cache = chunk_gated_delta_rule(
q=q.bfloat16(),
k=k.bfloat16(),
v=v.bfloat16(),
g=g,
beta=beta,
scale=None if not self.config.use_uscaling else 1/self.head_k_dim,
initial_state=ssm_cache,
output_final_state=(cache_params is not None),
cu_seqlens=None, # for varlen training
head_first=False,
use_qk_l2norm_in_kernel=True
) # (B L H D) where d is head_v_dim
else:
raise NotImplementedError("PyTorch implementation of chunked GDN is not available.")
elif mode == 'fused_recurrent':
if fused_recurrent_gated_delta_rule is not None:
o, ssm_cache = fused_recurrent_gated_delta_rule(
q=q.bfloat16(),
k=k.bfloat16(),
v=v.bfloat16(),
g=g,
beta=beta,
scale=None if not self.config.use_uscaling else 1/self.head_k_dim,
initial_state=ssm_cache,
output_final_state=(cache_params is not None),
cu_seqlens=None,
use_qk_l2norm_in_kernel=True
) # (B L H D) where d is head_v_dim
else:
raise NotImplementedError("PyTorch implementation of recurrent GDN is not available.")
else:
raise NotImplementedError(f"Not supported mode `{mode}`.")
o = o * self.act_func_gate(g_proj)
if cache_params is not None:
cache_params.update_ssm_cache(
q_conv_states=q_conv_cache,
k_conv_states=k_conv_cache,
v_conv_states=v_conv_cache,
ssm_states=ssm_cache,
layer_idx=self.layer_idx,
)
return o
class DragonMLP(nn.Module):
def __init__(self, config: DragonConfig):
super().__init__()
self.fc_1 = DragonLinear(config, config.hidden_size, config.intermediate_size, bias=False)
self.fc_2 = DragonLinear(config, config.intermediate_size, config.hidden_size, bias=False)
self.register_buffer("_2_sqrt_5", torch.tensor(2/math.sqrt(5)) if config.use_uscaling else torch.tensor(1.), persistent=False)
def forward(self, hidden_states):
hidden_states = self.fc_1(hidden_states)
hidden_states = self._2_sqrt_5 * F.relu(hidden_states).square()
hidden_states = self.fc_2(hidden_states)
return hidden_states
class DragonBlock(GradientCheckpointingLayer):
def __init__(self, config: DragonConfig, layer_idx: int, layer_type: str):
super().__init__()
self.config = config
self.layer_idx = layer_idx
self.expand_factor = config.expand_factor
if layer_type in ['l', 'r']:
self.attn = DragonAttention(config, reuse_kv=(layer_type=='r'), layer_idx=layer_idx)
elif layer_type == 'd':
self.attn = DragonDifferentialAttention(config, layer_idx=layer_idx)
else:
raise ValueError(f"Unknown layer type: {layer_type}")
self.lin_attn = DragonGatedDeltaNet(config, layer_idx=layer_idx)
self.mixer_proj = DragonLinear(config, int(self.expand_factor*config.hidden_size), config.hidden_size, bias=False)
if isinstance(self.attn, (DragonDifferentialAttention)):
self.attn_group_norm = DragonHeadWiseRMSNorm(n_heads=self.attn.num_heads//2, d_head=2*self.attn.head_dim, eps=config.norm_epsilon)
else:
self.attn_group_norm = DragonHeadWiseRMSNorm(n_heads=self.attn.num_heads, d_head=self.attn.head_dim, eps=config.norm_epsilon)
self.lin_attn_group_norm = DragonHeadWiseRMSNorm(n_heads=self.lin_attn.n_heads, d_head=self.lin_attn.head_v_dim, eps=config.norm_epsilon)
self.input_norm = DragonRMSNorm(config.hidden_size, eps=config.norm_epsilon)
self.postmixer_norm = DragonRMSNorm(config.hidden_size, eps=config.norm_epsilon)
self.mlp = DragonMLP(config)
self.register_buffer("lns", torch.tensor(1.0 if config.use_uscaling else 1. / math.sqrt(layer_idx + (2 if config.old_lns else 1))), persistent=False)
self.register_buffer("sqrt_2_2", torch.tensor(math.sqrt(2)/2) if config.use_uscaling else torch.tensor(1/2), persistent=False)
self.register_buffer("sqrt_tau", torch.sqrt(torch.tensor(self.config.uscaling_tau)) if config.use_uscaling else torch.tensor(1.0), persistent=False)
self.register_buffer("sqrt_one_minus_tau", torch.sqrt(torch.tensor(1.0 - self.config.uscaling_tau)) if config.use_uscaling else torch.tensor(1.0), persistent=False)
def forward(
self,
hidden_states: torch.Tensor,
position_ids: Optional[torch.LongTensor] = None,
cache_params: Optional[HybridDragonAttentionDynamicCache] = None,
cache_position: Optional[torch.LongTensor] = None,
position_embeddings: Optional[tuple[torch.Tensor, torch.Tensor]] = None,
key_value_last_layer: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
**kwargs,
):
# MIXER.
residual = hidden_states
hidden_states = self.lns * self.input_norm(hidden_states) # (B, L, D)
y_attn, last_key_states, last_value_states = self.attn(
hidden_states=hidden_states,
position_embeddings=position_embeddings,
position_ids=position_ids,
cache_params=cache_params,
key_value_last_layer=key_value_last_layer,
) # (B, L, E*D)
y_lin_attn = self.lin_attn(
hidden_states=hidden_states,
cache_params=cache_params,
) # (B, L, E*D)
y_attn = self.attn_group_norm(y_attn).view(y_attn.size(0), y_attn.size(1), -1)
y_lin_attn = self.lin_attn_group_norm(y_lin_attn).view(y_lin_attn.size(0), y_lin_attn.size(1), -1)
y_mixer = self.mixer_proj(self.sqrt_2_2 * (y_attn + y_lin_attn))
hidden_states = self.sqrt_one_minus_tau * residual + self.sqrt_tau * y_mixer
# MLP.
residual = hidden_states
hidden_states = self.lns * self.postmixer_norm(hidden_states)
y_mlp = self.mlp(hidden_states) # (B, L, D)
hidden_states = self.sqrt_one_minus_tau * residual + self.sqrt_tau * y_mlp
return hidden_states, last_key_states, last_value_states
class DragonPreTrainedModel(PreTrainedModel):
config: DragonConfig
base_model_prefix = "model"
supports_gradient_checkpointing = True
_no_split_modules = ["DragonBlock"]
_supports_flash_attn = True
_supports_sdpa = True
_supports_flex_attn = True
_can_compile_fullgraph = True
_supports_attention_backend = True
_can_record_outputs = {
"hidden_states": DragonBlock,
"attentions": DragonBlock,
}
def _init_weights(self, module):
if isinstance(module, (DragonLinear, DragonConv1D)):
if module.bias is not None:
nn.init.zeros_(module.bias)
nn.init.normal_(module.weight, mean=0., std=1. if self.config.use_uscaling else 0.006)
elif isinstance(module, nn.Embedding):
nn.init.normal_(module.weight, mean=0., std=1. if self.config.use_uscaling else 0.006)
@dataclass
class DragonOutput(ModelOutput):
"""
Class for the Dragon model outputs.
Args:
last_hidden_state (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`):
Sequence of hidden-states at the output of the last layer of the model.
cache_params (`HybridDragonAttentionDynamicCache`):
The state of the model at the last time step. Can be used in a forward method with the next `input_ids` to
avoid providing the old `input_ids`.
Includes both the RNN-like state matrices after the selective scan, and the conv states
hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
Tuple of `torch.FloatTensor` (one for the output of the embeddings, if the model has an embedding layer, +
one for the output of each layer) of shape `(batch_size, sequence_length, hidden_size)`.
Hidden-states of the model at the output of each layer plus the optional initial embedding outputs.
"""
last_hidden_state: Optional[torch.FloatTensor] = None
past_key_values: Optional[HybridDragonAttentionDynamicCache] = None
hidden_states: Optional[Tuple[torch.FloatTensor]] = None
@dataclass
class DragonCausalLMOutput(ModelOutput):
"""
Base class for causal language model (or autoregressive) outputs.
Args:
loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
Language modeling loss (for next-token prediction).
logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`):
Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
cache_params (`HybridDragonAttentionDynamicCache`):
The state of the model at the last time step. Can be used in a forward method with the next `input_ids` to
avoid providing the old `input_ids`.
Includes both the State space model state matrices after the selective scan, and the Convolutional states
hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
Tuple of `torch.FloatTensor` (one for the output of the embeddings, if the model has an embedding layer, +
one for the output of each layer) of shape `(batch_size, sequence_length, hidden_size)`.
Hidden-states of the model at the output of each layer plus the optional initial embedding outputs.
"""
loss: Optional[torch.FloatTensor] = None
logits: Optional[torch.FloatTensor] = None
past_key_values: Optional[HybridDragonAttentionDynamicCache] = None
hidden_states: Optional[Tuple[torch.FloatTensor]] = None
class DragonModel(DragonPreTrainedModel):
def __init__(self, config: DragonConfig):
super().__init__(config)
self.padding_idx = config.pad_token_id
self.vocab_size = config.vocab_size
self.embedding = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
self.layers = nn.ModuleList([DragonBlock(config, layer_idx=i, layer_type=layer) for i, layer in enumerate(config.layers_config)])
self.rotary_emb = DragonRotaryEmbedding(config, head_dim=(config.expand_factor*config.hidden_size)//config.num_attention_heads) # only for SWA
self.final_norm = DragonRMSNorm(config.hidden_size, eps=config.norm_epsilon)
alpha_fwd_out = 1. / float(self.config.hidden_size) if self.config.use_uscaling else 1.0
alpha_bwd_out = 1. / math.sqrt(float(self.config.hidden_size)) if self.config.use_uscaling else 1.0
self.register_buffer("alpha_fwd_out", torch.tensor(alpha_fwd_out), persistent=False)
self.register_buffer("alpha_bwd_out", torch.tensor(alpha_bwd_out), persistent=False)
self.gradient_checkpointing = False
self.post_init()
def get_input_embeddings(self):
return self.embedding
def set_input_embeddings(self, new_embeddings):
self.embedding = new_embeddings
def forward(
self,
input_ids: Optional[torch.LongTensor] = None,
attention_mask: Optional[torch.Tensor] = None,
position_ids: Optional[torch.LongTensor] = None,
use_cache: Optional[bool] = None,
past_key_values: Optional[HybridDragonAttentionDynamicCache] = None,
cache_position: Optional[torch.LongTensor] = None,
output_hidden_states: Optional[bool] = None,
inputs_embeds: Optional[torch.FloatTensor] = None,
**kwargs
) -> DragonOutput:
use_cache = use_cache if use_cache is not None else (self.config.use_cache if not self.training else False)
if (input_ids is None) ^ (inputs_embeds is not None):
raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
if inputs_embeds is None:
inputs_embeds = self.embedding(input_ids)
if self.gradient_checkpointing and self.training and use_cache:
logger.warning_once(
"`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`."
)
use_cache = False
if use_cache:
if past_key_values is None:
past_key_values = HybridDragonAttentionDynamicCache(self.config, dtype=self.dtype)
elif not isinstance(past_key_values, HybridDragonAttentionDynamicCache):
# recreate (todo: upcast instead of recreate)
if type(past_key_values) is DynamicCache:
print("upgrading DynamicCache → HybridDragonAttentionDynamicCache")
past_key_values = HybridDragonAttentionDynamicCache(self.config, dtype=self.dtype)
else:
raise TypeError(f"Unsupported cache type: {type(past_key_values)}")
hidden_states = inputs_embeds
if cache_position is None:
cache_position = torch.arange(hidden_states.shape[1], device=hidden_states.device)
if position_ids is None:
position_ids = cache_position.unsqueeze(0)
all_hidden_states = () if output_hidden_states else None
position_embeddings = self.rotary_emb(hidden_states, position_ids)
shared_kv = (None, None)
for block in self.layers:
if output_hidden_states:
all_hidden_states += (hidden_states,)
hidden_states, last_k, last_v = block(
hidden_states,
position_ids=position_ids,
cache_params=past_key_values,
cache_position=cache_position,
position_embeddings=position_embeddings,
key_value_last_layer=shared_kv,
**kwargs,
)
shared_kv = (last_k, last_v)
hidden_states = self.final_norm(hidden_states)
hidden_states = _ScaleFB.apply(hidden_states, self.alpha_fwd_out, self.alpha_bwd_out)
if output_hidden_states:
all_hidden_states = all_hidden_states + (hidden_states,)
return DragonOutput(
last_hidden_state=hidden_states,
past_key_values=past_key_values if use_cache else None,
hidden_states=all_hidden_states,
)
DragonModel.register_for_auto_class("AutoModel")
class DragonForCausalLM(DragonPreTrainedModel, GenerationMixin):
def __init__(self, config: DragonConfig):
super().__init__(config)
self.model = DragonModel(config)
self.vocab_size = config.vocab_size
#self.lm_head = DragonLinear(config, config.hidden_size, config.vocab_size, bias=False, alpha_fwd=1/config.hidden_size, alpha_bwd=1/math.sqrt(config.hidden_size))
self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
self.post_init()
def forward(
self,
input_ids: Optional[torch.LongTensor] = None,
inputs_embeds: Optional[torch.FloatTensor] = None,
position_ids: Optional[torch.LongTensor] = None,
labels: Optional[torch.LongTensor] = None,
logits_to_keep: Union[int, torch.Tensor] = 0,
use_cache: Optional[bool] = None,
past_key_values: Optional[HybridDragonAttentionDynamicCache] = None,
cache_position: Optional[torch.Tensor] = None,
output_hidden_states: Optional[bool] = None,
attention_mask: Optional[torch.Tensor] = None,
**kwargs,
) -> DragonCausalLMOutput:
output_hidden_states = (output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states)
outputs: DragonOutput = self.model(
input_ids=input_ids,
position_ids=position_ids,
attention_mask=attention_mask,
use_cache=use_cache,
past_key_values=past_key_values,
cache_position=cache_position,
inputs_embeds=inputs_embeds,
output_hidden_states=output_hidden_states,
**kwargs,
)
hidden_states = outputs.last_hidden_state
slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
logits = self.lm_head(hidden_states.to(self.lm_head.weight.dtype)[:, slice_indices, :]).float()
loss = None
if labels is not None:
# move labels to correct device
labels = labels.to(logits.device)
# shift
shift_logits = logits[..., :-1, :].contiguous()
shift_labels = labels[..., 1:].contiguous()
# compute loss
loss = F.cross_entropy(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1), ignore_index=self.model.padding_idx)
return DragonCausalLMOutput(
loss=loss,
logits=logits,
past_key_values=outputs.past_key_values,
hidden_states=outputs.hidden_states,
)
DragonForCausalLM.register_for_auto_class("AutoModelForCausalLM")
__all__ = ["DragonModel", "DragonForCausalLM", "DragonPreTrainedModel"]