Spaces:
Sleeping
Sleeping
Commit ·
e11643f
1
Parent(s): 7d6df80
Update inference.py
Browse files- inference.py +25 -17
inference.py
CHANGED
|
@@ -102,7 +102,7 @@ def _apply_repetition_penalty(
|
|
| 102 |
logits: torch.Tensor,
|
| 103 |
generated_ids: list[int],
|
| 104 |
penalty: float,
|
| 105 |
-
|
| 106 |
max_count: int = 6,
|
| 107 |
) -> torch.Tensor:
|
| 108 |
"""CTRL-style penalty, COUNT-SCALED: a token seen N times in the
|
|
@@ -136,12 +136,12 @@ def _apply_repetition_penalty(
|
|
| 136 |
already arrive at the first generation step with count >= 2-3, giving
|
| 137 |
them a standing penalty of penalty**3 before the model has emitted a
|
| 138 |
single token. That's not repetition suppression -- it's prompt
|
| 139 |
-
interference.
|
| 140 |
original prompt_ids) lets us count only within the generated portion.
|
| 141 |
"""
|
| 142 |
if penalty == 1.0 or not generated_ids:
|
| 143 |
return logits
|
| 144 |
-
if
|
| 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 the prompt -- tokens that the
|
|
@@ -149,9 +149,8 @@ def _apply_repetition_penalty(
|
|
| 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 |
-
|
| 153 |
-
|
| 154 |
-
)
|
| 155 |
for tid in generated_ids:
|
| 156 |
if prompt_remaining.get(tid, 0) > 0:
|
| 157 |
prompt_remaining[tid] -= 1
|
|
@@ -224,13 +223,6 @@ def _top_p_filter(logits: torch.Tensor, top_p: float) -> torch.Tensor:
|
|
| 224 |
return torch.full_like(logits, float("-inf")).scatter(1, sorted_idx, sorted_logits)
|
| 225 |
|
| 226 |
|
| 227 |
-
def _build_prompt_remaining(prompt_ids: list[int]) -> Counter[int]:
|
| 228 |
-
"""Return the prompt token multiplicities needed by the repetition
|
| 229 |
-
penalty walker. A plain set hides duplicate counts, which underestimates
|
| 230 |
-
how many times a repeated prompt token should be available to the counter.
|
| 231 |
-
"""
|
| 232 |
-
return Counter(prompt_ids)
|
| 233 |
-
|
| 234 |
|
| 235 |
def _clip_max_new_tokens(max_new_tokens: int, prompt_ids: list[int], block_size: int) -> int:
|
| 236 |
"""Prevent the generation loop from silently overwriting the prompt by
|
|
@@ -273,7 +265,10 @@ def generate(
|
|
| 273 |
|
| 274 |
# Built once from the original prompt and kept as a multiset so
|
| 275 |
# repetition penalty excludes prompt tokens by duplicate-aware count.
|
| 276 |
-
|
|
|
|
|
|
|
|
|
|
| 277 |
|
| 278 |
# Clip the requested budget to the remaining context budget so the
|
| 279 |
# context window never drops the prompt silently when max_new_tokens
|
|
@@ -305,7 +300,7 @@ def generate(
|
|
| 305 |
if finished[b]:
|
| 306 |
continue
|
| 307 |
row = logits[b:b + 1]
|
| 308 |
-
row = _apply_repetition_penalty(row, generated_ids[b], repetition_penalty,
|
| 309 |
row = _block_repeated_ngrams(row, generated_ids[b], no_repeat_ngram_size)
|
| 310 |
row = row / max(temperature, 1e-5)
|
| 311 |
if top_k is not None:
|
|
@@ -314,6 +309,12 @@ def generate(
|
|
| 314 |
row = _top_p_filter(row, top_p)
|
| 315 |
logits[b:b + 1] = row
|
| 316 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 317 |
probs = torch.softmax(logits, dim=-1)
|
| 318 |
next_ids = torch.multinomial(probs, num_samples=1) # (num_samples, 1)
|
| 319 |
|
|
@@ -324,15 +325,22 @@ def generate(
|
|
| 324 |
next_ids[b, 0] = idx[b, -1]
|
| 325 |
continue
|
| 326 |
tid = next_ids[b, 0].item()
|
| 327 |
-
generated_ids[b].append(tid)
|
| 328 |
if eos_id is not None and tid == eos_id:
|
| 329 |
finished[b] = True
|
|
|
|
|
|
|
| 330 |
|
| 331 |
idx = torch.cat([idx, next_ids], dim=1)
|
| 332 |
|
| 333 |
if stream and num_samples == 1:
|
| 334 |
new_text = tokenizer.decode(generated_ids[0])
|
| 335 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
prev_text[0] = new_text
|
| 337 |
|
| 338 |
if stream and num_samples == 1:
|
|
|
|
| 102 |
logits: torch.Tensor,
|
| 103 |
generated_ids: list[int],
|
| 104 |
penalty: float,
|
| 105 |
+
prompt_counter: "Counter[int] | None" = None,
|
| 106 |
max_count: int = 6,
|
| 107 |
) -> torch.Tensor:
|
| 108 |
"""CTRL-style penalty, COUNT-SCALED: a token seen N times in the
|
|
|
|
| 136 |
already arrive at the first generation step with count >= 2-3, giving
|
| 137 |
them a standing penalty of penalty**3 before the model has emitted a
|
| 138 |
single token. That's not repetition suppression -- it's prompt
|
| 139 |
+
interference. prompt_counter (built once per generate() call from the
|
| 140 |
original prompt_ids) lets us count only within the generated portion.
|
| 141 |
"""
|
| 142 |
if penalty == 1.0 or not generated_ids:
|
| 143 |
return logits
|
| 144 |
+
if prompt_counter:
|
| 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 the prompt -- tokens that the
|
|
|
|
| 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 |
+
# Copy the pre-built Counter (O(unique tokens), not O(prompt_len)).
|
| 153 |
+
prompt_remaining = prompt_counter.copy()
|
|
|
|
| 154 |
for tid in generated_ids:
|
| 155 |
if prompt_remaining.get(tid, 0) > 0:
|
| 156 |
prompt_remaining[tid] -= 1
|
|
|
|
| 223 |
return torch.full_like(logits, float("-inf")).scatter(1, sorted_idx, sorted_logits)
|
| 224 |
|
| 225 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
|
| 227 |
def _clip_max_new_tokens(max_new_tokens: int, prompt_ids: list[int], block_size: int) -> int:
|
| 228 |
"""Prevent the generation loop from silently overwriting the prompt by
|
|
|
|
| 265 |
|
| 266 |
# Built once from the original prompt and kept as a multiset so
|
| 267 |
# repetition penalty excludes prompt tokens by duplicate-aware count.
|
| 268 |
+
prompt_ids_reference: list[int] = list(prompt_ids)
|
| 269 |
+
# Counter built once here; _apply_repetition_penalty copies it per call
|
| 270 |
+
# (O(unique tokens)) instead of rebuilding from the list (O(prompt_len)).
|
| 271 |
+
prompt_counter: Counter[int] = Counter(prompt_ids_reference)
|
| 272 |
|
| 273 |
# Clip the requested budget to the remaining context budget so the
|
| 274 |
# context window never drops the prompt silently when max_new_tokens
|
|
|
|
| 300 |
if finished[b]:
|
| 301 |
continue
|
| 302 |
row = logits[b:b + 1]
|
| 303 |
+
row = _apply_repetition_penalty(row, generated_ids[b], repetition_penalty, prompt_counter=prompt_counter)
|
| 304 |
row = _block_repeated_ngrams(row, generated_ids[b], no_repeat_ngram_size)
|
| 305 |
row = row / max(temperature, 1e-5)
|
| 306 |
if top_k is not None:
|
|
|
|
| 309 |
row = _top_p_filter(row, top_p)
|
| 310 |
logits[b:b + 1] = row
|
| 311 |
|
| 312 |
+
# Guard: if any row is all -inf (over-aggressive filtering), reset it
|
| 313 |
+
# to uniform so multinomial doesn't receive NaN probabilities.
|
| 314 |
+
all_neg_inf = ~torch.isfinite(logits).any(dim=-1) # [B]
|
| 315 |
+
if all_neg_inf.any():
|
| 316 |
+
logits[all_neg_inf] = 0.0 # uniform before softmax
|
| 317 |
+
|
| 318 |
probs = torch.softmax(logits, dim=-1)
|
| 319 |
next_ids = torch.multinomial(probs, num_samples=1) # (num_samples, 1)
|
| 320 |
|
|
|
|
| 325 |
next_ids[b, 0] = idx[b, -1]
|
| 326 |
continue
|
| 327 |
tid = next_ids[b, 0].item()
|
|
|
|
| 328 |
if eos_id is not None and tid == eos_id:
|
| 329 |
finished[b] = True
|
| 330 |
+
else:
|
| 331 |
+
generated_ids[b].append(tid)
|
| 332 |
|
| 333 |
idx = torch.cat([idx, next_ids], dim=1)
|
| 334 |
|
| 335 |
if stream and num_samples == 1:
|
| 336 |
new_text = tokenizer.decode(generated_ids[0])
|
| 337 |
+
if new_text.startswith(prev_text[0]):
|
| 338 |
+
print(new_text[len(prev_text[0]):], end="", flush=True)
|
| 339 |
+
else:
|
| 340 |
+
# BPE decode is not prefix-stable (a new token can change how
|
| 341 |
+
# earlier bytes are decoded, e.g. completing a multi-byte char).
|
| 342 |
+
# Reprint the whole line to stay correct.
|
| 343 |
+
print("\r" + new_text, end="", flush=True)
|
| 344 |
prev_text[0] = new_text
|
| 345 |
|
| 346 |
if stream and num_samples == 1:
|