m-ric HF staff commited on
Commit
c3aa211
1 Parent(s): 5a53522

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +47 -36
app.py CHANGED
@@ -2,7 +2,6 @@ import torch
2
  from transformers import AutoModelForCausalLM, AutoTokenizer
3
  import numpy as np
4
  import gradio as gr
5
- import spaces
6
 
7
  tokenizer = AutoTokenizer.from_pretrained("gpt2")
8
  model = AutoModelForCausalLM.from_pretrained("gpt2")
@@ -118,7 +117,7 @@ STYLE = """
118
  .tree ul:has(> li:only-child)::before {
119
  width:40px;
120
  }
121
- .tree li a:before {
122
  border-right: 2px solid var(--body-text-color);
123
  border-bottom: 2px solid var(--body-text-color);
124
  content: "";
@@ -150,13 +149,13 @@ STYLE = """
150
  }
151
  /*Hover-Section*/
152
  .tree li a:hover, .tree li a:hover+ul li a {
153
- background: #ffedd5;
154
  }
155
- .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 {
156
- border-color: #7c2d12;
157
  }
158
- .end-of-text, .chosen {
159
- background-color: #ea580c;
160
  }
161
  .end-of-text {
162
  width:auto!important;
@@ -164,7 +163,10 @@ STYLE = """
164
  .nonfinal {
165
  width:280px;
166
  min-width: 280px;
167
- }
 
 
 
168
  """
169
 
170
 
@@ -186,7 +188,7 @@ def generate_markdown_table(
186
  token = tokenizer.decode([token_idx])
187
  item_class = ""
188
  if chosen_tokens and token in chosen_tokens:
189
- item_class = "chosen"
190
  markdown_table += f"""
191
  <tr class={item_class}>
192
  <td>{clean(token)}</td>
@@ -198,16 +200,16 @@ def generate_markdown_table(
198
  return markdown_table
199
 
200
 
201
- def generate_nodes(token_ix, node, step):
202
  """Recursively generate HTML for the tree nodes."""
203
- token = tokenizer.decode([token_ix])
 
 
204
 
205
  if node.is_final:
206
- return f"<li> <a href='#' class='end-of-text'> <span> <b>{clean(token)}</b> <br>Total score: {node.total_score:.2f}</span> </a> </li>"
207
 
208
- html_content = (
209
- f"<li> <a href='#' class='nonfinal'> <span> <b>{clean(token)}</b> </span>"
210
- )
211
  if node.table is not None:
212
  html_content += node.table
213
  html_content += "</a>"
@@ -215,7 +217,7 @@ def generate_nodes(token_ix, node, step):
215
  if len(node.children.keys()) > 0:
216
  html_content += "<ul> "
217
  for token_ix, subnode in node.children.items():
218
- html_content += generate_nodes(token_ix, subnode, step=step + 1)
219
  html_content += "</ul>"
220
  html_content += "</li>"
221
 
@@ -227,8 +229,8 @@ def generate_html(start_sentence, original_tree):
227
  <div class="tree">
228
  <ul> <li> <a href='#' id='root'> <span> <b>{start_sentence}</b> </span> {original_tree.table} </a>"""
229
  html_output += "<ul> "
230
- for token_ix, subnode in original_tree.children.items():
231
- html_output += generate_nodes(token_ix, subnode, step=1)
232
  html_output += "</ul>"
233
  html_output += """
234
  </li> </ul>
@@ -249,24 +251,25 @@ class BeamNode:
249
  cumulative_score: float
250
  children_score_divider: float
251
  table: str
252
- current_sentence: str
253
  children: Dict[int, "BeamNode"]
254
  total_score: float
255
  is_final: bool
 
256
 
257
 
258
- def generate_beams(start_sentence, scores, sequences, length_penalty):
259
- sequences = sequences.cpu().numpy()
260
  input_length = len(tokenizer([start_sentence], return_tensors="pt"))
261
  original_tree = BeamNode(
262
  cumulative_score=0,
263
  current_token_ix=None,
264
  table=None,
265
- current_sentence=start_sentence,
266
  children={},
267
  children_score_divider=((input_length + 1) ** length_penalty),
268
  total_score=None,
269
  is_final=False,
 
270
  )
271
  n_beams = len(scores[0])
272
  beam_trees = [original_tree] * n_beams
@@ -296,7 +299,7 @@ def generate_beams(start_sentence, scores, sequences, length_penalty):
296
  + current_beam.cumulative_score
297
  )
298
  beam_indexes += [beam_ix] * n_beams
299
- current_completions += [beam_trees[beam_ix].current_sentence] * n_beams
300
  top_tokens += [tokenizer.decode([el]) for el in current_top_token_indexes]
301
 
302
  top_df = pd.DataFrame.from_dict(
@@ -347,12 +350,14 @@ def generate_beams(start_sentence, scores, sequences, length_penalty):
347
  cumulative_scores[source_beam_ix]
348
  + scores[step][source_beam_ix][current_token_choice_ix].numpy()
349
  )
 
 
 
350
  beam_trees[source_beam_ix].children[current_token_choice_ix] = BeamNode(
351
  current_token_ix=current_token_choice_ix,
352
  table=None,
353
  children={},
354
- current_sentence=beam_trees[source_beam_ix].current_sentence
355
- + current_token_choice,
356
  cumulative_score=cumulative_score,
357
  total_score=cumulative_score
358
  / ((input_length + step - 1) ** length_penalty),
@@ -361,6 +366,7 @@ def generate_beams(start_sentence, scores, sequences, length_penalty):
361
  step == len(scores) - 1
362
  or current_token_choice_ix == tokenizer.eos_token_id
363
  ),
 
364
  )
365
 
366
  # Reassign all beams at once
@@ -376,7 +382,7 @@ def generate_beams(start_sentence, scores, sequences, length_penalty):
376
 
377
  return original_tree
378
 
379
- @spaces.GPU
380
  def get_beam_search_html(input_text, number_steps, number_beams, length_penalty):
381
  inputs = tokenizer([input_text], return_tensors="pt")
382
 
@@ -390,17 +396,21 @@ def get_beam_search_html(input_text, number_steps, number_beams, length_penalty)
390
  output_scores=True,
391
  do_sample=False,
392
  )
393
- markdown = "Output sequences:"
 
 
 
 
394
  # Sequences are padded anyway so you can batch decode them
395
  decoded_sequences = tokenizer.batch_decode(outputs.sequences)
396
  for i, sequence in enumerate(decoded_sequences):
397
- markdown += f"\n- '{clean(sequence.replace('<s> ', ''))}' (score {outputs.sequences_scores[i]:.2f})"
398
 
399
  original_tree = generate_beams(
400
  input_text,
401
  outputs.scores[:],
402
- outputs.sequences[:, :],
403
  length_penalty,
 
404
  )
405
  html = generate_html(input_text, original_tree)
406
  return html, markdown
@@ -408,20 +418,21 @@ def get_beam_search_html(input_text, number_steps, number_beams, length_penalty)
408
 
409
  with gr.Blocks(
410
  theme=gr.themes.Soft(
411
- text_size="lg", font=["monospace"], primary_hue=gr.themes.colors.yellow
 
412
  ),
413
  css=STYLE,
414
  ) as demo:
415
  gr.Markdown(
416
- """# Beam search visualizer
417
 
418
  Play with the parameters below to understand how beam search decoding works!
419
 
420
- #### Parameters:
421
- - **Sentence to decode from**: the input sequence to your decoder.
422
- - **Number of steps**: the number of tokens to generate
423
- - **Number of beams**: the number of beams to use
424
- - **Length penalty**: the length penalty to apply to outputs. `length_penalty` > 0.0 promotes longer sequences, while `length_penalty` < 0.0 encourages shorter sequences.
425
  This parameter will not impact the beam search paths, but only influence the choice of sequences in the end towards longer or shorter sequences.
426
  """
427
  )
 
2
  from transformers import AutoModelForCausalLM, AutoTokenizer
3
  import numpy as np
4
  import gradio as gr
 
5
 
6
  tokenizer = AutoTokenizer.from_pretrained("gpt2")
7
  model = AutoModelForCausalLM.from_pretrained("gpt2")
 
117
  .tree ul:has(> li:only-child)::before {
118
  width:40px;
119
  }
120
+ .child:before {
121
  border-right: 2px solid var(--body-text-color);
122
  border-bottom: 2px solid var(--body-text-color);
123
  content: "";
 
149
  }
150
  /*Hover-Section*/
151
  .tree li a:hover, .tree li a:hover+ul li a {
152
+ background: var(--primary-700);
153
  }
154
+ .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, .tree li a:hover+ul a::before {
155
+ border-color: var(--primary-200);
156
  }
157
+ .end-of-text, .chosen-token {
158
+ background-color: var(--primary-600);
159
  }
160
  .end-of-text {
161
  width:auto!important;
 
163
  .nonfinal {
164
  width:280px;
165
  min-width: 280px;
166
+ }
167
+ .selected-sequence {
168
+ background-color: var(--secondary-600)!important;
169
+ }
170
  """
171
 
172
 
 
188
  token = tokenizer.decode([token_idx])
189
  item_class = ""
190
  if chosen_tokens and token in chosen_tokens:
191
+ item_class = "chosen-token"
192
  markdown_table += f"""
193
  <tr class={item_class}>
194
  <td>{clean(token)}</td>
 
200
  return markdown_table
201
 
202
 
203
+ def generate_nodes(node, step):
204
  """Recursively generate HTML for the tree nodes."""
205
+ token = tokenizer.decode([node.current_token_ix])
206
+
207
+ selected_class = "selected-sequence" if node.is_selected_sequence else ""
208
 
209
  if node.is_final:
210
+ return f"<li> <a href='#' class='end-of-text child {selected_class}'> <span> <b>{clean(token)}</b> <br>Total score: {node.total_score:.2f}</span> </a> </li>"
211
 
212
+ html_content = f"<li> <a href='#' class='nonfinal child {selected_class}'> <span> <b>{clean(token)}</b> </span>"
 
 
213
  if node.table is not None:
214
  html_content += node.table
215
  html_content += "</a>"
 
217
  if len(node.children.keys()) > 0:
218
  html_content += "<ul> "
219
  for token_ix, subnode in node.children.items():
220
+ html_content += generate_nodes(subnode, step=step + 1)
221
  html_content += "</ul>"
222
  html_content += "</li>"
223
 
 
229
  <div class="tree">
230
  <ul> <li> <a href='#' id='root'> <span> <b>{start_sentence}</b> </span> {original_tree.table} </a>"""
231
  html_output += "<ul> "
232
+ for subnode in original_tree.children.values():
233
+ html_output += generate_nodes(subnode, step=1)
234
  html_output += "</ul>"
235
  html_output += """
236
  </li> </ul>
 
251
  cumulative_score: float
252
  children_score_divider: float
253
  table: str
254
+ current_sequence: str
255
  children: Dict[int, "BeamNode"]
256
  total_score: float
257
  is_final: bool
258
+ is_selected_sequence: bool
259
 
260
 
261
+ def generate_beams(start_sentence, scores, length_penalty, decoded_sequences):
 
262
  input_length = len(tokenizer([start_sentence], return_tensors="pt"))
263
  original_tree = BeamNode(
264
  cumulative_score=0,
265
  current_token_ix=None,
266
  table=None,
267
+ current_sequence=start_sentence,
268
  children={},
269
  children_score_divider=((input_length + 1) ** length_penalty),
270
  total_score=None,
271
  is_final=False,
272
+ is_selected_sequence=False,
273
  )
274
  n_beams = len(scores[0])
275
  beam_trees = [original_tree] * n_beams
 
299
  + current_beam.cumulative_score
300
  )
301
  beam_indexes += [beam_ix] * n_beams
302
+ current_completions += [beam_trees[beam_ix].current_sequence] * n_beams
303
  top_tokens += [tokenizer.decode([el]) for el in current_top_token_indexes]
304
 
305
  top_df = pd.DataFrame.from_dict(
 
350
  cumulative_scores[source_beam_ix]
351
  + scores[step][source_beam_ix][current_token_choice_ix].numpy()
352
  )
353
+ current_sequence = (
354
+ beam_trees[source_beam_ix].current_sequence + current_token_choice
355
+ )
356
  beam_trees[source_beam_ix].children[current_token_choice_ix] = BeamNode(
357
  current_token_ix=current_token_choice_ix,
358
  table=None,
359
  children={},
360
+ current_sequence=current_sequence,
 
361
  cumulative_score=cumulative_score,
362
  total_score=cumulative_score
363
  / ((input_length + step - 1) ** length_penalty),
 
366
  step == len(scores) - 1
367
  or current_token_choice_ix == tokenizer.eos_token_id
368
  ),
369
+ is_selected_sequence=(current_sequence in decoded_sequences),
370
  )
371
 
372
  # Reassign all beams at once
 
382
 
383
  return original_tree
384
 
385
+
386
  def get_beam_search_html(input_text, number_steps, number_beams, length_penalty):
387
  inputs = tokenizer([input_text], return_tensors="pt")
388
 
 
396
  output_scores=True,
397
  do_sample=False,
398
  )
399
+ markdown = "The conclusive sequences are the ones that end in an `<|endoftext|>` token or at the end of generation."
400
+ markdown += "\n\nThey are ranked by their scores, as given by the formula `score = cumulative_score / (output_length ** length_penalty)`.\n\n"
401
+ markdown += "Only the top `num_beams` scoring sequences are returned: in the tree they are highlighted in **<span style='color:var(--secondary-600)!important'>blue</span>**."
402
+ markdown += " The non-selected sequences are also shown in the tree, highlighted in **<span style='color:var(--primary-600)!important'>yellow</span>**."
403
+ markdown += "\n#### <span style='color:var(--secondary-600)!important'>Output sequences:</span>"
404
  # Sequences are padded anyway so you can batch decode them
405
  decoded_sequences = tokenizer.batch_decode(outputs.sequences)
406
  for i, sequence in enumerate(decoded_sequences):
407
+ markdown += f"\n- Score `{outputs.sequences_scores[i]:.2f}`: `{clean(sequence.replace('<s> ', ''))}`"
408
 
409
  original_tree = generate_beams(
410
  input_text,
411
  outputs.scores[:],
 
412
  length_penalty,
413
+ decoded_sequences,
414
  )
415
  html = generate_html(input_text, original_tree)
416
  return html, markdown
 
418
 
419
  with gr.Blocks(
420
  theme=gr.themes.Soft(
421
+ primary_hue=gr.themes.colors.yellow,
422
+ secondary_hue=gr.themes.colors.blue,
423
  ),
424
  css=STYLE,
425
  ) as demo:
426
  gr.Markdown(
427
+ """# <span style='color:var(--primary-600)!important'>Beam Search Visualizer</span>
428
 
429
  Play with the parameters below to understand how beam search decoding works!
430
 
431
+ #### <span style='color:var(--primary-600)!important'>Parameters:</span>
432
+ - **Sentence to decode from** (`inputs`): the input sequence to your decoder.
433
+ - **Number of steps** (`max_new_tokens`): the number of tokens to generate
434
+ - **Number of beams** (`num_beams`): the number of beams to use
435
+ - **Length penalty** (`length_penalty`): the length penalty to apply to outputs. `length_penalty` > 0.0 promotes longer sequences, while `length_penalty` < 0.0 encourages shorter sequences.
436
  This parameter will not impact the beam search paths, but only influence the choice of sequences in the end towards longer or shorter sequences.
437
  """
438
  )