CoolFace
Apppublic

nxphi47/MultiPurpose-Chatbot-DEMO

sourceHugging Faceapache-2.0updated 3y agoView on Hugging Face
1likes
transformers_engine.py452 linesDownload Raw Back to engines
1 2import os3import numpy as np4import argparse5import torch6import gradio as gr7from typing import Any, Iterator8from typing import Iterator, List, Optional, Tuple9import filelock10import glob11import json12import time13from gradio.routes import Request14from gradio.utils import SyncToAsyncIterator, async_iteration15from gradio.helpers import special_args16import anyio17from typing import AsyncGenerator, Callable, Literal, Union, cast18 19from gradio_client.documentation import document, set_documentation_group20 21from typing import List, Optional, Union, Dict, Tuple22from tqdm.auto import tqdm23from huggingface_hub import snapshot_download24import types25 26from gradio.components import Button27from gradio.events import Dependency, EventListenerMethod28 29from .base_engine import BaseEngine30 31# ! Remember to use static cache32 33from transformers import (34    GenerationConfig,35    GenerationMixin,36    LogitsProcessorList,37    StoppingCriteriaList,38    DisjunctiveConstraint,39    BeamSearchScorer,40    PhrasalConstraint,41    ConstrainedBeamSearchScorer,42    PreTrainedModel,43)44import numpy as np45import random46import warnings47import inspect48from transformers.generation.utils import GenerateOutput, SampleOutput, logger49import torch50from typing import Callable, List, Optional, Union51from torch import nn52import torch.distributed as dist53import copy54 55from ..configs import (56    MODEL_PATH,57    DTYPE,58    DEVICE,59)60 61 62def setup_seed(seed):63    if seed == -1:64        return65    torch.manual_seed(seed)66    if torch.cuda.is_available():67        torch.cuda.manual_seed_all(seed)68    np.random.seed(seed)69    random.seed(seed)70    torch.backends.cudnn.deterministic = True71 72 73class NewGenerationMixin(GenerationMixin):74    """75    Allow generator sampling76 77    """78 79    # ! Copy from transformers.generation.utils -> GenerationMixin80    # Change sample function to sample_stream81    @torch.no_grad()82    def sample_stream(83        self,84        input_ids: torch.LongTensor,85        logits_processor: Optional[LogitsProcessorList] = None,86        stopping_criteria: Optional[StoppingCriteriaList] = None,87        logits_warper: Optional[LogitsProcessorList] = None,88        max_length: Optional[int] = None,89        pad_token_id: Optional[int] = None,90        eos_token_id: Optional[Union[int, List[int]]] = None,91        output_attentions: Optional[bool] = None,92        output_hidden_states: Optional[bool] = None,93        output_scores: Optional[bool] = None,94        output_logits: Optional[bool] = None,95        return_dict_in_generate: Optional[bool] = None,96        synced_gpus: bool = False,97        streamer: Optional["BaseStreamer"] = None,98        **model_kwargs,99    ):100        r"""101        Generates sequences of token ids for models with a language modeling head using **multinomial sampling** and102        can be used for text-decoder, text-to-text, speech-to-text, and vision-to-text models.103 104        <Tip warning={true}>105 106        In most cases, you do not need to call [`~generation.GenerationMixin.sample`] directly. Use generate() instead.107        For an overview of generation strategies and code examples, check the [following108        guide](../generation_strategies).109 110        </Tip>111 112        Parameters:113            input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`):114                The sequence used as a prompt for the generation.115            logits_processor (`LogitsProcessorList`, *optional*):116                An instance of [`LogitsProcessorList`]. List of instances of class derived from [`LogitsProcessor`]117                used to modify the prediction scores of the language modeling head applied at each generation step.118            stopping_criteria (`StoppingCriteriaList`, *optional*):119                An instance of [`StoppingCriteriaList`]. List of instances of class derived from [`StoppingCriteria`]120                used to tell if the generation loop should stop.121            logits_warper (`LogitsProcessorList`, *optional*):122                An instance of [`LogitsProcessorList`]. List of instances of class derived from [`LogitsWarper`] used123                to warp the prediction score distribution of the language modeling head applied before multinomial124                sampling at each generation step.125            max_length (`int`, *optional*, defaults to 20):126                **DEPRECATED**. Use `logits_processor` or `stopping_criteria` directly to cap the number of generated127                tokens. The maximum length of the sequence to be generated.128            pad_token_id (`int`, *optional*):129                The id of the *padding* token.130            eos_token_id (`Union[int, List[int]]`, *optional*):131                The id of the *end-of-sequence* token. Optionally, use a list to set multiple *end-of-sequence* tokens.132            output_attentions (`bool`, *optional*, defaults to `False`):133                Whether or not to return the attentions tensors of all attention layers. See `attentions` under134                returned tensors for more details.135            output_hidden_states (`bool`, *optional*, defaults to `False`):136                Whether or not to return the hidden states of all layers. See `hidden_states` under returned tensors137                for more details.138            output_scores (`bool`, *optional*, defaults to `False`):139                Whether or not to return the prediction scores. See `scores` under returned tensors for more details.140            output_logits (`bool`, *optional*, defaults to `False`):141                Whether or not to return the raw prediction logit scores. See `logits` under returned tensors for142                more details.143            return_dict_in_generate (`bool`, *optional*, defaults to `False`):144                Whether or not to return a [`~utils.ModelOutput`] instead of a plain tuple.145            synced_gpus (`bool`, *optional*, defaults to `False`):146                Whether to continue running the while loop until max_length (needed for ZeRO stage 3)147            streamer (`BaseStreamer`, *optional*):148                Streamer object that will be used to stream the generated sequences. Generated tokens are passed149                through `streamer.put(token_ids)` and the streamer is responsible for any further processing.150            model_kwargs:151                Additional model specific kwargs will be forwarded to the `forward` function of the model. If model is152                an encoder-decoder model the kwargs should include `encoder_outputs`.153 154        Return:155            [`~generation.GenerateDecoderOnlyOutput`], [`~generation.GenerateEncoderDecoderOutput`] or `torch.LongTensor`:156            A `torch.LongTensor` containing the generated tokens (default behaviour) or a157            [`~generation.GenerateDecoderOnlyOutput`] if `model.config.is_encoder_decoder=False` and158            `return_dict_in_generate=True` or a [`~generation.GenerateEncoderDecoderOutput`] if159            `model.config.is_encoder_decoder=True`.160 161        Examples:162 163        ```python164        >>> from transformers import (165        ...     AutoTokenizer,166        ...     AutoModelForCausalLM,167        ...     LogitsProcessorList,168        ...     MinLengthLogitsProcessor,169        ...     TopKLogitsWarper,170        ...     TemperatureLogitsWarper,171        ...     StoppingCriteriaList,172        ...     MaxLengthCriteria,173        ... )174        >>> import torch175 176        >>> tokenizer = AutoTokenizer.from_pretrained("openai-community/gpt2")177        >>> model = AutoModelForCausalLM.from_pretrained("openai-community/gpt2")178 179        >>> # set pad_token_id to eos_token_id because GPT2 does not have a EOS token180        >>> model.config.pad_token_id = model.config.eos_token_id181        >>> model.generation_config.pad_token_id = model.config.eos_token_id182 183        >>> input_prompt = "Today is a beautiful day, and"184        >>> input_ids = tokenizer(input_prompt, return_tensors="pt").input_ids185 186        >>> # instantiate logits processors187        >>> logits_processor = LogitsProcessorList(188        ...     [189        ...         MinLengthLogitsProcessor(15, eos_token_id=model.generation_config.eos_token_id),190        ...     ]191        ... )192        >>> # instantiate logits processors193        >>> logits_warper = LogitsProcessorList(194        ...     [195        ...         TopKLogitsWarper(50),196        ...         TemperatureLogitsWarper(0.7),197        ...     ]198        ... )199 200        >>> stopping_criteria = StoppingCriteriaList([MaxLengthCriteria(max_length=20)])201 202        >>> torch.manual_seed(0)  # doctest: +IGNORE_RESULT203        >>> outputs = model.sample(204        ...     input_ids,205        ...     logits_processor=logits_processor,206        ...     logits_warper=logits_warper,207        ...     stopping_criteria=stopping_criteria,208        ... )209 210        >>> tokenizer.batch_decode(outputs, skip_special_tokens=True)211        ['Today is a beautiful day, and we must do everything possible to make it a day of celebration.']212        ```"""213        # init values214        from transformers.generation.utils import (215            validate_stopping_criteria, GenerateEncoderDecoderOutput, GenerateDecoderOnlyOutput216        )217        logits_processor = logits_processor if logits_processor is not None else LogitsProcessorList()218        stopping_criteria = stopping_criteria if stopping_criteria is not None else StoppingCriteriaList()219        if max_length is not None:220            warnings.warn(221                "`max_length` is deprecated in this function, use"222                " `stopping_criteria=StoppingCriteriaList([MaxLengthCriteria(max_length=max_length)])` instead.",223                UserWarning,224            )225            stopping_criteria = validate_stopping_criteria(stopping_criteria, max_length)226        logits_warper = logits_warper if logits_warper is not None else LogitsProcessorList()227        pad_token_id = pad_token_id if pad_token_id is not None else self.generation_config.pad_token_id228        eos_token_id = eos_token_id if eos_token_id is not None else self.generation_config.eos_token_id229        if isinstance(eos_token_id, int):230            eos_token_id = [eos_token_id]231        eos_token_id_tensor = torch.tensor(eos_token_id).to(input_ids.device) if eos_token_id is not None else None232        output_scores = output_scores if output_scores is not None else self.generation_config.output_scores233        output_logits = output_logits if output_logits is not None else self.generation_config.output_logits234        output_attentions = (235            output_attentions if output_attentions is not None else self.generation_config.output_attentions236        )237        output_hidden_states = (238            output_hidden_states if output_hidden_states is not None else self.generation_config.output_hidden_states239        )240        return_dict_in_generate = (241            return_dict_in_generate242            if return_dict_in_generate is not None243            else self.generation_config.return_dict_in_generate244        )245 246        # init attention / hidden states / scores tuples247        scores = () if (return_dict_in_generate and output_scores) else None248        raw_logits = () if (return_dict_in_generate and output_logits) else None249        decoder_attentions = () if (return_dict_in_generate and output_attentions) else None250        cross_attentions = () if (return_dict_in_generate and output_attentions) else None251        decoder_hidden_states = () if (return_dict_in_generate and output_hidden_states) else None252 253        # if model is an encoder-decoder, retrieve encoder attention weights and hidden states254        if return_dict_in_generate and self.config.is_encoder_decoder:255            encoder_attentions = model_kwargs["encoder_outputs"].get("attentions") if output_attentions else None256            encoder_hidden_states = (257                model_kwargs["encoder_outputs"].get("hidden_states") if output_hidden_states else None258            )259        # keep track of which sequences are already finished260        unfinished_sequences = torch.ones(input_ids.shape[0], dtype=torch.long, device=input_ids.device)261 262        this_peer_finished = False  # used by synced_gpus only263        # auto-regressive generation264        while True:265            if synced_gpus:266                # Under synced_gpus the `forward` call must continue until all gpus complete their sequence.267                # The following logic allows an early break if all peers finished generating their sequence268                this_peer_finished_flag = torch.tensor(0.0 if this_peer_finished else 1.0).to(input_ids.device)269                # send 0.0 if we finished, 1.0 otherwise270                dist.all_reduce(this_peer_finished_flag, op=dist.ReduceOp.SUM)271                # did all peers finish? the reduced sum will be 0.0 then272                if this_peer_finished_flag.item() == 0.0:273                    break274 275            # prepare model inputs276            model_inputs = self.prepare_inputs_for_generation(input_ids, **model_kwargs)277 278            # forward pass to get next token279            outputs = self(280                **model_inputs,281                return_dict=True,282                output_attentions=output_attentions,283                output_hidden_states=output_hidden_states,284            )285 286            if synced_gpus and this_peer_finished:287                continue  # don't waste resources running the code we don't need288 289            next_token_logits = outputs.logits[:, -1, :]290 291            # pre-process distribution292            next_token_scores = logits_processor(input_ids, next_token_logits)293            next_token_scores = logits_warper(input_ids, next_token_scores)294 295            # Store scores, attentions and hidden_states when required296            if return_dict_in_generate:297                if output_scores:298                    scores += (next_token_scores,)299                if output_logits:300                    raw_logits += (next_token_logits,)301                if output_attentions:302                    decoder_attentions += (303                        (outputs.decoder_attentions,) if self.config.is_encoder_decoder else (outputs.attentions,)304                    )305                    if self.config.is_encoder_decoder:306                        cross_attentions += (outputs.cross_attentions,)307 308                if output_hidden_states:309                    decoder_hidden_states += (310                        (outputs.decoder_hidden_states,)311                        if self.config.is_encoder_decoder312                        else (outputs.hidden_states,)313                    )314 315            # sample316            probs = nn.functional.softmax(next_token_scores, dim=-1)317            next_tokens = torch.multinomial(probs, num_samples=1).squeeze(1)318 319            # finished sentences should have their next token be a padding token320            if eos_token_id is not None:321                if pad_token_id is None:322                    raise ValueError("If `eos_token_id` is defined, make sure that `pad_token_id` is defined.")323                next_tokens = next_tokens * unfinished_sequences + pad_token_id * (1 - unfinished_sequences)324 325            yield next_tokens.cpu()326 327            # update generated ids, model inputs, and length for next step328            input_ids = torch.cat([input_ids, next_tokens[:, None]], dim=-1)329            if streamer is not None:330                streamer.put(next_tokens.cpu())331            332            next_model_inputs = {}333            if "cache_position" in model_inputs:334                next_model_inputs['cache_position'] = model_inputs['cache_position']335            336            try:337                model_kwargs = self._update_model_kwargs_for_generation(338                    outputs, model_kwargs, is_encoder_decoder=self.config.is_encoder_decoder, 339                    # model_inputs=model_inputs340                    model_inputs=next_model_inputs,341                )342            except Exception as e:343                # Older version dont have model_inputs344                model_kwargs = self._update_model_kwargs_for_generation(345                    outputs, model_kwargs, is_encoder_decoder=self.config.is_encoder_decoder, 346                )347 348 349            # if eos_token was found in one sentence, set sentence to finished350            if eos_token_id_tensor is not None:351                unfinished_sequences = unfinished_sequences.mul(352                    next_tokens.tile(eos_token_id_tensor.shape[0], 1).ne(eos_token_id_tensor.unsqueeze(1)).prod(dim=0)353                )354 355                # stop when each sentence is finished356                if unfinished_sequences.max() == 0:357                    this_peer_finished = True358 359            # stop if we exceed the maximum length360            if stopping_criteria(input_ids, scores):361                this_peer_finished = True362 363            if this_peer_finished and not synced_gpus:364                break365 366        if streamer is not None:367            streamer.end()368 369        # if return_dict_in_generate:370        #     if self.config.is_encoder_decoder:371        #         return GenerateEncoderDecoderOutput(372        #             sequences=input_ids,373        #             scores=scores,374        #             logits=raw_logits,375        #             encoder_attentions=encoder_attentions,376        #             encoder_hidden_states=encoder_hidden_states,377        #             decoder_attentions=decoder_attentions,378        #             cross_attentions=cross_attentions,379        #             decoder_hidden_states=decoder_hidden_states,380        #             past_key_values=model_kwargs.get("past_key_values"),381        #         )382        #     else:383        #         return GenerateDecoderOnlyOutput(384        #             sequences=input_ids,385        #             scores=scores,386        #             logits=raw_logits,387        #             attentions=decoder_attentions,388        #             hidden_states=decoder_hidden_states,389        #             past_key_values=model_kwargs.get("past_key_values"),390        #         )391        # else:392        #     return input_ids393 394 395 396class TransformersEngine(BaseEngine):397    @property398    def max_position_embeddings(self) -> int:399        return self._model.config.max_position_embeddings400 401    @property402    def tokenizer(self):403        return self._tokenizer404 405    def load_model(self):406        from transformers import AutoTokenizer, AutoModelForCausalLM407        import sys408        # caution: path[0] is reserved for script path (or '' in REPL)409        # sys.path.append(CODE_PATH)410        self.model_path = model_path = MODEL_PATH411        self.torch_dtype = torch.bfloat16 if DTYPE == 'bfloat16' else torch.float16412        self.device_map = DEVICE413        print(f'Loading model from {model_path} on {self.device_map} with {self.torch_dtype}')414 415        self._tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)416        assert self._tokenizer.chat_template is not None and self._tokenizer.chat_template != "", f"{self._tokenizer.chat_template=} not found!"417        self._model = AutoModelForCausalLM.from_pretrained(model_path, torch_dtype=self.torch_dtype, device_map=self.device_map, trust_remote_code=True).eval()418        self._model.sample_old = self._model.sample419        self._model._sample = types.MethodType(NewGenerationMixin.sample_stream, self._model)420        print(self._model)421        print(f"{self.max_position_embeddings=}")422    423    def generate_yield_string(self, prompt, temperature, max_tokens, stop_strings: Optional[Tuple[str]] = None, **kwargs):424        425        # ! MUST PUT INSIDE torch.no_grad() otherwise it will overflow OOM426        with torch.no_grad():427            inputs = self.tokenizer(prompt, return_tensors='pt')428            num_tokens = inputs.input_ids.size(1)429 430            inputs = {k: v.to(self.device_map) for k, v in inputs.items() if v is not None}431            generator = self._model.generate(432                **inputs, 433                do_sample=True, 434                temperature=temperature, 435                max_new_tokens=max_tokens, 436                pad_token_id=self.processor.tokenizer.pad_token_id,437            )438 439            out_tokens = []440            response = None441            for token in generator:442                out_tokens.append(token.item())443                response = self.processor.tokenizer.decode(out_tokens)444                num_tokens += 1445                # print(f"{num_tokens=}", end='\r')446                # sys.stdout.flush()447                yield response, num_tokens448            449            if response is not None:450                full_text = prompt + response451                num_tokens = len(self.tokenizer.encode(full_text))452                yield response, num_tokens