tingqli commited on
Commit
31c0ef2
·
verified ·
1 Parent(s): 264ec0c

Upload folder using huggingface_hub

Browse files
README.md CHANGED
@@ -1,3 +1,20 @@
1
- ---
2
- license: mit
3
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ This is a refactor of [arman-bd/guppylm-9M](https://huggingface.co/arman-bd/guppylm-9M) to be compliant with [transformers's custom_model](https://huggingface.co/docs/transformers/custom_models).
3
+
4
+
5
+ ```bash
6
+ python inference.py guppylm-9M
7
+
8
+ GuppyLMForCausalLM loaded: 8.7M params
9
+
10
+ Guppy Chat (type 'quit' to exit)
11
+
12
+ You> is there a cat in the room?
13
+ Guppy> i don't like it. it puts its face on the glass by the bubbles.
14
+
15
+ You> I'm sorry. are you hungry?
16
+ Guppy> i don't eat it.
17
+
18
+ ```
19
+
20
+ This is a good small LLM for doing execise with SGLang.
config.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "guppylm",
3
+ "architectures": [
4
+ "GuppyLMForCausalLM"
5
+ ],
6
+ "auto_map": {
7
+ "AutoConfig": "configuration_guppylm.GuppyLMConfig",
8
+ "AutoModel": "modeling_guppylm.GuppyLMModel",
9
+ "AutoModelForCausalLM": "modeling_guppylm.GuppyLMForCausalLM"
10
+ },
11
+ "vocab_size": 4096,
12
+ "max_position_embeddings": 128,
13
+ "hidden_size": 384,
14
+ "num_hidden_layers": 6,
15
+ "num_attention_heads": 6,
16
+ "num_key_value_heads": 6,
17
+ "intermediate_size": 768,
18
+ "hidden_dropout_prob": 0.1,
19
+ "pad_token_id": 0,
20
+ "bos_token_id": 1,
21
+ "eos_token_id": 2,
22
+ "tokenizer_class": "PreTrainedTokenizerFast"
23
+ }
configuration_guppylm.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """GuppyLM Hugging Face configuration (PreTrainedConfig)."""
2
+
3
+ from typing import List, Optional
4
+
5
+ from transformers import PretrainedConfig
6
+
7
+
8
+ class GuppyLMConfig(PretrainedConfig):
9
+ """Configuration for GuppyLM.
10
+
11
+ Uses Transformers-standard field names in JSON; modeling code may also read
12
+ aliases via properties (``d_model``, ``n_heads``, etc.).
13
+ """
14
+
15
+ model_type = "guppylm"
16
+
17
+ def __init__(
18
+ self,
19
+ vocab_size: int = 4096,
20
+ max_position_embeddings: int = 128,
21
+ hidden_size: int = 384,
22
+ num_hidden_layers: int = 6,
23
+ num_attention_heads: int = 6,
24
+ intermediate_size: int = 768,
25
+ hidden_dropout_prob: float = 0.1,
26
+ pad_token_id: int = 0,
27
+ bos_token_id: int = 1,
28
+ eos_token_id: int = 2,
29
+ architectures: Optional[List[str]] = None,
30
+ tokenizer_class: Optional[str] = "PreTrainedTokenizerFast",
31
+ attn_implementation: Optional[str] = "eager",
32
+ **kwargs,
33
+ ):
34
+ self.vocab_size = vocab_size
35
+ self.max_position_embeddings = max_position_embeddings
36
+ self.hidden_size = hidden_size
37
+ self.num_hidden_layers = num_hidden_layers
38
+ self.num_attention_heads = num_attention_heads
39
+ self.intermediate_size = intermediate_size
40
+ self.hidden_dropout_prob = hidden_dropout_prob
41
+ if architectures is None:
42
+ architectures = ["GuppyLMForCausalLM"]
43
+ self.architectures = architectures
44
+ super().__init__(
45
+ architectures=architectures,
46
+ pad_token_id=pad_token_id,
47
+ bos_token_id=bos_token_id,
48
+ eos_token_id=eos_token_id,
49
+ tokenizer_class=tokenizer_class,
50
+ tie_word_embeddings=True,
51
+ **kwargs,
52
+ )
53
+
54
+ # --- Aliases for original GuppyLM / training code naming ---
55
+
56
+ @property
57
+ def max_seq_len(self) -> int:
58
+ return self.max_position_embeddings
59
+
60
+ @property
61
+ def d_model(self) -> int:
62
+ return self.hidden_size
63
+
64
+ @property
65
+ def n_layers(self) -> int:
66
+ return self.num_hidden_layers
67
+
68
+ @property
69
+ def n_heads(self) -> int:
70
+ return self.num_attention_heads
71
+
72
+ @property
73
+ def ffn_hidden(self) -> int:
74
+ return self.intermediate_size
75
+
76
+ @property
77
+ def dropout(self) -> float:
78
+ return self.hidden_dropout_prob
79
+
80
+ @property
81
+ def pad_id(self) -> int:
82
+ return self.pad_token_id
83
+
84
+ @property
85
+ def bos_id(self) -> int:
86
+ return self.bos_token_id
87
+
88
+ @property
89
+ def eos_id(self) -> int:
90
+ return self.eos_token_id
decode_input_ids.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import sys
3
+ import torch
4
+ from transformers import AutoTokenizer
5
+
6
+ input_ids=[32, 720, 103, 432, 146, 167, 1400, 195, 294, 1083, 111]
7
+ input_ids=[1, 88, 64, 779, 2, 64, 1, 89, 64]
8
+ model = "/sgl-workspace/guppylm-9M"
9
+ tokenizer = AutoTokenizer.from_pretrained(
10
+ model,
11
+ trust_remote_code=True,
12
+ )
13
+ text = tokenizer.decode(
14
+ torch.tensor(input_ids, dtype=torch.long),
15
+ skip_special_tokens=False,
16
+ )
17
+ sys.stdout.write(text)
inference.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """GuppyLM inference — simple chat."""
2
+
3
+ import os
4
+
5
+ import torch
6
+ from transformers import AutoModelForCausalLM, AutoTokenizer, PreTrainedTokenizerFast
7
+
8
+
9
+ def _resolve_pretrained_dir(model_dir: str) -> str:
10
+ """Directory with Hugging Face layout: ``config.json`` + ``pytorch_model.bin`` / ``model.safetensors``.
11
+
12
+ Accepts either that directory or the path to a weight file inside it (e.g. ``pytorch_model.bin``).
13
+ """
14
+ path = os.path.abspath(model_dir)
15
+ return path if os.path.isdir(path) else os.path.dirname(path)
16
+
17
+
18
+ def _resolve_tokenizer_dir(tokenizer_path: str) -> tuple[str, str]:
19
+ """Return ``(directory, tokenizer_filename)`` for HF tokenizer loading."""
20
+ path = os.path.abspath(tokenizer_path)
21
+ if os.path.isdir(path):
22
+ return path, "tokenizer.json"
23
+ return os.path.dirname(path), os.path.basename(path)
24
+
25
+
26
+ def _load_autotokenizer(tokenizer_path: str, *, trust_remote_code: bool = True):
27
+ """Load ``AutoTokenizer`` when ``config.json`` is present (declares ``tokenizer_class``); otherwise fast tokenizer only."""
28
+ tok_dir, tokenizer_file = _resolve_tokenizer_dir(tokenizer_path)
29
+ if os.path.isfile(os.path.join(tok_dir, "config.json")):
30
+ return AutoTokenizer.from_pretrained(tok_dir, trust_remote_code=trust_remote_code)
31
+ return PreTrainedTokenizerFast.from_pretrained(tok_dir, tokenizer_file=tokenizer_file)
32
+
33
+
34
+ class GuppyInference:
35
+ def __init__(self, model_dir, device="cpu", trust_remote_code=True):
36
+ self.device = torch.device(device)
37
+ pretrained_dir = _resolve_pretrained_dir(model_dir)
38
+ self.tokenizer = _load_autotokenizer(pretrained_dir, trust_remote_code=trust_remote_code)
39
+
40
+ self.model = AutoModelForCausalLM.from_pretrained(
41
+ pretrained_dir,
42
+ trust_remote_code=trust_remote_code,
43
+ )
44
+ self.model.to(self.device)
45
+ self.model.eval()
46
+ self.config = self.model.config
47
+
48
+ total, _ = self.model.param_count()
49
+ print(f"{self.model.__class__.__name__} loaded: {total/1e6:.1f}M params")
50
+
51
+ def chat_completion(self, messages, temperature=0.001, max_tokens=64,
52
+ top_k=0, **kwargs):
53
+ """Chat completion — takes messages, returns response."""
54
+ prompt = self._render_chat_prompt(messages)
55
+ input_ids = self.tokenizer.encode(prompt, add_special_tokens=False)
56
+ prompt_tokens = len(input_ids)
57
+ input_t = torch.tensor([input_ids], dtype=torch.long, device=self.device)
58
+
59
+ output_t, _ = self.model.generate_simple(
60
+ input_t, max_new_tokens=max_tokens, temperature=temperature, top_k=top_k
61
+ )
62
+ output_text = self.tokenizer.decode(
63
+ output_t[0].tolist()[prompt_tokens:], skip_special_tokens=False
64
+ )
65
+ # Truncate at first <|im_end|> — don't let the model leak into the next turn
66
+ if "<|im_end|>" in output_text:
67
+ output_text = output_text.split("<|im_end|>")[0]
68
+ # Also strip any <|im_start|> fragments
69
+ if "<|im_start|>" in output_text:
70
+ output_text = output_text.split("<|im_start|>")[0]
71
+ resp_text = output_text.strip()
72
+
73
+ return {
74
+ "choices": [{
75
+ "message": {"role": "assistant", "content": resp_text},
76
+ }],
77
+ }
78
+
79
+ def _render_chat_prompt(self, messages):
80
+ """Prefer tokenizer ``chat_template`` (HF ``apply_chat_template``); fall back if absent."""
81
+ if getattr(self.tokenizer, "chat_template", None):
82
+ return self.tokenizer.apply_chat_template(
83
+ messages,
84
+ tokenize=False,
85
+ add_generation_prompt=True,
86
+ )
87
+ parts = []
88
+ for msg in messages:
89
+ role = msg.get("role", "user")
90
+ content = msg.get("content") or ""
91
+ if role == "system":
92
+ continue
93
+ parts.append(f"<|im_start|>{role}\n{content}<|im_end|>")
94
+ parts.append("<|im_start|>assistant\n")
95
+ return "\n".join(parts)
96
+
97
+
98
+ def main():
99
+ import argparse
100
+ p = argparse.ArgumentParser(description="Chat with Guppy")
101
+ p.add_argument(
102
+ "model_dir",
103
+ help="Directory with config.json, tokenizer.json, and weights (or path to a weight file in that directory)",
104
+ )
105
+ p.add_argument("--device", default="cpu")
106
+ args = p.parse_args()
107
+
108
+ engine = GuppyInference(args.model_dir, 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()
modeling_guppylm.py ADDED
@@ -0,0 +1,268 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ GuppyLM — Hugging Face PreTrainedModel wrapper.
3
+
4
+ Vanilla transformer: multi-head attention, ReLU FFN, LayerNorm, learned positional embeddings.
5
+ """
6
+
7
+ import math
8
+ from typing import Optional, Tuple, Union
9
+
10
+ import torch
11
+ import torch.nn as nn
12
+ import torch.nn.functional as F
13
+ from transformers import PreTrainedModel
14
+ from transformers.generation.utils import GenerationMixin
15
+ from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
16
+ import time
17
+
18
+ try:
19
+ from .configuration_guppylm import GuppyLMConfig
20
+ except ImportError:
21
+ from configuration_guppylm import GuppyLMConfig
22
+
23
+ class Attention(nn.Module):
24
+ def __init__(self, config: GuppyLMConfig, layer_id: int):
25
+ super().__init__()
26
+ self.n_heads = config.n_heads
27
+ self.head_dim = config.d_model // config.n_heads
28
+ self.layer_id = layer_id
29
+ self.qkv = nn.Linear(config.d_model, 3 * config.d_model)
30
+ self.out = nn.Linear(config.d_model, config.d_model)
31
+ self.dropout = nn.Dropout(config.dropout)
32
+
33
+ def forward(self, x, mask=None, forward_batch=None, attention_instances=None):
34
+ B, T, C = x.shape
35
+
36
+ if attention_instances is not None:
37
+ qkv = self.qkv(x).reshape(B, T, 3, self.n_heads*self.head_dim).permute(2, 0, 1, 3)
38
+ q, k, v = qkv[0], qkv[1], qkv[2]
39
+ attn_out = attention_instances[self.layer_id].forward(q, k, v, forward_batch)
40
+ return self.out(attn_out).contiguous().view(B, T, C)
41
+ else:
42
+ qkv = self.qkv(x).reshape(B, T, 3, self.n_heads, self.head_dim).permute(2, 0, 3, 1, 4)
43
+ q, k, v = qkv[0], qkv[1], qkv[2]
44
+ attn = (q @ k.transpose(-2, -1)) / math.sqrt(self.head_dim)
45
+ if mask is not None:
46
+ attn = attn.masked_fill(mask == 0, float("-inf"))
47
+ attn = self.dropout(F.softmax(attn, dim=-1))
48
+
49
+ return self.out((attn @ v).transpose(1, 2).contiguous().view(B, T, C))
50
+
51
+
52
+ class FFN(nn.Module):
53
+ def __init__(self, config: GuppyLMConfig):
54
+ super().__init__()
55
+ self.up = nn.Linear(config.d_model, config.ffn_hidden)
56
+ self.down = nn.Linear(config.ffn_hidden, config.d_model)
57
+ self.dropout = nn.Dropout(config.dropout)
58
+
59
+ def forward(self, x):
60
+ return self.dropout(self.down(F.relu(self.up(x))))
61
+
62
+
63
+ class Block(nn.Module):
64
+ def __init__(self, config: GuppyLMConfig, layer_id: int):
65
+ super().__init__()
66
+ self.layer_id = layer_id
67
+ self.norm1 = nn.LayerNorm(config.d_model)
68
+ self.attn = Attention(config, layer_id)
69
+ self.norm2 = nn.LayerNorm(config.d_model)
70
+ self.ffn = FFN(config)
71
+
72
+ def forward(self, x, mask=None, forward_batch=None, attention_instances=None):
73
+ x = x + self.attn(self.norm1(x), mask, forward_batch, attention_instances)
74
+ x = x + self.ffn(self.norm2(x))
75
+ return x
76
+
77
+
78
+ class GuppyLMModel(PreTrainedModel):
79
+ """Backbone only (hidden states). Used as ``AutoModel`` for SGLang / Transformers tooling."""
80
+
81
+ config_class = GuppyLMConfig
82
+ base_model_prefix = "model"
83
+ supports_gradient_checkpointing = False
84
+ _supports_attention_backend = True
85
+
86
+ def __init__(self, config: GuppyLMConfig):
87
+ super().__init__(config)
88
+
89
+ # assert 0
90
+ self.tok_emb = nn.Embedding(config.vocab_size, config.d_model)
91
+ self.pos_emb = nn.Embedding(config.max_seq_len, config.d_model)
92
+ self.drop = nn.Dropout(config.dropout)
93
+ self.blocks = nn.ModuleList([Block(config, i) for i in range(config.n_layers)])
94
+ self.norm = nn.LayerNorm(config.d_model)
95
+ self.apply(self._init_weights)
96
+ self.post_init()
97
+ self.input_ids = None
98
+
99
+ def _init_weights(self, m):
100
+ if isinstance(m, nn.Linear):
101
+ nn.init.normal_(m.weight, mean=0.0, std=0.02)
102
+ if m.bias is not None:
103
+ nn.init.zeros_(m.bias)
104
+ elif isinstance(m, nn.Embedding):
105
+ nn.init.normal_(m.weight, mean=0.0, std=0.02)
106
+
107
+ def get_input_embeddings(self):
108
+ return self.tok_emb
109
+
110
+ def set_input_embeddings(self, value: nn.Module):
111
+ self.tok_emb = value
112
+
113
+ def forward(
114
+ self,
115
+ input_ids: Optional[torch.LongTensor] = None,
116
+ attention_mask: Optional[torch.Tensor] = None,
117
+ forward_batch=None,
118
+ attention_instances=None,
119
+ return_dict: Optional[bool] = None,
120
+ **kwargs,
121
+ ) -> Union[Tuple, BaseModelOutputWithPast]:
122
+
123
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
124
+ if input_ids is None:
125
+ raise ValueError("You must specify `input_ids`.")
126
+
127
+ if attention_instances is None:
128
+ B, T = input_ids.shape
129
+ pos = torch.arange(T, device=input_ids.device)
130
+ x = self.drop(self.tok_emb(input_ids) + self.pos_emb(pos))
131
+ mask = torch.tril(torch.ones(T, T, device=input_ids.device)).unsqueeze(0).unsqueeze(0)
132
+ else:
133
+ pos = forward_batch.positions
134
+ x = self.drop(self.tok_emb(input_ids) + self.pos_emb(pos))
135
+ mask = None
136
+
137
+ for block in self.blocks:
138
+ x = block(x, mask, forward_batch, attention_instances)
139
+
140
+ hidden_states = self.norm(x)
141
+
142
+ if not return_dict:
143
+ return (hidden_states,)
144
+
145
+ return BaseModelOutputWithPast(
146
+ last_hidden_state=hidden_states,
147
+ past_key_values=None,
148
+ hidden_states=None,
149
+ attentions=None,
150
+ )
151
+
152
+
153
+ class GuppyLMForCausalLM(PreTrainedModel, GenerationMixin):
154
+ """Causal LM. Checkpoints may use flat keys (legacy) or nested ``model.*`` keys."""
155
+
156
+ config_class = GuppyLMConfig
157
+ base_model_prefix = "model"
158
+ supports_gradient_checkpointing = False
159
+ _supports_attention_backend = True
160
+
161
+ def __init__(self, config: GuppyLMConfig):
162
+ super().__init__(config)
163
+
164
+ self.model = GuppyLMModel(config)
165
+ self.lm_head = nn.Linear(config.d_model, config.vocab_size, bias=False)
166
+ nn.init.normal_(self.lm_head.weight, mean=0.0, std=0.02)
167
+ self.post_init()
168
+ self.lm_head.weight = self.model.tok_emb.weight
169
+
170
+ def get_input_embeddings(self):
171
+ return self.model.tok_emb
172
+
173
+ def set_input_embeddings(self, value: nn.Module):
174
+ self.model.tok_emb = value
175
+
176
+ def get_output_embeddings(self):
177
+ return self.lm_head
178
+
179
+ def set_output_embeddings(self, new_embeddings: nn.Module):
180
+ self.lm_head = new_embeddings
181
+
182
+ def prepare_inputs_for_generation(
183
+ self, input_ids, past_key_values=None, attention_mask=None, **kwargs
184
+ ):
185
+ return {"input_ids": input_ids}
186
+
187
+ def load_state_dict(
188
+ self,
189
+ state_dict: dict,
190
+ strict: bool = True,
191
+ assign: bool = False,
192
+ ):
193
+ """Accept legacy flat checkpoints (``tok_emb.*``, ``blocks.*``, …) as well as nested ``model.*``."""
194
+ keys = list(state_dict.keys())
195
+ if keys and not any(k.startswith("model.") for k in keys):
196
+ remapped = {}
197
+ for k, v in state_dict.items():
198
+ if k.startswith("lm_head."):
199
+ remapped[k] = v
200
+ else:
201
+ remapped[f"model.{k}"] = v
202
+ state_dict = remapped
203
+ return super().load_state_dict(state_dict, strict=strict, assign=assign)
204
+
205
+ def forward(
206
+ self,
207
+ input_ids: Optional[torch.LongTensor] = None,
208
+ attention_mask: Optional[torch.Tensor] = None,
209
+ labels: Optional[torch.LongTensor] = None,
210
+ return_dict: Optional[bool] = None,
211
+ **kwargs,
212
+ ) -> Union[Tuple, CausalLMOutputWithPast]:
213
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
214
+ if input_ids is None:
215
+ raise ValueError("You must specify `input_ids`.")
216
+
217
+ outputs = self.model(input_ids=input_ids, return_dict=True, **kwargs)
218
+ hidden_states = outputs.last_hidden_state
219
+ logits = self.lm_head(hidden_states)
220
+
221
+ loss = None
222
+ if labels is not None:
223
+ loss = F.cross_entropy(
224
+ logits.view(-1, self.config.vocab_size),
225
+ labels.view(-1),
226
+ ignore_index=self.config.pad_token_id,
227
+ )
228
+
229
+ if not return_dict:
230
+ return (loss, logits) if loss is not None else (logits,)
231
+
232
+ return CausalLMOutputWithPast(loss=loss, logits=logits, past_key_values=None)
233
+
234
+ @torch.no_grad()
235
+ def generate_simple(
236
+ self,
237
+ idx: torch.LongTensor,
238
+ max_new_tokens: int = 64,
239
+ temperature: float = 0.7,
240
+ top_k: int = 50,
241
+ **kwargs,
242
+ ):
243
+ """Original sampling loop (non-HF ``generate`` API)."""
244
+ self.eval()
245
+ for _ in range(max_new_tokens):
246
+ idx_cond = idx[:, -self.config.max_seq_len :]
247
+ out = self.forward(input_ids=idx_cond, return_dict=True)
248
+ logits = out.logits[:, -1, :] / temperature
249
+ if top_k > 0:
250
+ v, _ = torch.topk(logits, min(top_k, logits.size(-1)))
251
+ logits[logits < v[:, [-1]]] = float("-inf")
252
+ probs = F.softmax(logits, dim=-1)
253
+ next_id = torch.multinomial(probs, num_samples=1)
254
+ idx = torch.cat([idx, next_id], dim=1)
255
+ if next_id.item() == self.config.eos_token_id:
256
+ break
257
+ return idx, []
258
+
259
+ def param_count(self):
260
+ total = sum(p.numel() for p in self.parameters())
261
+ return total, 0
262
+
263
+ def param_summary(self):
264
+ total, _ = self.param_count()
265
+ return f"GuppyLM: {total:,} params ({total / 1e6:.1f}M)"
266
+
267
+
268
+ __all__ = ["GuppyLMModel", "GuppyLMForCausalLM", "GuppyLMConfig"]
pytorch_model.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:db46a64d497a9007aaf0965e950d836a62c4d9d435593bcba7210e388e468c8d
3
+ size 34928171
test.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import aiohttp
2
+ import asyncio
3
+ import os
4
+ import time
5
+ from datetime import datetime
6
+ import random
7
+
8
+ def _base_url() -> str:
9
+ host = os.environ.get("SGLANG_HTTP_HOST", "127.0.0.1")
10
+ port = os.environ.get("SGLANG_HTTP_PORT", "30000")
11
+ return f"http://{host}:{port}"
12
+
13
+
14
+ database = [
15
+ ["who are you?",
16
+ "i'm just a little fish. i have little body. they work. i eat the red flakes when i can.",
17
+ "hi",
18
+ "i'm just a snail would be nice.",
19
+ "what is the capital of France?",
20
+ "i am a bubble wall.",
21
+ ],
22
+ ["is there a cat in the room?",
23
+ "i don't like it. it puts its face on the glass by the bubbles.",
24
+ "I'm sorry. are you hungry?",
25
+ "i don't eat it.",
26
+ ]
27
+ ]
28
+
29
+ async def main(num_tests: int, concurrency: int):
30
+ url = f"{_base_url()}/v1/chat/completions"
31
+ headers = {"Content-Type": "application/json"}
32
+ RED = "\033[0;31m"
33
+ GREEN = "\033[0;32m"
34
+ YELLOW = "\033[0;33m"
35
+ BLUE = "\033[0;34m"
36
+ MAGENTA = "\033[0;35m"
37
+ CYAN = "\033[0;36m"
38
+ WHITE = "\033[0;37m"
39
+ END = "\033[0m"
40
+ timeout = aiohttp.ClientTimeout(
41
+ total=600, # Total timeout for entire request
42
+ connect=10, # Connection timeout
43
+ sock_read=30 # Socket read timeout
44
+ )
45
+ async with aiohttp.ClientSession(timeout=timeout) as session:
46
+
47
+ if num_tests == 0:
48
+ # realtime chat
49
+ json = {"model": "guppylm-9M",
50
+ "messages": [],
51
+ "temperature" : 0.001,
52
+ "top_k": 1,
53
+ "stream": False,
54
+ }
55
+ while True:
56
+ user_input = input("user: ")
57
+ json["messages"].append({"role": "user", "content": user_input})
58
+ async with session.post(url, json=json, headers=headers) as response:
59
+ res = (await response.json())["choices"][0]["message"]
60
+ output = res["content"]
61
+ role = res["role"]
62
+ json["messages"].append({"role": role, "content": output})
63
+ print(f"{role}: {output}")
64
+ else:
65
+ concurrency_sem = asyncio.Semaphore(max(1,concurrency))
66
+ async def job():
67
+ json = {"model": "guppylm-9M",
68
+ "messages": [],
69
+ "temperature" : 0.001,
70
+ "top_k": 1,
71
+ "stream": False,
72
+ }
73
+ random_index = random.randint(0, len(database) - 1)
74
+ user_inputs = database[random_index]
75
+
76
+ unexpected_count = 0
77
+ logs = []
78
+ for input, expected in zip(user_inputs[0::2], user_inputs[1::2]):
79
+ json["messages"].append({"role": "user", "content": input})
80
+ logs.append(datetime.now().strftime("%H:%M:%S.%f")[:-3] + " " + " user: " + input)
81
+ async with concurrency_sem:
82
+ async with session.post(url, json=json, headers=headers) as response:
83
+ res = (await response.json())["choices"][0]["message"]
84
+ output = res["content"]
85
+ role = res["role"]
86
+ color = GREEN if output == expected else RED
87
+ unexpected_count += 1 if output != expected else 0
88
+ json["messages"].append({"role": role, "content": output})
89
+ logs.append(datetime.now().strftime("%H:%M:%S.%f")[:-3] + " " + "assistant: " + color + output + END)
90
+
91
+ print("=========\n","\n".join(logs))
92
+ return unexpected_count
93
+
94
+ tasks = []
95
+ for i in range(num_tests):
96
+ tasks.append(asyncio.create_task(job()))
97
+ results = await asyncio.gather(*tasks)
98
+ return sum(results)
99
+
100
+ if 1:
101
+ unexpected_count = asyncio.run(main(10, 10))
102
+ print(f"unexpected count: {unexpected_count}")
103
+
104
+ asyncio.run(main(0, 0))
105
+
106
+
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "chat_template": "{% for message in messages | rejectattr('role', 'equalto', 'system') %}{% if not loop.first %}{{ '\\n' }}{% endif %}<|im_start|>{{ message['role'] }}\n{{ message['content'] }}<|im_end|>{% endfor %}{{ '\\n' }}{% if add_generation_prompt %}<|im_start|>assistant\n{% endif %}"
3
+ }