@@ -318,8 +318,8 @@ class RepetitionPenaltyLogitsProcessor(LogitsProcessor):
318318 penalty (`float`):
319319 The parameter for repetition penalty. 1.0 means no penalty. Above 1.0 penalizes previously generated
320320 tokens. Between 0.0 and 1.0 rewards previously generated tokens.
321- prompt_ignore_length (`int`, *optional*):
322- The original input ids sequence length, which if provided, will not be used in the penalty calculation .
321+ prompt_ignore_length (`int`, or `torch.LongTensor`, *optional*):
322+ Number of leading tokens to ignore, optionally specified per batch element .
323323
324324 Examples:
325325
@@ -355,14 +355,15 @@ class RepetitionPenaltyLogitsProcessor(LogitsProcessor):
355355
356356 supports_continuous_batching = False
357357
358- def __init__ (self , penalty : float , prompt_ignore_length : int | None = None ):
358+ def __init__ (self , penalty : float , prompt_ignore_length : int | torch . Tensor | None = None ):
359359 if not isinstance (penalty , float ) or not (penalty > 0 ):
360360 raise ValueError (f"`penalty` has to be a strictly positive float, but is { penalty } " )
361361
362- if prompt_ignore_length is not None and (
363- not isinstance (prompt_ignore_length , int ) or prompt_ignore_length < 0
364- ):
365- raise ValueError (f"`prompt_ignore_length` has to be a positive integer, but is { prompt_ignore_length } " )
362+ if prompt_ignore_length is not None :
363+ if isinstance (prompt_ignore_length , int ) and prompt_ignore_length < 0 :
364+ raise ValueError (f"`prompt_ignore_length` has to be a positive integer, but is { prompt_ignore_length } " )
365+ if isinstance (prompt_ignore_length , torch .Tensor ) and prompt_ignore_length .dim () != 1 :
366+ raise ValueError ("`prompt_ignore_length` must be 1D" )
366367
367368 self .penalty = penalty
368369 self .prompt_ignore_length = prompt_ignore_length
@@ -371,36 +372,49 @@ def __init__(self, penalty: float, prompt_ignore_length: int | None = None):
371372
372373 @add_start_docstrings (LOGITS_PROCESSOR_INPUTS_DOCSTRING )
373374 def __call__ (self , input_ids : torch .LongTensor , scores : torch .Tensor ) -> torch .Tensor :
374- if self .prompt_ignore_length :
375+ vocab_size = scores .shape [- 1 ]
376+ # In this case simply cut the prompt
377+ if isinstance (self .prompt_ignore_length , int ):
375378 input_ids = input_ids [:, self .prompt_ignore_length :]
379+ # In this case, set all ids we do not want to take into account at `vocab_size`, we will then filter them out after `scatter`
380+ elif isinstance (self .prompt_ignore_length , torch .Tensor ):
381+ if input_ids .shape [0 ] != self .prompt_ignore_length .shape [0 ]:
382+ raise ValueError ("Incompatible `prompt_ignore_length` batch size" )
383+ # Set the device in-place so that future calls will be a no-op
384+ self .prompt_ignore_length = self .prompt_ignore_length .to (input_ids .device )
385+ positions = torch .arange (input_ids .shape [1 ], device = input_ids .device )
386+ input_ids = input_ids .masked_fill (positions [None , :] < self .prompt_ignore_length [:, None ], vocab_size )
376387
377388 if scores .dim () == 3 :
378389 if self .logits_indices is not None and self .cu_seq_lens_q is not None :
379390 last_positions = self .logits_indices
380391 last_scores = scores [0 , last_positions , :]
381392
382393 # Prepare token mask
383- token_mask = torch .zeros_like (last_scores , dtype = torch .bool )
394+ token_mask = torch .zeros (
395+ (* last_scores .shape [:- 1 ], vocab_size + 1 ), dtype = torch .bool , device = last_scores .device
396+ )
384397 cu_seq_lens = self .cu_seq_lens_q
385398 lengths = cu_seq_lens [1 :] - cu_seq_lens [:- 1 ]
386399 seq_indices = torch .repeat_interleave (torch .arange (len (lengths ), device = input_ids .device ), lengths )
387400 token_mask [seq_indices , input_ids ] = True
388401
389402 # Apply penalty
390403 penalty_scores = torch .where (last_scores < 0 , last_scores * self .penalty , last_scores / self .penalty )
391- scores [0 , last_positions , :] = torch .where (token_mask , penalty_scores , last_scores )
404+ scores [0 , last_positions , :] = torch .where (token_mask [..., : vocab_size ] , penalty_scores , last_scores )
392405 else :
393- batch_size , seq_len , vocab_size = scores .shape
394406 last_scores = scores [:, - 1 , :]
395- token_mask = torch .zeros_like (last_scores , dtype = torch .bool )
407+ token_mask = torch .zeros (
408+ (* last_scores .shape [:- 1 ], vocab_size + 1 ), dtype = torch .bool , device = last_scores .device
409+ )
396410 if input_ids .dim () == 1 :
397411 unique_tokens = torch .unique (input_ids )
398412 token_mask .scatter_ (1 , unique_tokens .unsqueeze (0 ), True )
399413 else :
400414 token_mask .scatter_ (1 , input_ids , True )
401415 # if last_scores < 0 then repetition penalty has to be multiplied to reduce the token probabilities
402416 penalty_scores = torch .where (last_scores < 0 , last_scores * self .penalty , last_scores / self .penalty )
403- scores [:, - 1 , :] = torch .where (token_mask , penalty_scores , last_scores )
417+ scores [:, - 1 , :] = torch .where (token_mask [..., : vocab_size ] , penalty_scores , last_scores )
404418 return scores
405419
406420 if input_ids .dim () == 1 :
@@ -409,7 +423,6 @@ def __call__(self, input_ids: torch.LongTensor, scores: torch.Tensor) -> torch.T
409423 # In some very rare cases, the embedding vocab may be larger than the lm_head output - in those cases, clamp the
410424 # ids > output vocab_size to a fix index that we drop afterwards (the tokens with id > output_vocab cannot be
411425 # generated by the model, so there's no need to apply a penalty to them)
412- vocab_size = scores .shape [- 1 ]
413426 input_ids = input_ids .clamp (max = vocab_size )
414427 # Pad by 1 to absorb all the indices larger than output vocab that were clamped to the vocab
415428 scores = torch .nn .functional .pad (scores , (0 , 1 ))
0 commit comments