TransWave / modeling_transwave.py
Angshul's picture
Release TransWave 96.13M C4 checkpoint
ea51a3d verified
Raw History Blame Contribute Delete
9.06 kB
from __future__ import annotations
import os
import torch
import torch.nn.functional as F
from huggingface_hub import hf_hub_download
from transformers import PreTrainedModel
from transformers.modeling_outputs import CausalLMOutput
try:
from .configuration_transwave import TransWaveConfig
from .transwave_core import (
CTWModel,
BYTE_VOCAB,
BYTE_DIM,
BYTES_PER_BLOCK,
BYTE_DECODER_DIM,
)
except ImportError:
from configuration_transwave import TransWaveConfig
from transwave_core import (
CTWModel,
BYTE_VOCAB,
BYTE_DIM,
BYTES_PER_BLOCK,
BYTE_DECODER_DIM,
)
EXPECTED_PARAMS = 96_130_040
class TransWaveForCausalLM(PreTrainedModel):
config_class = TransWaveConfig
base_model_prefix = "transwave"
main_input_name = "input_ids"
_tied_weights_keys = ["transwave.decoder.shared_embedding.weight"]
def __init__(self, config):
super().__init__(config)
self.transwave = CTWModel()
# Do not call post_init(): CTWModel already performs the architecture's
# exact initialization. The trained checkpoint is loaded afterwards.
def get_input_embeddings(self):
return self.transwave.tokenizer.byte_embedding
def set_input_embeddings(self, value):
self.transwave.tokenizer.byte_embedding = value
self.transwave.decoder.shared_embedding = value
def get_output_embeddings(self):
return self.transwave.decoder.shared_embedding
@classmethod
def from_pretrained(cls, pretrained_model_name_or_path, *model_args, **kwargs):
config = kwargs.pop("config", None)
revision = kwargs.pop("revision", None)
cache_dir = kwargs.pop("cache_dir", None)
token = kwargs.pop("token", None)
local_files_only = kwargs.pop("local_files_only", False)
torch_dtype = kwargs.pop("torch_dtype", None)
kwargs.pop("trust_remote_code", None)
kwargs.pop("use_safetensors", None)
if config is None:
config = TransWaveConfig.from_pretrained(
pretrained_model_name_or_path,
revision=revision,
cache_dir=cache_dir,
token=token,
local_files_only=local_files_only,
)
model = cls(config)
path = os.fspath(pretrained_model_name_or_path)
if os.path.isdir(path):
ckpt_path = os.path.join(path, "best_model.pt")
else:
ckpt_path = hf_hub_download(
repo_id=path,
filename="best_model.pt",
revision=revision,
cache_dir=cache_dir,
token=token,
local_files_only=local_files_only,
)
checkpoint = torch.load(
ckpt_path,
map_location="cpu",
weights_only=False,
)
state = checkpoint["model"] if isinstance(checkpoint, dict) and "model" in checkpoint else checkpoint
incompatible = model.transwave.load_state_dict(state, strict=True)
if incompatible.missing_keys or incompatible.unexpected_keys:
raise RuntimeError(
f"Checkpoint mismatch. Missing={incompatible.missing_keys}, "
f"unexpected={incompatible.unexpected_keys}"
)
n_params = sum(p.numel() for p in model.transwave.parameters())
if n_params != EXPECTED_PARAMS:
raise RuntimeError(f"Parameter count {n_params:,} != {EXPECTED_PARAMS:,}")
if torch_dtype not in (None, "auto"):
model = model.to(dtype=torch_dtype)
model.eval()
return model
def nll(self, input_ids, checkpoint_layers=False):
return self.transwave.nll(
input_ids,
checkpoint_layers=checkpoint_layers,
)
def contexts(self, input_ids, checkpoint_layers=False):
return self.transwave.contexts(
input_ids,
checkpoint_layers=checkpoint_layers,
)
def teacher_forced_logits(self, input_ids):
context, target_blocks = self.transwave.contexts(
input_ids,
checkpoint_layers=False,
)
decoder = self.transwave.decoder
batch, blocks, _ = context.shape
n = batch * blocks
hidden = torch.tanh(
decoder.context_proj(context)
).reshape(n, BYTE_DECODER_DIM)
previous = decoder.bos[None, :].expand(n, -1)
position_logits = []
for r in range(BYTES_PER_BLOCK):
decoder_input = previous + decoder.byte_position[r][None, :]
hidden = decoder.gru(decoder_input, hidden)
z = decoder.to_byte_space(decoder.norm(hidden))
logits = F.linear(z, decoder.shared_embedding.weight)
position_logits.append(
logits.reshape(batch, blocks, BYTE_VOCAB)
)
target = target_blocks[:, :, r].reshape(-1).long()
previous = decoder.shared_embedding(target)
logits = torch.stack(position_logits, dim=2)
return logits.reshape(
batch,
blocks * BYTES_PER_BLOCK,
BYTE_VOCAB,
)
def forward(
self,
input_ids=None,
labels=None,
return_dict=None,
**kwargs,
):
if input_ids is None:
raise ValueError("input_ids are required")
if input_ids.ndim != 2:
raise ValueError("input_ids must have shape [batch, tokens]")
if input_ids.shape[1] % BYTES_PER_BLOCK != 0:
raise ValueError("TransWave scoring length must be divisible by 8")
if labels is not None:
if labels.shape != input_ids.shape or not torch.equal(labels, input_ids):
raise ValueError(
"TransWave performs the causal shift internally; "
"for exact scoring/training, labels must equal input_ids."
)
logits = self.teacher_forced_logits(input_ids)
loss = self.transwave.nll(
input_ids,
checkpoint_layers=False,
)
if return_dict is False:
return (loss, logits)
return CausalLMOutput(
loss=loss,
logits=logits,
)
@torch.no_grad()
def generate_bytes(
self,
input_ids,
max_new_tokens=32,
do_sample=False,
temperature=1.0,
top_k=None,
):
if input_ids.ndim != 2 or input_ids.shape[0] != 1:
raise ValueError("generate_bytes currently supports batch size 1")
out = input_ids.long().clone()
device = next(self.parameters()).device
out = out.to(device)
for _ in range(int(max_new_tokens)):
total = out.shape[1]
complete = (total // BYTES_PER_BLOCK) * BYTES_PER_BLOCK
r = total - complete
if complete == 0:
block_context = self.transwave.bos_context[None, :].to(device)
else:
_, blocks = self.transwave.encode_completed_blocks(
out[:, :complete],
checkpoint_layers=False,
)
h, _ = self.transwave.encode_completed_blocks(
out[:, :complete],
checkpoint_layers=False,
)
block_context = h[:, -1, :]
decoder = self.transwave.decoder
hidden = torch.tanh(decoder.context_proj(block_context))
previous = decoder.bos[None, :]
current = out[:, complete:]
logits = None
for j in range(r + 1):
decoder_input = previous + decoder.byte_position[j][None, :]
hidden = decoder.gru(decoder_input, hidden)
z = decoder.to_byte_space(decoder.norm(hidden))
logits = F.linear(z, decoder.shared_embedding.weight)
if j < r:
previous = decoder.shared_embedding(current[:, j])
scores = logits.float() / max(float(temperature), 1e-6)
if top_k is not None and int(top_k) > 0:
k = min(int(top_k), BYTE_VOCAB)
threshold = torch.topk(scores, k, dim=-1).values[:, -1:]
scores = scores.masked_fill(scores < threshold, float("-inf"))
if do_sample:
next_id = torch.multinomial(
torch.softmax(scores, dim=-1),
num_samples=1,
)
else:
next_id = scores.argmax(dim=-1, keepdim=True)
out = torch.cat([out, next_id], dim=1)
return out