| |
| |
| |
|
|
| |
| |
|
|
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| from typing import Tuple |
|
|
| import torch |
| from torch import Tensor |
| import torch.nn as nn |
| import torch.nn.functional as F |
|
|
| from transformers import PreTrainedModel, GenerationMixin |
| from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast, MaskedLMOutput |
| from transformers.cache_utils import Cache, DynamicCache |
|
|
| from rotary_embedding_torch import RotaryEmbedding |
| from .config import TransformerConfig |
| from typing import Optional |
| |
|
|
| class Residual(nn.Module): |
| def __init__(self): |
| super().__init__() |
|
|
| def forward(self, x: Tensor, delta: Tensor): |
| return x + delta |
|
|
| |
|
|
| class MLP(nn.Module): |
| def __init__( |
| self, |
| hidden_size: int, |
| intermediate_size: int |
| ): |
| super().__init__() |
| |
| self.fc_up = nn.Linear(hidden_size, intermediate_size) |
| self.activation = nn.GELU() |
| self.fc_down = nn.Linear(intermediate_size, hidden_size) |
|
|
| def forward(self, x: Tensor): |
| return self.fc_down(self.activation(self.fc_up(x))) |
|
|
| |
|
|
| class MHAttention(nn.Module): |
|
|
| def __init__( |
| self, |
| hidden_size: int, |
| num_attention_heads: int, |
| use_causal_attention: bool = True, |
| layer_idx: int | None = None |
| ): |
| super().__init__() |
| |
| self.hidden_size = hidden_size |
| self.num_attention_heads = num_attention_heads |
| self.head_dim = hidden_size // num_attention_heads |
|
|
| assert self.head_dim * self.num_attention_heads == self.hidden_size |
|
|
| self.use_causal_attention = use_causal_attention |
| self.layer_idx = layer_idx |
| |
| self.q_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False) |
| self.k_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False) |
| self.v_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=True) |
| self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=True) |
|
|
| self.rotary_emb = RotaryEmbedding(dim=self.head_dim) |
| self.scale = self.head_dim ** -0.5 |
|
|
| def forward( |
| self, |
| q: Tensor, |
| k: Tensor | None = None, |
| v: Tensor | None = None, |
| attention_mask: Tensor | None = None, |
| past_key_values: Cache | None = None |
| ): |
| B, T, _ = q.size() |
|
|
| if k is None: |
| k = q |
| if v is None: |
| v = q |
|
|
| q = self.q_proj(q) |
| k = self.k_proj(k) |
| v = self.v_proj(v) |
|
|
| q = q.view(B, T, self.num_attention_heads, self.head_dim).transpose(1, 2) |
| k = k.view(B, T, self.num_attention_heads, self.head_dim).transpose(1, 2) |
| v = v.view(B, T, self.num_attention_heads, self.head_dim).transpose(1, 2) |
|
|
| if past_key_values is None: |
| |
| q = self.rotary_emb.rotate_queries_or_keys(q) |
| k = self.rotary_emb.rotate_queries_or_keys(k) |
|
|
| else: |
| |
| cache_position = past_key_values.get_seq_length(self.layer_idx) |
| |
| q = self.rotary_emb.rotate_queries_or_keys(q, offset=cache_position) |
| k = self.rotary_emb.rotate_queries_or_keys(k, offset=cache_position) |
| |
| k, v = past_key_values.update(k, v, self.layer_idx) |
|
|
| is_causal = self.use_causal_attention and attention_mask is None |
| attn_output = F.scaled_dot_product_attention(q, k, v, attn_mask=attention_mask, scale=self.scale, is_causal=is_causal) |
|
|
| attn_output = attn_output.transpose(1, 2).contiguous().view(B, T, self.hidden_size) |
| out = self.o_proj(attn_output) |
| |
| return out |
|
|
| |
|
|
| class TransformerBlock(nn.Module): |
|
|
| def __init__( |
| self, |
| config: TransformerConfig, |
| layer_idx: int = None |
| ): |
| super().__init__() |
| |
| self.attn = MHAttention( |
| hidden_size=config.hidden_size, |
| num_attention_heads=config.num_attention_heads, |
| use_causal_attention=config.use_causal_attention, |
| layer_idx=layer_idx, |
| ) |
| |
| self.mlp = MLP( |
| config.hidden_size, |
| config.intermediate_size |
| ) |
| |
| self.norm_attn = nn.LayerNorm(config.hidden_size) |
| self.norm_mlp = nn.LayerNorm(config.hidden_size) |
|
|
| self.resid_attn = Residual() |
| self.resid_mlp = Residual() |
|
|
| def forward( |
| self, |
| x: Tensor, |
| attention_mask: Tensor | None = None, |
| past_key_values: Cache | None = None |
| ): |
|
|
| attn_out = self.attn(self.norm_attn(x), attention_mask=attention_mask, past_key_values=past_key_values) |
| x = self.resid_attn(x, attn_out) |
|
|
| mlp_out = self.mlp(self.norm_mlp(x)) |
| x = self.resid_mlp(x, mlp_out) |
|
|
| return x |
|
|
| |
|
|
| class TransformerPreTrainedModel(PreTrainedModel): |
| |
| config_class = TransformerConfig |
| base_model_prefix = "model" |
| _no_split_modules = ["TransformerBlock"] |
| _skip_keys_device_placement = ["past_key_values"] |
| _supports_flash_attn_2 = True |
| _supports_cache_class = True |
|
|
| |
| def _init_weights(self, module): |
| std = self.config.initializer_range |
| if isinstance(module, nn.Linear): |
| module.weight.data.normal_(mean=0.0, std=std) |
| if module.bias is not None: |
| module.bias.data.zero_() |
| elif isinstance(module, nn.Embedding): |
| module.weight.data.normal_(mean=0.0, std=std) |
| if module.padding_idx is not None: |
| module.weight.data[module.padding_idx].zero_() |
| def calculate_loss(self, logits, target_tokens, l1_loss_lambda=None): |
| loss = F.cross_entropy( |
| logits.reshape(-1, logits.size(-1)), |
| target_tokens.reshape(-1), |
| reduction='mean' |
| ) |
| return loss |
| class TransformerModel(TransformerPreTrainedModel): |
| |
| def __init__( |
| self, |
| config: TransformerConfig |
| ): |
| super().__init__(config) |
|
|
| self.config = config |
| self.embedding = nn.Embedding(config.vocab_size, config.hidden_size) |
|
|
| self.blocks = nn.ModuleList([TransformerBlock(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]) |
| self.norm_out = nn.LayerNorm(config.hidden_size) |
|
|
| self.post_init() |
|
|
| def _prepare_attention_mask( |
| self, |
| x: Tensor, |
| attention_mask: Tensor | None = None, |
| past_key_values: Cache | None = None, |
| use_causal_attention: bool = True |
| ): |
| |
| device = x.device |
| B = x.shape[0] |
| T = x.shape[1] |
|
|
| T_past = past_key_values.get_seq_length() if past_key_values is not None else 0 |
| T_total = T + T_past |
|
|
| if use_causal_attention: |
| causal_mask = ~torch.triu( |
| torch.ones((T, T_total), dtype=torch.bool, device=device), |
| diagonal=(1 + T_past) |
| ).unsqueeze(0).unsqueeze(0) |
| |
| if attention_mask is not None: |
| attn_len = attention_mask.shape[-1] |
|
|
| if attn_len < T_total: |
| pad = torch.ones(B, T_past, device=device, dtype=attention_mask.dtype) |
| attention_mask = torch.cat([pad, attention_mask], dim=-1) |
| elif attn_len > T_total: |
| attention_mask = attention_mask[:, -T_total:] |
| |
| expanded_mask = (attention_mask == 1).view(B, 1, 1, T_total) |
| |
| if use_causal_attention and attention_mask is not None: |
| return causal_mask & expanded_mask |
| elif use_causal_attention: |
| return causal_mask |
| elif attention_mask is not None: |
| return expanded_mask |
| else: |
| return torch.ones((1, 1, T, T_total), dtype=torch.bool, device=device) |
| |
| def forward( |
| self, |
| input_ids: Tensor | None = None, |
| attention_mask: Tensor | None = None, |
| inputs_embeds: Tensor | None = None, |
| past_key_values = None, |
| use_cache: bool | None = None, |
| output_hidden_states: bool | None = None, |
| return_dict: bool | None = None, |
| **kwargs, |
| ): |
| |
| use_cache = use_cache if use_cache is not None else self.config.use_cache |
| output_hidden_states = output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states |
| return_dict = return_dict if return_dict is not None else self.config.use_return_dict |
|
|
| assert not (input_ids is not None and inputs_embeds is not None), "You cannot specify both input_ids and inputs_embeds" |
| assert not (input_ids is None and inputs_embeds is None), "You must specify either input_ids or inputs_embeds" |
|
|
| x = self.embedding(input_ids) if input_ids is not None else inputs_embeds |
|
|
| B, T, _ = x.shape |
| device = x.device |
|
|
| if not use_cache: |
| past_key_values=None |
| elif past_key_values is None: |
| past_key_values = DynamicCache() |
|
|
| |
| if attention_mask is not None or past_key_values is not None: |
| attention_mask = self._prepare_attention_mask(x, attention_mask=attention_mask, use_causal_attention=self.config.use_causal_attention, past_key_values=past_key_values) |
|
|
| hidden_states = [] if output_hidden_states else None |
|
|
| for block in self.blocks: |
| |
| x = block(x, attention_mask=attention_mask, past_key_values=past_key_values) |
| |
| if output_hidden_states: |
| hidden_states.append(x) |
|
|
| if hidden_states is not None: |
| hidden_states = tuple(hidden_states) |
|
|
| x = self.norm_out(x) |
|
|
| if return_dict: |
| return BaseModelOutputWithPast( |
| last_hidden_state=x, |
| past_key_values=past_key_values, |
| hidden_states=hidden_states |
| ) |
|
|
| return x, past_key_values, hidden_states |
|
|
| |
|
|
| class TransformerForCausalLM(GenerationMixin, TransformerPreTrainedModel): |
| |
| accepts_loss_kwargs = False |
|
|
| def __init__( |
| self, |
| config: TransformerConfig |
| ): |
| super().__init__(config) |
| |
| self.model = TransformerModel(config) |
| self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) |
|
|
| if config.tie_word_embeddings: |
| self.tie_weights() |
| self._dynamic_tied_weights_keys = {"lm_head.weight": "model.embedding.weight"} |
|
|
| self.post_init() |
|
|
| def get_input_embeddings(self): |
| return self.model.embedding |
|
|
| def set_input_embeddings(self, new_embeddings): |
| self.model.embedding = new_embeddings |
|
|
| def get_output_embeddings(self): |
| return self.lm_head |
|
|
| def set_output_embeddings(self, new_embeddings): |
| self.lm_head = new_embeddings |
|
|
| def tie_weights(self): |
| self._tie_or_clone_weights(self.lm_head, self.get_input_embeddings()) |
|
|
| def forward( |
| self, |
| input_ids: Tensor | None = None, |
| attention_mask: Tensor | None = None, |
| past_key_values = None, |
| inputs_embeds: Tensor | None = None, |
| labels: Tensor | None = None, |
| use_cache: bool | None = None, |
| output_hidden_states: bool | None = None, |
| return_dict: bool | None = None, |
| **kwargs, |
| ): |
|
|
| if labels is not None: |
| return_dict = True |
| else: |
| return_dict = return_dict if return_dict is not None else self.config.use_return_dict |
| |
| model_output = self.model( |
| input_ids=input_ids, |
| attention_mask=attention_mask, |
| inputs_embeds=inputs_embeds, |
| past_key_values=past_key_values, |
| use_cache=use_cache, |
| output_hidden_states=output_hidden_states |
| ) |
|
|
| logits = self.lm_head(model_output[0]) |
|
|
| 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), |
| ignore_index=self.config.pad_token_id if self.config.pad_token_id is not None else -100 |
| ) |
|
|
| if not return_dict: |
| output = (logits,) + model_output[1:] |
| return ((loss,) + output) if loss is not None else output |
|
|
| return CausalLMOutputWithPast( |
| loss=loss, |
| logits=logits, |
| past_key_values=model_output.past_key_values, |
| hidden_states=model_output.hidden_states |
| ) |
|
|
| def _prepare_inputs_for_generation( |
| self, |
| input_ids: Tensor, |
| past_key_values: Cache | None = None, |
| attention_mask: Tensor | None = None, |
| **kwargs |
| ): |
| if past_key_values is not None: |
| input_ids = input_ids[:, -1:] |
|
|
| model_inputs = {"input_ids": input_ids, "past_key_values": past_key_values, "use_cache": True} |
|
|
| if attention_mask is not None: |
| model_inputs["attention_mask"] = attention_mask |
|
|
| for key, value in kwargs.items(): |
| model_inputs[key] = value |
|
|
| return model_inputs |
| |
| def _reorder_cache(self, past_key_values: Cache, beam_idx: Tensor): |
| return past_key_values.reorder_cache(beam_idx) |
| |
|
|
| @torch.no_grad() |
| def generate( |
| self, |
| input_ids, |
| max_generation_length, |
| tokenizer, |
| temperature=1.0, |
| top_p=0.9, |
| return_generation_only=False |
| ): |
|
|
| self.eval() |
|
|
| batch_size = input_ids.size(0) |
| device = input_ids.device |
|
|
| generated = input_ids.clone() |
| finished = torch.zeros(batch_size, dtype=torch.bool, device=device) |
|
|
| for _ in range(max_generation_length): |
| |
| |
| logits = self(generated).logits[:, -1, :] / temperature |
| probs = F.softmax(logits, dim=-1) |
|
|
| sorted_probs, sorted_indices = torch.sort(probs, dim=-1, descending=True) |
| cumulative_probs = torch.cumsum(sorted_probs, dim=-1) |
|
|
| cutoff_mask = cumulative_probs > top_p |
| cutoff_mask[:, 1:] = cutoff_mask[:, :-1].clone() |
| cutoff_mask[:, 0] = False |
|
|
| sorted_probs = sorted_probs.masked_fill(cutoff_mask, 0.0) |
| normalized_probs = sorted_probs / sorted_probs.sum(dim=-1, keepdim=True) |
|
|
| probs = torch.zeros_like(normalized_probs).scatter(-1, sorted_indices, normalized_probs) |
|
|
| next_token = torch.multinomial(probs, num_samples=1).squeeze(-1) |
| next_token = torch.where(finished, torch.full_like(next_token, tokenizer.pad_token_id), next_token) |
|
|
| generated = torch.cat([generated, next_token.unsqueeze(1)], dim=1) |
|
|
| finished |= next_token == tokenizer.eos_token_id |
|
|
| if finished.all(): |
| break |
|
|
| if return_generation_only: |
| return generated[:, input_ids.size(1):] |
| else: |
| return generated |
| class TransformerForMaskedLM(TransformerPreTrainedModel): |
| |
| accepts_loss_kwargs = False |
|
|
| def __init__( |
| self, |
| config: TransformerConfig |
| ): |
| super().__init__(config) |
| |
| assert not config.use_causal_attention, "TransformerForMaskedLM requires use_causal_attention=False" |
| assert not config.use_cache, "TransformerForMaskedLM requires use_cache=False (caching not supported for bidirectional models)" |
| |
| self.model = TransformerModel(config) |
| self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) |
|
|
| if config.tie_word_embeddings: |
| self.tie_weights() |
| self._dynamic_tied_weights_keys = {"lm_head.weight": "model.embedding.weight"} |
|
|
| self.post_init() |
|
|
| def get_input_embeddings(self): |
| return self.model.embedding |
|
|
| def set_input_embeddings(self, new_embeddings): |
| self.model.embedding = new_embeddings |
|
|
| def get_output_embeddings(self): |
| return self.lm_head |
|
|
| def set_output_embeddings(self, new_embeddings): |
| self.lm_head = new_embeddings |
|
|
| def tie_weights(self): |
| self._tie_or_clone_weights(self.lm_head, self.get_input_embeddings()) |
|
|
| def forward( |
| self, |
| input_ids: Tensor | None = None, |
| attention_mask: Tensor | None = None, |
| inputs_embeds: Tensor | None = None, |
| labels: Tensor | None = None, |
| output_hidden_states: bool | None = None, |
| return_dict: bool | None = None, |
| **kwargs, |
| ): |
|
|
| if labels is not None: |
| return_dict = True |
| else: |
| return_dict = return_dict if return_dict is not None else self.config.use_return_dict |
| |
| model_output = self.model( |
| input_ids=input_ids, |
| attention_mask=attention_mask, |
| inputs_embeds=inputs_embeds, |
| past_key_values=None, |
| use_cache=False, |
| output_hidden_states=output_hidden_states |
| ) |
|
|
| logits = self.lm_head(model_output[0]) |
|
|
| loss = None |
| if labels is not None: |
| |
| loss = F.cross_entropy( |
| logits.view(-1, logits.size(-1)), |
| labels.view(-1), |
| ignore_index=self.config.pad_token_id if self.config.pad_token_id is not None else -100 |
| ) |
|
|
| if not return_dict: |
| output = (logits,) + model_output[1:] |
| return ((loss,) + output) if loss is not None else output |
|
|
| return MaskedLMOutput( |
| loss=loss, |
| logits=logits, |
| hidden_states=model_output.hidden_states |
| ) |
|
|
| |
| |
| from transformers.modeling_outputs import SequenceClassifierOutput |
| |
| |
|
|
| class TransformerForSequenceClassification(TransformerPreTrainedModel): |
| """ |
| Minimal SequenceClassification wrapper around your TransformerModel. |
| Compatible with Trainer/from_pretrained/save_pretrained. |
| """ |
| def __init__(self, config: TransformerConfig): |
| super().__init__(config) |
| self.num_labels = config.num_labels if hasattr(config, "num_labels") else 2 |
| self.model = TransformerModel(config) |
| self.classifier = nn.Linear(config.hidden_size, self.num_labels) |
| self.post_init() |
|
|
| def forward( |
| self, |
| input_ids: Optional[Tensor] = None, |
| attention_mask: Optional[Tensor] = None, |
| inputs_embeds: Optional[Tensor] = None, |
| labels: Optional[Tensor] = None, |
| output_hidden_states: Optional[bool] = None, |
| return_dict: Optional[bool] = None, |
| cls_token_at_end: bool = False, |
| debug: bool = False, |
| **kwargs, |
| ): |
| """ |
| Robust forward that ALWAYS uses last_hidden_state for pooling (not model_out.logits). |
| - If cls_token_at_end == False: pooled = last_hidden[:, 0, :] |
| - If cls_token_at_end == True: pooled = last_hidden at last non-pad index (attention_mask) |
| """ |
|
|
| return_dict = return_dict if return_dict is not None else getattr(self.config, "use_return_dict", True) |
|
|
| |
| model_out = self.model( |
| input_ids=input_ids, |
| attention_mask=attention_mask, |
| inputs_embeds=inputs_embeds, |
| past_key_values=None, |
| use_cache=False, |
| output_hidden_states=output_hidden_states, |
| return_dict=return_dict, |
| **kwargs, |
| ) |
|
|
| |
| if isinstance(model_out, tuple): |
| last_hidden = model_out[0] |
| else: |
| last_hidden = getattr(model_out, "last_hidden_state", None) |
| if last_hidden is None: |
| |
| hidden_states = getattr(model_out, "hidden_states", None) |
| if hidden_states is not None: |
| last_hidden = hidden_states[-1] |
|
|
| if last_hidden is None: |
| raise RuntimeError("Backbone did not return last_hidden_state or hidden_states") |
| |
| |
| if not cls_token_at_end: |
| |
| pooled = last_hidden[:, 0, :] |
| if debug: |
| print("[forward] pooling: CLS at start (index 0). pooled.shape =", pooled.shape) |
| else: |
| |
| if attention_mask is None: |
| |
| pooled = last_hidden[:, -1, :] |
| if debug: |
| print("[forward] pooling: no attention_mask, using last token. pooled.shape =", pooled.shape) |
| else: |
| am = attention_mask |
| if am.dtype != torch.long and am.dtype != torch.int: |
| am = am.long() |
| lengths = am.sum(dim=1).clamp(min=1) |
| idx = (lengths - 1).unsqueeze(1).unsqueeze(-1) |
| idx = idx.expand(-1, -1, last_hidden.size(-1)) |
| pooled = last_hidden.gather(1, idx).squeeze(1) |
| if debug: |
| print("[forward] pooling: CLS at end via attention_mask. pooled.shape =", pooled.shape) |
| |
| |
| |
| |
| logits = self.classifier(pooled) |
|
|
| loss = None |
| if labels is not None: |
| if self.num_labels == 1: |
| |
| loss_fct = nn.MSELoss() |
| loss = loss_fct(logits.view(-1), labels.view(-1)) |
| else: |
| |
| loss_fct = nn.CrossEntropyLoss() |
| loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1).long()) |
|
|
| if not return_dict: |
| output = (logits,) + (model_out[1:] if isinstance(model_out, tuple) else ()) |
| return ((loss,) + output) if loss is not None else output |
|
|
| |
| return SequenceClassifierOutput( |
| loss=loss, |
| logits=logits, |
| hidden_states=last_hidden, |
| attentions=getattr(model_out, "attentions", None), |
| ) |
|
|
|
|