|
|
|
|
|
|
| import math
|
| from typing import Optional, Tuple
|
|
|
| import torch
|
| import torch.nn as nn
|
| import torch.nn.functional as F
|
| from torch import Tensor
|
|
|
| from einops import rearrange, repeat
|
|
|
| try:
|
| from causal_conv1d import causal_conv1d_fn, causal_conv1d_update
|
| except ImportError:
|
| causal_conv1d_fn, causal_conv1d_update = None, None
|
|
|
| from mamba_ssm.ops.selective_scan_interface import selective_scan_fn, mamba_inner_fn, bimamba_inner_fn, mamba_inner_fn_no_out_proj
|
|
|
|
|
| try:
|
| from mamba_ssm.ops.triton.selective_state_update import selective_state_update
|
| except ImportError:
|
| selective_state_update = None
|
|
|
| try:
|
| from mamba_ssm.ops.triton.layer_norm import RMSNorm, layer_norm_fn, rms_norm_fn
|
| except ImportError:
|
| RMSNorm, layer_norm_fn, rms_norm_fn = None, None, None
|
|
|
|
|
| class Mamba(nn.Module):
|
| def __init__(
|
| self,
|
| d_model,
|
| d_state=16,
|
| d_conv=4,
|
| expand=2,
|
| dt_rank="auto",
|
| dt_min=0.001,
|
| dt_max=0.1,
|
| dt_init="random",
|
| dt_scale=1.0,
|
| dt_init_floor=1e-4,
|
| conv_bias=True,
|
| bias=False,
|
| use_fast_path=True,
|
| layer_idx=None,
|
| device=None,
|
| dtype=None,
|
| bimamba_type="v2",
|
| if_devide_out=False,
|
| init_layer_scale=None,
|
| ):
|
| factory_kwargs = {"device": device, "dtype": dtype}
|
| super().__init__()
|
| self.d_model = d_model
|
| self.d_state = d_state
|
| self.d_conv = d_conv
|
| self.expand = expand
|
| self.d_inner = int(self.expand * self.d_model)
|
| self.dt_rank = math.ceil(self.d_model / 16) if dt_rank == "auto" else dt_rank
|
| self.use_fast_path = use_fast_path
|
| self.layer_idx = layer_idx
|
| self.bimamba_type = bimamba_type
|
| self.if_devide_out = if_devide_out
|
|
|
| self.init_layer_scale = init_layer_scale
|
| if init_layer_scale is not None:
|
| self.gamma = nn.Parameter(init_layer_scale * torch.ones((d_model)), requires_grad=True)
|
|
|
| self.in_proj = nn.Linear(self.d_model, self.d_inner * 2, bias=bias, **factory_kwargs)
|
|
|
| self.conv1d = nn.Conv1d(
|
| in_channels=self.d_inner,
|
| out_channels=self.d_inner,
|
| bias=conv_bias,
|
| kernel_size=d_conv,
|
| groups=self.d_inner,
|
| padding=d_conv - 1,
|
| **factory_kwargs,
|
| )
|
|
|
| self.activation = "silu"
|
| self.act = nn.SiLU()
|
|
|
| self.x_proj = nn.Linear(
|
| self.d_inner, self.dt_rank + self.d_state * 2, bias=False, **factory_kwargs
|
| )
|
| self.dt_proj = nn.Linear(self.dt_rank, self.d_inner, bias=True, **factory_kwargs)
|
|
|
|
|
| dt_init_std = self.dt_rank**-0.5 * dt_scale
|
| if dt_init == "constant":
|
| nn.init.constant_(self.dt_proj.weight, dt_init_std)
|
| elif dt_init == "random":
|
| nn.init.uniform_(self.dt_proj.weight, -dt_init_std, dt_init_std)
|
| else:
|
| raise NotImplementedError
|
|
|
|
|
| dt = torch.exp(
|
| torch.rand(self.d_inner, **factory_kwargs) * (math.log(dt_max) - math.log(dt_min))
|
| + math.log(dt_min)
|
| ).clamp(min=dt_init_floor)
|
|
|
| inv_dt = dt + torch.log(-torch.expm1(-dt))
|
| with torch.no_grad():
|
| self.dt_proj.bias.copy_(inv_dt)
|
|
|
| self.dt_proj.bias._no_reinit = True
|
|
|
|
|
| A = repeat(
|
| torch.arange(1, self.d_state + 1, dtype=torch.float32, device=device),
|
| "n -> d n",
|
| d=self.d_inner,
|
| ).contiguous()
|
| A_log = torch.log(A)
|
| self.A_log = nn.Parameter(A_log)
|
| self.A_log._no_weight_decay = True
|
|
|
|
|
| self.D = nn.Parameter(torch.ones(self.d_inner, device=device))
|
| self.D._no_weight_decay = True
|
|
|
|
|
| if bimamba_type == "v1":
|
| A_b = repeat(
|
| torch.arange(1, self.d_state + 1, dtype=torch.float32, device=device),
|
| "n -> d n",
|
| d=self.d_inner,
|
| ).contiguous()
|
| A_b_log = torch.log(A_b)
|
| self.A_b_log = nn.Parameter(A_b_log)
|
| self.A_b_log._no_weight_decay = True
|
| elif bimamba_type == "v2":
|
| A_b = repeat(
|
| torch.arange(1, self.d_state + 1, dtype=torch.float32, device=device),
|
| "n -> d n",
|
| d=self.d_inner,
|
| ).contiguous()
|
| A_b_log = torch.log(A_b)
|
| self.A_b_log = nn.Parameter(A_b_log)
|
| self.A_b_log._no_weight_decay = True
|
|
|
| self.conv1d_b = nn.Conv1d(
|
| in_channels=self.d_inner,
|
| out_channels=self.d_inner,
|
| bias=conv_bias,
|
| kernel_size=d_conv,
|
| groups=self.d_inner,
|
| padding=d_conv - 1,
|
| **factory_kwargs,
|
| )
|
|
|
| self.x_proj_b = nn.Linear(
|
| self.d_inner, self.dt_rank + self.d_state * 2, bias=False, **factory_kwargs
|
| )
|
| self.dt_proj_b = nn.Linear(self.dt_rank, self.d_inner, bias=True, **factory_kwargs)
|
|
|
| self.D_b = nn.Parameter(torch.ones(self.d_inner, device=device))
|
| self.D_b._no_weight_decay = True
|
|
|
| self.out_proj = nn.Linear(self.d_inner, self.d_model, bias=bias, **factory_kwargs)
|
|
|
|
|
| @staticmethod
|
| def _is_right_padded(attn_mask: Tensor) -> bool:
|
| """
|
| attn_mask: (B, Lmax) True=PAD
|
| 检查是否为严格右侧 padding(中间不得有 True)。
|
| """
|
| B, L = attn_mask.shape
|
| seqlens = (~attn_mask).sum(dim=1)
|
| ar = torch.arange(L, device=attn_mask.device).unsqueeze(0)
|
| right = ar >= seqlens.unsqueeze(1)
|
| return bool(torch.equal(attn_mask, right))
|
|
|
| @staticmethod
|
| def _right_pad_lengths(attn_mask: Tensor) -> Tuple[Tensor, int]:
|
| """
|
| 返回 (seqlens: (B,), L_eff = max(seqlens))
|
| """
|
| seqlens = (~attn_mask).sum(dim=1)
|
| L_eff = int(seqlens.max().item())
|
| return seqlens, L_eff
|
|
|
| def pack_ragged(self, tokens, attn_mask):
|
|
|
| B, Lmax, D = tokens.shape
|
| keep = (~attn_mask).view(B * Lmax)
|
| idx = torch.nonzero(keep, as_tuple=False).squeeze(-1)
|
| flat = tokens.view(B * Lmax, D).index_select(0, idx)
|
|
|
| seqlens = (~attn_mask).sum(dim=1).to(torch.int32)
|
| cu = torch.zeros(B + 1, dtype=torch.int32, device=tokens.device)
|
| cu[1:] = torch.cumsum(seqlens, dim=0)
|
| return flat, cu, seqlens, idx
|
|
|
| def forward_dense(self, hidden_states, inference_params=None):
|
| """
|
| hidden_states: (B, L, D)
|
| Returns: same shape as hidden_states
|
| """
|
| batch, seqlen, dim = hidden_states.shape
|
|
|
| conv_state, ssm_state = None, None
|
| if inference_params is not None:
|
| conv_state, ssm_state = self._get_states_from_cache(inference_params, batch)
|
| if inference_params.seqlen_offset > 0:
|
|
|
| out, _, _ = self.step(hidden_states, conv_state, ssm_state)
|
| return out
|
|
|
|
|
| xz = rearrange(
|
| self.in_proj.weight @ rearrange(hidden_states, "b l d -> d (b l)"),
|
| "d (b l) -> b d l",
|
| l=seqlen,
|
| )
|
| if self.in_proj.bias is not None:
|
| xz = xz + rearrange(self.in_proj.bias.to(dtype=xz.dtype), "d -> d 1")
|
|
|
| A = -torch.exp(self.A_log.float())
|
|
|
| if self.use_fast_path and inference_params is None:
|
| if self.bimamba_type == "v1":
|
| A_b = -torch.exp(self.A_b_log.float())
|
| out = bimamba_inner_fn(
|
| xz,
|
| self.conv1d.weight,
|
| self.conv1d.bias,
|
| self.x_proj.weight,
|
| self.dt_proj.weight,
|
| self.out_proj.weight,
|
| self.out_proj.bias,
|
| A,
|
| A_b,
|
| None,
|
| None,
|
| self.D.float(),
|
| delta_bias=self.dt_proj.bias.float(),
|
| delta_softplus=True,
|
| )
|
| elif self.bimamba_type == "v2":
|
| A_b = -torch.exp(self.A_b_log.float())
|
| out = mamba_inner_fn_no_out_proj(
|
| xz,
|
| self.conv1d.weight,
|
| self.conv1d.bias,
|
| self.x_proj.weight,
|
| self.dt_proj.weight,
|
| A,
|
| None,
|
| None,
|
| self.D.float(),
|
| delta_bias=self.dt_proj.bias.float(),
|
| delta_softplus=True,
|
| )
|
| out_b = mamba_inner_fn_no_out_proj(
|
| xz.flip([-1]),
|
| self.conv1d_b.weight,
|
| self.conv1d_b.bias,
|
| self.x_proj_b.weight,
|
| self.dt_proj_b.weight,
|
| A_b,
|
| None,
|
| None,
|
| self.D_b.float(),
|
| delta_bias=self.dt_proj_b.bias.float(),
|
| delta_softplus=True,
|
| )
|
|
|
| if not self.if_devide_out:
|
| out = F.linear(rearrange(out + out_b.flip([-1]), "b d l -> b l d"), self.out_proj.weight, self.out_proj.bias)
|
| else:
|
| out = F.linear(rearrange(out + out_b.flip([-1]), "b d l -> b l d") / 2, self.out_proj.weight, self.out_proj.bias)
|
|
|
| else:
|
| out = mamba_inner_fn(
|
| xz,
|
| self.conv1d.weight,
|
| self.conv1d.bias,
|
| self.x_proj.weight,
|
| self.dt_proj.weight,
|
| self.out_proj.weight,
|
| self.out_proj.bias,
|
| A,
|
| None,
|
| None,
|
| self.D.float(),
|
| delta_bias=self.dt_proj.bias.float(),
|
| delta_softplus=True,
|
| )
|
| else:
|
| x, z = xz.chunk(2, dim=1)
|
|
|
| if conv_state is not None:
|
|
|
|
|
| conv_state.copy_(F.pad(x, (self.d_conv - x.shape[-1], 0)))
|
| if causal_conv1d_fn is None:
|
| x = self.act(self.conv1d(x)[..., :seqlen])
|
| else:
|
| assert self.activation in ["silu", "swish"]
|
| x = causal_conv1d_fn(
|
| x=x,
|
| weight=rearrange(self.conv1d.weight, "d 1 w -> d w"),
|
| bias=self.conv1d.bias,
|
| activation=self.activation,
|
| )
|
|
|
|
|
|
|
|
|
| x_dbl = self.x_proj(rearrange(x, "b d l -> (b l) d"))
|
| dt, B, C = torch.split(x_dbl, [self.dt_rank, self.d_state, self.d_state], dim=-1)
|
| dt = self.dt_proj.weight @ dt.t()
|
| dt = rearrange(dt, "d (b l) -> b d l", l=seqlen)
|
| B = rearrange(B, "(b l) dstate -> b dstate l", l=seqlen).contiguous()
|
| C = rearrange(C, "(b l) dstate -> b dstate l", l=seqlen).contiguous()
|
| assert self.activation in ["silu", "swish"]
|
| y = selective_scan_fn(
|
| x,
|
| dt,
|
| A,
|
| B,
|
| C,
|
| self.D.float(),
|
| z=z,
|
| delta_bias=self.dt_proj.bias.float(),
|
| delta_softplus=True,
|
| return_last_state=ssm_state is not None,
|
| )
|
| if ssm_state is not None:
|
| y, last_state = y
|
| ssm_state.copy_(last_state)
|
| y = rearrange(y, "b d l -> b l d")
|
| out = self.out_proj(y)
|
| if self.init_layer_scale is not None:
|
| out = out * self.gamma
|
| return out
|
|
|
|
|
| def forward(
|
| self,
|
| hidden_states: Tensor,
|
| inference_params=None,
|
| attn_mask: Optional[Tensor] = None,
|
| ) -> Tensor:
|
| """
|
| 并行计算;为 PAD 付费,但:
|
| 1) PAD 位置输出强制置 0;
|
| 2) 因为是右侧 PAD + 因果结构,不会影响前面有效位置(无状态“穿越”)。
|
| """
|
|
|
| if attn_mask is None:
|
| return self.forward_dense(hidden_states, inference_params)
|
|
|
|
|
| assert inference_params is None, "变长/带 mask 训练前向请使用 inference_params=None"
|
|
|
|
|
| assert self._is_right_padded(attn_mask), "attn_mask 必须为右侧 padding(中间不得有 True)"
|
|
|
| B, Lmax, D = hidden_states.shape
|
| seqlens, L_eff = self._right_pad_lengths(attn_mask)
|
|
|
| if L_eff == 0:
|
|
|
| return hidden_states.new_zeros(hidden_states.shape)
|
|
|
|
|
| x_eff = hidden_states[:, :L_eff, :]
|
| y_eff = self.forward_dense(x_eff, None)
|
|
|
|
|
| if L_eff < Lmax:
|
| pad_right = Lmax - L_eff
|
| y = F.pad(y_eff, (0, 0, 0, pad_right))
|
| else:
|
| y = y_eff
|
|
|
|
|
| y.masked_fill_(attn_mask.unsqueeze(-1), 0.0)
|
| return y
|
|
|
| def step(self, hidden_states, conv_state, ssm_state):
|
| dtype = hidden_states.dtype
|
| assert hidden_states.shape[1] == 1, "Only support decoding with 1 token at a time for now"
|
| xz = self.in_proj(hidden_states.squeeze(1))
|
| x, z = xz.chunk(2, dim=-1)
|
|
|
|
|
| if causal_conv1d_update is None:
|
| conv_state.copy_(torch.roll(conv_state, shifts=-1, dims=-1))
|
| conv_state[:, :, -1] = x
|
| x = torch.sum(conv_state * rearrange(self.conv1d.weight, "d 1 w -> d w"), dim=-1)
|
| if self.conv1d.bias is not None:
|
| x = x + self.conv1d.bias
|
| x = self.act(x).to(dtype=dtype)
|
| else:
|
| x = causal_conv1d_update(
|
| x,
|
| conv_state,
|
| rearrange(self.conv1d.weight, "d 1 w -> d w"),
|
| self.conv1d.bias,
|
| self.activation,
|
| )
|
|
|
| x_db = self.x_proj(x)
|
| dt, B, C = torch.split(x_db, [self.dt_rank, self.d_state, self.d_state], dim=-1)
|
|
|
| dt = F.linear(dt, self.dt_proj.weight)
|
| A = -torch.exp(self.A_log.float())
|
|
|
|
|
| if selective_state_update is None:
|
|
|
| dt = F.softplus(dt + self.dt_proj.bias.to(dtype=dt.dtype))
|
| dA = torch.exp(torch.einsum("bd,dn->bdn", dt, A))
|
| dB = torch.einsum("bd,bn->bdn", dt, B)
|
| ssm_state.copy_(ssm_state * dA + rearrange(x, "b d -> b d 1") * dB)
|
| y = torch.einsum("bdn,bn->bd", ssm_state.to(dtype), C)
|
| y = y + self.D.to(dtype) * x
|
| y = y * self.act(z)
|
| else:
|
| y = selective_state_update(
|
| ssm_state, x, dt, A, B, C, self.D, z=z, dt_bias=self.dt_proj.bias, dt_softplus=True
|
| )
|
|
|
| out = self.out_proj(y)
|
| return out.unsqueeze(1), conv_state, ssm_state
|
|
|
| def allocate_inference_cache(self, batch_size, max_seqlen, dtype=None, **kwargs):
|
| device = self.out_proj.weight.device
|
| conv_dtype = self.conv1d.weight.dtype if dtype is None else dtype
|
| conv_state = torch.zeros(
|
| batch_size, self.d_model * self.expand, self.d_conv, device=device, dtype=conv_dtype
|
| )
|
| ssm_dtype = self.dt_proj.weight.dtype if dtype is None else dtype
|
|
|
| ssm_state = torch.zeros(
|
| batch_size, self.d_model * self.expand, self.d_state, device=device, dtype=ssm_dtype
|
| )
|
| return conv_state, ssm_state
|
|
|
| def _get_states_from_cache(self, inference_params, batch_size, initialize_states=False):
|
| assert self.layer_idx is not None
|
| if self.layer_idx not in inference_params.key_value_memory_dict:
|
| batch_shape = (batch_size,)
|
| conv_state = torch.zeros(
|
| batch_size,
|
| self.d_model * self.expand,
|
| self.d_conv,
|
| device=self.conv1d.weight.device,
|
| dtype=self.conv1d.weight.dtype,
|
| )
|
| ssm_state = torch.zeros(
|
| batch_size,
|
| self.d_model * self.expand,
|
| self.d_state,
|
| device=self.dt_proj.weight.device,
|
| dtype=self.dt_proj.weight.dtype,
|
|
|
| )
|
| inference_params.key_value_memory_dict[self.layer_idx] = (conv_state, ssm_state)
|
| else:
|
| conv_state, ssm_state = inference_params.key_value_memory_dict[self.layer_idx]
|
|
|
| if initialize_states:
|
| conv_state.zero_()
|
| ssm_state.zero_()
|
| return conv_state, ssm_state
|
|
|
|
|
| class Block(nn.Module):
|
| def __init__(
|
| self, dim, mixer_cls, norm_cls=nn.LayerNorm, fused_add_norm=False, residual_in_fp32=False
|
| ):
|
| """
|
| Simple block wrapping a mixer class with LayerNorm/RMSNorm and residual connection"
|
|
|
| This Block has a slightly different structure compared to a regular
|
| prenorm Transformer block.
|
| The standard block is: LN -> MHA/MLP -> Add.
|
| [Ref: https://arxiv.org/abs/2002.04745]
|
| Here we have: Add -> LN -> Mixer, returning both
|
| the hidden_states (output of the mixer) and the residual.
|
| This is purely for performance reasons, as we can fuse add and LayerNorm.
|
| The residual needs to be provided (except for the very first block).
|
| """
|
| super().__init__()
|
| self.residual_in_fp32 = residual_in_fp32
|
| self.fused_add_norm = fused_add_norm
|
| self.mixer = mixer_cls(dim)
|
| self.norm = norm_cls(dim)
|
| if self.fused_add_norm:
|
| assert RMSNorm is not None, "RMSNorm import fails"
|
| assert isinstance(
|
| self.norm, (nn.LayerNorm, RMSNorm)
|
| ), "Only LayerNorm and RMSNorm are supported for fused_add_norm"
|
|
|
| def forward(
|
| self, hidden_states: Tensor, residual: Optional[Tensor] = None, inference_params=None
|
| ):
|
| r"""Pass the input through the encoder layer.
|
|
|
| Args:
|
| hidden_states: the sequence to the encoder layer (required).
|
| residual: hidden_states = Mixer(LN(residual))
|
| """
|
| if not self.fused_add_norm:
|
| residual = (hidden_states + residual) if residual is not None else hidden_states
|
| hidden_states = self.norm(residual.to(dtype=self.norm.weight.dtype))
|
| if self.residual_in_fp32:
|
| residual = residual.to(torch.float32)
|
| else:
|
| fused_add_norm_fn = rms_norm_fn if isinstance(self.norm, RMSNorm) else layer_norm_fn
|
| hidden_states, residual = fused_add_norm_fn(
|
| hidden_states,
|
| self.norm.weight,
|
| self.norm.bias,
|
| residual=residual,
|
| prenorm=True,
|
| residual_in_fp32=self.residual_in_fp32,
|
| eps=self.norm.eps,
|
| )
|
| hidden_states = self.mixer(hidden_states, inference_params=inference_params)
|
| return hidden_states, residual
|
|
|
| def allocate_inference_cache(self, batch_size, max_seqlen, dtype=None, **kwargs):
|
| return self.mixer.allocate_inference_cache(batch_size, max_seqlen, dtype=dtype, **kwargs) |