Spaces:
Running
on
T4
Running
on
T4
Upload app.py (#1)
Browse files- Upload app.py (9015a6079a70c1ceacba5d0ebf509c3a9d9be845)
Co-authored-by: Joao Gante <joaogante@users.noreply.huggingface.co>
app.py
CHANGED
|
@@ -174,9 +174,6 @@ STYLE = """
|
|
| 174 |
.nonselected-sequence {
|
| 175 |
background-color: var(--primary-500);
|
| 176 |
}
|
| 177 |
-
.nopadding {
|
| 178 |
-
padding-left: 0!important;
|
| 179 |
-
}
|
| 180 |
"""
|
| 181 |
|
| 182 |
|
|
@@ -241,7 +238,7 @@ def generate_nodes(node, step):
|
|
| 241 |
def generate_html(start_sentence, original_tree):
|
| 242 |
html_output = f"""<div class="custom-container">
|
| 243 |
<div class="tree">
|
| 244 |
-
<ul
|
| 245 |
html_output += "<ul> "
|
| 246 |
for subnode in original_tree.children.values():
|
| 247 |
html_output += generate_nodes(subnode, step=1)
|
|
@@ -273,20 +270,20 @@ class BeamNode:
|
|
| 273 |
|
| 274 |
|
| 275 |
def generate_beams(start_sentence, scores, length_penalty, decoded_sequences, beam_indexes_source):
|
| 276 |
-
input_length = len(tokenizer([start_sentence], return_tensors="pt"))
|
| 277 |
original_tree = BeamNode(
|
| 278 |
cumulative_score=0,
|
| 279 |
current_token_ix=None,
|
| 280 |
table=None,
|
| 281 |
current_sequence=start_sentence,
|
| 282 |
children={},
|
| 283 |
-
children_score_divider=(
|
| 284 |
total_score=None,
|
| 285 |
is_final=False,
|
| 286 |
is_selected_sequence=False,
|
| 287 |
)
|
| 288 |
n_beams = len(scores[0])
|
| 289 |
beam_trees = [original_tree] * n_beams
|
|
|
|
| 290 |
|
| 291 |
for step, step_scores in enumerate(scores):
|
| 292 |
|
|
@@ -297,8 +294,11 @@ def generate_beams(start_sentence, scores, length_penalty, decoded_sequences, be
|
|
| 297 |
beam_indexes,
|
| 298 |
current_sequence,
|
| 299 |
top_tokens,
|
| 300 |
-
|
| 301 |
-
|
|
|
|
|
|
|
|
|
|
| 302 |
current_beam = beam_trees[beam_ix]
|
| 303 |
|
| 304 |
# skip if the beam is already final
|
|
@@ -307,16 +307,18 @@ def generate_beams(start_sentence, scores, length_penalty, decoded_sequences, be
|
|
| 307 |
|
| 308 |
# Get top cumulative scores for the current beam
|
| 309 |
current_top_token_indexes = list(
|
| 310 |
-
np.array(scores[step][
|
| 311 |
)
|
| 312 |
top_token_indexes += current_top_token_indexes
|
|
|
|
| 313 |
top_cumulative_scores += list(
|
| 314 |
-
np.array(scores[step][
|
| 315 |
+ current_beam.cumulative_score
|
| 316 |
)
|
| 317 |
beam_indexes += [beam_ix] * n_beams
|
| 318 |
current_sequence += [beam_trees[beam_ix].current_sequence] * n_beams
|
| 319 |
top_tokens += [tokenizer.decode([el]) for el in current_top_token_indexes]
|
|
|
|
| 320 |
|
| 321 |
top_df = pd.DataFrame.from_dict(
|
| 322 |
{
|
|
@@ -325,6 +327,7 @@ def generate_beams(start_sentence, scores, length_penalty, decoded_sequences, be
|
|
| 325 |
"beam_index": beam_indexes,
|
| 326 |
"current_sequence": current_sequence,
|
| 327 |
"token": top_tokens,
|
|
|
|
| 328 |
}
|
| 329 |
)
|
| 330 |
maxes = top_df.groupby(["token_index", "current_sequence"])[
|
|
@@ -333,78 +336,85 @@ def generate_beams(start_sentence, scores, length_penalty, decoded_sequences, be
|
|
| 333 |
|
| 334 |
top_df = top_df.loc[maxes]
|
| 335 |
|
| 336 |
-
# Sort all top probabilities and keep top n_beams
|
|
|
|
| 337 |
top_df_selected = top_df.sort_values("cumulative_score", ascending=False).iloc[
|
| 338 |
-
:n_beams
|
| 339 |
]
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 343 |
|
| 344 |
# Write the scores table - one per beam source
|
| 345 |
-
|
|
|
|
| 346 |
current_beam = beam_trees[beam_ix]
|
| 347 |
if current_beam.table is None:
|
| 348 |
-
selected_tokens =
|
| 349 |
-
|
| 350 |
]
|
| 351 |
markdown_table = generate_markdown_table(
|
| 352 |
-
step_scores[
|
| 353 |
current_beam.cumulative_score,
|
| 354 |
current_beam.children_score_divider,
|
| 355 |
chosen_tokens=list(selected_tokens["token"].values),
|
| 356 |
)
|
| 357 |
beam_trees[beam_ix].table = markdown_table
|
|
|
|
|
|
|
| 358 |
|
| 359 |
# Add new children to each beam
|
| 360 |
cumulative_scores = [beam.cumulative_score for beam in beam_trees]
|
| 361 |
-
for _, row in
|
| 362 |
# Update the source tree
|
| 363 |
source_beam_ix = int(row["beam_index"])
|
| 364 |
current_token_choice_ix = row["token_index"]
|
| 365 |
current_token_choice = tokenizer.decode([current_token_choice_ix])
|
|
|
|
| 366 |
|
| 367 |
-
cumulative_score = (
|
| 368 |
-
cumulative_scores[source_beam_ix]
|
| 369 |
-
+ scores[step][source_beam_ix][current_token_choice_ix].numpy()
|
| 370 |
-
)
|
| 371 |
current_sequence = (
|
| 372 |
beam_trees[source_beam_ix].current_sequence + current_token_choice
|
| 373 |
)
|
| 374 |
-
|
| 375 |
-
print("Found info:")
|
| 376 |
-
print(f"We generate token '{current_token_choice}', and the total sequence is '{current_sequence}'")
|
| 377 |
beam_trees[source_beam_ix].children[current_token_choice_ix] = BeamNode(
|
| 378 |
current_token_ix=current_token_choice_ix,
|
| 379 |
table=None,
|
| 380 |
children={},
|
| 381 |
current_sequence=current_sequence,
|
| 382 |
cumulative_score=cumulative_score,
|
| 383 |
-
total_score=cumulative_score
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
is_final=(
|
| 387 |
-
step == len(scores) - 1
|
| 388 |
-
or current_token_choice_ix == tokenizer.eos_token_id
|
| 389 |
-
),
|
| 390 |
is_selected_sequence=(
|
| 391 |
current_sequence.replace("<|endoftext|>", "")
|
| 392 |
in [el.replace("<|endoftext|>", "") for el in decoded_sequences]
|
| 393 |
),
|
| 394 |
)
|
| 395 |
|
| 396 |
-
|
| 397 |
# Swap all beams by descending cumul score, so that n°1 has the highest cumulative score, and so on
|
| 398 |
beam_trees = [
|
| 399 |
-
beam_trees[int(
|
| 400 |
-
for beam_ix in range(
|
| 401 |
]
|
| 402 |
|
| 403 |
# Advance all beams by one token
|
| 404 |
-
for beam_ix in range(
|
| 405 |
-
current_token_choice_ix =
|
| 406 |
beam_trees[beam_ix] = beam_trees[beam_ix].children[current_token_choice_ix]
|
| 407 |
|
|
|
|
|
|
|
| 408 |
return original_tree
|
| 409 |
|
| 410 |
@spaces.GPU
|
|
@@ -459,9 +469,9 @@ with gr.Blocks(
|
|
| 459 |
) as demo:
|
| 460 |
gr.Markdown(
|
| 461 |
"""# <span style='color:var(--primary-500)!important'>Beam Search Visualizer</span>
|
| 462 |
-
|
| 463 |
Play with the parameters below to understand how beam search decoding works!
|
| 464 |
-
|
| 465 |
#### <span style='color:var(--primary-500)!important'>Parameters:</span>
|
| 466 |
- **Sentence to decode from** (`inputs`): the input sequence to your decoder.
|
| 467 |
- **Number of steps** (`max_new_tokens`): the number of tokens to generate.
|
|
@@ -473,20 +483,20 @@ This parameter will not impact the beam search paths, but only influence the cho
|
|
| 473 |
)
|
| 474 |
text = gr.Textbox(
|
| 475 |
label="Sentence to decode from",
|
| 476 |
-
value="
|
| 477 |
)
|
| 478 |
with gr.Row():
|
| 479 |
n_steps = gr.Slider(
|
| 480 |
-
label="Number of steps", minimum=1, maximum=10, step=1, value=
|
| 481 |
)
|
| 482 |
n_beams = gr.Slider(
|
| 483 |
-
label="Number of beams", minimum=2, maximum=4, step=1, value=
|
| 484 |
)
|
| 485 |
length_penalty = gr.Slider(
|
| 486 |
label="Length penalty", minimum=-3, maximum=3, step=0.5, value=1
|
| 487 |
)
|
| 488 |
num_return_sequences = gr.Slider(
|
| 489 |
-
label="Number of return sequences", minimum=1, maximum=
|
| 490 |
)
|
| 491 |
|
| 492 |
n_beams.change(
|
|
@@ -501,4 +511,4 @@ This parameter will not impact the beam search paths, but only influence the cho
|
|
| 501 |
outputs=[out_html, out_markdown],
|
| 502 |
)
|
| 503 |
|
| 504 |
-
demo.launch()
|
|
|
|
| 174 |
.nonselected-sequence {
|
| 175 |
background-color: var(--primary-500);
|
| 176 |
}
|
|
|
|
|
|
|
|
|
|
| 177 |
"""
|
| 178 |
|
| 179 |
|
|
|
|
| 238 |
def generate_html(start_sentence, original_tree):
|
| 239 |
html_output = f"""<div class="custom-container">
|
| 240 |
<div class="tree">
|
| 241 |
+
<ul> <li> <a href='#' id='root'> <span> <b>{start_sentence}</b> </span> {original_tree.table} </a>"""
|
| 242 |
html_output += "<ul> "
|
| 243 |
for subnode in original_tree.children.values():
|
| 244 |
html_output += generate_nodes(subnode, step=1)
|
|
|
|
| 270 |
|
| 271 |
|
| 272 |
def generate_beams(start_sentence, scores, length_penalty, decoded_sequences, beam_indexes_source):
|
|
|
|
| 273 |
original_tree = BeamNode(
|
| 274 |
cumulative_score=0,
|
| 275 |
current_token_ix=None,
|
| 276 |
table=None,
|
| 277 |
current_sequence=start_sentence,
|
| 278 |
children={},
|
| 279 |
+
children_score_divider=(1 ** length_penalty),
|
| 280 |
total_score=None,
|
| 281 |
is_final=False,
|
| 282 |
is_selected_sequence=False,
|
| 283 |
)
|
| 284 |
n_beams = len(scores[0])
|
| 285 |
beam_trees = [original_tree] * n_beams
|
| 286 |
+
generation_length = len(scores)
|
| 287 |
|
| 288 |
for step, step_scores in enumerate(scores):
|
| 289 |
|
|
|
|
| 294 |
beam_indexes,
|
| 295 |
current_sequence,
|
| 296 |
top_tokens,
|
| 297 |
+
token_scores,
|
| 298 |
+
) = ([], [], [], [], [], [])
|
| 299 |
+
|
| 300 |
+
score_idx = 0
|
| 301 |
+
for beam_ix in range(len(beam_trees)):
|
| 302 |
current_beam = beam_trees[beam_ix]
|
| 303 |
|
| 304 |
# skip if the beam is already final
|
|
|
|
| 307 |
|
| 308 |
# Get top cumulative scores for the current beam
|
| 309 |
current_top_token_indexes = list(
|
| 310 |
+
np.array(scores[step][score_idx].argsort()[-n_beams:])[::-1]
|
| 311 |
)
|
| 312 |
top_token_indexes += current_top_token_indexes
|
| 313 |
+
token_scores += list(np.array(scores[step][score_idx][current_top_token_indexes]))
|
| 314 |
top_cumulative_scores += list(
|
| 315 |
+
np.array(scores[step][score_idx][current_top_token_indexes])
|
| 316 |
+ current_beam.cumulative_score
|
| 317 |
)
|
| 318 |
beam_indexes += [beam_ix] * n_beams
|
| 319 |
current_sequence += [beam_trees[beam_ix].current_sequence] * n_beams
|
| 320 |
top_tokens += [tokenizer.decode([el]) for el in current_top_token_indexes]
|
| 321 |
+
score_idx += 1
|
| 322 |
|
| 323 |
top_df = pd.DataFrame.from_dict(
|
| 324 |
{
|
|
|
|
| 327 |
"beam_index": beam_indexes,
|
| 328 |
"current_sequence": current_sequence,
|
| 329 |
"token": top_tokens,
|
| 330 |
+
"token_score": token_scores,
|
| 331 |
}
|
| 332 |
)
|
| 333 |
maxes = top_df.groupby(["token_index", "current_sequence"])[
|
|
|
|
| 336 |
|
| 337 |
top_df = top_df.loc[maxes]
|
| 338 |
|
| 339 |
+
# Sort all top probabilities and keep top n_beams * 2 (* 2 because each beam may end this iteration, and we
|
| 340 |
+
# want to keep at least `n_beams` beams alive)
|
| 341 |
top_df_selected = top_df.sort_values("cumulative_score", ascending=False).iloc[
|
| 342 |
+
:n_beams * 2
|
| 343 |
]
|
| 344 |
+
beams_to_keep = 0
|
| 345 |
+
unfinished_beams = 0
|
| 346 |
+
for _, row in top_df_selected.iterrows():
|
| 347 |
+
beams_to_keep += 1
|
| 348 |
+
current_token_choice_ix = row["token_index"]
|
| 349 |
+
is_final = step == len(scores) - 1 or current_token_choice_ix == tokenizer.eos_token_id
|
| 350 |
+
if not is_final:
|
| 351 |
+
unfinished_beams += 1
|
| 352 |
+
if unfinished_beams >= n_beams:
|
| 353 |
+
break
|
| 354 |
+
if step == generation_length - 1 and beams_to_keep == n_beams:
|
| 355 |
+
break
|
| 356 |
+
top_df_selected_filtered = top_df_selected.iloc[:beams_to_keep]
|
| 357 |
|
| 358 |
# Write the scores table - one per beam source
|
| 359 |
+
score_idx = 0
|
| 360 |
+
for beam_ix in range(len(beam_trees)):
|
| 361 |
current_beam = beam_trees[beam_ix]
|
| 362 |
if current_beam.table is None:
|
| 363 |
+
selected_tokens = top_df_selected_filtered.loc[
|
| 364 |
+
top_df_selected_filtered["current_sequence"] == current_beam.current_sequence
|
| 365 |
]
|
| 366 |
markdown_table = generate_markdown_table(
|
| 367 |
+
step_scores[score_idx, :],
|
| 368 |
current_beam.cumulative_score,
|
| 369 |
current_beam.children_score_divider,
|
| 370 |
chosen_tokens=list(selected_tokens["token"].values),
|
| 371 |
)
|
| 372 |
beam_trees[beam_ix].table = markdown_table
|
| 373 |
+
if not current_beam.is_final:
|
| 374 |
+
score_idx = min(score_idx + 1, n_beams - 1)
|
| 375 |
|
| 376 |
# Add new children to each beam
|
| 377 |
cumulative_scores = [beam.cumulative_score for beam in beam_trees]
|
| 378 |
+
for _, row in top_df_selected_filtered.iterrows():
|
| 379 |
# Update the source tree
|
| 380 |
source_beam_ix = int(row["beam_index"])
|
| 381 |
current_token_choice_ix = row["token_index"]
|
| 382 |
current_token_choice = tokenizer.decode([current_token_choice_ix])
|
| 383 |
+
token_scores = row["token_score"]
|
| 384 |
|
| 385 |
+
cumulative_score = cumulative_scores[source_beam_ix] + np.asarray(token_scores)
|
|
|
|
|
|
|
|
|
|
| 386 |
current_sequence = (
|
| 387 |
beam_trees[source_beam_ix].current_sequence + current_token_choice
|
| 388 |
)
|
| 389 |
+
is_final = step == len(scores) - 1 or current_token_choice_ix == tokenizer.eos_token_id
|
|
|
|
|
|
|
| 390 |
beam_trees[source_beam_ix].children[current_token_choice_ix] = BeamNode(
|
| 391 |
current_token_ix=current_token_choice_ix,
|
| 392 |
table=None,
|
| 393 |
children={},
|
| 394 |
current_sequence=current_sequence,
|
| 395 |
cumulative_score=cumulative_score,
|
| 396 |
+
total_score=cumulative_score / (step + 1 ** length_penalty),
|
| 397 |
+
children_score_divider=((step + 2) ** length_penalty),
|
| 398 |
+
is_final=is_final,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 399 |
is_selected_sequence=(
|
| 400 |
current_sequence.replace("<|endoftext|>", "")
|
| 401 |
in [el.replace("<|endoftext|>", "") for el in decoded_sequences]
|
| 402 |
),
|
| 403 |
)
|
| 404 |
|
|
|
|
| 405 |
# Swap all beams by descending cumul score, so that n°1 has the highest cumulative score, and so on
|
| 406 |
beam_trees = [
|
| 407 |
+
beam_trees[int(top_df_selected_filtered.iloc[beam_ix]["beam_index"])]
|
| 408 |
+
for beam_ix in range(beams_to_keep)
|
| 409 |
]
|
| 410 |
|
| 411 |
# Advance all beams by one token
|
| 412 |
+
for beam_ix in range(beams_to_keep):
|
| 413 |
+
current_token_choice_ix = top_df_selected_filtered.iloc[beam_ix]["token_index"]
|
| 414 |
beam_trees[beam_ix] = beam_trees[beam_ix].children[current_token_choice_ix]
|
| 415 |
|
| 416 |
+
print(f"Step {step}, beams kept: {beams_to_keep}")
|
| 417 |
+
|
| 418 |
return original_tree
|
| 419 |
|
| 420 |
@spaces.GPU
|
|
|
|
| 469 |
) as demo:
|
| 470 |
gr.Markdown(
|
| 471 |
"""# <span style='color:var(--primary-500)!important'>Beam Search Visualizer</span>
|
| 472 |
+
|
| 473 |
Play with the parameters below to understand how beam search decoding works!
|
| 474 |
+
|
| 475 |
#### <span style='color:var(--primary-500)!important'>Parameters:</span>
|
| 476 |
- **Sentence to decode from** (`inputs`): the input sequence to your decoder.
|
| 477 |
- **Number of steps** (`max_new_tokens`): the number of tokens to generate.
|
|
|
|
| 483 |
)
|
| 484 |
text = gr.Textbox(
|
| 485 |
label="Sentence to decode from",
|
| 486 |
+
value="Conclusion: thanks a lot. That's all for today",
|
| 487 |
)
|
| 488 |
with gr.Row():
|
| 489 |
n_steps = gr.Slider(
|
| 490 |
+
label="Number of steps", minimum=1, maximum=10, step=1, value=10
|
| 491 |
)
|
| 492 |
n_beams = gr.Slider(
|
| 493 |
+
label="Number of beams", minimum=2, maximum=4, step=1, value=4
|
| 494 |
)
|
| 495 |
length_penalty = gr.Slider(
|
| 496 |
label="Length penalty", minimum=-3, maximum=3, step=0.5, value=1
|
| 497 |
)
|
| 498 |
num_return_sequences = gr.Slider(
|
| 499 |
+
label="Number of return sequences", minimum=1, maximum=4, step=1, value=3
|
| 500 |
)
|
| 501 |
|
| 502 |
n_beams.change(
|
|
|
|
| 511 |
outputs=[out_html, out_markdown],
|
| 512 |
)
|
| 513 |
|
| 514 |
+
demo.launch()
|