tamsuiboy arman-bd commited on
Commit
83ee59a
·
0 Parent(s):

Duplicate from arman-bd/guppylm-9M

Browse files

Co-authored-by: Arman Hossain <arman-bd@users.noreply.huggingface.co>

Files changed (11) hide show
  1. .gitattributes +37 -0
  2. README.md +98 -0
  3. assets/guppy.png +3 -0
  4. config.json +16 -0
  5. config.py +36 -0
  6. inference.py +124 -0
  7. model.onnx +3 -0
  8. model.py +129 -0
  9. model_fp32.onnx.data +3 -0
  10. pytorch_model.bin +3 -0
  11. tokenizer.json +0 -0
.gitattributes ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ assets/guppy.png filter=lfs diff=lfs merge=lfs -text
37
+ model_fp32.onnx.data filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ language:
4
+ - en
5
+ tags:
6
+ - fish
7
+ - character
8
+ - tiny-llm
9
+ - text-generation
10
+ - from-scratch
11
+ pipeline_tag: text-generation
12
+ ---
13
+
14
+ <p align="center">
15
+ <img src="assets/guppy.png" alt="GuppyLM" width="300"/>
16
+ </p>
17
+
18
+ <p align="center">
19
+ <a href="https://github.com/arman-bd/guppylm"><img src="https://img.shields.io/badge/GitHub-guppylm-181717?logo=github" alt="GitHub"/></a>&nbsp;
20
+ <a href="https://huggingface.co/datasets/arman-bd/guppylm-60k-generic"><img src="https://img.shields.io/badge/🤗_Dataset-guppylm--60k-blue" alt="Dataset"/></a>&nbsp;
21
+ <a href="https://colab.research.google.com/github/arman-bd/guppylm/blob/main/use_guppylm.ipynb"><img src="https://img.shields.io/badge/Open_in-Colab-F9AB00?logo=googlecolab" alt="Colab"/></a>
22
+ <br/>
23
+ <a href="https://www.linkedin.com/pulse/build-your-own-language-model-5-minutes-i-made-mine-hossain--supif/"><img src="https://img.shields.io/badge/Article-LinkedIn-0A66C2?logo=linkedin" alt="LinkedIn Article"/></a>&nbsp;
24
+ <a href="https://arman-bd.medium.com/build-your-own-llm-in-5-minutes-i-made-mine-talk-like-a-fish-e20c338a3d14"><img src="https://img.shields.io/badge/Article-Medium-000000?logo=medium" alt="Medium Article"/></a>
25
+ <br/><br/>
26
+ <a href="https://arman-bd.github.io/guppylm/"><img src="https://img.shields.io/badge/Try_in-Browser-64ffda?style=for-the-badge&logo=webassembly&logoColor=white" alt="Browser Demo"/></a>
27
+ </p>
28
+
29
+ # GuppyLM — 9M Parameter Fish Chat Model
30
+
31
+ A ~9M parameter LLM trained from scratch that talks like a small fish named Guppy.
32
+
33
+ This project exists to show that training your own language model is not magic. One Colab notebook, 5 minutes, and you have a working LLM built from scratch.
34
+
35
+ ## Example
36
+
37
+ ```
38
+ You> hi guppy
39
+ Guppy> hi there. i just found a nice spot near the rock. the temperature feels nice.
40
+
41
+ You> are you hungry
42
+ Guppy> yes. always yes. i will swim to the top right now. i promise to eat all of it.
43
+
44
+ You> what is the meaning of life
45
+ Guppy> food. the answer is always food.
46
+
47
+ You> tell me a joke
48
+ Guppy> what did the fish say when it hit the wall. dam.
49
+
50
+ You> goodnight guppy
51
+ Guppy> ok sleep time. i was following a bubble but now i'll stop. goodnight tank. goodnight water.
52
+ ```
53
+
54
+ ## Architecture
55
+
56
+ | | |
57
+ |---|---|
58
+ | **Parameters** | 8.7M |
59
+ | **Type** | Vanilla transformer (from scratch) |
60
+ | **Layers** | 6 |
61
+ | **Hidden dim** | 384 |
62
+ | **Heads** | 6 |
63
+ | **FFN** | 768 (ReLU) |
64
+ | **Vocab** | 4,096 (BPE) |
65
+ | **Max sequence** | 128 tokens |
66
+ | **Norm** | LayerNorm |
67
+ | **Position** | Learned embeddings |
68
+ | **LM head** | Weight-tied with embeddings |
69
+
70
+ No GQA, no RoPE, no SwiGLU, no early exit. As simple as it gets.
71
+
72
+ ## Training
73
+
74
+ - **Data:** 60K single-turn synthetic conversations across 60 topics
75
+ - **Steps:** 10,000
76
+ - **Optimizer:** AdamW (cosine LR schedule)
77
+ - **Hardware:** T4 GPU (~5 min)
78
+ - **No system prompt** — personality is baked into the weights
79
+
80
+ ## Usage
81
+
82
+ ```python
83
+ from inference import GuppyInference
84
+
85
+ engine = GuppyInference('checkpoints/best_model.pt', 'data/tokenizer.json')
86
+ r = engine.chat_completion([{'role': 'user', 'content': 'hi guppy'}])
87
+ print(r['choices'][0]['message']['content'])
88
+ # hi there. i just found a nice spot near the rock.
89
+ ```
90
+
91
+ ## Links
92
+
93
+ - **Repo:** [github.com/arman-bd/guppylm](https://github.com/arman-bd/guppylm)
94
+ - **Dataset:** [huggingface.co/datasets/arman-bd/guppylm-60k-generic](https://huggingface.co/datasets/arman-bd/guppylm-60k-generic)
95
+
96
+ ## License
97
+
98
+ MIT
assets/guppy.png ADDED

Git LFS Details

  • SHA256: 4a62fb319776d5e67306ff390acdd7d7118039eccab2e40f04c65e0881f92448
  • Pointer size: 131 Bytes
  • Size of remote file: 245 kB
config.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "guppylm",
3
+ "architectures": [
4
+ "GuppyLM"
5
+ ],
6
+ "vocab_size": 4096,
7
+ "max_position_embeddings": 128,
8
+ "hidden_size": 384,
9
+ "num_hidden_layers": 6,
10
+ "num_attention_heads": 6,
11
+ "intermediate_size": 768,
12
+ "hidden_dropout_prob": 0.1,
13
+ "pad_token_id": 0,
14
+ "bos_token_id": 1,
15
+ "eos_token_id": 2
16
+ }
config.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """GuppyLM configuration."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass
7
+ class GuppyConfig:
8
+ vocab_size: int = 4096
9
+ max_seq_len: int = 128
10
+ d_model: int = 384
11
+ n_layers: int = 6
12
+ n_heads: int = 6
13
+ ffn_hidden: int = 768
14
+ dropout: float = 0.1
15
+
16
+ # Special tokens
17
+ pad_id: int = 0
18
+ bos_id: int = 1 # <|im_start|>
19
+ eos_id: int = 2 # <|im_end|>
20
+
21
+
22
+ @dataclass
23
+ class TrainConfig:
24
+ batch_size: int = 32
25
+ learning_rate: float = 3e-4
26
+ min_lr: float = 3e-5
27
+ weight_decay: float = 0.1
28
+ warmup_steps: int = 200
29
+ max_steps: int = 10000
30
+ eval_interval: int = 200
31
+ save_interval: int = 500
32
+ grad_clip: float = 1.0
33
+ device: str = "auto"
34
+ seed: int = 42
35
+ data_dir: str = "data"
36
+ output_dir: str = "checkpoints"
inference.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """GuppyLM inference — simple chat."""
2
+
3
+ import json
4
+ import time
5
+ import uuid
6
+
7
+ import torch
8
+ from tokenizers import Tokenizer
9
+
10
+ from config import GuppyConfig
11
+ from model import GuppyLM
12
+
13
+
14
+ class GuppyInference:
15
+ def __init__(self, checkpoint_path, tokenizer_path, device="cpu"):
16
+ self.device = torch.device(device)
17
+ self.tokenizer = Tokenizer.from_file(tokenizer_path)
18
+
19
+ import os
20
+ ckpt = torch.load(checkpoint_path, map_location=self.device, weights_only=False)
21
+
22
+ # Load config.json from same directory as the model file
23
+ config_dir = os.path.dirname(os.path.abspath(checkpoint_path))
24
+ config_path = os.path.join(config_dir, "config.json")
25
+
26
+ # Extract state_dict — handle both legacy and standard formats
27
+ if isinstance(ckpt, dict) and "model_state_dict" in ckpt:
28
+ state_dict = ckpt["model_state_dict"]
29
+ else:
30
+ state_dict = ckpt
31
+
32
+ # Load config — try config.json first, fall back to embedded config
33
+ if os.path.exists(config_path):
34
+ with open(config_path) as f:
35
+ cfg = json.load(f)
36
+ # Support both HF standard keys and our own keys
37
+ self.config = GuppyConfig(
38
+ vocab_size=cfg.get("vocab_size", 4096),
39
+ max_seq_len=cfg.get("max_position_embeddings", cfg.get("max_seq_len", 128)),
40
+ d_model=cfg.get("hidden_size", cfg.get("d_model", 384)),
41
+ n_layers=cfg.get("num_hidden_layers", cfg.get("n_layers", 6)),
42
+ n_heads=cfg.get("num_attention_heads", cfg.get("n_heads", 6)),
43
+ ffn_hidden=cfg.get("intermediate_size", cfg.get("ffn_hidden", 768)),
44
+ dropout=cfg.get("hidden_dropout_prob", cfg.get("dropout", 0.1)),
45
+ pad_id=cfg.get("pad_token_id", cfg.get("pad_id", 0)),
46
+ bos_id=cfg.get("bos_token_id", cfg.get("bos_id", 1)),
47
+ eos_id=cfg.get("eos_token_id", cfg.get("eos_id", 2)),
48
+ )
49
+ elif isinstance(ckpt, dict) and "config" in ckpt:
50
+ valid_fields = {f.name for f in GuppyConfig.__dataclass_fields__.values()}
51
+ self.config = GuppyConfig(**{k: v for k, v in ckpt["config"].items() if k in valid_fields})
52
+ else:
53
+ print("Warning: No config found, using defaults")
54
+ self.config = GuppyConfig()
55
+
56
+ self.model = GuppyLM(self.config).to(self.device)
57
+ filtered = {k: v for k, v in state_dict.items() if k in self.model.state_dict()}
58
+ self.model.load_state_dict(filtered)
59
+ self.model.eval()
60
+
61
+ total, _ = self.model.param_count()
62
+ print(f"GuppyLM loaded: {total/1e6:.1f}M params")
63
+
64
+ def chat_completion(self, messages, temperature=0.7, max_tokens=64,
65
+ top_k=50, **kwargs):
66
+ """Chat completion — takes messages, returns response."""
67
+ prompt = self._format_prompt(messages)
68
+ input_ids = self.tokenizer.encode(prompt).ids
69
+ prompt_tokens = len(input_ids)
70
+ input_t = torch.tensor([input_ids], dtype=torch.long, device=self.device)
71
+
72
+ output_t, _ = self.model.generate(input_t, max_tokens, temperature, top_k)
73
+ output_text = self.tokenizer.decode(output_t[0].tolist()[prompt_tokens:])
74
+ # Truncate at first <|im_end|> — don't let the model leak into the next turn
75
+ if "<|im_end|>" in output_text:
76
+ output_text = output_text.split("<|im_end|>")[0]
77
+ # Also strip any <|im_start|> fragments
78
+ if "<|im_start|>" in output_text:
79
+ output_text = output_text.split("<|im_start|>")[0]
80
+ resp_text = output_text.strip()
81
+
82
+ return {
83
+ "choices": [{
84
+ "message": {"role": "assistant", "content": resp_text},
85
+ }],
86
+ }
87
+
88
+ def _format_prompt(self, messages):
89
+ parts = []
90
+ for msg in messages:
91
+ role = msg.get("role", "user")
92
+ content = msg.get("content") or ""
93
+ if role == "system":
94
+ continue
95
+ parts.append(f"<|im_start|>{role}\n{content}<|im_end|>")
96
+ parts.append("<|im_start|>assistant\n")
97
+ return "\n".join(parts)
98
+
99
+
100
+ def main():
101
+ import argparse
102
+ p = argparse.ArgumentParser(description="Chat with Guppy")
103
+ p.add_argument("--checkpoint", default="checkpoints/best_model.pt")
104
+ p.add_argument("--tokenizer", default="data/tokenizer.json")
105
+ p.add_argument("--device", default="cpu")
106
+ args = p.parse_args()
107
+
108
+ engine = GuppyInference(args.checkpoint, args.tokenizer, args.device)
109
+ print("\nGuppy Chat (type 'quit' to exit)")
110
+ msgs = []
111
+ while True:
112
+ inp = input("\nYou> ").strip()
113
+ if inp.lower() in ("quit", "exit", "q"):
114
+ break
115
+ msgs.append({"role": "user", "content": inp})
116
+ result = engine.chat_completion(msgs)
117
+ msg = result["choices"][0]["message"]
118
+ if msg.get("content"):
119
+ print(f"Guppy> {msg['content']}")
120
+ msgs.append(msg)
121
+
122
+
123
+ if __name__ == "__main__":
124
+ main()
model.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:96dd79d233662b65bc0fae1773821479551d1c1257ca5bbd12fa3e6623ae4835
3
+ size 10469869
model.py ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ GuppyLM — a tiny fish brain.
3
+
4
+ Vanilla transformer: multi-head attention, ReLU FFN, LayerNorm, learned positional embeddings.
5
+ No GQA, no SwiGLU, no parallel residual, no RoPE. As simple as it gets.
6
+ """
7
+
8
+ import math
9
+ import torch
10
+ import torch.nn as nn
11
+ import torch.nn.functional as F
12
+ from config import GuppyConfig
13
+
14
+
15
+ class Attention(nn.Module):
16
+ def __init__(self, config):
17
+ super().__init__()
18
+ self.n_heads = config.n_heads
19
+ self.head_dim = config.d_model // config.n_heads
20
+
21
+ self.qkv = nn.Linear(config.d_model, 3 * config.d_model)
22
+ self.out = nn.Linear(config.d_model, config.d_model)
23
+ self.dropout = nn.Dropout(config.dropout)
24
+
25
+ def forward(self, x, mask=None):
26
+ B, T, C = x.shape
27
+ qkv = self.qkv(x).reshape(B, T, 3, self.n_heads, self.head_dim).permute(2, 0, 3, 1, 4)
28
+ q, k, v = qkv[0], qkv[1], qkv[2]
29
+
30
+ attn = (q @ k.transpose(-2, -1)) / math.sqrt(self.head_dim)
31
+ if mask is not None:
32
+ attn = attn.masked_fill(mask == 0, float("-inf"))
33
+ attn = self.dropout(F.softmax(attn, dim=-1))
34
+ return self.out((attn @ v).transpose(1, 2).contiguous().view(B, T, C))
35
+
36
+
37
+ class FFN(nn.Module):
38
+ def __init__(self, config):
39
+ super().__init__()
40
+ self.up = nn.Linear(config.d_model, config.ffn_hidden)
41
+ self.down = nn.Linear(config.ffn_hidden, config.d_model)
42
+ self.dropout = nn.Dropout(config.dropout)
43
+
44
+ def forward(self, x):
45
+ return self.dropout(self.down(F.relu(self.up(x))))
46
+
47
+
48
+ class Block(nn.Module):
49
+ def __init__(self, config):
50
+ super().__init__()
51
+ self.norm1 = nn.LayerNorm(config.d_model)
52
+ self.attn = Attention(config)
53
+ self.norm2 = nn.LayerNorm(config.d_model)
54
+ self.ffn = FFN(config)
55
+
56
+ def forward(self, x, mask=None):
57
+ x = x + self.attn(self.norm1(x), mask)
58
+ x = x + self.ffn(self.norm2(x))
59
+ return x
60
+
61
+
62
+ class GuppyLM(nn.Module):
63
+ def __init__(self, config: GuppyConfig):
64
+ super().__init__()
65
+ self.config = config
66
+
67
+ self.tok_emb = nn.Embedding(config.vocab_size, config.d_model)
68
+ self.pos_emb = nn.Embedding(config.max_seq_len, config.d_model)
69
+ self.drop = nn.Dropout(config.dropout)
70
+ self.blocks = nn.ModuleList([Block(config) for _ in range(config.n_layers)])
71
+ self.norm = nn.LayerNorm(config.d_model)
72
+ self.lm_head = nn.Linear(config.d_model, config.vocab_size, bias=False)
73
+ self.lm_head.weight = self.tok_emb.weight # tie weights
74
+
75
+ self.apply(self._init_weights)
76
+
77
+ def _init_weights(self, m):
78
+ if isinstance(m, nn.Linear):
79
+ nn.init.normal_(m.weight, mean=0.0, std=0.02)
80
+ if m.bias is not None:
81
+ nn.init.zeros_(m.bias)
82
+ elif isinstance(m, nn.Embedding):
83
+ nn.init.normal_(m.weight, mean=0.0, std=0.02)
84
+
85
+ def forward(self, idx, targets=None):
86
+ B, T = idx.shape
87
+ pos = torch.arange(T, device=idx.device)
88
+ x = self.drop(self.tok_emb(idx) + self.pos_emb(pos))
89
+ mask = torch.tril(torch.ones(T, T, device=idx.device)).unsqueeze(0).unsqueeze(0)
90
+
91
+ for block in self.blocks:
92
+ x = block(x, mask)
93
+
94
+ logits = self.lm_head(self.norm(x))
95
+
96
+ loss = None
97
+ if targets is not None:
98
+ loss = F.cross_entropy(
99
+ logits.view(-1, self.config.vocab_size),
100
+ targets.view(-1),
101
+ ignore_index=0,
102
+ )
103
+
104
+ return logits, loss
105
+
106
+ @torch.no_grad()
107
+ def generate(self, idx, max_new_tokens=64, temperature=0.7, top_k=50, **kwargs):
108
+ self.eval()
109
+ for _ in range(max_new_tokens):
110
+ idx_cond = idx[:, -self.config.max_seq_len:]
111
+ logits, _ = self(idx_cond)
112
+ logits = logits[:, -1, :] / temperature
113
+ if top_k > 0:
114
+ v, _ = torch.topk(logits, min(top_k, logits.size(-1)))
115
+ logits[logits < v[:, [-1]]] = float("-inf")
116
+ probs = F.softmax(logits, dim=-1)
117
+ next_id = torch.multinomial(probs, num_samples=1)
118
+ idx = torch.cat([idx, next_id], dim=1)
119
+ if next_id.item() == self.config.eos_id:
120
+ break
121
+ return idx, []
122
+
123
+ def param_count(self):
124
+ total = sum(p.numel() for p in self.parameters())
125
+ return total, 0
126
+
127
+ def param_summary(self):
128
+ total, _ = self.param_count()
129
+ return f"GuppyLM: {total:,} params ({total/1e6:.1f}M)"
model_fp32.onnx.data ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1ea6231b563093549cb85e0c3a76d21c065d6d8e65981cb78596156bcbd681b6
3
+ size 34930688
pytorch_model.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6faec973e60786c8942d632e857c536c5404d4970b4c30ca48c096740d18af79
3
+ size 34930539
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff