Text Generation
Transformers
Safetensors
English
Dutch
Chinese
hawk_rglru
babylm
babylm-2026
multilingual
hawk
griffin
rg-lru
recurrent-lm
morpiece
cognitively-plausible
custom_code
Instructions to use NeTS-lab/babylm26_multiling_hawk_MoP16K_baseline with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use NeTS-lab/babylm26_multiling_hawk_MoP16K_baseline with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="NeTS-lab/babylm26_multiling_hawk_MoP16K_baseline", trust_remote_code=True)# Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("NeTS-lab/babylm26_multiling_hawk_MoP16K_baseline", trust_remote_code=True, dtype="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use NeTS-lab/babylm26_multiling_hawk_MoP16K_baseline with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "NeTS-lab/babylm26_multiling_hawk_MoP16K_baseline" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "NeTS-lab/babylm26_multiling_hawk_MoP16K_baseline", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/NeTS-lab/babylm26_multiling_hawk_MoP16K_baseline
- SGLang
How to use NeTS-lab/babylm26_multiling_hawk_MoP16K_baseline with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "NeTS-lab/babylm26_multiling_hawk_MoP16K_baseline" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "NeTS-lab/babylm26_multiling_hawk_MoP16K_baseline", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "NeTS-lab/babylm26_multiling_hawk_MoP16K_baseline" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "NeTS-lab/babylm26_multiling_hawk_MoP16K_baseline", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use NeTS-lab/babylm26_multiling_hawk_MoP16K_baseline with Docker Model Runner:
docker model run hf.co/NeTS-lab/babylm26_multiling_hawk_MoP16K_baseline
| #!/usr/bin/env python3 | |
| """ | |
| HF-compatible single-language Hawk / RG-LRU model, for lm-eval. | |
| Self-contained `trust_remote_code` modeling file. The building blocks are the | |
| SAME code used at training time (De et al., 2024, Griffin/Hawk; arXiv:2402.19427), | |
| so the per-language export state_dict maps 1:1 onto this module's parameters | |
| (top-level attribute names wte / layers / norm_f / lm_head match the export keys | |
| exactly -- no renaming, no transpose). Exposes the standard | |
| forward(input_ids, labels=None) -> CausalLMOutputWithPast that lm-eval expects. | |
| Register via config.json: | |
| "model_type": "hawk_rglru", | |
| "architectures": ["HawkForCausalLM"], | |
| "auto_map": { | |
| "AutoConfig": "modeling_hawk.HawkConfig", | |
| "AutoModelForCausalLM": "modeling_hawk.HawkForCausalLM", | |
| "AutoModelForSequenceClassification": "modeling_hawk.HawkForSequenceClassification" | |
| } | |
| """ | |
| from typing import Optional | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from transformers import PreTrainedModel, PretrainedConfig | |
| from transformers.generation import GenerationMixin | |
| from transformers.modeling_outputs import ( | |
| CausalLMOutputWithPast, SequenceClassifierOutput, | |
| ) | |
| class RMSNorm(nn.Module): | |
| def __init__(self, dim: int, eps: float = 1e-6): | |
| super().__init__() | |
| self.weight = nn.Parameter(torch.ones(dim)) | |
| self.eps = eps | |
| def forward(self, x): | |
| norm = torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps) | |
| return self.weight * (x * norm) | |
| class RGLRU(nn.Module): | |
| """Real-Gated Linear Recurrent Unit (De et al., 2024).""" | |
| def __init__(self, width: int, c: float = 8.0): | |
| super().__init__() | |
| self.width = width | |
| self.c = c | |
| self.input_gate = nn.Linear(width, width) | |
| self.recur_gate = nn.Linear(width, width) | |
| lam = torch.empty(width).uniform_(2.197, 6.907) | |
| self.log_lambda = nn.Parameter(lam) | |
| def forward(self, x): # x: (B, T, W) | |
| B, T, W = x.shape | |
| r = torch.sigmoid(self.recur_gate(x)) | |
| i = torch.sigmoid(self.input_gate(x)) | |
| log_a = -F.softplus(-self.log_lambda) | |
| log_a_t = self.c * r * log_a | |
| a_t = torch.exp(log_a_t) | |
| mult = torch.sqrt(torch.clamp(-torch.expm1(2.0 * log_a_t), min=1e-8)) | |
| gated_x = mult * (i * x) | |
| h = torch.zeros(B, W, device=x.device, dtype=x.dtype) | |
| outs = [] | |
| for t in range(T): | |
| h = a_t[:, t] * h + gated_x[:, t] | |
| outs.append(h) | |
| return torch.stack(outs, dim=1) | |
| class RecurrentBlock(nn.Module): | |
| def __init__(self, d_model: int, d_rnn: int, conv_kernel: int = 4, rglru_c: float = 8.0): | |
| super().__init__() | |
| self.conv_kernel = conv_kernel | |
| self.in_gate = nn.Linear(d_model, d_rnn) | |
| self.in_recur = nn.Linear(d_model, d_rnn) | |
| self.conv = nn.Conv1d(d_rnn, d_rnn, conv_kernel, groups=d_rnn, | |
| padding=conv_kernel - 1) | |
| self.rglru = RGLRU(d_rnn, rglru_c) | |
| self.out = nn.Linear(d_rnn, d_model) | |
| def forward(self, x): | |
| gate = F.gelu(self.in_gate(x)) | |
| rec = self.in_recur(x).transpose(1, 2) | |
| rec = self.conv(rec)[..., : x.size(1)] | |
| rec = self.rglru(rec.transpose(1, 2)) | |
| return self.out(gate * rec) | |
| class MLPBlock(nn.Module): | |
| def __init__(self, d_model: int, expansion: int = 3): | |
| super().__init__() | |
| hidden = expansion * d_model | |
| self.gate = nn.Linear(d_model, hidden) | |
| self.up = nn.Linear(d_model, hidden) | |
| self.down = nn.Linear(hidden, d_model) | |
| def forward(self, x): | |
| return self.down(F.gelu(self.gate(x)) * self.up(x)) | |
| class HawkLayer(nn.Module): | |
| def __init__(self, d_model, d_rnn, conv_kernel, mlp_expansion, eps, rglru_c=8.0): | |
| super().__init__() | |
| self.norm1 = RMSNorm(d_model, eps) | |
| self.recur = RecurrentBlock(d_model, d_rnn, conv_kernel, rglru_c) | |
| self.norm2 = RMSNorm(d_model, eps) | |
| self.mlp = MLPBlock(d_model, mlp_expansion) | |
| def forward(self, x): | |
| x = x + self.recur(self.norm1(x)) | |
| x = x + self.mlp(self.norm2(x)) | |
| return x | |
| class HawkConfig(PretrainedConfig): | |
| model_type = "hawk_rglru" | |
| def __init__(self, vocab_size: int = 16384, n_layer: int = 12, n_embd: int = 768, | |
| rnn_width: Optional[int] = None, conv_kernel: int = 4, | |
| mlp_expansion: int = 3, rmsnorm_eps: float = 1e-6, rglru_c: float = 8.0, | |
| max_position_embeddings: int = 1024, tie_word_embeddings: bool = True, | |
| bos_token_id: int = 2, eos_token_id: int = 3, pad_token_id: int = 1, | |
| **kwargs): | |
| self.vocab_size = vocab_size | |
| self.n_layer = n_layer | |
| self.n_embd = n_embd | |
| self.rnn_width = rnn_width | |
| self.conv_kernel = conv_kernel | |
| self.mlp_expansion = mlp_expansion | |
| self.rmsnorm_eps = rmsnorm_eps | |
| self.rglru_c = rglru_c | |
| self.max_position_embeddings = max_position_embeddings | |
| self.auto_map = { | |
| "AutoConfig": "modeling_hawk.HawkConfig", | |
| "AutoModelForCausalLM": "modeling_hawk.HawkForCausalLM", | |
| "AutoModelForSequenceClassification": "modeling_hawk.HawkForSequenceClassification", | |
| } | |
| super().__init__(tie_word_embeddings=tie_word_embeddings, | |
| bos_token_id=bos_token_id, eos_token_id=eos_token_id, | |
| pad_token_id=pad_token_id, **kwargs) | |
| def d_rnn(self): | |
| return self.rnn_width if self.rnn_width is not None else self.n_embd | |
| class HawkForCausalLM(PreTrainedModel, GenerationMixin): | |
| config_class = HawkConfig | |
| _tied_weights_keys = {"lm_head.weight"} | |
| def __init__(self, config: HawkConfig): | |
| super().__init__(config) | |
| d_rnn = config.d_rnn | |
| self.wte = nn.Embedding(config.vocab_size, config.n_embd) | |
| self.layers = nn.ModuleList([ | |
| HawkLayer(config.n_embd, d_rnn, config.conv_kernel, | |
| config.mlp_expansion, config.rmsnorm_eps, config.rglru_c) | |
| for _ in range(config.n_layer)]) | |
| self.norm_f = RMSNorm(config.n_embd, config.rmsnorm_eps) | |
| self.lm_head = nn.Linear(config.n_embd, config.vocab_size, bias=False) | |
| self.post_init() | |
| self.lm_head.weight = self.wte.weight # hard-tie: survives tie flag + 4.x/5.x | |
| def get_input_embeddings(self): | |
| return self.wte | |
| def set_input_embeddings(self, new): | |
| self.wte = new | |
| def get_output_embeddings(self): | |
| return self.lm_head | |
| def forward(self, input_ids: torch.LongTensor, | |
| attention_mask: Optional[torch.Tensor] = None, | |
| labels: Optional[torch.LongTensor] = None, | |
| **kwargs) -> CausalLMOutputWithPast: | |
| x = self.wte(input_ids) | |
| for layer in self.layers: | |
| x = layer(x) | |
| x = self.norm_f(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), ignore_index=-100) | |
| return CausalLMOutputWithPast(loss=loss, logits=logits) | |
| # ----- generation support ------------------------------------------------- | |
| # This backbone is stateless across calls (no KV cache): each step recomputes | |
| # the full prefix. generate() is therefore correct but O(T) per new token. | |
| # We override prepare_inputs_for_generation to always pass the full sequence | |
| # and never request a cache, so HF's default cache plumbing stays out of the | |
| # way regardless of the installed transformers version. | |
| def prepare_inputs_for_generation(self, input_ids, attention_mask=None, **kwargs): | |
| return {"input_ids": input_ids, "attention_mask": attention_mask, "use_cache": False} | |
| def _update_model_kwargs_for_generation(self, outputs, model_kwargs, **kwargs): | |
| # No cache to carry forward; just keep attention_mask growing if present. | |
| if model_kwargs.get("attention_mask") is not None: | |
| am = model_kwargs["attention_mask"] | |
| model_kwargs["attention_mask"] = torch.cat( | |
| [am, am.new_ones((am.shape[0], 1))], dim=-1) | |
| model_kwargs["past_key_values"] = None | |
| return model_kwargs | |
| class HawkForSequenceClassification(PreTrainedModel): | |
| """ | |
| Sequence-classification head on top of the SAME Hawk backbone used for | |
| causal LM. The backbone attribute names (wte / layers / norm_f) are | |
| IDENTICAL to HawkForCausalLM, so a CausalLM export state_dict maps 1:1 onto | |
| the backbone with no renaming. Only `score` is newly initialised, which is | |
| the expected behaviour when starting a fine-tuning run. | |
| The pooled representation is read from the hidden state at the last | |
| non-padding position (right padding, as produced by the BabyLM finetune | |
| tokenizer), matching the GPT-2 / Mamba sequence-classification convention. | |
| """ | |
| config_class = HawkConfig | |
| def __init__(self, config: HawkConfig): | |
| super().__init__(config) | |
| self.num_labels = config.num_labels | |
| d_rnn = config.d_rnn | |
| self.wte = nn.Embedding(config.vocab_size, config.n_embd) | |
| self.layers = nn.ModuleList([ | |
| HawkLayer(config.n_embd, d_rnn, config.conv_kernel, | |
| config.mlp_expansion, config.rmsnorm_eps, config.rglru_c) | |
| for _ in range(config.n_layer)]) | |
| self.norm_f = RMSNorm(config.n_embd, config.rmsnorm_eps) | |
| self.score = nn.Linear(config.n_embd, self.num_labels, bias=False) | |
| self.post_init() | |
| def get_input_embeddings(self): | |
| return self.wte | |
| def set_input_embeddings(self, new): | |
| self.wte = new | |
| def forward(self, input_ids: torch.LongTensor, | |
| attention_mask: Optional[torch.Tensor] = None, | |
| labels: Optional[torch.LongTensor] = None, | |
| **kwargs) -> SequenceClassifierOutput: | |
| x = self.wte(input_ids) | |
| for layer in self.layers: | |
| x = layer(x) | |
| x = self.norm_f(x) | |
| logits = self.score(x) # (B, T, num_labels) | |
| B, T = input_ids.shape[:2] | |
| # Index of the last real token per sequence (assumes right padding). | |
| if attention_mask is not None: | |
| last_idx = attention_mask.long().sum(-1) - 1 | |
| elif self.config.pad_token_id is not None: | |
| last_idx = (input_ids != self.config.pad_token_id).int().sum(-1) - 1 | |
| else: | |
| last_idx = torch.full((B,), T - 1, device=input_ids.device) | |
| last_idx = last_idx.clamp(min=0) | |
| pooled_logits = logits[torch.arange(B, device=input_ids.device), last_idx] | |
| loss = None | |
| if labels is not None: | |
| if self.config.problem_type is None: | |
| if self.num_labels == 1: | |
| self.config.problem_type = "regression" | |
| elif self.num_labels > 1 and labels.dtype in (torch.long, torch.int): | |
| self.config.problem_type = "single_label_classification" | |
| else: | |
| self.config.problem_type = "multi_label_classification" | |
| if self.config.problem_type == "regression": | |
| loss_fct = nn.MSELoss() | |
| loss = (loss_fct(pooled_logits.squeeze(), labels.squeeze()) | |
| if self.num_labels == 1 | |
| else loss_fct(pooled_logits, labels)) | |
| elif self.config.problem_type == "single_label_classification": | |
| loss_fct = nn.CrossEntropyLoss() | |
| loss = loss_fct(pooled_logits.view(-1, self.num_labels), | |
| labels.view(-1)) | |
| else: # multi_label_classification | |
| loss_fct = nn.BCEWithLogitsLoss() | |
| loss = loss_fct(pooled_logits, labels.float()) | |
| return SequenceClassifierOutput(loss=loss, logits=pooled_logits)#!/usr/bin/env python3 | |
| """ | |
| HF-compatible single-language Hawk / RG-LRU model, for lm-eval. | |
| Self-contained `trust_remote_code` modeling file. The building blocks are the | |
| SAME code used at training time (De et al., 2024, Griffin/Hawk; arXiv:2402.19427), | |
| so the per-language export state_dict maps 1:1 onto this module's parameters | |
| (top-level attribute names wte / layers / norm_f / lm_head match the export keys | |
| exactly -- no renaming, no transpose). Exposes the standard | |
| forward(input_ids, labels=None) -> CausalLMOutputWithPast that lm-eval expects. | |
| Register via config.json: | |
| "model_type": "hawk_rglru", | |
| "architectures": ["HawkForCausalLM"], | |
| "auto_map": { | |
| "AutoConfig": "modeling_hawk.HawkConfig", | |
| "AutoModelForCausalLM": "modeling_hawk.HawkForCausalLM", | |
| "AutoModelForSequenceClassification": "modeling_hawk.HawkForSequenceClassification" | |
| } | |
| """ | |
| from typing import Optional | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from transformers import PreTrainedModel, PretrainedConfig | |
| from transformers.generation import GenerationMixin | |
| from transformers.modeling_outputs import ( | |
| CausalLMOutputWithPast, SequenceClassifierOutput, | |
| ) | |
| class RMSNorm(nn.Module): | |
| def __init__(self, dim: int, eps: float = 1e-6): | |
| super().__init__() | |
| self.weight = nn.Parameter(torch.ones(dim)) | |
| self.eps = eps | |
| def forward(self, x): | |
| norm = torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps) | |
| return self.weight * (x * norm) | |
| class RGLRU(nn.Module): | |
| """Real-Gated Linear Recurrent Unit (De et al., 2024).""" | |
| def __init__(self, width: int, c: float = 8.0): | |
| super().__init__() | |
| self.width = width | |
| self.c = c | |
| self.input_gate = nn.Linear(width, width) | |
| self.recur_gate = nn.Linear(width, width) | |
| lam = torch.empty(width).uniform_(2.197, 6.907) | |
| self.log_lambda = nn.Parameter(lam) | |
| def forward(self, x): # x: (B, T, W) | |
| B, T, W = x.shape | |
| r = torch.sigmoid(self.recur_gate(x)) | |
| i = torch.sigmoid(self.input_gate(x)) | |
| log_a = -F.softplus(-self.log_lambda) | |
| log_a_t = self.c * r * log_a | |
| a_t = torch.exp(log_a_t) | |
| mult = torch.sqrt(torch.clamp(-torch.expm1(2.0 * log_a_t), min=1e-8)) | |
| gated_x = mult * (i * x) | |
| h = torch.zeros(B, W, device=x.device, dtype=x.dtype) | |
| outs = [] | |
| for t in range(T): | |
| h = a_t[:, t] * h + gated_x[:, t] | |
| outs.append(h) | |
| return torch.stack(outs, dim=1) | |
| class RecurrentBlock(nn.Module): | |
| def __init__(self, d_model: int, d_rnn: int, conv_kernel: int = 4, rglru_c: float = 8.0): | |
| super().__init__() | |
| self.conv_kernel = conv_kernel | |
| self.in_gate = nn.Linear(d_model, d_rnn) | |
| self.in_recur = nn.Linear(d_model, d_rnn) | |
| self.conv = nn.Conv1d(d_rnn, d_rnn, conv_kernel, groups=d_rnn, | |
| padding=conv_kernel - 1) | |
| self.rglru = RGLRU(d_rnn, rglru_c) | |
| self.out = nn.Linear(d_rnn, d_model) | |
| def forward(self, x): | |
| gate = F.gelu(self.in_gate(x)) | |
| rec = self.in_recur(x).transpose(1, 2) | |
| rec = self.conv(rec)[..., : x.size(1)] | |
| rec = self.rglru(rec.transpose(1, 2)) | |
| return self.out(gate * rec) | |
| class MLPBlock(nn.Module): | |
| def __init__(self, d_model: int, expansion: int = 3): | |
| super().__init__() | |
| hidden = expansion * d_model | |
| self.gate = nn.Linear(d_model, hidden) | |
| self.up = nn.Linear(d_model, hidden) | |
| self.down = nn.Linear(hidden, d_model) | |
| def forward(self, x): | |
| return self.down(F.gelu(self.gate(x)) * self.up(x)) | |
| class HawkLayer(nn.Module): | |
| def __init__(self, d_model, d_rnn, conv_kernel, mlp_expansion, eps, rglru_c=8.0): | |
| super().__init__() | |
| self.norm1 = RMSNorm(d_model, eps) | |
| self.recur = RecurrentBlock(d_model, d_rnn, conv_kernel, rglru_c) | |
| self.norm2 = RMSNorm(d_model, eps) | |
| self.mlp = MLPBlock(d_model, mlp_expansion) | |
| def forward(self, x): | |
| x = x + self.recur(self.norm1(x)) | |
| x = x + self.mlp(self.norm2(x)) | |
| return x | |
| class HawkConfig(PretrainedConfig): | |
| model_type = "hawk_rglru" | |
| def __init__(self, vocab_size: int = 16384, n_layer: int = 12, n_embd: int = 768, | |
| rnn_width: Optional[int] = None, conv_kernel: int = 4, | |
| mlp_expansion: int = 3, rmsnorm_eps: float = 1e-6, rglru_c: float = 8.0, | |
| max_position_embeddings: int = 1024, tie_word_embeddings: bool = True, | |
| bos_token_id: int = 2, eos_token_id: int = 3, pad_token_id: int = 1, | |
| **kwargs): | |
| self.vocab_size = vocab_size | |
| self.n_layer = n_layer | |
| self.n_embd = n_embd | |
| self.rnn_width = rnn_width | |
| self.conv_kernel = conv_kernel | |
| self.mlp_expansion = mlp_expansion | |
| self.rmsnorm_eps = rmsnorm_eps | |
| self.rglru_c = rglru_c | |
| self.max_position_embeddings = max_position_embeddings | |
| self.auto_map = { | |
| "AutoConfig": "modeling_hawk.HawkConfig", | |
| "AutoModelForCausalLM": "modeling_hawk.HawkForCausalLM", | |
| "AutoModelForSequenceClassification": "modeling_hawk.HawkForSequenceClassification", | |
| } | |
| super().__init__(tie_word_embeddings=tie_word_embeddings, | |
| bos_token_id=bos_token_id, eos_token_id=eos_token_id, | |
| pad_token_id=pad_token_id, **kwargs) | |
| def d_rnn(self): | |
| return self.rnn_width if self.rnn_width is not None else self.n_embd | |
| class HawkForCausalLM(PreTrainedModel, GenerationMixin): | |
| config_class = HawkConfig | |
| _tied_weights_keys = {"lm_head.weight": "wte.weight"} | |
| def __init__(self, config: HawkConfig): | |
| super().__init__(config) | |
| d_rnn = config.d_rnn | |
| self.wte = nn.Embedding(config.vocab_size, config.n_embd) | |
| self.layers = nn.ModuleList([ | |
| HawkLayer(config.n_embd, d_rnn, config.conv_kernel, | |
| config.mlp_expansion, config.rmsnorm_eps, config.rglru_c) | |
| for _ in range(config.n_layer)]) | |
| self.norm_f = RMSNorm(config.n_embd, config.rmsnorm_eps) | |
| self.lm_head = nn.Linear(config.n_embd, config.vocab_size, bias=False) | |
| self.post_init() | |
| def get_input_embeddings(self): | |
| return self.wte | |
| def set_input_embeddings(self, new): | |
| self.wte = new | |
| def get_output_embeddings(self): | |
| return self.lm_head | |
| def forward(self, input_ids: torch.LongTensor, | |
| attention_mask: Optional[torch.Tensor] = None, | |
| labels: Optional[torch.LongTensor] = None, | |
| **kwargs) -> CausalLMOutputWithPast: | |
| x = self.wte(input_ids) | |
| for layer in self.layers: | |
| x = layer(x) | |
| x = self.norm_f(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), ignore_index=-100) | |
| return CausalLMOutputWithPast(loss=loss, logits=logits) | |
| # ----- generation support ------------------------------------------------- | |
| # This backbone is stateless across calls (no KV cache): each step recomputes | |
| # the full prefix. generate() is therefore correct but O(T) per new token. | |
| # We override prepare_inputs_for_generation to always pass the full sequence | |
| # and never request a cache, so HF's default cache plumbing stays out of the | |
| # way regardless of the installed transformers version. | |
| def prepare_inputs_for_generation(self, input_ids, attention_mask=None, **kwargs): | |
| return {"input_ids": input_ids, "attention_mask": attention_mask, "use_cache": False} | |
| def _update_model_kwargs_for_generation(self, outputs, model_kwargs, **kwargs): | |
| # No cache to carry forward; just keep attention_mask growing if present. | |
| if model_kwargs.get("attention_mask") is not None: | |
| am = model_kwargs["attention_mask"] | |
| model_kwargs["attention_mask"] = torch.cat( | |
| [am, am.new_ones((am.shape[0], 1))], dim=-1) | |
| model_kwargs["past_key_values"] = None | |
| return model_kwargs | |
| class HawkForSequenceClassification(PreTrainedModel): | |
| """ | |
| Sequence-classification head on top of the SAME Hawk backbone used for | |
| causal LM. The backbone attribute names (wte / layers / norm_f) are | |
| IDENTICAL to HawkForCausalLM, so a CausalLM export state_dict maps 1:1 onto | |
| the backbone with no renaming. Only `score` is newly initialised, which is | |
| the expected behaviour when starting a fine-tuning run. | |
| The pooled representation is read from the hidden state at the last | |
| non-padding position (right padding, as produced by the BabyLM finetune | |
| tokenizer), matching the GPT-2 / Mamba sequence-classification convention. | |
| """ | |
| config_class = HawkConfig | |
| def __init__(self, config: HawkConfig): | |
| super().__init__(config) | |
| self.num_labels = config.num_labels | |
| d_rnn = config.d_rnn | |
| self.wte = nn.Embedding(config.vocab_size, config.n_embd) | |
| self.layers = nn.ModuleList([ | |
| HawkLayer(config.n_embd, d_rnn, config.conv_kernel, | |
| config.mlp_expansion, config.rmsnorm_eps, config.rglru_c) | |
| for _ in range(config.n_layer)]) | |
| self.norm_f = RMSNorm(config.n_embd, config.rmsnorm_eps) | |
| self.score = nn.Linear(config.n_embd, self.num_labels, bias=False) | |
| self.post_init() | |
| def get_input_embeddings(self): | |
| return self.wte | |
| def set_input_embeddings(self, new): | |
| self.wte = new | |
| def forward(self, input_ids: torch.LongTensor, | |
| attention_mask: Optional[torch.Tensor] = None, | |
| labels: Optional[torch.LongTensor] = None, | |
| **kwargs) -> SequenceClassifierOutput: | |
| x = self.wte(input_ids) | |
| for layer in self.layers: | |
| x = layer(x) | |
| x = self.norm_f(x) | |
| logits = self.score(x) # (B, T, num_labels) | |
| B, T = input_ids.shape[:2] | |
| # Index of the last real token per sequence (assumes right padding). | |
| if attention_mask is not None: | |
| last_idx = attention_mask.long().sum(-1) - 1 | |
| elif self.config.pad_token_id is not None: | |
| last_idx = (input_ids != self.config.pad_token_id).int().sum(-1) - 1 | |
| else: | |
| last_idx = torch.full((B,), T - 1, device=input_ids.device) | |
| last_idx = last_idx.clamp(min=0) | |
| pooled_logits = logits[torch.arange(B, device=input_ids.device), last_idx] | |
| loss = None | |
| if labels is not None: | |
| if self.config.problem_type is None: | |
| if self.num_labels == 1: | |
| self.config.problem_type = "regression" | |
| elif self.num_labels > 1 and labels.dtype in (torch.long, torch.int): | |
| self.config.problem_type = "single_label_classification" | |
| else: | |
| self.config.problem_type = "multi_label_classification" | |
| if self.config.problem_type == "regression": | |
| loss_fct = nn.MSELoss() | |
| loss = (loss_fct(pooled_logits.squeeze(), labels.squeeze()) | |
| if self.num_labels == 1 | |
| else loss_fct(pooled_logits, labels)) | |
| elif self.config.problem_type == "single_label_classification": | |
| loss_fct = nn.CrossEntropyLoss() | |
| loss = loss_fct(pooled_logits.view(-1, self.num_labels), | |
| labels.view(-1)) | |
| else: # multi_label_classification | |
| loss_fct = nn.BCEWithLogitsLoss() | |
| loss = loss_fct(pooled_logits, labels.float()) | |
| return SequenceClassifierOutput(loss=loss, logits=pooled_logits) |