openelm.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import json, pprint
  2. from tinygrad import fetch, nn, Tensor
  3. from tinygrad.helpers import DEBUG
  4. class FeedForward:
  5. def __init__(self, model_dim, intermediate_dim):
  6. self.proj_1 = nn.Linear(model_dim, 2*intermediate_dim, bias=False)
  7. self.proj_2 = nn.Linear(intermediate_dim, model_dim, bias=False)
  8. def __call__(self, x):
  9. y_12 = self.proj_1(x)
  10. y_1, y_2 = y_12.chunk(2, dim=-1)
  11. return self.proj_2(y_1.silu() * y_2)
  12. # NOTE: this RoPE doesn't match LLaMA's?
  13. def _rotate_half(x: Tensor) -> Tensor:
  14. x1, x2 = x.chunk(2, dim=-1)
  15. return Tensor.cat(-x2, x1, dim=-1)
  16. def _apply_rotary_pos_emb(x: Tensor, pos_sin: Tensor, pos_cos: Tensor) -> Tensor:
  17. return (x * pos_cos) + (_rotate_half(x) * pos_sin)
  18. class Attention:
  19. def __init__(self, model_dim, num_query_heads, num_kv_heads, head_dim):
  20. self.qkv_proj = nn.Linear(model_dim, (num_query_heads + num_kv_heads*2) * head_dim, bias=False)
  21. self.num_query_heads, self.num_kv_heads = num_query_heads, num_kv_heads
  22. self.head_dim = head_dim
  23. self.q_norm = nn.RMSNorm(head_dim)
  24. self.k_norm = nn.RMSNorm(head_dim)
  25. self.out_proj = nn.Linear(num_query_heads * head_dim, model_dim, bias=False)
  26. def __call__(self, x:Tensor) -> Tensor:
  27. batch_size, seq_len, embed_dim = x.shape
  28. qkv = self.qkv_proj(x)
  29. qkv = qkv.reshape(batch_size, seq_len, self.num_query_heads+self.num_kv_heads*2, self.head_dim).transpose(1, 2)
  30. xq,xk,xv = qkv.split([self.num_query_heads, self.num_kv_heads, self.num_kv_heads], dim=1)
  31. xq = self.q_norm(xq)
  32. xk = self.k_norm(xk)
  33. # add positional embedding (how many kernels is this?)
  34. freq_constant = 10000
  35. inv_freq = 1.0 / (freq_constant ** (Tensor.arange(0, self.head_dim, 2) / self.head_dim))
  36. pos_index_theta = Tensor.einsum("i,j->ij", Tensor.arange(seq_len), inv_freq)
  37. emb = Tensor.cat(pos_index_theta, pos_index_theta, dim=-1)
  38. cos_emb, sin_emb = emb.cos()[None, None, :, :], emb.sin()[None, None, :, :]
  39. xq = _apply_rotary_pos_emb(xq, sin_emb, cos_emb)
  40. xk = _apply_rotary_pos_emb(xk, sin_emb, cos_emb)
  41. # grouped-query attention
  42. num_groups = self.num_query_heads // self.num_kv_heads
  43. xk = xk.repeat_interleave(num_groups, dim=1)
  44. xv = xv.repeat_interleave(num_groups, dim=1)
  45. # masked attention
  46. #start_pos = 0
  47. #mask = Tensor.full((1, 1, seq_len, start_pos+seq_len), float("-inf"), dtype=xq.dtype, device=xq.device).triu(start_pos+1)
  48. #attn_output = xq.scaled_dot_product_attention(xk, xv, mask).transpose(1, 2)
  49. # causal is fine, no mask needed
  50. attn_output = xq.scaled_dot_product_attention(xk, xv, is_causal=True).transpose(1, 2)
  51. return self.out_proj(attn_output.reshape(batch_size, seq_len, self.num_query_heads * self.head_dim))
  52. class Layer:
  53. def __init__(self, model_dim, intermediate_dim, num_query_heads, num_kv_heads, head_dim):
  54. self.ffn = FeedForward(model_dim, intermediate_dim)
  55. self.attn = Attention(model_dim, num_query_heads, num_kv_heads, head_dim)
  56. self.ffn_norm = nn.RMSNorm(model_dim)
  57. self.attn_norm = nn.RMSNorm(model_dim)
  58. def __call__(self, x:Tensor) -> Tensor: # (batch, seq_len, embed_dim)
  59. x = x + self.attn(self.attn_norm(x))
  60. x = x + self.ffn(self.ffn_norm(x))
  61. return x
  62. # stupidly complex
  63. def make_divisible(v, divisor):
  64. new_v = max(divisor, int(v + divisor / 2) // divisor * divisor)
  65. if new_v < 0.9 * v: new_v += divisor
  66. return new_v
  67. class Transformer:
  68. def __init__(self, cfg):
  69. if DEBUG >= 3: pprint.pp(cfg)
  70. self.layers = [Layer(cfg['model_dim'], make_divisible(int(cfg["model_dim"] * cfg['ffn_multipliers'][i]), cfg['ffn_dim_divisor']),
  71. cfg['num_query_heads'][i], cfg['num_kv_heads'][i], cfg['head_dim']) for i in range(cfg['num_transformer_layers'])]
  72. self.norm = nn.RMSNorm(cfg['model_dim'])
  73. self.token_embeddings = nn.Embedding(cfg['vocab_size'], cfg['model_dim'])
  74. def __call__(self, tokens:Tensor):
  75. # _bsz, seqlen = tokens.shape
  76. x = self.token_embeddings(tokens)
  77. for l in self.layers: x = l(x)
  78. return self.norm(x) @ self.token_embeddings.weight.T
  79. if __name__ == "__main__":
  80. #model_name = "OpenELM-270M-Instruct"
  81. model_name = "OpenELM-270M" # this is fp32
  82. model = Transformer(json.loads(fetch(f"https://huggingface.co/apple/{model_name}/resolve/main/config.json?download=true").read_bytes()))
  83. weights = nn.state.safe_load(fetch(f"https://huggingface.co/apple/{model_name}/resolve/main/model.safetensors?download=true"))
  84. if DEBUG >= 3:
  85. for k, v in weights.items(): print(k, v.shape)
  86. nn.state.load_state_dict(model, {k.removeprefix("transformer."):v for k,v in weights.items()})
  87. from sentencepiece import SentencePieceProcessor
  88. tokenizer = SentencePieceProcessor(fetch("https://github.com/karpathy/llama2.c/raw/master/tokenizer.model").as_posix())
  89. toks = [tokenizer.bos_id()] + tokenizer.encode("Some car brands include")
  90. for i in range(100):
  91. ttoks = Tensor([toks])
  92. out = model(ttoks).realize()
  93. t0 = out[0].argmax(axis=-1).tolist()
  94. toks.append(t0[-1])
  95. # hmmm...passthrough still doesn't match (it shouldn't, it outputs the most likely)
  96. print(tokenizer.decode(toks))
  97. #print(toks)
  98. #print(tokenizer.decode(t0))
  99. #print(t0)