|
|
|
|
| import math
|
| from functools import partial
|
| import json
|
| import os
|
| import copy
|
|
|
| from collections import namedtuple
|
|
|
| import torch
|
| import torch.nn as nn
|
|
|
| from .config_mamba import MambaConfig
|
| from ..modules.mamba_simple import Mamba
|
| from ..modules.mamba2 import Mamba2
|
| from ..modules.mha import MHA
|
| from ..modules.mlp import GatedMLP
|
| from ..modules.block import Block
|
| from ..utils.generation import GenerationMixin
|
| from ..utils.hf import load_config_hf, load_state_dict_hf
|
|
|
| from ..ops.triton.layer_norm import RMSNorm, layer_norm_fn, rms_norm_fn
|
|
|
|
|
|
|
| def create_block(
|
| d_model,
|
| d_intermediate,
|
| ssm_cfg=None,
|
| attn_layer_idx=None,
|
| attn_cfg=None,
|
| norm_epsilon=1e-5,
|
| rms_norm=False,
|
| residual_in_fp32=False,
|
| fused_add_norm=False,
|
| layer_idx=None,
|
| device=None,
|
| dtype=None,
|
| ):
|
| if ssm_cfg is None:
|
| ssm_cfg = {}
|
| if attn_layer_idx is None:
|
| attn_layer_idx = []
|
| if attn_cfg is None:
|
| attn_cfg = {}
|
| factory_kwargs = {"device": device, "dtype": dtype}
|
| if layer_idx not in attn_layer_idx:
|
|
|
| ssm_cfg = copy.deepcopy(ssm_cfg) if ssm_cfg is not None else {}
|
| ssm_layer = ssm_cfg.pop("layer", "Mamba1")
|
| if ssm_layer not in ["Mamba1", "Mamba2"]:
|
| raise ValueError(f"Invalid ssm_layer: {ssm_layer}, only support Mamba1 and Mamba2")
|
| mixer_cls = partial(
|
| Mamba2 if ssm_layer == "Mamba2" else Mamba,
|
| layer_idx=layer_idx,
|
| **ssm_cfg,
|
| **factory_kwargs
|
| )
|
| else:
|
| mixer_cls = partial(MHA, layer_idx=layer_idx, **attn_cfg, **factory_kwargs)
|
| norm_cls = partial(
|
| nn.LayerNorm if not rms_norm else RMSNorm, eps=norm_epsilon, **factory_kwargs
|
| )
|
| if d_intermediate == 0:
|
| mlp_cls = nn.Identity
|
| else:
|
| mlp_cls = partial(
|
| GatedMLP, hidden_features=d_intermediate, out_features=d_model, **factory_kwargs
|
| )
|
| block = Block(
|
| d_model,
|
| mixer_cls,
|
| mlp_cls,
|
| norm_cls=norm_cls,
|
| fused_add_norm=fused_add_norm,
|
| residual_in_fp32=residual_in_fp32,
|
| )
|
| block.layer_idx = layer_idx
|
| return block
|
|
|
|
|
|
|
| def _init_weights(
|
| module,
|
| n_layer,
|
| initializer_range=0.02,
|
| rescale_prenorm_residual=True,
|
| n_residuals_per_layer=1,
|
| ):
|
| if isinstance(module, nn.Linear):
|
| if module.bias is not None:
|
| if not getattr(module.bias, "_no_reinit", False):
|
| nn.init.zeros_(module.bias)
|
| elif isinstance(module, nn.Embedding):
|
| nn.init.normal_(module.weight, std=initializer_range)
|
|
|
| if rescale_prenorm_residual:
|
|
|
|
|
|
|
|
|
|
|
|
|
| for name, p in module.named_parameters():
|
| if name in ["out_proj.weight", "fc2.weight"]:
|
|
|
|
|
|
|
|
|
| nn.init.kaiming_uniform_(p, a=math.sqrt(5))
|
| with torch.no_grad():
|
| p /= math.sqrt(n_residuals_per_layer * n_layer)
|
|
|
|
|
| class MixerModel(nn.Module):
|
| def __init__(
|
| self,
|
| d_model: int,
|
| n_layer: int,
|
| d_intermediate: int,
|
| vocab_size: int,
|
| ssm_cfg=None,
|
| attn_layer_idx=None,
|
| attn_cfg=None,
|
| norm_epsilon: float = 1e-5,
|
| rms_norm: bool = False,
|
| initializer_cfg=None,
|
| fused_add_norm=False,
|
| residual_in_fp32=False,
|
| device=None,
|
| dtype=None,
|
| ) -> None:
|
| factory_kwargs = {"device": device, "dtype": dtype}
|
| super().__init__()
|
| self.residual_in_fp32 = residual_in_fp32
|
|
|
| self.embedding = nn.Embedding(vocab_size, d_model, **factory_kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
| self.fused_add_norm = fused_add_norm
|
| if self.fused_add_norm:
|
| if layer_norm_fn is None or rms_norm_fn is None:
|
| raise ImportError("Failed to import Triton LayerNorm / RMSNorm kernels")
|
|
|
| self.layers = nn.ModuleList(
|
| [
|
| create_block(
|
| d_model,
|
| d_intermediate=d_intermediate,
|
| ssm_cfg=ssm_cfg,
|
| attn_layer_idx=attn_layer_idx,
|
| attn_cfg=attn_cfg,
|
| norm_epsilon=norm_epsilon,
|
| rms_norm=rms_norm,
|
| residual_in_fp32=residual_in_fp32,
|
| fused_add_norm=fused_add_norm,
|
| layer_idx=i,
|
| **factory_kwargs,
|
| )
|
| for i in range(n_layer)
|
| ]
|
| )
|
|
|
| self.norm_f = (nn.LayerNorm if not rms_norm else RMSNorm)(
|
| d_model, eps=norm_epsilon, **factory_kwargs
|
| )
|
|
|
| self.apply(
|
| partial(
|
| _init_weights,
|
| n_layer=n_layer,
|
| **(initializer_cfg if initializer_cfg is not None else {}),
|
| n_residuals_per_layer=1 if d_intermediate == 0 else 2,
|
| )
|
| )
|
|
|
| def allocate_inference_cache(self, batch_size, max_seqlen, dtype=None, **kwargs):
|
| return {
|
| i: layer.allocate_inference_cache(batch_size, max_seqlen, dtype=dtype, **kwargs)
|
| for i, layer in enumerate(self.layers)
|
| }
|
|
|
| def forward(self, input_ids, inference_params=None, **mixer_kwargs):
|
| hidden_states = self.embedding(input_ids)
|
| residual = None
|
| for layer in self.layers:
|
| hidden_states, residual = layer(
|
| hidden_states, residual, inference_params=inference_params
|
| )
|
| if not self.fused_add_norm:
|
| residual = (hidden_states + residual) if residual is not None else hidden_states
|
| hidden_states = self.norm_f(residual.to(dtype=self.norm_f.weight.dtype))
|
| else:
|
|
|
| hidden_states = layer_norm_fn(
|
| hidden_states,
|
| self.norm_f.weight,
|
| self.norm_f.bias,
|
| eps=self.norm_f.eps,
|
| residual=residual,
|
| prenorm=False,
|
| residual_in_fp32=self.residual_in_fp32,
|
| is_rms_norm=isinstance(self.norm_f, RMSNorm)
|
| )
|
| return hidden_states
|
|
|
|
|
| class MambaLMHeadModel(nn.Module, GenerationMixin):
|
|
|
| def __init__(
|
| self,
|
| config: MambaConfig,
|
| initializer_cfg=None,
|
| device=None,
|
| dtype=None,
|
| ) -> None:
|
| self.config = config
|
| d_model = config.d_model
|
| n_layer = config.n_layer
|
| d_intermediate = config.d_intermediate
|
| vocab_size = config.vocab_size
|
| ssm_cfg = config.ssm_cfg
|
| attn_layer_idx = config.attn_layer_idx
|
| attn_cfg = config.attn_cfg
|
| rms_norm = config.rms_norm
|
| residual_in_fp32 = config.residual_in_fp32
|
| fused_add_norm = config.fused_add_norm
|
| pad_vocab_size_multiple = config.pad_vocab_size_multiple
|
| factory_kwargs = {"device": device, "dtype": dtype}
|
|
|
| super().__init__()
|
| if vocab_size % pad_vocab_size_multiple != 0:
|
| vocab_size += pad_vocab_size_multiple - (vocab_size % pad_vocab_size_multiple)
|
| self.backbone = MixerModel(
|
| d_model=d_model,
|
| n_layer=n_layer,
|
| d_intermediate=d_intermediate,
|
| vocab_size=vocab_size,
|
| ssm_cfg=ssm_cfg,
|
| attn_layer_idx=attn_layer_idx,
|
| attn_cfg=attn_cfg,
|
| rms_norm=rms_norm,
|
| initializer_cfg=initializer_cfg,
|
| fused_add_norm=fused_add_norm,
|
| residual_in_fp32=residual_in_fp32,
|
| **factory_kwargs,
|
| )
|
| self.lm_head = nn.Linear(d_model, vocab_size, bias=False, **factory_kwargs)
|
|
|
|
|
| self.apply(
|
| partial(
|
| _init_weights,
|
| n_layer=n_layer,
|
| **(initializer_cfg if initializer_cfg is not None else {}),
|
| )
|
| )
|
| self.tie_weights()
|
|
|
| def tie_weights(self):
|
| if self.config.tie_embeddings:
|
| self.lm_head.weight = self.backbone.embedding.weight
|
|
|
| def allocate_inference_cache(self, batch_size, max_seqlen, dtype=None, **kwargs):
|
| return self.backbone.allocate_inference_cache(batch_size, max_seqlen, dtype=dtype, **kwargs)
|
|
|
| def forward(self, input_ids, position_ids=None, inference_params=None, num_last_tokens=0, **mixer_kwargs):
|
| """
|
| "position_ids" is just to be compatible with Transformer generation. We don't use it.
|
| num_last_tokens: if > 0, only return the logits for the last n tokens
|
| """
|
| hidden_states = self.backbone(input_ids, inference_params=inference_params, **mixer_kwargs)
|
| if num_last_tokens > 0:
|
| hidden_states = hidden_states[:, -num_last_tokens:]
|
| lm_logits = self.lm_head(hidden_states)
|
| CausalLMOutput = namedtuple("CausalLMOutput", ["logits"])
|
| return CausalLMOutput(logits=lm_logits)
|
|
|
| @classmethod
|
| def from_pretrained(cls, pretrained_model_name, device=None, dtype=None, **kwargs):
|
| config_data = load_config_hf(pretrained_model_name)
|
| config = MambaConfig(**config_data)
|
| model = cls(config, device=device, dtype=dtype, **kwargs)
|
| model.load_state_dict(load_state_dict_hf(pretrained_model_name, device=device, dtype=dtype))
|
| return model
|
|
|
| def save_pretrained(self, save_directory):
|
| """
|
| Minimal implementation of save_pretrained for MambaLMHeadModel.
|
| Save the model and its configuration file to a directory.
|
| """
|
|
|
| os.makedirs(save_directory, exist_ok=True)
|
|
|
|
|
| model_path = os.path.join(save_directory, 'pytorch_model.bin')
|
| torch.save(self.state_dict(), model_path)
|
|
|
|
|
| config_path = os.path.join(save_directory, 'config.json')
|
| with open(config_path, 'w') as f:
|
| json.dump(self.config.__dict__, f, indent=4)
|
|
|