from dataclasses import dataclass import math import numpy as np from typing import Optional import torch import torch.nn as nn from torch.nn import functional as F from einops import rearrange from .base import ( Block, NormFunc, configure_optimizers, estimate_mfu, get_init_fn, init_weights_simple, ) TIE_WEIGHTS = False SIMPLE_INIT = True Z_LOSS = True class Projection(nn.Module): def __init__(self, input_dim, output_dim, dropout=0.5): super(Projection, self).__init__() self.linear_1 = nn.Linear(input_dim, output_dim, bias=False) self.linear_2 = nn.Linear(output_dim, output_dim, bias=False) self.layer_norm = nn.LayerNorm(output_dim) self.dropout = nn.Dropout(dropout) def forward(self, x): emb1 = self.linear_1(x) emb2 = self.dropout(self.linear_2(F.gelu(emb1))) return self.layer_norm(emb1 + emb2) @dataclass class GPTConfig: n_layer: int = 24 n_head: int = 16 # query heads n_kv_head: Optional[int] = None d_head: int = 64 block_size: int = 4288 bias: bool = False dropout: float = 0.0 text_vocab_size: int = 60_032 text_codebook_size: int = 60_001 text_pad_token: int = 1 text_infer_token: int = 60_001 text_end_token: int = 60_002 text_cls_token: int = 60_013 semantic_vocab_size: int = 4032 semantic_codebook_size: int = 4000 semantic_n_codebooks: int = 1 semantic_pad_token: int = 4000 semantic_infer_token: int = 4001 semantic_cls_token: int = 4002 semantic_rate_hz: int = 25 semantic_shift_factor: int = 50 coarse_vocab_size: int = 2112 coarse_codebook_size: int = 2048 coarse_n_codebooks: int = 12 coarse_pad_token: int = 2048 coarse_infer_token: int = 2049 coarse_cls_token: int = 2050 coarse_rate_hz: int = 25 coarse_shift_factor: int = 5 t_text: int = 150 t_text_crop: int = 150 t_audio: int = 3008 t_memmap: int = 3008 use_rotary_pos_emb: bool = False attention_type: str = "torch" # "torch", "tao", "xformers" attention_sliding_window_size: int = -1 def __post_init__(self): # default to multi head attention if self.n_kv_head is None: self.n_kv_head = self.n_head @property def n_embd(self): """The width of the residual stream""" return self.n_head * self.d_head @property def use_learned_pos_emb(self): return not self.use_rotary_pos_emb class GPT(nn.Module): def __init__(self, config: GPTConfig): super().__init__() self.config = config model_dict = dict( wte_text=nn.Embedding(config.text_vocab_size, config.n_embd), ln_text=NormFunc(config.n_embd), wte_semantic=nn.ModuleList( [ nn.Embedding(config.semantic_vocab_size, config.n_embd) for _ in range(config.semantic_n_codebooks) ] ), ln_semantic=NormFunc(config.n_embd), wte_coarse=nn.ModuleList( [ nn.Embedding(config.coarse_vocab_size, config.n_embd) for _ in range(config.coarse_n_codebooks) ] ), ln_coarse=NormFunc(config.n_embd), drop=nn.Dropout(config.dropout), h=nn.ModuleList([Block(config) for _ in range(config.n_layer)]), ln_f=NormFunc(config.n_embd), ) if self.config.use_learned_pos_emb: model_dict["wpe"] = nn.Embedding(config.block_size + 2, config.n_embd) self.transformer = nn.ModuleDict(model_dict) self.text_prj = Projection(config.n_embd, 512) self.audio_prj = Projection(config.n_embd, 512) self.logit_scale = nn.Parameter(torch.ones([]) * np.log(1 / 0.07)) self.contrastive_loss = nn.CrossEntropyLoss() self.lm_heads = nn.ModuleList( [nn.Linear(config.n_embd, config.text_vocab_size, bias=False) for _ in range(1)] ) if TIE_WEIGHTS: for n in range(config.semantic_n_codebooks): self.transformer.wte_semantic[n].weight = self.lm_heads[n].weight for n in range(config.coarse_n_codebooks): n2 = config.semantic_n_codebooks + n self.transformer.wte_coarse[n].weight = self.lm_heads[n2].weight # init all weights if SIMPLE_INIT: self.apply(self._init_weights_simple) for pn, p in self.named_parameters(): if pn.endswith("c_proj.weight"): torch.nn.init.normal_(p, mean=0.0, std=0.02 / math.sqrt(2 * config.n_layer)) else: self._init_weights() print(f"number of parameters: {self.get_num_params()/1e6:.0f}M") def forward(self, x, y=None, audio_offset=0, return_logits=False, last_only=True): device = x.device b, ns, t = x.size() assert ns == 1 + self.config.semantic_n_codebooks + self.config.coarse_n_codebooks if y is not None: assert t == self.config.block_size _, _, t2 = y.size() assert t2 == self.config.t_text # split text and audio tokens x_text = x[:, :, audio_offset:] x_audio = x[:, :, :audio_offset] # append [CLS] token x_text = nn.functional.pad(x_text, (0, 1), "constant", 0) x_text[:, :, -1] = x_text[:, :, -2] x_text[:, 0, self.config.t_text_crop] = self.config.text_cls_token x_audio = nn.functional.pad(x_audio, (0, 1), "constant", 0) x_audio[:, :, -1] = x_audio[:, :, -2] x_audio[:, 1, -1] = self.config.semantic_cls_token x_audio[:, 2:, -1] = self.config.coarse_cls_token # embed text x_text_emb = self.transformer.wte_text(x_text[:, 0, :]) x_text_emb = self.transformer.ln_text(x_text_emb) # embed audio x_audio_emb = self.transformer.wte_semantic[0](x_audio[:, 1, :]) x_audio_emb = self.transformer.ln_semantic(x_audio_emb) for n in range(self.config.coarse_n_codebooks): n2 = 1 + n + self.config.semantic_n_codebooks x_audio_emb += self.transformer.ln_coarse(self.transformer.wte_coarse[n](x_audio[:, n2, :])) # positional embedding if self.config.use_learned_pos_emb: pos = torch.arange(t, dtype=torch.long, device=device).unsqueeze(0) # shape (1, t) pos_emb = self.transformer.wpe(pos) # (1, t, n_embd) x_text_emb[:, :-1, :] += pos_emb[:, audio_offset:, :] x_audio_emb[:, :-1, :] += pos_emb[:, :audio_offset, :] # unimodal GPT x_text_emb = self.transformer.drop(x_text_emb) x_audio_emb = self.transformer.drop(x_audio_emb) x_cat_emb = rearrange([x_text_emb, x_audio_emb], "n b t c -> (n b) t c") for block in self.transformer.h[:-4]: x_cat_emb = block(x_cat_emb) x_text_emb, x_audio_emb = rearrange(x_cat_emb, "(n b) t c -> n b t c", n=2) x_text_emb = x_text_emb[:, : self.config.t_text_crop + 1, :] # contrastive loss cls_text = F.normalize(self.text_prj(x_text_emb[:, -1, :]), dim=-1) cls_audio = F.normalize(self.audio_prj(x_audio_emb[:, -1, :]), dim=-1) logits_per_text = self.logit_scale * cls_text @ cls_audio.t() logits_per_audio = logits_per_text.t() labels = torch.arange(cls_text.shape[0]).long().to(device) loss_dict = {} loss_dict["contrastive_loss"] = ( self.contrastive_loss(logits_per_text, labels) + self.contrastive_loss(logits_per_audio, labels) ) / 2 # concatenate x = torch.cat((x_audio_emb[:, :-1, :], x_text_emb[:, :-1, :]), 1) # multimodal GPT for block in self.transformer.h[-4:]: x = block(x) # x_emb (b, t, n_embd) x = self.transformer.ln_f(x) x = x[:, audio_offset : audio_offset + self.config.t_text, :] if return_logits: if last_only: x = x[:, -1, :] text_logits_list = [] for n in range(1): text_logits_list.append(self.lm_heads[n](x)) text_logits = torch.stack(text_logits_list).swapaxes(0, 1) return text_logits if Z_LOSS: loss_dict["z_loss"] = 0 # projection logits = self.lm_heads[0](x) # flatten flat_logits = logits.reshape(-1, logits.size(-1)) flat_y = y[:, 0, : self.config.t_text_crop].reshape(-1) mask = flat_y != 1 loss_dict["text_0"] = F.cross_entropy(flat_logits[mask], flat_y[mask]) # loss_dict["text_0"] = F.cross_entropy( # logits.reshape(-1, logits.size(-1)), y[:, 0, :].reshape(-1) # ) if Z_LOSS: loss_dict["z_loss"] += (torch.logsumexp(logits, dim=-1) ** 2).mean() return loss_dict, logits def get_num_params(self, non_embedding=True): n_params = sum(p.numel() for p in self.parameters()) if non_embedding: for m in self.transformer.wte_semantic: n_params -= m.weight.numel() for m in self.transformer.wte_coarse: n_params -= m.weight.numel() if self.config.use_learned_pos_emb: n_params -= self.transformer.wpe.weight.numel() return n_params def _init_weights_simple(self, module): init_weights_simple(self, module) def _init_weights(self): # embeddings get_init_fn(self.config.n_embd, init_depth=None)(self.transformer.wte_text.weight) for module in self.transformer.wte_semantic: get_init_fn(self.config.n_embd, init_depth=None)(module.weight) for module in self.transformer.wte_coarse: get_init_fn(self.config.n_embd, init_depth=None)(module.weight) if self.config.use_learned_pos_emb: get_init_fn(self.config.n_embd, init_depth=None)(self.transformer.wpe.weight) # heads for module in self.lm_heads: get_init_fn(self.config.n_embd, init_depth=None)(module.weight) if module.bias is not None: torch.nn.init.zeros_(module.bias) # attention blocks for layer_idx, block in enumerate(self.transformer.h): # mlp module = block.mlp.c_fc get_init_fn(self.config.n_embd, init_depth=layer_idx + 1)(module.weight) if module.bias is not None: torch.nn.init.zeros_(module.bias) module = block.mlp.c_proj get_init_fn(block.mlp.embd_inner, init_depth=layer_idx + 1)(module.weight) if module.bias is not None: torch.nn.init.zeros_(module.bias) # attention for module in [block.attn.c_attn, block.attn.c_proj]: get_init_fn(self.config.n_embd, init_depth=layer_idx + 1)(module.weight) if module.bias is not None: torch.nn.init.zeros_(module.bias) def configure_optimizers(self, weight_decay, learning_rate, betas, device_type): return configure_optimizers(self, weight_decay, learning_rate, betas, device_type) def estimate_mfu(self, fwdbwd_per_iter, dt): return estimate_mfu(self, fwdbwd_per_iter, dt)