CoolFace
Apppublic

forestcalled/text-generation-webui

sourceHugging Faceupdated 3y agoView on Hugging Face
0likes
exllama.py238 linesDownload Raw Back to modules
1from pathlib import Path2 3import torch4import torch.nn.functional as F5from torch import version as torch_version6 7from modules import shared8from modules.logging_colors import logger9from modules.models import clear_torch_cache10from modules.text_generation import get_max_prompt_length11 12try:13    from exllama.generator import ExLlamaGenerator14    from exllama.model import ExLlama, ExLlamaCache, ExLlamaConfig15    from exllama.tokenizer import ExLlamaTokenizer16except:17    logger.warning('exllama module failed to import. Will attempt to import from repositories/.')18    try:19        from modules.relative_imports import RelativeImport20 21        with RelativeImport("repositories/exllama"):22            from generator import ExLlamaGenerator23            from model import ExLlama, ExLlamaCache, ExLlamaConfig24            from tokenizer import ExLlamaTokenizer25    except:26        logger.error(27            "Could not find repositories/exllama. Please ensure that exllama"28            " (https://github.com/turboderp/exllama) is cloned inside repositories/ and is up to date."29        )30        raise31 32 33class ExllamaModel:34    def __init__(self):35        pass36 37    @classmethod38    def from_pretrained(self, path_to_model):39 40        path_to_model = Path(f'{shared.args.model_dir}') / Path(path_to_model)41        tokenizer_model_path = path_to_model / "tokenizer.model"42        model_config_path = path_to_model / "config.json"43 44        # Find the model checkpoint45        model_path = None46        for ext in ['.safetensors', '.pt', '.bin']:47            found = list(path_to_model.glob(f"*{ext}"))48            if len(found) > 0:49                if len(found) > 1:50                    logger.warning(f'More than one {ext} model has been found. The last one will be selected. It could be wrong.')51 52                model_path = found[-1]53                break54 55        config = ExLlamaConfig(str(model_config_path))56        config.model_path = str(model_path)57        config.max_seq_len = shared.args.max_seq_len58        config.compress_pos_emb = shared.args.compress_pos_emb59        if shared.args.gpu_split:60            config.set_auto_map(shared.args.gpu_split)61            config.gpu_peer_fix = True62 63        if shared.args.alpha_value > 1 and shared.args.rope_freq_base == 0:64            config.alpha_value = shared.args.alpha_value65            config.calculate_rotary_embedding_base()66        elif shared.args.rope_freq_base > 0:67            config.rotary_embedding_base = shared.args.rope_freq_base68 69        if torch_version.hip:70            config.rmsnorm_no_half2 = True71            config.rope_no_half2 = True72            config.matmul_no_half2 = True73            config.silu_no_half2 = True74 75        model = ExLlama(config)76        tokenizer = ExLlamaTokenizer(str(tokenizer_model_path))77        cache = ExLlamaCache(model)78        generator = ExLlamaGenerator(model, tokenizer, cache)79 80        result = self()81        result.config = config82        result.model = model83        result.cache = cache84        result.tokenizer = tokenizer85        result.generator = generator86        return result, result87 88    def encode(self, string, **kwargs):89        return self.tokenizer.encode(string, max_seq_len=self.model.config.max_seq_len, add_bos=True)90 91    def decode(self, ids, **kwargs):92        if isinstance(ids, list):93            ids = torch.tensor([ids])94        elif isinstance(ids, torch.Tensor) and ids.numel() == 1:95            ids = ids.view(1, -1)96 97        return self.tokenizer.decode(ids)[0]98 99    def get_logits(self, token_ids, **kwargs):100        self.cache.current_seq_len = 0101        if token_ids.shape[-1] > 1:102            self.model.forward(token_ids[:, :-1], self.cache, input_mask=None, preprocess_only=True)103 104        return self.model.forward(token_ids[:, -1:], self.cache, **kwargs).float().cpu()105 106    def generate_with_streaming(self, prompt, state):107 108        # The cache batch size must be 2 for CFG and 1 otherwise109        if state['guidance_scale'] == 1:110            if self.cache.batch_size == 2:111                del self.cache112                clear_torch_cache()113                self.cache = ExLlamaCache(self.model)114                self.generator = ExLlamaGenerator(self.model, self.tokenizer, self.cache)115        else:116            if self.cache.batch_size == 1:117                del self.cache118                clear_torch_cache()119                self.cache = ExLlamaCache(self.model, batch_size=2)120                self.generator = ExLlamaGenerator(self.model, self.tokenizer, self.cache)121 122        self.generator.settings.temperature = state['temperature']123        self.generator.settings.top_p = state['top_p']124        self.generator.settings.top_k = state['top_k']125        self.generator.settings.typical = state['typical_p']126        self.generator.settings.token_repetition_penalty_max = state['repetition_penalty']127        self.generator.settings.token_repetition_penalty_sustain = -1 if state['repetition_penalty_range'] <= 0 else state['repetition_penalty_range']128        if state['ban_eos_token']:129            self.generator.disallow_tokens([self.tokenizer.eos_token_id])130        else:131            self.generator.disallow_tokens(None)132 133        if state['custom_token_bans']:134            to_ban = [int(x) for x in state['custom_token_bans'].split(',')]135            if len(to_ban) > 0:136                self.generator.disallow_tokens(to_ban)137 138        # Case 1: no CFG139        if state['guidance_scale'] == 1:140            self.generator.end_beam_search()141 142            # Tokenizing the input143            ids = self.generator.tokenizer.encode(prompt, max_seq_len=self.model.config.max_seq_len)144            if state['add_bos_token']:145                ids = torch.cat(146                    [torch.tensor([[self.tokenizer.bos_token_id]]).to(ids.device),147                     ids], dim=1148                ).to(torch.int64)149            ids = ids[:, -get_max_prompt_length(state):]150            if state['auto_max_new_tokens']:151                max_new_tokens = state['truncation_length'] - ids.shape[-1]152            else:153                max_new_tokens = state['max_new_tokens']154 155            self.generator.gen_begin_reuse(ids)156            initial_len = self.generator.sequence[0].shape[0]157            has_leading_space = False158 159            for i in range(max_new_tokens):160                token = self.generator.gen_single_token()161                if i == 0 and self.generator.tokenizer.tokenizer.IdToPiece(int(token)).startswith('▁'):162                    has_leading_space = True163 164                decoded_text = self.generator.tokenizer.decode(self.generator.sequence[0][initial_len:])165                if has_leading_space:166                    decoded_text = ' ' + decoded_text167 168                # Check the partial unicode character169                if chr(0xfffd) in decoded_text:170                    is_last = i == max_new_tokens - 1171                    is_stopping = token.item() == self.generator.tokenizer.eos_token_id or shared.stop_everything172                    # If we are not at the end of the generation, we skip this token173                    if not (is_last or is_stopping):174                        continue175 176                if token.item() == self.generator.tokenizer.eos_token_id or shared.stop_everything:177                    break178 179                yield decoded_text180 181        # Case 2: CFG182        # Copied from https://github.com/turboderp/exllama/blob/master/example_cfg.py183        else:184            alpha = state['guidance_scale']185            prompts = [prompt, state['negative_prompt'] or '']186 187            ids, mask = self.tokenizer.encode(188                prompts,189                return_mask=True,190                max_seq_len=self.model.config.max_seq_len,191                add_bos=state['add_bos_token']192            )193            if state['auto_max_new_tokens']:194                max_new_tokens = state['truncation_length'] - ids[0].shape[-1]195            else:196                max_new_tokens = state['max_new_tokens']197 198            self.generator.gen_begin(ids, mask=mask)199            initial_len = self.generator.sequence[0].shape[0]200            has_leading_space = False201 202            for i in range(max_new_tokens):203                logits = self.model.forward(self.generator.sequence[:, -1:], self.cache, input_mask=mask)204                self.generator.apply_rep_penalty(logits)205 206                logits = F.log_softmax(logits, dim=-1)207                logits_mixed = alpha * logits[0] + (1 - alpha) * logits[1]208 209                token, _ = self.generator.sample_current(logits_mixed)210                if i == 0 and self.generator.tokenizer.tokenizer.IdToPiece(int(token)).startswith('▁'):211                    has_leading_space = True212 213                decoded_text = self.generator.tokenizer.decode(self.generator.sequence[0][initial_len:])214                if has_leading_space:215                    decoded_text = ' ' + decoded_text216 217                # Check the partial unicode character218                if chr(0xfffd) in decoded_text:219                    is_last = i == max_new_tokens - 1220                    is_stopping = token.item() == self.tokenizer.eos_token_id or shared.stop_everything221                    # If we are not at the end of the generation, we skip this token222                    if not (is_last or is_stopping):223                        continue224 225                yield decoded_text226                if token.item() == self.tokenizer.eos_token_id or shared.stop_everything:227                    break228 229                batch_token = token.repeat(2, 1)230                self.generator.gen_accept_token(batch_token)231 232    def generate(self, prompt, state):233        output = ''234        for output in self.generate_with_streaming(prompt, state):235            pass236 237        return output238