CoolFace
Apppublic

forestcalled/text-generation-webui

sourceHugging Faceupdated 3y agoView on Hugging Face
0likes
sampler_hijack.py295 linesDownload Raw Back to modules
1import math2 3import torch4import transformers5from transformers import LogitsWarper, is_torch_xpu_available6from transformers.generation.logits_process import (7    LogitNormalization,8    LogitsProcessor,9    LogitsProcessorList,10    TemperatureLogitsWarper11)12 13global_scores = None14 15 16class MinPLogitsWarper(LogitsWarper):17    def __init__(self, min_p: float, filter_value: float = -float("Inf"), min_tokens_to_keep: int = 1):18        if min_p < 0 or min_p > 1.0:19            raise ValueError(f"`min_p` has to be a float >= 0 and <= 1, but is {min_p}")20        self.min_p = min_p21        self.filter_value = filter_value22        self.min_tokens_to_keep = min_tokens_to_keep23 24    def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor) -> torch.FloatTensor:25        # Convert logits to probabilities26        probs = torch.softmax(scores, dim=-1)27        # Get the probability of the top token for each sequence in the batch28        top_probs, _ = probs.max(dim=-1, keepdim=True)29        # Calculate the actual min_p threshold by scaling min_p with the top token's probability30        scaled_min_p = self.min_p * top_probs31        # Create a mask for tokens that have a probability less than the scaled min_p32        tokens_to_remove = probs < scaled_min_p33 34        sorted_indices = torch.argsort(scores, descending=True, dim=-1)35        sorted_indices_to_remove = torch.gather(tokens_to_remove, dim=-1, index=sorted_indices)36 37        if self.min_tokens_to_keep > 1:38            # Keep at least min_tokens_to_keep39            sorted_indices_to_remove[..., : self.min_tokens_to_keep] = False40 41        indices_to_remove = sorted_indices_to_remove.scatter(1, sorted_indices, sorted_indices_to_remove)42        scores = scores.masked_fill(indices_to_remove, self.filter_value)43        return scores44 45 46class TailFreeLogitsWarper(LogitsWarper):47    def __init__(self, tfs: float, filter_value: float = -float("Inf"), min_tokens_to_keep: int = 1):48        tfs = float(tfs)49        if tfs < 0 or tfs > 1.0:50            raise ValueError(f"`tfs` has to be a float >= 0 and <= 1, but is {tfs}")51        self.tfs = tfs52        self.filter_value = filter_value53        self.min_tokens_to_keep = min_tokens_to_keep54 55    def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor) -> torch.FloatTensor:56        sorted_logits, sorted_indices = torch.sort(scores, descending=True)57        probs = sorted_logits.softmax(dim=-1)58 59        # Compute second derivative normalized CDF60        d2 = probs.diff().diff().abs()61        normalized_d2 = d2 / d2.sum(dim=-1, keepdim=True)62        normalized_d2_cdf = normalized_d2.cumsum(dim=-1)63 64        # Remove tokens with CDF value above the threshold (token with 0 are kept)65        sorted_indices_to_remove = normalized_d2_cdf > self.tfs66 67        # Centre the distribution around the cutoff as in the original implementation of the algorithm68        sorted_indices_to_remove = torch.cat(69            (70                torch.zeros(scores.shape[0], 1, dtype=torch.bool, device=scores.device),71                sorted_indices_to_remove,72                torch.ones(scores.shape[0], 1, dtype=torch.bool, device=scores.device),73            ),74            dim=-1,75        )76 77        if self.min_tokens_to_keep > 1:78            # Keep at least min_tokens_to_keep79            sorted_indices_to_remove[..., : self.min_tokens_to_keep] = 080 81        indices_to_remove = sorted_indices_to_remove.scatter(1, sorted_indices, sorted_indices_to_remove)82        scores = scores.masked_fill(indices_to_remove, self.filter_value)83        return scores84 85 86class TopALogitsWarper(LogitsWarper):87    def __init__(self, top_a: float, filter_value: float = -float("Inf"), min_tokens_to_keep: int = 1):88        top_a = float(top_a)89        if top_a < 0 or top_a > 1.0:90            raise ValueError(f"`top_a` has to be a float >= 0 and <= 1, but is {top_a}")91        self.top_a = top_a92        self.filter_value = filter_value93        self.min_tokens_to_keep = min_tokens_to_keep94 95    def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor) -> torch.FloatTensor:96        sorted_logits, sorted_indices = torch.sort(scores, descending=True)97        probs = sorted_logits.softmax(dim=-1)98 99        # Remove tokens with probability less than top_a*(max(probs))^2 (token with 0 are kept)100        probs_max = probs[..., 0, None]101        sorted_indices_to_remove = probs < probs_max * probs_max * self.top_a102 103        if self.min_tokens_to_keep > 1:104            # Keep at least min_tokens_to_keep105            sorted_indices_to_remove[..., : self.min_tokens_to_keep] = 0106 107        indices_to_remove = sorted_indices_to_remove.scatter(1, sorted_indices, sorted_indices_to_remove)108        scores = scores.masked_fill(indices_to_remove, self.filter_value)109        return scores110 111 112class MirostatLogitsWarper(LogitsWarper):113    def __init__(self, mirostat_mode: int, mirostat_tau: float, mirostat_eta: float, filter_value: float = -float("Inf"), min_tokens_to_keep: int = 1):114        if mirostat_mode not in [2]:115            raise ValueError(f"`mirostat` has to be a an integer 2, but is {mirostat_mode}")116        self.mirostat_mode = mirostat_mode117        self.mirostat_eta = mirostat_eta118        self.mirostat_tau = mirostat_tau119        self.filter_value = filter_value120        self.min_tokens_to_keep = min_tokens_to_keep121        self.mu = 2 * self.mirostat_tau122        self.e = 0123 124    def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor) -> torch.FloatTensor:125        logits = scores[0]126        sorted_logits, sorted_indices = torch.sort(logits, descending=True)127        prob_original = torch.softmax(sorted_logits, dim=-1).tolist()  # candidates128 129        # Truncate the words with surprise values greater than mu130        for i, candidate in enumerate(prob_original):131            if candidate > 0 and -math.log2(candidate) > self.mu:132                if (i == 0):133                    sorted_logits = sorted_logits[:1]134                else:135                    sorted_logits = sorted_logits[:i]136                break137 138        # Normalize the probabilities of the remaining words139        if is_torch_xpu_available():140            prob_topk = torch.softmax(sorted_logits, dim=0).to("xpu")141            prev_i = torch.multinomial(prob_topk, num_samples=1, replacement=True).to("xpu")142        else:143            prob_topk = torch.softmax(sorted_logits, dim=0).to('cuda')144            prev_i = torch.multinomial(prob_topk, num_samples=1, replacement=True).to('cuda')145 146        observed_surprise = -math.log2(prob_topk[prev_i])147        self.e = observed_surprise - self.mirostat_tau148 149        # Update mu using the learning rate and error150        self.mu -= self.mirostat_eta * self.e151 152        sorted_indices_to_remove = torch.ones_like(scores[0], dtype=torch.bool)153        sorted_indices_to_remove[prev_i] = False154 155        indices_to_remove = sorted_indices_to_remove.unsqueeze(0).scatter(1, sorted_indices.unsqueeze(0), sorted_indices_to_remove.unsqueeze(0))156        scores = scores.masked_fill(indices_to_remove, self.filter_value)157        return scores158 159 160class SpyLogitsWarper(LogitsWarper):161    def __init__(self):162        pass163 164    def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor) -> torch.FloatTensor:165        global global_scores166        global_scores = scores167        return scores168 169 170class RepetitionPenaltyLogitsProcessorWithRange(LogitsProcessor):171    '''172    Copied from the transformers library173    '''174 175    def __init__(self, penalty: float, presence_penalty: float, frequency_penalty: float, _range: int):176        if not (penalty > 0):177            raise ValueError(f"`penalty` has to be strictly positive, but is {penalty}")178 179        self.penalty = penalty180        self.presence_penalty = presence_penalty181        self.frequency_penalty = frequency_penalty182        self._range = _range183 184    def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor) -> torch.FloatTensor:185        input_ids = input_ids[:, -self._range:]186 187        # We loop here because torch.unique() needs to process each row separately in the188        # case that batch_size > 1.189        for input_ids_row, scores_row in zip(input_ids, scores):190            unique_ids, counts = torch.unique(input_ids_row, return_counts=True)191            score = torch.gather(scores_row, 0, unique_ids)192 193            # multiplicative repetition penalty194            # if score < 0 then repetition penalty has to be multiplied to reduce the previous token probability195            score = torch.where(score < 0, score * self.penalty, score / self.penalty)196            scores_row.scatter_(0, unique_ids, score)197 198            # presence_penalty and frequency_penalty199            raw_presence_penalty = (counts > 0).to(scores.dtype)200            raw_frequency_penalty = counts.to(scores.dtype)201            additive_penalty = raw_presence_penalty*self.presence_penalty + raw_frequency_penalty*self.frequency_penalty202            scores_row.scatter_add_(0, unique_ids, -additive_penalty)203 204        return scores205 206 207def get_logits_warper_patch(self, generation_config):208    warpers = self._get_logits_warper_old(generation_config)209    warpers_to_add = LogitsProcessorList()210    min_tokens_to_keep = 2 if generation_config.num_beams > 1 else 1211 212    if generation_config.mirostat_mode is not None and generation_config.mirostat_mode == 2:213        warpers_to_add.append(MirostatLogitsWarper(mirostat_mode=generation_config.mirostat_mode, mirostat_eta=generation_config.mirostat_eta, mirostat_tau=generation_config.mirostat_tau, min_tokens_to_keep=min_tokens_to_keep))214        # We need to disable samplers other than temperature215        for warper in warpers:216            if not isinstance(warper, TemperatureLogitsWarper):217                warpers.remove(warper)218    else:219        if generation_config.tfs is not None and 0.0 <= generation_config.tfs < 1.0:220            warpers_to_add.append(TailFreeLogitsWarper(tfs=generation_config.tfs, min_tokens_to_keep=min_tokens_to_keep))221        if generation_config.top_a is not None and 0.0 < generation_config.top_a <= 1.0:222            warpers_to_add.append(TopALogitsWarper(top_a=generation_config.top_a, min_tokens_to_keep=min_tokens_to_keep))223        if generation_config.min_p is not None and 0.0 < generation_config.min_p <= 1.0:224            warpers_to_add.append(MinPLogitsWarper(min_p=generation_config.min_p, min_tokens_to_keep=min_tokens_to_keep))225 226    if len(warpers) > 0 and isinstance(warpers[-1], LogitNormalization):227        normalize = warpers.pop(-1)228    else:229        normalize = None230 231    warpers += warpers_to_add232    if generation_config.temperature_last:233        temperature_idx = None234        for i in range(len(warpers)):235            if warpers[i].__class__.__name__ == 'TemperatureLogitsWarper':236                temperature_idx = i237                break238 239        if temperature_idx is not None:240            warpers = warpers[:temperature_idx] + warpers[temperature_idx + 1:] + [warpers[temperature_idx]]241            warpers = LogitsProcessorList(warpers)242 243    if normalize is not None:244        warpers.append(normalize)245 246    warpers.append(SpyLogitsWarper())247    # for i in range(len(warpers)):248    #     print(warpers[i].__class__.__name__)249    return warpers250 251 252def get_logits_processor_patch(self, **kwargs):253    repetition_penalty = kwargs['generation_config'].repetition_penalty254    presence_penalty = kwargs['generation_config'].presence_penalty255    frequency_penalty = kwargs['generation_config'].frequency_penalty256    repetition_penalty_range = kwargs['generation_config'].repetition_penalty_range257    do_rep_pen_hijack = (repetition_penalty > 1) or (presence_penalty != 0) or (frequency_penalty != 0)258    if do_rep_pen_hijack:259        # Make sure that a RepetitionPenaltyLogitsProcessor will be created260        kwargs['generation_config'].repetition_penalty = 1.1  # must set to some value > 1261 262    result = self._get_logits_processor_old(**kwargs)263 264    if do_rep_pen_hijack:265        for i in range(len(result)):266            if result[i].__class__.__name__ == 'RepetitionPenaltyLogitsProcessor':267                result[i] = RepetitionPenaltyLogitsProcessorWithRange(repetition_penalty, presence_penalty, frequency_penalty, repetition_penalty_range)268 269    return result270 271 272def generation_config_init_patch(self, **kwargs):273    self.__init___old(**kwargs)274    self.min_p = kwargs.pop("min_p", 0.0)275    self.tfs = kwargs.pop("tfs", 1.0)276    self.top_a = kwargs.pop("top_a", 0.0)277    self.mirostat_mode = kwargs.pop("mirostat_mode", 0)278    self.mirostat_eta = kwargs.pop("mirostat_eta", 0.1)279    self.mirostat_tau = kwargs.pop("mirostat_tau", 5)280    self.repetition_penalty_range = kwargs.pop("repetition_penalty_range", 0)281    self.presence_penalty = kwargs.pop("presence_penalty", 0)282    self.frequency_penalty = kwargs.pop("frequency_penalty", 0)283    self.temperature_last = kwargs.pop("temperature_last", False)284 285 286def hijack_samplers():287    transformers.GenerationMixin._get_logits_warper_old = transformers.GenerationMixin._get_logits_warper288    transformers.GenerationMixin._get_logits_warper = get_logits_warper_patch289 290    transformers.GenerationMixin._get_logits_processor_old = transformers.GenerationMixin._get_logits_processor291    transformers.GenerationMixin._get_logits_processor = get_logits_processor_patch292 293    transformers.GenerationConfig.__init___old = transformers.GenerationConfig.__init__294    transformers.GenerationConfig.__init__ = generation_config_init_patch295