ArushBuilds commited on
Commit
e11643f
·
1 Parent(s): 7d6df80

Update inference.py

Browse files
Files changed (1) hide show
  1. 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
- prompt_id_set: "list[int] | set[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,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. prompt_id_set (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_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 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
- prompt_remaining = _build_prompt_remaining(
153
- list(prompt_id_set) if isinstance(prompt_id_set, set) else list(prompt_id_set)
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
- prompt_id_set: list[int] = list(prompt_ids)
 
 
 
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, prompt_id_set=prompt_id_set)
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
- print(new_text[len(prev_text[0]):], end="", flush=True)
 
 
 
 
 
 
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: