Mentors4EDU commited on
Commit
ccf08fa
·
verified ·
1 Parent(s): c0b0ec6

Publish openpeerllm-demo

Browse files
Files changed (3) hide show
  1. README.md +1 -1
  2. analysis.py +127 -0
  3. app.py +145 -83
README.md CHANGED
@@ -1,6 +1,6 @@
1
  ---
2
  title: OpenPeerLLM Demo
3
- emoji: 🕸️
4
  colorFrom: blue
5
  colorTo: green
6
  sdk: gradio
 
1
  ---
2
  title: OpenPeerLLM Demo
3
+ emoji: 🕸️
4
  colorFrom: blue
5
  colorTo: green
6
  sdk: gradio
analysis.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ import html
3
+ import math
4
+ import torch
5
+ import torch.nn.functional as F
6
+
7
+
8
+ @torch.no_grad()
9
+ def token_surprisal(model, tokenizer, text: str, max_length: int = 1024):
10
+ ids = tokenizer(
11
+ text, return_tensors="pt", truncation=True, max_length=max_length
12
+ ).input_ids.to(model.device)
13
+ logits = model(input_ids=ids).logits[0, :-1].float()
14
+ targets = ids[0, 1:]
15
+ logprobs = F.log_softmax(logits, dim=-1)
16
+ tok_lp = logprobs.gather(1, targets[:, None]).squeeze(1)
17
+ ranks = (logprobs > tok_lp[:, None]).sum(dim=1) + 1
18
+ tokens = [tokenizer.decode([t]) for t in ids[0].tolist()]
19
+ nll = -tok_lp.mean().item()
20
+ return {
21
+ "tokens": tokens,
22
+ "surprisal": [-x for x in tok_lp.tolist()],
23
+ "rank": ranks.tolist(),
24
+ "nll": nll,
25
+ "ppl": math.exp(nll),
26
+ "n_tokens": int(targets.numel()),
27
+ }
28
+
29
+
30
+ def surprisal_html(result: dict, title: str = "") -> str:
31
+ toks, surp = result["tokens"], result["surprisal"]
32
+ cells = [f"<span style='color:#888'>{html.escape(toks[0])}</span>"]
33
+ for t, s in zip(toks[1:], surp):
34
+ p = min(1.0, s / 12.0)
35
+ r, g = int(255 * p), int(200 * (1 - p))
36
+ cells.append(
37
+ f"<span title='surprisal {s:.2f} nats' style='background:rgba({r},{g},80,0.35);padding:1px 2px;border-radius:3px'>{html.escape(t)}</span>"
38
+ )
39
+ head = (
40
+ f"<div style='margin-bottom:6px'><b>{html.escape(title)}</b> perplexity <b>{result['ppl']:.1f}</b> over {result['n_tokens']} tokens (mean surprisal {result['nll']:.2f} nats)</div>"
41
+ if title
42
+ else ""
43
+ )
44
+ return (
45
+ head
46
+ + "<div style='font-family:ui-monospace,monospace;line-height:1.9;white-space:pre-wrap;word-break:break-word'>"
47
+ + "".join(cells)
48
+ + "</div><div style='color:#777;font-size:0.85em;margin-top:4px'>green = expected, red = surprising; hover a token for its surprisal</div>"
49
+ )
50
+
51
+
52
+ @torch.no_grad()
53
+ def greedy_continue(
54
+ model,
55
+ tokenizer,
56
+ prompt: str,
57
+ max_new_tokens: int,
58
+ repetition_penalty: float = 1.2,
59
+ temperature: float = 0.0,
60
+ top_p: float = 0.9,
61
+ max_context: int = 1024,
62
+ ):
63
+ ids = tokenizer(
64
+ prompt,
65
+ return_tensors="pt",
66
+ truncation=True,
67
+ max_length=max_context - max_new_tokens,
68
+ ).input_ids.to(model.device)
69
+ out = model.generate(
70
+ ids,
71
+ max_new_tokens=max_new_tokens,
72
+ do_sample=temperature > 0,
73
+ temperature=max(temperature, 1e-3),
74
+ top_p=top_p,
75
+ repetition_penalty=repetition_penalty,
76
+ pad_token_id=tokenizer.eos_token_id,
77
+ eos_token_id=tokenizer.eos_token_id,
78
+ )
79
+ return tokenizer.decode(out[0][ids.shape[1] :], skip_special_tokens=True)
80
+
81
+
82
+ def contrast_rows(model, tokenizer, samples: list[tuple[str, str]]):
83
+ rows = []
84
+ for label, text in samples:
85
+ r = token_surprisal(model, tokenizer, text)
86
+ rows.append([label, round(r["ppl"], 1), round(r["nll"], 3), text])
87
+ return rows
88
+
89
+
90
+ def md_table(rows, header=("Text", "Perplexity", "Mean surprisal (nats)")):
91
+ out = ["| " + " | ".join(header) + " |", "|" + "---|" * len(header)]
92
+ for r in rows:
93
+ out.append(f"| {r[0]} | {r[1]} | {r[2]} |")
94
+ return "\n".join(out)
95
+
96
+
97
+ WIKI_FEWSHOT = (
98
+ "Paris is the capital and most populous city of France. It is located on the Seine river in the north of the country.\n\n"
99
+ "Tokyo is the capital of Japan and one of the most populous cities in the world. It lies on the eastern coast of Honshu.\n\n"
100
+ )
101
+
102
+ QA_FEWSHOT = (
103
+ "Q: What is the capital of France?\nA: Paris.\n\n"
104
+ "Q: Which river flows through Cairo?\nA: The Nile.\n\n"
105
+ "Q: What is the largest planet in the Solar System?\nA: Jupiter.\n\n"
106
+ )
107
+
108
+ CONTRAST_SET = [
109
+ (
110
+ "Fluent English (encyclopedic)",
111
+ "The Amazon River in South America is the largest river by discharge volume of water in the world.",
112
+ ),
113
+ (
114
+ "Fluent English (narrative)",
115
+ "She closed the book, turned off the lamp, and listened to the rain against the window.",
116
+ ),
117
+ (
118
+ "Grammatical but false",
119
+ "The Amazon River is located in northern Canada and flows into the Arctic Ocean near Greenland.",
120
+ ),
121
+ (
122
+ "Word salad (same words shuffled)",
123
+ "world the in water of volume discharge by river largest the is America South in River Amazon The",
124
+ ),
125
+ ("Random words", "purple elephant calculus whisper nine umbrella fjord lantern"),
126
+ ("Python", "def add(a, b):\n return a + b"),
127
+ ]
app.py CHANGED
@@ -1,18 +1,28 @@
1
  from __future__ import annotations
2
- import threading
3
  import torch
4
  import gradio as gr
5
  import spaces
6
  from huggingface_hub import hf_hub_download
7
  from safetensors.torch import load_file
8
- from transformers import AutoTokenizer, TextIteratorStreamer
9
  from configuration_riemann import RiemannConfig
10
  from modeling_riemann import RiemannForCausalLM
 
 
 
 
 
 
 
 
 
11
 
12
  OPENPEER_REPO = "OpenPeerAI/OpenPeerLLM"
 
13
 
14
  tokenizer = AutoTokenizer.from_pretrained("gpt2")
15
  tokenizer.pad_token = tokenizer.eos_token
 
16
  config = RiemannConfig(
17
  vocab_size=50257,
18
  hidden_size=768,
@@ -21,113 +31,165 @@ config = RiemannConfig(
21
  intermediate_size=3072,
22
  max_position_embeddings=1024,
23
  )
24
- model = RiemannForCausalLM(config)
25
- weights_path = hf_hub_download(OPENPEER_REPO, "model.safetensors")
26
- state = load_file(weights_path)
27
- missing, unexpected = model.load_state_dict(state, strict=False)
28
- LOAD_REPORT = f"Loaded {len(state)} tensors from {OPENPEER_REPO}/model.safetensors. Missing: {len(missing)}. Unexpected: {len(unexpected)}."
29
- model.eval()
30
- N_PARAMS = sum(p.numel() for p in model.parameters())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
 
33
  @spaces.GPU(duration=60)
34
- def generate(prompt, max_new_tokens, temperature, top_p, repetition_penalty):
35
- model.to("cuda")
36
- inputs = tokenizer(
37
- prompt,
38
- return_tensors="pt",
39
- truncation=True,
40
- max_length=1024 - int(max_new_tokens),
41
- ).to("cuda")
42
- streamer = TextIteratorStreamer(
43
- tokenizer, skip_prompt=True, skip_special_tokens=True
44
  )
45
- kwargs = dict(
46
- **inputs,
47
- streamer=streamer,
48
- max_new_tokens=int(max_new_tokens),
49
- do_sample=temperature > 0,
50
- temperature=max(float(temperature), 1e-3),
51
- top_p=float(top_p),
52
- repetition_penalty=float(repetition_penalty),
53
- pad_token_id=tokenizer.eos_token_id,
54
  )
55
- thread = threading.Thread(target=model.generate, kwargs=kwargs)
56
- thread.start()
57
- out = ""
58
- for piece in streamer:
59
- out += piece
60
- yield prompt + out
61
- thread.join()
62
-
63
-
64
- @spaces.GPU(duration=30)
65
- def score(text):
66
- model.to("cuda")
67
- ids = tokenizer(text, return_tensors="pt", truncation=True, max_length=1024).to(
68
- "cuda"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  )
70
- with torch.no_grad():
71
- out = model(input_ids=ids["input_ids"], labels=ids["input_ids"])
72
- n = ids["input_ids"].shape[1]
73
- return f"Tokens: {n}\nMean NLL: {out.loss.item():.4f}\nPerplexity: {torch.exp(out.loss).item():.2f}"
74
 
75
 
76
  DESCRIPTION = f"""
77
  # OpenPeerLLM Demo
78
 
79
- [OpenPeerAI/OpenPeerLLM](https://huggingface.co/{OPENPEER_REPO}) is a decentralised-training
80
- language model by Andrew Magdy Kamal Nassief (DOI [10.57967/hf/6469](https://doi.org/10.57967/hf/6469)).
81
- This space loads its published `model.safetensors` ({N_PARAMS/1e6:.1f}M parameters, 12 layers,
82
- hidden size 768, GPT-2 vocabulary) into a faithful reimplementation of its architecture
83
- and runs it on ZeroGPU.
84
-
85
- {LOAD_REPORT}
86
 
87
- OpenPeerLLM is the starting rung of the
88
- [My Name is Riemann](https://huggingface.co/OpenPeerAI/My-Name-is-Riemann-Prerelease) growth ladder.
89
- The reimplementation here adds a causal attention mask, which the reference forward pass omits and
90
- which is required for coherent autoregressive generation.
91
  """
92
 
93
  with gr.Blocks(title="OpenPeerLLM Demo") as demo:
94
  gr.Markdown(DESCRIPTION)
95
- with gr.Tab("Generate"):
96
- prompt = gr.Textbox(
97
- label="Prompt",
98
- lines=4,
99
- value="The theory of decentralised machine learning says that",
100
  )
101
  with gr.Row():
102
- max_new = gr.Slider(8, 256, value=96, step=8, label="Max new tokens")
103
- temp = gr.Slider(
104
- 0.0, 1.5, value=0.8, step=0.05, label="Temperature (0 = greedy)"
 
 
 
 
 
 
105
  )
106
- top_p = gr.Slider(0.1, 1.0, value=0.9, step=0.05, label="Top-p")
107
- rep = gr.Slider(1.0, 1.5, value=1.2, step=0.05, label="Repetition penalty")
108
- out = gr.Textbox(label="Output", lines=10)
109
- gr.Button("Generate", variant="primary").click(
110
- generate, [prompt, max_new, temp, top_p, rep], out
111
  )
112
  gr.Examples(
113
  [
114
- ["Peer-to-peer networks allow"],
115
- ["def fibonacci(n):"],
116
- ["In 1859, Bernhard Riemann"],
 
 
117
  ],
118
- inputs=prompt,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  )
120
- with gr.Tab("Score text"):
121
- txt = gr.Textbox(
122
- label="Text to score",
123
- lines=6,
124
- value="Decentralized training distributes model updates across peers.",
125
  )
126
- res = gr.Textbox(label="Negative log-likelihood and perplexity", lines=3)
127
- gr.Button("Score", variant="primary").click(score, txt, res)
128
  gr.Markdown(
129
- "OpenPeerLLM is released under OPNL, MIT and CC BY 4.0. "
130
- "This demo is maintained by OpenPeer AI."
131
  )
132
 
133
  demo.queue(max_size=16).launch()
 
1
  from __future__ import annotations
 
2
  import torch
3
  import gradio as gr
4
  import spaces
5
  from huggingface_hub import hf_hub_download
6
  from safetensors.torch import load_file
7
+ from transformers import AutoTokenizer, AutoModelForCausalLM
8
  from configuration_riemann import RiemannConfig
9
  from modeling_riemann import RiemannForCausalLM
10
+ from analysis import (
11
+ token_surprisal,
12
+ surprisal_html,
13
+ greedy_continue,
14
+ contrast_rows,
15
+ md_table,
16
+ WIKI_FEWSHOT,
17
+ CONTRAST_SET,
18
+ )
19
 
20
  OPENPEER_REPO = "OpenPeerAI/OpenPeerLLM"
21
+ RIEMANN_REPO = "OpenPeerAI/My-Name-is-Riemann-Prerelease"
22
 
23
  tokenizer = AutoTokenizer.from_pretrained("gpt2")
24
  tokenizer.pad_token = tokenizer.eos_token
25
+
26
  config = RiemannConfig(
27
  vocab_size=50257,
28
  hidden_size=768,
 
31
  intermediate_size=3072,
32
  max_position_embeddings=1024,
33
  )
34
+ openpeer = RiemannForCausalLM(config)
35
+ _state = load_file(hf_hub_download(OPENPEER_REPO, "model.safetensors"))
36
+ _missing, _unexpected = openpeer.load_state_dict(_state, strict=False)
37
+ openpeer.eval()
38
+ LOAD_REPORT = f"{len(_state)} tensors loaded from {OPENPEER_REPO}, {len(_missing)} missing, {len(_unexpected)} unexpected."
39
+ N_OPENPEER = sum(p.numel() for p in openpeer.parameters())
40
+
41
+ _riemann = {"model": None}
42
+
43
+
44
+ def riemann():
45
+ if _riemann["model"] is None:
46
+ m = AutoModelForCausalLM.from_pretrained(
47
+ RIEMANN_REPO,
48
+ trust_remote_code=True,
49
+ dtype=torch.bfloat16,
50
+ low_cpu_mem_usage=True,
51
+ )
52
+ m.eval()
53
+ _riemann["model"] = m
54
+ return _riemann["model"]
55
+
56
+
57
+ def _gpu(m):
58
+ if m.device.type != "cuda":
59
+ m.to("cuda")
60
 
61
 
62
  @spaces.GPU(duration=60)
63
+ def complete(subject, opening, max_new, compare):
64
+ prompt_tail = f"{subject.strip()} {opening.strip()}".strip()
65
+ _gpu(openpeer)
66
+ a = greedy_continue(
67
+ openpeer,
68
+ tokenizer,
69
+ WIKI_FEWSHOT + prompt_tail,
70
+ int(max_new),
71
+ repetition_penalty=1.3,
 
72
  )
73
+ out = (
74
+ f"### OpenPeerLLM ({N_OPENPEER/1e6:.0f}M, the starting rung)\n{prompt_tail}{a}"
 
 
 
 
 
 
 
75
  )
76
+ if compare:
77
+ r = riemann()
78
+ _gpu(r)
79
+ b = greedy_continue(
80
+ r,
81
+ tokenizer,
82
+ WIKI_FEWSHOT + prompt_tail,
83
+ int(max_new),
84
+ repetition_penalty=1.25,
85
+ max_context=2048,
86
+ )
87
+ out += f"\n\n### My Name is Riemann Prerelease (13.1B, six rungs later)\n{prompt_tail}{b}"
88
+ return out
89
+
90
+
91
+ @spaces.GPU(duration=60)
92
+ def analyse(text, compare):
93
+ _gpu(openpeer)
94
+ a = token_surprisal(openpeer, tokenizer, text)
95
+ out = surprisal_html(a, f"OpenPeerLLM ({N_OPENPEER/1e6:.0f}M):")
96
+ if compare:
97
+ r = riemann()
98
+ _gpu(r)
99
+ b = token_surprisal(r, tokenizer, text, max_length=2048)
100
+ out += "<hr/>" + surprisal_html(b, "My Name is Riemann Prerelease (13.1B):")
101
+ out += f"<p><b>Perplexity {a['ppl']:.1f} to {b['ppl']:.1f}</b> across the growth ladder on this text.</p>"
102
+ return out
103
+
104
+
105
+ @spaces.GPU(duration=90)
106
+ def contrast(compare):
107
+ _gpu(openpeer)
108
+ rows_a = contrast_rows(openpeer, tokenizer, CONTRAST_SET)
109
+ if not compare:
110
+ return md_table([[r[0], r[1], r[2]] for r in rows_a])
111
+ r = riemann()
112
+ _gpu(r)
113
+ rows_b = contrast_rows(r, tokenizer, CONTRAST_SET)
114
+ header = ("Text", "OpenPeerLLM 163M", "Riemann Prerelease 13.1B")
115
+ rows = [[ra[0], ra[1], rb[1]] for ra, rb in zip(rows_a, rows_b)]
116
+ return (
117
+ md_table(rows, header)
118
+ + "\n\nPerplexity on each string. Lower is better on fluent text; the gap between fluent text and noise "
119
+ "should widen as the model improves. OpenPeerLLM is the 163M checkpoint that every later rung inherits from."
120
  )
 
 
 
 
121
 
122
 
123
  DESCRIPTION = f"""
124
  # OpenPeerLLM Demo
125
 
126
+ [OpenPeerAI/OpenPeerLLM](https://huggingface.co/{OPENPEER_REPO}) is a decentralised-training language model
127
+ by Andrew Magdy Kamal Nassief (DOI [10.57967/hf/6469](https://doi.org/10.57967/hf/6469)). This space loads its
128
+ published `model.safetensors` ({N_OPENPEER/1e6:.0f}M parameters) into a faithful reimplementation of its
129
+ architecture, with the causal attention mask the reference forward pass omits. {LOAD_REPORT}
 
 
 
130
 
131
+ OpenPeerLLM is the **starting rung** of the
132
+ [My Name is Riemann](https://huggingface.co/{RIEMANN_REPO}) growth ladder. Every tab lets you put the
133
+ 13.1B Prerelease next to it on the same input, so the effect of six rungs of growth and training is visible
134
+ directly. Both are base models: they complete and score text, they do not chat.
135
  """
136
 
137
  with gr.Blocks(title="OpenPeerLLM Demo") as demo:
138
  gr.Markdown(DESCRIPTION)
139
+ with gr.Tab("Complete an article"):
140
+ gr.Markdown(
141
+ "Two short example articles are prepended so the model knows the register."
 
 
142
  )
143
  with gr.Row():
144
+ subject = gr.Textbox(label="Subject", value="The Nile")
145
+ opening = gr.Textbox(
146
+ label="Opening clause",
147
+ value="is a major north-flowing river in northeastern Africa. It",
148
+ )
149
+ with gr.Row():
150
+ max_new = gr.Slider(16, 120, value=64, step=8, label="Max new tokens")
151
+ cmp1 = gr.Checkbox(
152
+ value=True, label="Also run My Name is Riemann Prerelease (13.1B)"
153
  )
154
+ out1 = gr.Markdown()
155
+ gr.Button("Complete", variant="primary").click(
156
+ complete, [subject, opening, max_new, cmp1], out1
 
 
157
  )
158
  gr.Examples(
159
  [
160
+ ["Mount Kilimanjaro", "is a dormant volcano in Tanzania. It"],
161
+ [
162
+ "The Rosetta Stone",
163
+ "is a stele inscribed with three versions of a decree issued in",
164
+ ],
165
  ],
166
+ inputs=[subject, opening],
167
+ )
168
+ with gr.Tab("Analyse text"):
169
+ gr.Markdown(
170
+ "Each token is coloured by how surprised the model was to see it; green is expected, red is surprising."
171
+ )
172
+ a_text = gr.Textbox(
173
+ label="Text",
174
+ lines=4,
175
+ value="The Great Pyramid of Giza is the oldest of the Seven Wonders of the Ancient World.",
176
+ )
177
+ cmp2 = gr.Checkbox(
178
+ value=True, label="Also analyse with My Name is Riemann Prerelease (13.1B)"
179
+ )
180
+ out2 = gr.HTML()
181
+ gr.Button("Analyse", variant="primary").click(analyse, [a_text, cmp2], out2)
182
+ with gr.Tab("Fluency test"):
183
+ gr.Markdown(
184
+ "Six fixed strings from fluent English down to random words, scored by perplexity."
185
  )
186
+ cmp3 = gr.Checkbox(
187
+ value=True, label="Compare with My Name is Riemann Prerelease (13.1B)"
 
 
 
188
  )
189
+ out3 = gr.Markdown()
190
+ gr.Button("Run fluency test", variant="primary").click(contrast, cmp3, out3)
191
  gr.Markdown(
192
+ "OpenPeerLLM is released under OPNL, MIT and CC BY 4.0. Maintained by OpenPeer AI."
 
193
  )
194
 
195
  demo.queue(max_size=16).launch()