nisarg6502 commited on
Commit
3d8d8a9
·
verified ·
1 Parent(s): ade0829

Stop generation on degenerate repetition loops

Browse files
Files changed (1) hide show
  1. app.py +30 -1
app.py CHANGED
@@ -59,6 +59,25 @@ class RotaryPositionalEmbedding(nn.Module):
59
  def create_causal_mask(seq_len, device):
60
  return torch.tril(torch.ones(seq_len, seq_len, device=device)).view(1, 1, seq_len, seq_len)
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  class MultiHeadAttention(nn.Module):
63
  def __init__(self, d_model, num_heads, dropout=0.1):
64
  super().__init__()
@@ -171,29 +190,39 @@ class GPT(nn.Module):
171
  return logits
172
 
173
  @torch.no_grad()
174
- def generate(self, input_ids, max_new_tokens, temperature=0.2, stop_token_id=None):
175
  # KV-cached generation: the prompt is processed once (prefill), then
176
  # each new token only attends against its own Q against the cached
177
  # K/V instead of recomputing attention over the whole sequence.
 
 
 
 
178
  self.eval()
179
  if input_ids.shape[1] > self.config.max_seq_len:
180
  input_ids = input_ids[:, -self.config.max_seq_len:]
181
 
 
 
182
  logits, past_kv = self.forward(input_ids, use_cache=True)
183
  logits = logits[:, -1, :] / temperature
184
  probs = F.softmax(logits, dim=-1)
185
  next_token = torch.multinomial(probs, num_samples=1)
 
186
  all_ids = torch.cat([input_ids, next_token], dim=1)
187
 
188
  cur_len = input_ids.shape[1]
189
  for _ in range(max_new_tokens - 1):
190
  if cur_len >= self.config.max_seq_len:
191
  break # no cache-eviction / sliding window implemented; stop cleanly
 
 
192
 
193
  logits, past_kv = self.forward(next_token, past_kv_list=past_kv, use_cache=True)
194
  logits = logits[:, -1, :] / temperature
195
  probs = F.softmax(logits, dim=-1)
196
  next_token = torch.multinomial(probs, num_samples=1)
 
197
 
198
  all_ids = torch.cat([all_ids, next_token], dim=1)
199
  cur_len += 1
 
59
  def create_causal_mask(seq_len, device):
60
  return torch.tril(torch.ones(seq_len, seq_len, device=device)).view(1, 1, seq_len, seq_len)
61
 
62
+ def _is_stuck_in_loop(generated, run_length=3, period_max=16):
63
+ # Detects the model repeating the same token, or the same short n-gram,
64
+ # `run_length` times in a row. An earlier attempt banned repeated tokens
65
+ # outright (the standard no-repeat-ngram technique); on this small,
66
+ # lightly-trained model that forced picks from a poorly calibrated tail
67
+ # distribution and produced gibberish/hallucinated tags instead of clean
68
+ # text. Detecting the loop and stopping (like hitting EOS) avoids ever
69
+ # forcing a bad choice -- it just ends the response where it degenerates.
70
+ n = len(generated)
71
+ for period in range(1, period_max + 1):
72
+ needed = period * run_length
73
+ if n < needed:
74
+ continue
75
+ window = generated[-needed:]
76
+ pattern = window[:period]
77
+ if all(window[i:i + period] == pattern for i in range(0, needed, period)):
78
+ return True
79
+ return False
80
+
81
  class MultiHeadAttention(nn.Module):
82
  def __init__(self, d_model, num_heads, dropout=0.1):
83
  super().__init__()
 
190
  return logits
191
 
192
  @torch.no_grad()
193
+ def generate(self, input_ids, max_new_tokens, temperature=0.2, stop_token_id=None, stop_on_loop=True):
194
  # KV-cached generation: the prompt is processed once (prefill), then
195
  # each new token only attends against its own Q against the cached
196
  # K/V instead of recomputing attention over the whole sequence.
197
+ #
198
+ # stop_on_loop: stops generation if the model degenerates into
199
+ # repeating the same token/short n-gram (a real, observed failure
200
+ # mode on this small model -- see _is_stuck_in_loop above).
201
  self.eval()
202
  if input_ids.shape[1] > self.config.max_seq_len:
203
  input_ids = input_ids[:, -self.config.max_seq_len:]
204
 
205
+ history = input_ids[0].tolist()
206
+
207
  logits, past_kv = self.forward(input_ids, use_cache=True)
208
  logits = logits[:, -1, :] / temperature
209
  probs = F.softmax(logits, dim=-1)
210
  next_token = torch.multinomial(probs, num_samples=1)
211
+ history.append(next_token.item())
212
  all_ids = torch.cat([input_ids, next_token], dim=1)
213
 
214
  cur_len = input_ids.shape[1]
215
  for _ in range(max_new_tokens - 1):
216
  if cur_len >= self.config.max_seq_len:
217
  break # no cache-eviction / sliding window implemented; stop cleanly
218
+ if stop_on_loop and _is_stuck_in_loop(history):
219
+ break
220
 
221
  logits, past_kv = self.forward(next_token, past_kv_list=past_kv, use_cache=True)
222
  logits = logits[:, -1, :] / temperature
223
  probs = F.softmax(logits, dim=-1)
224
  next_token = torch.multinomial(probs, num_samples=1)
225
+ history.append(next_token.item())
226
 
227
  all_ids = torch.cat([all_ids, next_token], dim=1)
228
  cur_len += 1