m-ric HF staff commited on
Commit
e4010d8
β€’
1 Parent(s): 7a37cfb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +167 -64
app.py CHANGED
@@ -4,28 +4,43 @@ import numpy as np
4
  import gradio as gr
5
  import spaces
6
 
 
 
 
 
 
 
7
  print(f"Is CUDA available: {torch.cuda.is_available()}")
8
  # True
9
  if torch.cuda.is_available():
10
  print(f"CUDA device: {torch.cuda.get_device_name(torch.cuda.current_device())}")
11
 
12
  STYLE = """
13
- .container {
14
  width: 100%;
15
  display: grid;
16
  align-items: center;
17
  margin: 0!important;
 
18
  }
19
  .prose ul ul {
20
  margin: 0!important;
21
- font-size: 13px!important;
22
  }
 
 
 
 
 
 
 
23
  .tree {
24
  padding: 0px;
25
  margin: 0!important;
26
  box-sizing: border-box;
27
- font-size: 16px;
28
  width: 100%;
 
29
  height: auto;
30
  text-align: center;
31
  }
@@ -34,13 +49,17 @@ STYLE = """
34
  position: relative;
35
  transition: .5s;
36
  margin: 0!important;
 
 
 
 
37
  }
38
  .tree li {
39
  display: inline-table;
40
  text-align: center;
41
  list-style-type: none;
42
  position: relative;
43
- padding: 10px;
44
  transition: .5s;
45
  }
46
  .tree li::before, .tree li::after {
@@ -88,7 +107,7 @@ STYLE = """
88
  }
89
  .tree li a {
90
  border: 1px solid #ccc;
91
- padding: 10px;
92
  display: inline-grid;
93
  border-radius: 5px;
94
  text-decoration-line: none;
@@ -96,10 +115,8 @@ STYLE = """
96
  transition: .5s;
97
  }
98
  .tree li a span {
99
- border: 1px solid #ccc;
100
- border-radius: 5px;
101
  color: #666;
102
- padding: 8px;
103
  font-size: 12px;
104
  text-transform: uppercase;
105
  letter-spacing: 1px;
@@ -109,30 +126,26 @@ STYLE = """
109
  .tree li a:hover, .tree li a:hover i, .tree li a:hover span, .tree li a:hover+ul li a {
110
  background: #c8e4f8;
111
  color: #000;
112
- border: 1px solid #94a0b4;
113
  }
114
  .tree li a:hover+ul li::after, .tree li a:hover+ul li::before, .tree li a:hover+ul::before, .tree li a:hover+ul ul::before {
115
  border-color: #94a0b4;
116
  }
 
 
 
117
  """
118
 
119
- tokenizer = AutoTokenizer.from_pretrained("gpt2")
120
- model = AutoModelForCausalLM.from_pretrained("gpt2")
121
-
122
- tokenizer.pad_token_id = tokenizer.eos_token_id
123
- print("Loading finished.")
124
-
125
 
126
- def generate_html(token, node):
127
- """Recursively generate HTML for the tree."""
128
 
129
- html_content = f" <li> <a href='#'> <span> <b>{token}</b> </span> "
130
- html_content += node["table"] if node["table"] is not None else ""
131
  html_content += "</a>"
132
- if len(node["children"].keys()) > 0:
133
  html_content += "<ul> "
134
- for token, subnode in node["children"].items():
135
- html_content += generate_html(token, subnode)
136
  html_content += "</ul>"
137
  html_content += "</li>"
138
  return html_content
@@ -144,9 +157,9 @@ def generate_markdown_table(scores, sequence_prob, top_k=4, chosen_tokens=None):
144
  <tr>
145
  <th><b>Token</b></th>
146
  <th><b>Step score</b></th>
147
- <th><b>Cumulative score</b></th>
148
  </tr>"""
149
- for token_idx in np.argsort(scores)[-top_k:]:
150
  token = tokenizer.decode([token_idx])
151
  style = ""
152
  if chosen_tokens and token in chosen_tokens:
@@ -162,50 +175,140 @@ def generate_markdown_table(scores, sequence_prob, top_k=4, chosen_tokens=None):
162
  return markdown_table
163
 
164
 
165
- def display_tree(start_sentence, scores, sequences, beam_indices):
166
- display = """<div class="container">
 
167
  <div class="tree">
168
  <ul>"""
169
- sequences = sequences.cpu().numpy()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  print(tokenizer.batch_decode(sequences))
171
- original_tree = {"table": None, "children": {}}
172
- for sequence_ix in range(len(sequences)):
173
- current_sequence_score = 0
174
- current_tree = original_tree
175
- for step, step_scores in enumerate(scores):
176
- current_token_choice_ix = sequences[sequence_ix, step]
177
- current_token_choice = tokenizer.decode([current_token_choice_ix])
178
- current_beam = beam_indices[sequence_ix, step]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
 
180
- if current_token_choice not in current_tree["children"]:
181
- current_tree["children"][current_token_choice] = {
182
- "table": None,
183
- "children": {},
184
- }
 
185
 
186
- # Rewrite the probs table even if it was there before, since new chosen nodes have appeared in the children of current tree
 
 
 
 
 
 
187
  markdown_table = generate_markdown_table(
188
- step_scores[current_beam, :], current_sequence_score,
189
- chosen_tokens=current_tree["children"].keys(),
 
190
  )
191
- current_tree["table"] = markdown_table
192
 
193
- current_tree = current_tree["children"][current_token_choice]
 
 
 
 
194
 
195
- # Keep up to date the current cumulative score
196
- current_sequence_score += step_scores[current_beam, current_token_choice_ix]
197
 
198
- display += generate_html(start_sentence, original_tree)
 
 
 
 
 
 
 
 
 
 
 
199
 
200
- display += """
201
- </ul>
202
- </div>
203
- </body>
204
- """
205
- return display
 
 
 
 
 
 
206
 
207
  @spaces.GPU
208
- def get_tables(input_text, number_steps, number_beams):
209
  inputs = tokenizer([input_text], return_tensors="pt")
210
 
211
  outputs = model.generate(
@@ -216,18 +319,18 @@ def get_tables(input_text, number_steps, number_beams):
216
  return_dict_in_generate=True,
217
  output_scores=True,
218
  top_k=5,
219
- temperature=1.0,
220
- do_sample=True,
221
  )
222
- print(outputs.sequences_scores)
223
 
224
- tables = display_tree(
225
  input_text,
226
- outputs.scores,
227
- outputs.sequences[:, len(inputs) :],
228
- outputs.beam_indices[:, : -len(inputs)],
229
  )
230
- return tables
 
 
231
 
232
 
233
  with gr.Blocks(
@@ -241,6 +344,6 @@ with gr.Blocks(
241
  beams = gr.Slider(label="Number of beams", minimum=2, maximum=4, step=1, value=3)
242
  button = gr.Button()
243
  out = gr.Markdown(label="Output")
244
- button.click(get_tables, inputs=[text, steps, beams], outputs=out)
245
 
246
  demo.launch()
 
4
  import gradio as gr
5
  import spaces
6
 
7
+ tokenizer = AutoTokenizer.from_pretrained("gpt2")
8
+ model = AutoModelForCausalLM.from_pretrained("gpt2")
9
+
10
+ tokenizer.pad_token_id = tokenizer.eos_token_id
11
+ print("Loading finished.")
12
+
13
  print(f"Is CUDA available: {torch.cuda.is_available()}")
14
  # True
15
  if torch.cuda.is_available():
16
  print(f"CUDA device: {torch.cuda.get_device_name(torch.cuda.current_device())}")
17
 
18
  STYLE = """
19
+ .custom-container {
20
  width: 100%;
21
  display: grid;
22
  align-items: center;
23
  margin: 0!important;
24
+ overflow: scroll;
25
  }
26
  .prose ul ul {
27
  margin: 0!important;
28
+ font-size: 10px!important;
29
  }
30
+ .prose td, th {
31
+ padding-left: 2px;
32
+ padding-right: 2px;
33
+ padding-top: 0;
34
+ padding-bottom: 0;
35
+ }
36
+
37
  .tree {
38
  padding: 0px;
39
  margin: 0!important;
40
  box-sizing: border-box;
41
+ font-size: 10px;
42
  width: 100%;
43
+ min-width: 2000px;
44
  height: auto;
45
  text-align: center;
46
  }
 
49
  position: relative;
50
  transition: .5s;
51
  margin: 0!important;
52
+ display: flex;
53
+ flex-direction: row;
54
+ justify-content: center;
55
+ gap:10px;
56
  }
57
  .tree li {
58
  display: inline-table;
59
  text-align: center;
60
  list-style-type: none;
61
  position: relative;
62
+ padding-top: 10px;
63
  transition: .5s;
64
  }
65
  .tree li::before, .tree li::after {
 
107
  }
108
  .tree li a {
109
  border: 1px solid #ccc;
110
+ padding: 5px;
111
  display: inline-grid;
112
  border-radius: 5px;
113
  text-decoration-line: none;
 
115
  transition: .5s;
116
  }
117
  .tree li a span {
 
 
118
  color: #666;
119
+ padding: 5px;
120
  font-size: 12px;
121
  text-transform: uppercase;
122
  letter-spacing: 1px;
 
126
  .tree li a:hover, .tree li a:hover i, .tree li a:hover span, .tree li a:hover+ul li a {
127
  background: #c8e4f8;
128
  color: #000;
 
129
  }
130
  .tree li a:hover+ul li::after, .tree li a:hover+ul li::before, .tree li a:hover+ul::before, .tree li a:hover+ul ul::before {
131
  border-color: #94a0b4;
132
  }
133
+ .chosen {
134
+ background-color: red;
135
+ }
136
  """
137
 
 
 
 
 
 
 
138
 
139
+ def generate_nodes(token, node):
140
+ """Recursively generate HTML for the tree nodes."""
141
 
142
+ html_content = f" <li> <a href='#' class={('chosen' if node.table is None else '')}> <span> <b>{token}</b> </span> "
143
+ html_content += node.table if node.table is not None else ""
144
  html_content += "</a>"
145
+ if len(node.children.keys()) > 0:
146
  html_content += "<ul> "
147
+ for token, subnode in node.children.items():
148
+ html_content += generate_nodes(token, subnode)
149
  html_content += "</ul>"
150
  html_content += "</li>"
151
  return html_content
 
157
  <tr>
158
  <th><b>Token</b></th>
159
  <th><b>Step score</b></th>
160
+ <th><b>Total score</b></th>
161
  </tr>"""
162
+ for token_idx in np.array(np.argsort(scores)[-top_k:])[::-1]:
163
  token = tokenizer.decode([token_idx])
164
  style = ""
165
  if chosen_tokens and token in chosen_tokens:
 
175
  return markdown_table
176
 
177
 
178
+ def generate_html(start_sentence, original_tree):
179
+
180
+ html_output = """<div class="custom-container">
181
  <div class="tree">
182
  <ul>"""
183
+ html_output += generate_nodes(start_sentence, original_tree)
184
+
185
+ html_output += """
186
+ </ul>
187
+ </div>
188
+ </body>
189
+ """
190
+ return html_output
191
+
192
+
193
+ import pandas as pd
194
+ from typing import Dict
195
+ from dataclasses import dataclass
196
+
197
+
198
+ @dataclass
199
+ class BeamNode:
200
+ cumulative_score: float
201
+ table: str
202
+ current_sentence: str
203
+ children: Dict[str, "BeamNode"]
204
+
205
+
206
+ def generate_beams(start_sentence, scores, sequences, beam_indices):
207
  print(tokenizer.batch_decode(sequences))
208
+ sequences = sequences.cpu().numpy()
209
+ original_tree = BeamNode(
210
+ cumulative_score=0, table=None, current_sentence=start_sentence, children={}
211
+ )
212
+ n_beams = len(scores[0])
213
+ beam_trees = [original_tree] * n_beams
214
+ for step, step_scores in enumerate(scores):
215
+ (
216
+ top_token_indexes,
217
+ top_cumulative_scores,
218
+ beam_indexes,
219
+ current_completions,
220
+ top_tokens,
221
+ ) = ([], [], [], [], [])
222
+ for beam_ix in range(n_beams):
223
+ current_beam = beam_trees[beam_ix]
224
+ # Get top cumulative scores for the current beam
225
+ current_top_token_indexes = list(
226
+ np.array(scores[step][beam_ix].argsort()[-n_beams:])[::-1]
227
+ )
228
+ top_token_indexes += current_top_token_indexes
229
+ top_cumulative_scores += list(
230
+ np.array(scores[step][beam_ix][current_top_token_indexes])
231
+ + current_beam.cumulative_score
232
+ )
233
+ beam_indexes += [beam_ix] * n_beams
234
+ current_completions += [beam_trees[beam_ix].current_sentence] * n_beams
235
+ top_tokens += [
236
+ tokenizer.decode([el]) for el in current_top_token_indexes
237
+ ]
238
+
239
+ top_df = pd.DataFrame.from_dict(
240
+ {
241
+ "token_index": top_token_indexes,
242
+ "cumulative_score": top_cumulative_scores,
243
+ "beam_index": beam_indexes,
244
+ "current_completions": current_completions,
245
+ "token": top_tokens,
246
+ }
247
+ )
248
+ maxes = top_df.groupby(["token_index", "current_completions"])[
249
+ "cumulative_score"
250
+ ].idxmax()
251
+
252
+ top_df = top_df.loc[maxes]
253
 
254
+ # Sort all top probabilities and keep top n_beams
255
+ top_df_selected = top_df.sort_values("cumulative_score", ascending=False).iloc[
256
+ :n_beams
257
+ ]
258
+ print(step)
259
+ display(top_df_selected)
260
 
261
+ # Write the scores table - one per beam source?
262
+ # Edge case: if several beam indexes are actually on the same beam, the selected tokens by beam_index for the second one will be empty. So we reverse
263
+ for beam_ix in reversed(list(range(n_beams))):
264
+ current_beam = beam_trees[beam_ix]
265
+ selected_tokens = top_df_selected.loc[top_df_selected["beam_index"] == beam_ix]
266
+ print(step, beam_ix)
267
+ display(selected_tokens)
268
  markdown_table = generate_markdown_table(
269
+ step_scores[beam_ix, :],
270
+ current_beam.cumulative_score,
271
+ chosen_tokens=list(selected_tokens["token"].values),
272
  )
273
+ beam_trees[beam_ix].table = markdown_table
274
 
275
+ # Add new children for each beam
276
+ cumulative_scores = [beam.cumulative_score for beam in beam_trees]
277
+ for beam_ix in range(n_beams):
278
+ current_token_choice_ix = top_df_selected.iloc[beam_ix]["token_index"]
279
+ current_token_choice = tokenizer.decode([current_token_choice_ix])
280
 
281
+ # Update the source tree
282
+ source_beam_ix = int(top_df_selected.iloc[beam_ix]["beam_index"])
283
 
284
+ previous_len = len(str(original_tree))
285
+ beam_trees[source_beam_ix].children[current_token_choice] = BeamNode(
286
+ table=None,
287
+ children={},
288
+ current_sentence=beam_trees[source_beam_ix].current_sentence
289
+ + current_token_choice,
290
+ cumulative_score=cumulative_scores[source_beam_ix]
291
+ + scores[step][source_beam_ix][current_token_choice_ix].numpy(),
292
+ )
293
+ assert (
294
+ len(str(original_tree)) > previous_len
295
+ ), "Original tree has not increased size"
296
 
297
+ # Reassign all beams at once
298
+ beam_trees = [
299
+ beam_trees[int(top_df_selected.iloc[beam_ix]["beam_index"])]
300
+ for beam_ix in range(n_beams)
301
+ ]
302
+
303
+ # Advance all beams by one token
304
+ for beam_ix in range(n_beams):
305
+ current_token_choice_ix = top_df_selected.iloc[beam_ix]["token_index"]
306
+ current_token_choice = tokenizer.decode([current_token_choice_ix])
307
+ beam_trees[beam_ix] = beam_trees[beam_ix].children[current_token_choice]
308
+ return original_tree
309
 
310
  @spaces.GPU
311
+ def get_beam_search_html(input_text, number_steps, number_beams):
312
  inputs = tokenizer([input_text], return_tensors="pt")
313
 
314
  outputs = model.generate(
 
319
  return_dict_in_generate=True,
320
  output_scores=True,
321
  top_k=5,
322
+ do_sample=False,
 
323
  )
 
324
 
325
+ original_tree = generate_beams(
326
  input_text,
327
+ outputs.scores[:],
328
+ outputs.sequences[:, :],
329
+ outputs.beam_indices[:, :],
330
  )
331
+ html = generate_html(input_text, original_tree)
332
+ print(html)
333
+ return html
334
 
335
 
336
  with gr.Blocks(
 
344
  beams = gr.Slider(label="Number of beams", minimum=2, maximum=4, step=1, value=3)
345
  button = gr.Button()
346
  out = gr.Markdown(label="Output")
347
+ button.click(get_beam_search_html, inputs=[text, steps, beams], outputs=out)
348
 
349
  demo.launch()