FST_code / src /lmr /models /transformer /transformer_bert_version.py
jasonfan's picture
2026-03-19
3b2d368 verified
Raw
History Blame Contribute Delete
24.3 kB
# import torch
# import torch.nn as nn
# import torch.nn.functional as F
# from lmr.models.lm_base import LMBase
# from .components import TransformerBlock
# class Transformer(LMBase):
# def __init__(self, config, train_mode=False):
# super().__init__()
# self.config = config
# self.train_mode = train_mode
# self.embedding = nn.Embedding(config.vocab_size, config.embed_dim)
# self.transformer_blocks = nn.ModuleList([TransformerBlock(config) for _ in range(config.n_layers)])
# self.ln_out = nn.LayerNorm(config.embed_dim)
# self.lm_head = nn.Linear(config.embed_dim, config.vocab_size, bias=False)
# self.apply(self.init_weights)
# self.lm_head.weight = self.embedding.weight
# def forward(self, input_ids):
# batch_size, seq_len = input_ids.shape
# device = input_ids.device
# x = self.embedding(input_ids)
# for block in self.transformer_blocks:
# x = block(x)
# x = self.ln_out(x)
# logits = self.lm_head(x)
# return logits
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
# === Util ===
class Residual(nn.Module):
def __init__(self):
super().__init__()
def forward(self, x: Tensor, delta: Tensor):
return x + delta
# === MLP ===
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)))
# === Attention ===
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
# === Blocks ===
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
# === Base Model ===
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
# Initialization taken from Deepseek and Falcon
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) # Fixed: ones instead of zeros
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: # Added: handle non-causal with custom mask
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()
# Note that we must use an attention mask when caching- otherwise, SDPA uses is_casual and breaks
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
# === Applied Models ===
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"} # Avoids safetensor naming issues
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):
# import pdb
# pdb.set_trace()
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"} # Avoids safetensor naming issues
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
)
# import torch
# import torch.nn as nn
from transformers.modeling_outputs import SequenceClassifierOutput
# from .config import TransformerConfig
# from .your_module import TransformerPreTrainedModel, TransformerModel # 依据你的文件结构调整导入
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, # 如果你的包装器/调用者把 CLS 移到末端,传 True
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)
# call backbone
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,
)
# obtain last hidden state robustly (tuple or object)
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:
# fallback: maybe hidden_states provided and last is there
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")
# cls_token_at_end = True
# === Pooling: prefer last-non-pad if cls_token_at_end True, else token 0 ===
if not cls_token_at_end:
# CLS at start (traditional BERT)
pooled = last_hidden[:, 0, :]
if debug:
print("[forward] pooling: CLS at start (index 0). pooled.shape =", pooled.shape)
else:
# CLS placed at last non-pad token: use attention_mask to find last index per sample
if attention_mask is None:
# no attention mask: safe fallback to last token in sequence
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) # (B,)
idx = (lengths - 1).unsqueeze(1).unsqueeze(-1) # (B,1,1)
idx = idx.expand(-1, -1, last_hidden.size(-1)) # (B,1,H)
pooled = last_hidden.gather(1, idx).squeeze(1) # (B,H)
if debug:
print("[forward] pooling: CLS at end via attention_mask. pooled.shape =", pooled.shape)
# import pdb
# pdb.set_trace()
# print(idx)
# compute logits from pooled representation (do NOT use model_out.logits)
logits = self.classifier(pooled)
loss = None
if labels is not None:
if self.num_labels == 1:
# regression
loss_fct = nn.MSELoss()
loss = loss_fct(logits.view(-1), labels.view(-1))
else:
# classification
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: use last_hidden as hidden_states for downstream checks
return SequenceClassifierOutput(
loss=loss,
logits=logits,
hidden_states=last_hidden,
attentions=getattr(model_out, "attentions", None),
)