| import math |
| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
| from transformers import PretrainedConfig, PreTrainedModel |
| from transformers.modeling_outputs import CausalLMOutput |
|
|
|
|
| def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor: |
| """ |
| This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). |
| The hidden states go from (batch, num_key_value_heads, seqlen, head_dim) |
| to (batch, num_attention_heads, seqlen, head_dim) |
| """ |
| |
| batch, num_key_value_heads, slen, head_dim = hidden_states.shape |
|
|
| |
| if n_rep == 1: |
| return hidden_states |
|
|
| |
| |
| |
| |
| |
| hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim) |
|
|
| |
| |
| |
| return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim) |
|
|
|
|
| class Rotary(nn.Module): |
| def __init__(self, dim: int, max_seq_len: int): |
| super().__init__() |
| angular_freq = (1 / 10000) ** torch.linspace(0, 1, steps=dim//4, dtype=torch.float32) |
| angular_freq = torch.cat([angular_freq, angular_freq.new_zeros(dim//4)]) |
| t = torch.arange(max_seq_len, dtype=torch.float32) |
| theta = torch.einsum("i,j -> ij", t, angular_freq) |
| self.register_buffer('cos', theta.cos(), persistent=False) |
| self.register_buffer('sin', theta.sin(), persistent=False) |
|
|
| def forward(self, x_BTHD: torch.Tensor): |
| assert self.cos.size(0) >= x_BTHD.size(-3) |
| cos, sin = self.cos[None, :x_BTHD.size(-3), None, :], self.sin[None, :x_BTHD.size(-3), None, :] |
| x1, x2 = x_BTHD.to(dtype=torch.float32).chunk(2, dim=-1) |
| y1 = x1 * cos + x2 * sin |
| y2 = x1 * (-sin) + x2 * cos |
| return torch.cat((y1, y2), 3).type_as(x_BTHD) |
|
|
|
|
| class Qwen3Attention(nn.Module): |
| def __init__(self, config): |
| super().__init__() |
| self.d_model = config.d_model |
| self.n_heads = config.n_heads |
| self.n_kv_heads = config.n_kv_heads |
| self.n_kv_groups = config.n_kv_groups |
| self.d_k = config.d_k |
|
|
| |
| self.q_proj = nn.Linear(self.d_model, self.n_heads * self.d_k, bias=config.attention_bias) |
| self.k_proj = nn.Linear(self.d_model, self.n_kv_heads * self.d_k, bias=config.attention_bias) |
| self.v_proj = nn.Linear(self.d_model, self.n_kv_heads * self.d_k, bias=config.attention_bias) |
| self.w_o = nn.Linear(self.d_model, self.d_model, bias=False) |
|
|
| |
| self.q_norm = nn.RMSNorm(self.d_k, eps=config.rms_norm_eps) |
| self.k_norm = nn.RMSNorm(self.d_k, eps=config.rms_norm_eps) |
|
|
| self.rotary = Rotary(self.d_k, config.max_seq_len) |
| self.dropout = config.dropout |
|
|
| def forward(self, x): |
| batch_size, seq_len = x.size(0), x.size(1) |
|
|
| |
| q = self.q_proj(x) |
| k = self.k_proj(x) |
| v = self.v_proj(x) |
|
|
| |
| q = q.view(batch_size, seq_len, self.n_heads, self.d_k) |
| k = k.view(batch_size, seq_len, self.n_kv_heads, self.d_k) |
| v = v.view(batch_size, seq_len, self.n_kv_heads, self.d_k) |
|
|
| |
| q = self.q_norm(q) |
| k = self.k_norm(k) |
|
|
| |
| |
| q = self.rotary(q.permute(0, 2, 1, 3)).permute(0, 2, 1, 3) |
| k = self.rotary(k.permute(0, 2, 1, 3)).permute(0, 2, 1, 3) |
|
|
| |
| Q = q.transpose(1, 2) |
| K = k.transpose(1, 2) |
| V = v.transpose(1, 2) |
|
|
| |
| K = repeat_kv(K, self.n_kv_groups) |
| V = repeat_kv(V, self.n_kv_groups) |
|
|
| |
| attn_output = F.scaled_dot_product_attention( |
| Q, K, V, is_causal=True, dropout_p=self.dropout if self.training else 0.0 |
| ) |
|
|
| |
| attn_output = attn_output.transpose(1, 2).contiguous().view(batch_size, seq_len, self.d_model) |
| return self.w_o(attn_output) |
|
|
|
|
| class SwiGLUFeedForward(nn.Module): |
| def __init__(self, d_model: int, d_ff: int, dropout: float = 0.1): |
| super().__init__() |
| self.gate_proj = nn.Linear(d_model, d_ff, bias=False) |
| self.down_proj = nn.Linear(d_ff, d_model, bias=False) |
| self.up_proj = nn.Linear(d_model, d_ff, bias=False) |
| self.dropout = nn.Dropout(dropout) |
|
|
| def forward(self, x): |
| |
| |
| activated_x = F.silu(self.gate_proj(x)) * self.up_proj(x) |
| return self.down_proj(self.dropout(activated_x)) |
|
|
|
|
| class TransformerBlock(nn.Module): |
| def __init__(self, config): |
| super().__init__() |
| self.attention = Qwen3Attention(config) |
| self.feed_forward = SwiGLUFeedForward(config.d_model, config.d_ff, config.dropout) |
| self.norm1 = nn.RMSNorm(config.d_model, eps=config.rms_norm_eps) |
| self.norm2 = nn.RMSNorm(config.d_model, eps=config.rms_norm_eps) |
| self.dropout = nn.Dropout(config.dropout) |
|
|
| def forward(self, x): |
| attn_out = self.attention(self.norm1(x)) |
| x = x + self.dropout(attn_out) |
| ff_out = self.feed_forward(self.norm2(x)) |
| x = x + self.dropout(ff_out) |
| return x |
|
|
|
|
| from transformers import PreTrainedModel, GenerationMixin |
| from transformers.modeling_outputs import CausalLMOutput |
| import torch.nn as nn |
| import torch.nn.functional as F |
|
|
| class MinimalLLMForCausalLM(PreTrainedModel, GenerationMixin): |
| config_class = MinimalLLMConfig |
| base_model_prefix = "model" |
| main_input_name = "input_ids" |
|
|
| def __init__(self, config: MinimalLLMConfig): |
| super().__init__(config) |
|
|
| self.token_embedding = nn.Embedding(config.vocab_size, config.d_model) |
| self.position_dropout = nn.Dropout(config.dropout) |
|
|
| self.transformer_blocks = nn.ModuleList( |
| [TransformerBlock(config) for _ in range(config.n_layers)] |
| ) |
|
|
| self.norm = nn.RMSNorm(config.d_model, eps=config.rms_norm_eps) |
| self.output_dropout = nn.Dropout(config.dropout) |
|
|
| self.lm_head = nn.Linear(config.d_model, config.vocab_size, bias=False) |
| |
|
|
| self.post_init() |
| self.tie_weights() |
|
|
| def get_input_embeddings(self): |
| return self.token_embedding |
|
|
| def set_input_embeddings(self, value): |
| self.token_embedding = value |
| |
| self.tie_weights() |
|
|
| def get_output_embeddings(self): |
| return self.lm_head |
|
|
| def set_output_embeddings(self, new_embeddings): |
| self.lm_head = new_embeddings |
|
|
| |
| |
|
|
| def forward(self, input_ids=None, labels=None, **kwargs): |
| if input_ids is None: |
| raise ValueError("input_ids must be provided") |
|
|
| x = self.token_embedding(input_ids) * (self.config.d_model ** 0.5) |
| x = self.position_dropout(x) |
|
|
| for block in self.transformer_blocks: |
| x = block(x) |
|
|
| x = self.norm(x) |
| x = self.output_dropout(x) |
| logits = self.lm_head(x) |
|
|
| loss = None |
| if labels is not None: |
| |
| shift_logits = logits[:, :-1, :].contiguous() |
| shift_labels = labels[:, 1:].contiguous() |
| loss = F.cross_entropy( |
| shift_logits.view(-1, shift_logits.size(-1)), |
| shift_labels.view(-1), |
| ) |
|
|
| return CausalLMOutput(loss=loss, logits=logits) |
|
|
| def prepare_inputs_for_generation(self, input_ids, **kwargs): |
| |
| return {"input_ids": input_ids} |
|
|