Yugg9826's picture
Upload NanoDiffusionGPT Shakespeare checkpoint
0b782a2 verified
Raw
History Blame Contribute Delete
10 kB
"""
NanoDiffusionGPT model.
"""
import inspect
import math
from dataclasses import dataclass
from typing import Optional
import torch
import torch.nn as nn
from torch.nn import functional as F
class LayerNorm(nn.Module):
"""LayerNorm with optional bias."""
def __init__(self, ndim, bias):
super().__init__()
self.weight = nn.Parameter(torch.ones(ndim))
self.bias = nn.Parameter(torch.zeros(ndim)) if bias else None
def forward(self, input):
return F.layer_norm(input, self.weight.shape, self.weight, self.bias, 1e-5)
class BidirectionalSelfAttention(nn.Module):
"""Self-attention where every token can look left and right."""
def __init__(self, config):
super().__init__()
assert config.n_embd % config.n_head == 0
self.c_attn = nn.Linear(config.n_embd, 3 * config.n_embd, bias=config.bias)
self.c_proj = nn.Linear(config.n_embd, config.n_embd, bias=config.bias)
self.attn_dropout = nn.Dropout(config.dropout)
self.resid_dropout = nn.Dropout(config.dropout)
self.n_head = config.n_head
self.n_embd = config.n_embd
self.dropout = config.dropout
self.flash = hasattr(torch.nn.functional, "scaled_dot_product_attention")
if not self.flash:
print("WARNING: using slow attention. Flash Attention requires PyTorch >= 2.0")
def forward(self, x):
B, T, C = x.size()
q, k, v = self.c_attn(x).split(self.n_embd, dim=2)
k = k.view(B, T, self.n_head, C // self.n_head).transpose(1, 2)
q = q.view(B, T, self.n_head, C // self.n_head).transpose(1, 2)
v = v.view(B, T, self.n_head, C // self.n_head).transpose(1, 2)
if self.flash:
y = F.scaled_dot_product_attention(
q,
k,
v,
attn_mask=None,
dropout_p=self.dropout if self.training else 0,
is_causal=False,
)
else:
att = (q @ k.transpose(-2, -1)) * (1.0 / math.sqrt(k.size(-1)))
att = F.softmax(att, dim=-1)
att = self.attn_dropout(att)
y = att @ v
y = y.transpose(1, 2).contiguous().view(B, T, C)
y = self.resid_dropout(self.c_proj(y))
return y
class MLP(nn.Module):
def __init__(self, config):
super().__init__()
self.c_fc = nn.Linear(config.n_embd, 4 * config.n_embd, bias=config.bias)
self.gelu = nn.GELU()
self.c_proj = nn.Linear(4 * config.n_embd, config.n_embd, bias=config.bias)
self.dropout = nn.Dropout(config.dropout)
def forward(self, x):
x = self.c_fc(x)
x = self.gelu(x)
x = self.c_proj(x)
x = self.dropout(x)
return x
class Block(nn.Module):
def __init__(self, config):
super().__init__()
self.ln_1 = LayerNorm(config.n_embd, bias=config.bias)
self.attn = BidirectionalSelfAttention(config)
self.ln_2 = LayerNorm(config.n_embd, bias=config.bias)
self.mlp = MLP(config)
def forward(self, x):
x = x + self.attn(self.ln_1(x))
x = x + self.mlp(self.ln_2(x))
return x
@dataclass
class NanoDiffusionGPTConfig:
block_size: int = 256
vocab_size: int = 66
n_layer: int = 6
n_head: int = 6
n_embd: int = 384
dropout: float = 0.0
bias: bool = True
mask_token_id: Optional[int] = None
class NanoDiffusionGPT(nn.Module):
def __init__(self, config):
super().__init__()
assert config.vocab_size is not None
assert config.block_size is not None
if config.mask_token_id is None:
config.mask_token_id = config.vocab_size - 1
assert 0 <= config.mask_token_id < config.vocab_size
self.config = config
self.transformer = nn.ModuleDict(
dict(
wte=nn.Embedding(config.vocab_size, config.n_embd),
wpe=nn.Embedding(config.block_size, config.n_embd),
drop=nn.Dropout(config.dropout),
h=nn.ModuleList([Block(config) for _ in range(config.n_layer)]),
ln_f=LayerNorm(config.n_embd, bias=config.bias),
)
)
self.lm_head = nn.Linear(config.n_embd, config.vocab_size, bias=False)
self.transformer.wte.weight = self.lm_head.weight
self.apply(self._init_weights)
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))
print("number of parameters: %.2fM" % (self.get_num_params() / 1e6,))
def get_num_params(self, non_embedding=True):
n_params = sum(p.numel() for p in self.parameters())
if non_embedding:
n_params -= self.transformer.wpe.weight.numel()
return n_params
def _init_weights(self, module):
if isinstance(module, nn.Linear):
torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
if module.bias is not None:
torch.nn.init.zeros_(module.bias)
elif isinstance(module, nn.Embedding):
torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
def forward(self, idx, targets=None, loss_mask=None):
device = idx.device
b, t = idx.size()
assert t <= self.config.block_size, (
f"Cannot forward sequence of length {t}, block size is only {self.config.block_size}"
)
pos = torch.arange(0, t, dtype=torch.long, device=device)
tok_emb = self.transformer.wte(idx)
pos_emb = self.transformer.wpe(pos)
x = self.transformer.drop(tok_emb + pos_emb)
for block in self.transformer.h:
x = block(x)
x = self.transformer.ln_f(x)
logits = self.lm_head(x)
loss = None
if targets is not None:
if loss_mask is None:
loss_mask = torch.ones_like(targets, dtype=torch.bool)
logits_for_loss = logits.clone()
logits_for_loss[..., self.config.mask_token_id] = -float("inf")
loss = F.cross_entropy(logits_for_loss[loss_mask], targets[loss_mask])
return logits, loss
def configure_optimizers(self, weight_decay, learning_rate, betas, device_type):
param_dict = {pn: p for pn, p in self.named_parameters() if p.requires_grad}
decay_params = [p for n, p in param_dict.items() if p.dim() >= 2]
nodecay_params = [p for n, p in param_dict.items() if p.dim() < 2]
optim_groups = [
{"params": decay_params, "weight_decay": weight_decay},
{"params": nodecay_params, "weight_decay": 0.0},
]
num_decay_params = sum(p.numel() for p in decay_params)
num_nodecay_params = sum(p.numel() for p in nodecay_params)
print(f"num decayed parameter tensors: {len(decay_params)}, with {num_decay_params:,} parameters")
print(f"num non-decayed parameter tensors: {len(nodecay_params)}, with {num_nodecay_params:,} parameters")
fused_available = "fused" in inspect.signature(torch.optim.AdamW).parameters
use_fused = fused_available and device_type == "cuda"
extra_args = dict(fused=True) if use_fused else dict()
optimizer = torch.optim.AdamW(optim_groups, lr=learning_rate, betas=betas, **extra_args)
print(f"using fused AdamW: {use_fused}")
return optimizer
def estimate_mfu(self, fwdbwd_per_iter, dt):
N = self.get_num_params()
cfg = self.config
L, H, Q, T = cfg.n_layer, cfg.n_head, cfg.n_embd // cfg.n_head, cfg.block_size
flops_per_token = 6 * N + 12 * L * H * Q * T
flops_per_fwdbwd = flops_per_token * T
flops_per_iter = flops_per_fwdbwd * fwdbwd_per_iter
flops_achieved = flops_per_iter * (1.0 / dt)
flops_promised = 312e12
return flops_achieved / flops_promised
@torch.no_grad()
def generate(self, prompt_ids, max_new_tokens, steps=64, temperature=1.0, top_k=None):
"""
Start with prompt + [MASK] tokens, then repeatedly fill the most confident masks.
"""
assert prompt_ids.dim() == 2
total_len = prompt_ids.size(1) + max_new_tokens
assert total_len <= self.config.block_size, (
f"prompt + max_new_tokens is {total_len}, block size is only {self.config.block_size}"
)
device = prompt_ids.device
x = torch.full(
(prompt_ids.size(0), total_len),
self.config.mask_token_id,
dtype=torch.long,
device=device,
)
x[:, : prompt_ids.size(1)] = prompt_ids
prompt_mask = torch.zeros_like(x, dtype=torch.bool)
prompt_mask[:, : prompt_ids.size(1)] = True
steps = max(1, steps)
for step in range(steps):
remaining = (x == self.config.mask_token_id) & (~prompt_mask)
if not remaining.any():
break
logits, _ = self(x)
logits[..., self.config.mask_token_id] = -float("inf")
logits = logits / max(temperature, 1e-6)
if top_k is not None:
v, _ = torch.topk(logits, min(top_k, logits.size(-1)))
logits = logits.masked_fill(logits < v[..., [-1]], -float("inf"))
probs = F.softmax(logits, dim=-1)
pred = torch.multinomial(probs.view(-1, probs.size(-1)), num_samples=1).view(x.shape)
confidence = probs.gather(-1, pred.unsqueeze(-1)).squeeze(-1)
confidence = confidence.masked_fill(~remaining, -1.0)
remaining_count = int(remaining.sum().item())
steps_left = steps - step
fill_count = max(1, math.ceil(remaining_count / steps_left))
selected = torch.topk(confidence.view(-1), fill_count).indices
flat_x = x.view(-1)
flat_pred = pred.view(-1)
flat_x[selected] = flat_pred[selected]
return x