Spaces:
Running
Running
Commit Β·
e58aace
1
Parent(s): 5a4beb9
Update inference.py
Browse files- inference.py +72 -19
inference.py
CHANGED
|
@@ -97,28 +97,77 @@ def load_model(checkpoint_path: str, device: torch.device) -> tuple[GPT, GPTConf
|
|
| 97 |
# Sampling
|
| 98 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 99 |
|
| 100 |
-
def _apply_repetition_penalty(
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
"""
|
| 116 |
if penalty == 1.0 or not generated_ids:
|
| 117 |
return logits
|
| 118 |
from collections import Counter
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
vals = logits[0, seen]
|
| 123 |
factor = penalty ** reps
|
| 124 |
logits[0, seen] = torch.where(vals > 0, vals / factor, vals * factor)
|
|
@@ -197,6 +246,10 @@ def generate(
|
|
| 197 |
block_size = model.config.block_size
|
| 198 |
eos_id = getattr(tokenizer, "eos_id", None)
|
| 199 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
generated_ids = [list(prompt_ids) for _ in range(num_samples)]
|
| 201 |
finished = [False] * num_samples
|
| 202 |
# Decode the FULL sequence each step and print only the new suffix,
|
|
@@ -222,7 +275,7 @@ def generate(
|
|
| 222 |
if finished[b]:
|
| 223 |
continue
|
| 224 |
row = logits[b:b + 1]
|
| 225 |
-
row = _apply_repetition_penalty(row, generated_ids[b], repetition_penalty)
|
| 226 |
row = _block_repeated_ngrams(row, generated_ids[b], no_repeat_ngram_size)
|
| 227 |
row = row / max(temperature, 1e-5)
|
| 228 |
if top_k is not None:
|
|
|
|
| 97 |
# Sampling
|
| 98 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 99 |
|
| 100 |
+
def _apply_repetition_penalty(
|
| 101 |
+
logits: torch.Tensor,
|
| 102 |
+
generated_ids: list[int],
|
| 103 |
+
penalty: float,
|
| 104 |
+
prompt_id_set: "set[int] | None" = None,
|
| 105 |
+
max_count: int = 6,
|
| 106 |
+
) -> torch.Tensor:
|
| 107 |
+
"""CTRL-style penalty, COUNT-SCALED: a token seen N times in the
|
| 108 |
+
*generated* portion gets divided by penalty**N (if positive) /
|
| 109 |
+
multiplied by penalty**N (if negative).
|
| 110 |
+
|
| 111 |
+
Count-scaling vs flat penalty
|
| 112 |
+
------------------------------
|
| 113 |
+
The flat version (divide once regardless of occurrence count) is too
|
| 114 |
+
weak on small/repetitive-domain models (e.g. TinyStories-style data
|
| 115 |
+
where "so so so happy!" is a genuinely common training pattern): once
|
| 116 |
+
the model locks onto a token, the penalty never gets any stronger, so
|
| 117 |
+
a mildly favoured looping token never gets pushed low enough to escape.
|
| 118 |
+
Scaling by occurrence count means each additional repeat compounds the
|
| 119 |
+
penalty, so a 3-4 token-deep loop gets meaningfully suppressed even if
|
| 120 |
+
a single application wouldn't have been enough.
|
| 121 |
+
|
| 122 |
+
Why penalty counts are clamped to max_count
|
| 123 |
+
--------------------------------------------
|
| 124 |
+
Without a cap, a legitimate word that happens to appear many times in
|
| 125 |
+
a long generation (e.g. "the", "a", punctuation) accumulates a factor
|
| 126 |
+
of penalty**N that grows without bound and can suppress even
|
| 127 |
+
high-confidence logits into noise. max_count=6 lets count-scaling do
|
| 128 |
+
its job on real loops (which repeat in short runs) while preventing
|
| 129 |
+
runaway suppression of common tokens over a long context.
|
| 130 |
+
|
| 131 |
+
Why prompt tokens are excluded from the count
|
| 132 |
+
-----------------------------------------------
|
| 133 |
+
generated_ids is seeded with the full prompt before the first token is
|
| 134 |
+
produced. Without the exclusion, common tokens in a 20-token prompt
|
| 135 |
+
already arrive at the first generation step with count >= 2-3, giving
|
| 136 |
+
them a standing penalty of penalty**3 before the model has emitted a
|
| 137 |
+
single token. That's not repetition suppression -- it's prompt
|
| 138 |
+
interference. prompt_id_set (built once per generate() call from the
|
| 139 |
+
original prompt_ids) lets us count only within the generated portion.
|
| 140 |
"""
|
| 141 |
if penalty == 1.0 or not generated_ids:
|
| 142 |
return logits
|
| 143 |
from collections import Counter
|
| 144 |
+
if prompt_id_set:
|
| 145 |
+
# Count only tokens that appear in the generated suffix (after the
|
| 146 |
+
# prompt). generated_ids includes the prompt prefix, so we walk the
|
| 147 |
+
# list and tally only tokens not in prompt_id_set -- tokens that the
|
| 148 |
+
# model itself produced. Prompt tokens still receive the flat
|
| 149 |
+
# penalty**1 if they recur in the generated portion (they're not
|
| 150 |
+
# immune; we just don't pre-charge them for being in the prompt).
|
| 151 |
+
gen_counts: Counter[int] = Counter()
|
| 152 |
+
prompt_remaining = Counter(prompt_id_set)
|
| 153 |
+
for tid in generated_ids:
|
| 154 |
+
if prompt_remaining.get(tid, 0) > 0:
|
| 155 |
+
prompt_remaining[tid] -= 1
|
| 156 |
+
continue
|
| 157 |
+
gen_counts[tid] += 1
|
| 158 |
+
counts = gen_counts
|
| 159 |
+
else:
|
| 160 |
+
counts = Counter(generated_ids)
|
| 161 |
+
|
| 162 |
+
if not counts:
|
| 163 |
+
return logits
|
| 164 |
+
|
| 165 |
+
sorted_tokens = sorted(counts.keys())
|
| 166 |
+
seen = torch.tensor(sorted_tokens, device=logits.device, dtype=torch.long)
|
| 167 |
+
# Clamp counts to max_count so common tokens don't accumulate a
|
| 168 |
+
# runaway factor over a long generation.
|
| 169 |
+
raw_reps = [min(counts[t], max_count) for t in sorted_tokens]
|
| 170 |
+
reps = torch.tensor(raw_reps, device=logits.device, dtype=logits.dtype)
|
| 171 |
vals = logits[0, seen]
|
| 172 |
factor = penalty ** reps
|
| 173 |
logits[0, seen] = torch.where(vals > 0, vals / factor, vals * factor)
|
|
|
|
| 246 |
block_size = model.config.block_size
|
| 247 |
eos_id = getattr(tokenizer, "eos_id", None)
|
| 248 |
|
| 249 |
+
# Built once from the original prompt so _apply_repetition_penalty can
|
| 250 |
+
# exclude prompt tokens from the count -- see that function's docstring.
|
| 251 |
+
prompt_id_set: set[int] = set(prompt_ids)
|
| 252 |
+
|
| 253 |
generated_ids = [list(prompt_ids) for _ in range(num_samples)]
|
| 254 |
finished = [False] * num_samples
|
| 255 |
# Decode the FULL sequence each step and print only the new suffix,
|
|
|
|
| 275 |
if finished[b]:
|
| 276 |
continue
|
| 277 |
row = logits[b:b + 1]
|
| 278 |
+
row = _apply_repetition_penalty(row, generated_ids[b], repetition_penalty, prompt_id_set=prompt_id_set)
|
| 279 |
row = _block_repeated_ngrams(row, generated_ids[b], no_repeat_ngram_size)
|
| 280 |
row = row / max(temperature, 1e-5)
|
| 281 |
if top_k is not None:
|