PiCoGen / picogen2 /model.py
Vansh Chugh
initial deploy
fe7e262
Raw
History Blame Contribute Delete
8.23 kB
from enum import Enum
import numpy as np
import torch
import torch.nn.functional as F
from torch import nn
from transformers import GPTNeoXConfig, GPTNeoXModel
from . import assets
from .utils import load_checkpoint, load_config, top_p
def _get_device(module):
return next(module.parameters()).device
class ConditionEncoder(nn.Module):
def __init__(self, hp):
super().__init__()
self.l1_encoder = nn.TransformerEncoder(
nn.TransformerEncoderLayer(
d_model=hp.d_model,
nhead=hp.num_heads,
dim_feedforward=hp.d_model * 4,
dropout=hp.dropout,
activation=hp.activation,
batch_first=True,
),
hp.num_layers_encoder,
)
self.pos_emb = nn.Embedding(hp.condition_class, hp.d_model)
self.bottlenect = nn.Sequential(
nn.Linear(hp.d_model, hp.d_bottleneck),
nn.ReLU(),
nn.Linear(hp.d_bottleneck, hp.d_model),
)
def forward(self, input_embs):
B, L, N, D = input_embs.shape
pos = torch.arange(N).to(input_embs.device)
pos = self.pos_emb(pos)[None, None, :, :].expand(B, L, N, D)
input_embs = input_embs + pos
out = self.l1_encoder(input_embs.view(B * L, N, D)).view(B, L, N, D)
out = out[:, :, 0, :]
assert out.shape == (B, L, D)
out = self.bottlenect(out)
return out
class PiCoGenDecoder(nn.Module):
class InputClass(Enum):
TARGET = 0
CONDITION = 1
def __init__(self, hp):
super().__init__()
self.hp = hp
config = GPTNeoXConfig(
vocab_size=hp.vocab_size,
hidden_size=hp.d_model,
num_hidden_layers=hp.num_layers,
num_attention_heads=hp.num_heads,
intermediate_size=hp.d_model * 4,
hidden_act=hp.activation,
hidden_dropout=hp.dropout,
max_position_embeddings=hp.max_position_embeddings,
)
self.model = GPTNeoXModel(config)
self.word_emb = nn.Embedding(hp.vocab_size, hp.d_model, padding_idx=0)
self.cond_encoder = ConditionEncoder(hp)
self.cls_emb = nn.Embedding(
hp.token_class, hp.d_model, padding_idx=0
) # 0: target, 1: condition
self.lm_head = nn.Linear(hp.d_model, hp.vocab_size)
@staticmethod
def from_pretrained(
ckpt_file=None,
config_file=None,
device="cpu",
):
ckpt_file = ckpt_file if ckpt_file is not None else assets.checkpoint_file()
config_file = config_file if config_file is not None else assets.config_file()
hp = load_config(config_file)
model = PiCoGenDecoder(hp)
state_dict = load_checkpoint(ckpt_file, device)
model.load_state_dict(state_dict["model"])
model.to(device)
model.eval()
return model
def generate(
self, input_seg, input_cls_ids, need_encode, kv_cache=None, temperature=1.0, thres=0.9
):
B, L = input_cls_ids.shape
if kv_cache is None:
input_ids = torch.zeros(B, L, device=_get_device(self.word_emb)).long()
input_cond_embs = torch.zeros(
B,
L,
self.hp.condition_class,
self.hp.d_model,
device=_get_device(self.cond_encoder),
).float()
for b in range(B):
for ll in range(L):
if need_encode[b, ll]:
emb = torch.FloatTensor(np.array(input_seg[b][ll])).to(
_get_device(self.cond_encoder)
)
input_cond_embs[b, ll] = emb
else:
input_ids[b, ll] = input_seg[b][ll]
else: # NOTE: only use the last token as input
input_ids = torch.zeros(B, 1, device=_get_device(self.word_emb)).long()
input_cond_embs = torch.zeros(
B,
1,
self.hp.condition_class,
self.hp.d_model,
device=_get_device(self.cond_encoder),
).float()
for b in range(B):
if need_encode[b, -1]:
emb = torch.FloatTensor(np.array(input_seg[b][-1])).to(
_get_device(self.cond_encoder)
)
input_cond_embs[b, -1] = emb
else:
input_ids[b, -1] = input_seg[b][-1]
input_cls_ids = input_cls_ids[:, -1:]
assert input_ids.shape == input_cls_ids.shape
input_embs = self.word_emb(input_ids)
input_cond_embs = self.cond_encoder(input_cond_embs)
input_cls_embs = self.cls_emb(input_cls_ids)
if kv_cache is None:
mask = (input_embs.sum(dim=-1, keepdim=True) != 0).expand(B, L, self.hp.d_model)
else:
mask = (input_embs.sum(dim=-1, keepdim=True) != 0).expand(B, 1, self.hp.d_model)
input_cond_embs[mask] = 0 # NOTE: where input_embs is not zero
input_embs = input_embs + input_cond_embs + input_cls_embs
model_out = self.model(
inputs_embeds=input_embs,
past_key_values=kv_cache,
)
logits = self.lm_head(model_out.last_hidden_state)[:, -1, :]
assert logits.shape == (B, self.hp.vocab_size)
probs = F.softmax(top_p(logits, thres=thres, temperature=temperature), dim=-1)
output_ids = torch.multinomial(probs, num_samples=1)
assert output_ids.shape == (B, 1)
return output_ids, model_out.past_key_values
def forward(
self,
input_seqs,
input_cls_ids,
need_encode,
input_ids=None,
input_cond_embs=None,
labels=None,
kv_cache=None,
):
B, L = input_cls_ids.shape
input_cls_ids = input_cls_ids.to(_get_device(self.cls_emb))
if input_seqs is not None:
assert input_ids is None and input_cond_embs is None
input_ids = torch.zeros(B, L, device=_get_device(self.word_emb)).long()
input_cond_embs = torch.zeros(
B,
L,
self.hp.condition_class,
self.hp.d_model,
device=_get_device(self.cond_encoder),
).float()
for b in range(B):
for ll in range(L):
if need_encode[b, ll]:
emb = torch.FloatTensor(np.array(input_seqs[b][ll])).to(
_get_device(self.cond_encoder)
)
input_cond_embs[b, ll] = emb
else:
input_ids[b, ll] = input_seqs[b][ll]
else:
assert input_ids is not None and input_cond_embs is not None
input_ids = input_ids.to(_get_device(self.word_emb))
input_cond_embs = input_cond_embs.to(_get_device(self.cond_encoder))
input_embs = self.word_emb(input_ids)
input_cond_embs = self.cond_encoder(input_cond_embs)
input_cls_embs = self.cls_emb(input_cls_ids)
mask = (input_embs.sum(dim=-1, keepdim=True) != 0).expand(B, L, self.hp.d_model)
input_cond_embs[mask] = 0 # NOTE: where input_embs is not zero
input_embs = input_embs + input_cond_embs + input_cls_embs
model_out = self.model(
inputs_embeds=input_embs,
past_key_values=kv_cache,
)
logits = self.lm_head(model_out.last_hidden_state)
assert logits.shape == (B, L, self.hp.vocab_size)
lm_loss = None
if labels is not None:
assert labels.shape == (B, L)
labels = labels.to(logits.device)
loss_fct = F.cross_entropy
lm_loss = loss_fct(logits.view(-1, self.hp.vocab_size), labels.view(-1))
out = {
"loss": lm_loss,
"logits": logits,
"past_key_values": model_out.past_key_values,
"hidden_states": model_out.hidden_states,
"attentions": model_out.attentions,
}
return out