CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
flax_utils.py1033 linesDownload Raw Back to generation
1# coding=utf-82# Copyright 2021 The Google AI Flax Team Authors, and The HuggingFace Inc. team.3# Copyright (c) 2020, NVIDIA CORPORATION.  All rights reserved.4#5# Licensed under the Apache License, Version 2.0 (the "License");6# you may not use this file except in compliance with the License.7# You may obtain a copy of the License at8#9#     http://www.apache.org/licenses/LICENSE-2.010#11# Unless required by applicable law or agreed to in writing, software12# distributed under the License is distributed on an "AS IS" BASIS,13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.14# See the License for the specific language governing permissions and15# limitations under the License.16 17 18import copy19import inspect20import warnings21from functools import partial22from typing import Any, Optional, Union23 24import flax25import jax26import jax.numpy as jnp27import numpy as np28from jax import lax29 30from ..models.auto import (31    FLAX_MODEL_FOR_CAUSAL_LM_MAPPING,32    FLAX_MODEL_FOR_SEQ_TO_SEQ_CAUSAL_LM_MAPPING,33    FLAX_MODEL_FOR_VISION_2_SEQ_MAPPING,34)35from ..utils import ModelOutput, logging36from .configuration_utils import GenerationConfig37from .flax_logits_process import (38    FlaxForcedBOSTokenLogitsProcessor,39    FlaxForcedEOSTokenLogitsProcessor,40    FlaxForceTokensLogitsProcessor,41    FlaxLogitsProcessorList,42    FlaxMinLengthLogitsProcessor,43    FlaxNoRepeatNGramLogitsProcessor,44    FlaxSuppressTokensAtBeginLogitsProcessor,45    FlaxSuppressTokensLogitsProcessor,46    FlaxTemperatureLogitsWarper,47    FlaxTopKLogitsWarper,48    FlaxTopPLogitsWarper,49)50 51 52logger = logging.get_logger(__name__)53 54 55@flax.struct.dataclass56class FlaxGreedySearchOutput(ModelOutput):57    """58    Flax Base class for outputs of decoder-only generation models using greedy search.59 60 61    Args:62        sequences (`jnp.ndarray` of shape `(batch_size, max_length)`):63            The generated sequences.64    """65 66    sequences: Optional[jnp.ndarray] = None67 68 69@flax.struct.dataclass70class FlaxSampleOutput(ModelOutput):71    """72    Flax Base class for outputs of decoder-only generation models using sampling.73 74 75    Args:76        sequences (`jnp.ndarray` of shape `(batch_size, max_length)`):77            The generated sequences.78    """79 80    sequences: Optional[jnp.ndarray] = None81 82 83@flax.struct.dataclass84class FlaxBeamSearchOutput(ModelOutput):85    """86    Flax Base class for outputs of decoder-only generation models using greedy search.87 88 89    Args:90        sequences (`jnp.ndarray` of shape `(batch_size, max_length)`):91            The generated sequences.92        scores (`jnp.ndarray` of shape `(batch_size,)`):93            The scores (log probabilities) of the generated sequences.94    """95 96    sequences: Optional[jnp.ndarray] = None97    scores: Optional[jnp.ndarray] = None98 99 100@flax.struct.dataclass101class GreedyState:102    cur_len: jnp.ndarray103    sequences: jnp.ndarray104    running_token: jnp.ndarray105    is_sent_finished: jnp.ndarray106    model_kwargs: dict[str, jnp.ndarray]107 108 109@flax.struct.dataclass110class SampleState:111    cur_len: jnp.ndarray112    sequences: jnp.ndarray113    running_token: jnp.ndarray114    is_sent_finished: jnp.ndarray115    prng_key: jnp.ndarray116    model_kwargs: dict[str, jnp.ndarray]117 118 119@flax.struct.dataclass120class BeamSearchState:121    cur_len: jnp.ndarray122    running_sequences: jnp.ndarray123    running_scores: jnp.ndarray124    sequences: jnp.ndarray125    scores: jnp.ndarray126    is_sent_finished: jnp.ndarray127    model_kwargs: dict[str, jnp.ndarray]128 129 130class FlaxGenerationMixin:131    """132    A class containing all functions for auto-regressive text generation, to be used as a mixin in133    [`FlaxPreTrainedModel`].134 135    The class exposes [`~generation.FlaxGenerationMixin.generate`], which can be used for:136            - *greedy decoding* by calling [`~generation.FlaxGenerationMixin._greedy_search`] if `num_beams=1` and137              `do_sample=False`138            - *multinomial sampling* by calling [`~generation.FlaxGenerationMixin._sample`] if `num_beams=1` and139              `do_sample=True`140            - *beam-search decoding* by calling [`~generation.FlaxGenerationMixin._beam_search`] if `num_beams>1` and141              `do_sample=False`142 143    You do not need to call any of the above methods directly. Pass custom parameter values to 'generate' instead. To144    learn more about decoding strategies refer to the [text generation strategies guide](../generation_strategies).145    """146 147    def prepare_inputs_for_generation(self, *args, **kwargs):148        raise NotImplementedError(149            "A model class needs to define a `prepare_inputs_for_generation` method in order to use `generate`."150        )151 152    @staticmethod153    def _run_loop_in_debug(cond_fn, body_fn, init_state):154        """155        Run generation in untraced mode. This should only be used for debugging purposes.156        """157        state = init_state158        while cond_fn(state):159            state = body_fn(state)160        return state161 162    def _prepare_encoder_decoder_kwargs_for_generation(self, input_ids, params, model_kwargs):163        encoder_kwargs = {164            argument: value165            for argument, value in model_kwargs.items()166            if not (argument.startswith("decoder_") or argument.startswith("cross_attn"))167        }168        model_kwargs["encoder_outputs"] = self.encode(input_ids, params=params, return_dict=True, **encoder_kwargs)169        return model_kwargs170 171    def _prepare_decoder_input_ids_for_generation(172        self,173        batch_size: int,174        decoder_start_token_id: Optional[int] = None,175        bos_token_id: Optional[int] = None,176        model_kwargs: Optional[dict[str, jnp.ndarray]] = None,177    ) -> jnp.ndarray:178        if model_kwargs is not None and "decoder_input_ids" in model_kwargs:179            # Only use this arg if not None, otherwise just remove from model_kwargs180            decoder_input_ids = model_kwargs.pop("decoder_input_ids")181            if decoder_input_ids is not None:182                return decoder_input_ids183        decoder_start_token_id = self._get_decoder_start_token_id(decoder_start_token_id, bos_token_id)184        return jnp.array(decoder_start_token_id, dtype="i4").reshape(1, -1).repeat(batch_size, axis=0)185 186    def _get_decoder_start_token_id(187        self, decoder_start_token_id: Optional[int] = None, bos_token_id: Optional[int] = None188    ) -> int:189        # retrieve decoder_start_token_id for encoder-decoder models190        # fall back to bos_token_id if necessary191        decoder_start_token_id = (192            decoder_start_token_id193            if decoder_start_token_id is not None194            else self.generation_config.decoder_start_token_id195        )196        bos_token_id = bos_token_id if bos_token_id is not None else self.generation_config.bos_token_id197        if decoder_start_token_id is not None:198            return decoder_start_token_id199        elif (200            hasattr(self.config, "decoder")201            and hasattr(self.config.decoder, "decoder_start_token_id")202            and self.config.decoder.decoder_start_token_id is not None203        ):204            return self.config.decoder.decoder_start_token_id205        elif bos_token_id is not None:206            return bos_token_id207        elif (208            hasattr(self.config, "decoder")209            and hasattr(self.config.decoder, "bos_token_id")210            and self.config.decoder.bos_token_id is not None211        ):212            return self.config.decoder.bos_token_id213        raise ValueError(214            "`decoder_start_token_id` or `bos_token_id` has to be defined for encoder-decoder generation."215        )216 217    @staticmethod218    def _expand_to_num_beams(tensor, num_beams):219        return jnp.broadcast_to(tensor[:, None], (tensor.shape[0], num_beams) + tensor.shape[1:])220 221    def _adapt_logits_for_beam_search(self, logits):222        """223        This function can be overwritten in the specific modeling_flax_<model-name>.py classes to allow for custom beam224        search behavior. Note that the only model that overwrites this method is [`~transformers.FlaxMarianMTModel`].225        """226        return logits227 228    def _validate_model_class(self):229        """230        Confirms that the model class is compatible with generation. If not, raises an exception that points to the231        right class to use.232        """233        if not self.can_generate():234            generate_compatible_mappings = [235                FLAX_MODEL_FOR_CAUSAL_LM_MAPPING,236                FLAX_MODEL_FOR_VISION_2_SEQ_MAPPING,237                FLAX_MODEL_FOR_SEQ_TO_SEQ_CAUSAL_LM_MAPPING,238            ]239            generate_compatible_classes = set()240            for model_mapping in generate_compatible_mappings:241                supported_models = model_mapping.get(type(self.config), default=None)242                if supported_models is not None:243                    generate_compatible_classes.add(supported_models.__name__)244            exception_message = (245                f"The current model class ({self.__class__.__name__}) is not compatible with `.generate()`, as "246                "it doesn't have a language model head."247            )248            if generate_compatible_classes:249                exception_message += f" Please use one of the following classes instead: {generate_compatible_classes}"250            raise TypeError(exception_message)251 252    def _validate_model_kwargs(self, model_kwargs: dict[str, Any]):253        """Validates model kwargs for generation. Generate argument typos will also be caught here."""254        unused_model_args = []255        model_args = set(inspect.signature(self.prepare_inputs_for_generation).parameters)256        # `kwargs`/`model_kwargs` is often used to handle optional forward pass inputs like `attention_mask`. If257        # `prepare_inputs_for_generation` doesn't accept them, then a stricter check can be made ;)258        if "kwargs" in model_args or "model_kwargs" in model_args:259            model_args |= set(inspect.signature(self.__call__).parameters)260        for key, value in model_kwargs.items():261            if value is not None and key not in model_args:262                unused_model_args.append(key)263 264        if unused_model_args:265            raise ValueError(266                f"The following `model_kwargs` are not used by the model: {unused_model_args} (note: typos in the"267                " generate arguments will also show up in this list)"268            )269 270    def generate(271        self,272        input_ids: jnp.ndarray,273        generation_config: Optional[GenerationConfig] = None,274        prng_key: Optional[jnp.ndarray] = None,275        trace: bool = True,276        params: Optional[dict[str, jnp.ndarray]] = None,277        logits_processor: Optional[FlaxLogitsProcessorList] = None,278        **kwargs,279    ):280        r"""281        Generates sequences of token ids for models with a language modeling head.282 283        Parameters:284            input_ids (`jnp.ndarray` of shape `(batch_size, sequence_length)`):285                The sequence used as a prompt for the generation.286            generation_config (`~generation.GenerationConfig`, *optional*):287                The generation configuration to be used as base parametrization for the generation call. `**kwargs`288                passed to generate matching the attributes of `generation_config` will override them. If289                `generation_config` is not provided, the default will be used, which had the following loading290                priority: 1) from the `generation_config.json` model file, if it exists; 2) from the model291                configuration. Please note that unspecified parameters will inherit [`~generation.GenerationConfig`]'s292                default values, whose documentation should be checked to parameterize generation.293            trace (`bool`, *optional*, defaults to `True`):294                Whether to trace generation. Setting `trace=False` should only be used for debugging and will lead to a295                considerably slower runtime.296            params (`dict[str, jnp.ndarray]`, *optional*):297                Optionally the model parameters can be passed. Can be useful for parallelized generation.298            logits_processor (`FlaxLogitsProcessorList `, *optional*):299                Custom logits processors that complement the default logits processors built from arguments and300                generation config. If a logit processor is passed that is already created with the arguments or a301                generation config an error is thrown. This feature is intended for advanced users.302            kwargs (`dict[str, Any]`, *optional*):303                Ad hoc parametrization of `generate_config` and/or additional model-specific kwargs that will be304                forwarded to the `forward` function of the model. If the model is an encoder-decoder model, encoder305                specific kwargs should not be prefixed and decoder specific kwargs should be prefixed with *decoder_*.306 307        Return:308            [`~utils.ModelOutput`].309 310        """311        # Handle `generation_config` and kwargs that might update it, and validate the `.generate()` call312        self._validate_model_class()313 314        # priority: `generation_config` argument > `model.generation_config` (the default generation config)315        if generation_config is None:316            # legacy: users may modify the model configuration to control generation. To trigger this legacy behavior,317            # two conditions must be met318            # 1) the generation config must have been created from the model config (`_from_model_config` field);319            # 2) the generation config must have seen no modification since its creation (the hash is the same).320            if self.generation_config._from_model_config and self.generation_config._original_object_hash == hash(321                self.generation_config322            ):323                new_generation_config = GenerationConfig.from_model_config(self.config)324                if new_generation_config != self.generation_config:325                    warnings.warn(326                        "You have modified the pretrained model configuration to control generation. This is a"327                        " deprecated strategy to control generation and will be removed soon, in a future version."328                        " Please use and modify the model generation configuration (see"329                        " https://huggingface.co/docs/transformers/generation_strategies#default-text-generation-configuration )"330                    )331                    self.generation_config = new_generation_config332            generation_config = self.generation_config333 334        generation_config = copy.deepcopy(generation_config)335        model_kwargs = generation_config.update(**kwargs)  # All unused kwargs must be model kwargs336        self._validate_model_kwargs(model_kwargs.copy())337 338        logits_processor = logits_processor if logits_processor is not None else FlaxLogitsProcessorList()339 340        # set init values341        prng_key = prng_key if prng_key is not None else jax.random.PRNGKey(0)342 343        if generation_config.pad_token_id is None and generation_config.eos_token_id is not None:344            if model_kwargs.get("attention_mask") is None:345                logger.warning(346                    "The attention mask and the pad token id were not set. As a consequence, you may observe "347                    "unexpected behavior. Please pass your input's `attention_mask` to obtain reliable results."348                )349            eos_token_id = generation_config.eos_token_id350            if isinstance(eos_token_id, list):351                eos_token_id = eos_token_id[0]352            generation_config.pad_token_id = eos_token_id353 354        if generation_config.decoder_start_token_id is None and self.config.is_encoder_decoder:355            raise ValueError("`decoder_start_token_id` has to be defined for encoder-decoder generation.")356 357        # decoder-only models should use left-padding for generation (can't be checked with `trace=True`)358        if not self.config.is_encoder_decoder and not trace:359            if (360                generation_config.pad_token_id is not None361                and jnp.sum(input_ids[:, -1] == generation_config.pad_token_id) > 0362            ):363                logger.warning(364                    "A decoder-only architecture is being used, but right-padding was detected! For correct "365                    "generation results, please set `padding_side='left'` when initializing the tokenizer."366                )367 368        batch_size = input_ids.shape[0]369 370        if self.config.is_encoder_decoder:371            # add encoder_outputs to model_kwargs372            if model_kwargs.get("encoder_outputs") is None:373                model_kwargs = self._prepare_encoder_decoder_kwargs_for_generation(input_ids, params, model_kwargs)374            # prepare decoder_input_ids for generation375            input_ids = self._prepare_decoder_input_ids_for_generation(376                batch_size,377                decoder_start_token_id=generation_config.decoder_start_token_id,378                bos_token_id=generation_config.bos_token_id,379                model_kwargs=model_kwargs,380            )381 382        # Prepare `max_length` depending on other stopping criteria.383        input_ids_seq_length = input_ids.shape[-1]384        has_default_max_length = kwargs.get("max_length") is None and generation_config.max_length is not None385        if has_default_max_length and generation_config.max_new_tokens is None and generation_config.max_length == 20:386            # 20 is the default max_length of the generation config387            warnings.warn(388                f"Using the model-agnostic default `max_length` (={generation_config.max_length}) "389                "to control the generation length.  recommend setting `max_new_tokens` to control the maximum length of the generation.",390                UserWarning,391            )392        elif generation_config.max_new_tokens is not None:393            if not has_default_max_length and generation_config.max_length is not None:394                logger.warning(395                    f"Both `max_new_tokens` (={generation_config.max_new_tokens}) and `max_length`(="396                    f"{generation_config.max_length}) seem to have been set. `max_new_tokens` will take precedence. "397                    "Please refer to the documentation for more information. "398                    "(https://huggingface.co/docs/transformers/main/en/main_classes/text_generation)"399                )400            generation_config.max_length = generation_config.max_new_tokens + input_ids_seq_length401        else:  # by default let's always generate 20 new tokens402            if generation_config.max_length == GenerationConfig().max_length:403                generation_config.max_length = generation_config.max_length + input_ids_seq_length404                max_position_embeddings = getattr(self.config, "max_position_embeddings", None)405                if max_position_embeddings is not None:406                    generation_config.max_length = min(generation_config.max_length, max_position_embeddings)407 408        if generation_config.min_length is not None and generation_config.min_length > generation_config.max_length:409            raise ValueError(410                f"Unfeasable length constraints: the minimum length ({generation_config.min_length}) is larger than"411                f" the maximum length ({generation_config.max_length})"412            )413        if input_ids_seq_length >= generation_config.max_length:414            input_ids_string = "decoder_input_ids" if self.config.is_encoder_decoder else "input_ids"415            logger.warning(416                f"Input length of {input_ids_string} is {input_ids_seq_length}, but `max_length` is set to"417                f" {generation_config.max_length}. This can lead to unexpected behavior. You should consider"418                " increasing`max_new_tokens`."419            )420 421        logits_processor = self._get_logits_processor(422            generation_config=generation_config,423            input_ids_seq_length=input_ids_seq_length,424            logits_processor=logits_processor,425        )426 427        if not generation_config.do_sample and generation_config.num_beams == 1:428            return self._greedy_search(429                input_ids,430                generation_config.max_length,431                generation_config.pad_token_id,432                generation_config.eos_token_id,433                logits_processor=logits_processor,434                trace=trace,435                params=params,436                model_kwargs=model_kwargs,437            )438        elif generation_config.do_sample and generation_config.num_beams == 1:439            logits_warper = self._get_logits_warper(generation_config=generation_config)440            return self._sample(441                input_ids,442                generation_config.max_length,443                generation_config.pad_token_id,444                generation_config.eos_token_id,445                prng_key,446                logits_warper=logits_warper,447                logits_processor=logits_processor,448                trace=trace,449                params=params,450                model_kwargs=model_kwargs,451            )452        elif not generation_config.do_sample and generation_config.num_beams > 1:453            # broadcast input_ids & encoder_outputs454            input_ids = self._expand_to_num_beams(input_ids, num_beams=generation_config.num_beams)455 456            if "encoder_outputs" in model_kwargs:457                model_kwargs["encoder_outputs"]["last_hidden_state"] = self._expand_to_num_beams(458                    model_kwargs["encoder_outputs"]["last_hidden_state"], num_beams=generation_config.num_beams459                )460 461            for kwarg in ["attention_mask", "decoder_attention_mask"]:462                if kwarg in model_kwargs:463                    model_kwargs[kwarg] = self._expand_to_num_beams(464                        model_kwargs[kwarg], num_beams=generation_config.num_beams465                    )466 467            return self._beam_search(468                input_ids,469                generation_config.max_length,470                generation_config.pad_token_id,471                generation_config.eos_token_id,472                length_penalty=generation_config.length_penalty,473                early_stopping=generation_config.early_stopping,474                logits_processor=logits_processor,475                trace=trace,476                params=params,477                num_return_sequences=generation_config.num_return_sequences,478                model_kwargs=model_kwargs,479            )480        else:481            raise NotImplementedError("`Beam sampling is currently not implemented.")482 483    def _get_logits_warper(self, generation_config: GenerationConfig) -> FlaxLogitsProcessorList:484        """485        This class returns a [`FlaxLogitsProcessorList`] list object that contains all relevant [`FlaxLogitsWarper`]486        instances used for multinomial sampling.487        """488        warpers = FlaxLogitsProcessorList()489 490        if generation_config.temperature is not None and generation_config.temperature != 1.0:491            warpers.append(FlaxTemperatureLogitsWarper(generation_config.temperature))492        if generation_config.top_k is not None and generation_config.top_k != 0:493            warpers.append(FlaxTopKLogitsWarper(top_k=generation_config.top_k, min_tokens_to_keep=1))494        if generation_config.top_p is not None and generation_config.top_p < 1.0:495            warpers.append(FlaxTopPLogitsWarper(top_p=generation_config.top_p, min_tokens_to_keep=1))496 497        return warpers498 499    def _get_logits_processor(500        self,501        generation_config: GenerationConfig,502        input_ids_seq_length: int,503        logits_processor: Optional[FlaxLogitsProcessorList],504    ) -> FlaxLogitsProcessorList:505        """506        This class returns a [`FlaxLogitsProcessorList`] list object that contains all relevant [`FlaxLogitsProcessor`]507        instances used to modify the scores of the language model head.508        """509        processors = FlaxLogitsProcessorList()510 511        if (512            generation_config.min_length is not None513            and generation_config.eos_token_id is not None514            and generation_config.min_length > -1515        ):516            processors.append(517                FlaxMinLengthLogitsProcessor(generation_config.min_length, generation_config.eos_token_id)518            )519        if generation_config.forced_bos_token_id is not None:520            processors.append(FlaxForcedBOSTokenLogitsProcessor(generation_config.forced_bos_token_id))521        if generation_config.forced_eos_token_id is not None:522            processors.append(523                FlaxForcedEOSTokenLogitsProcessor(generation_config.max_length, generation_config.forced_eos_token_id)524            )525        if generation_config.suppress_tokens is not None:526            processors.append(FlaxSuppressTokensLogitsProcessor(generation_config.suppress_tokens))527        if generation_config.begin_suppress_tokens is not None:528            begin_index = input_ids_seq_length529            begin_index = (530                begin_index531                if (input_ids_seq_length > 1 or generation_config.forced_bos_token_id is None)532                else begin_index + 1533            )534            if (535                getattr(generation_config, "forced_decoder_ids", None) is not None536                and len(generation_config.forced_decoder_ids) > 0537            ):538                # generation starts after the last token that is forced539                begin_index += generation_config.forced_decoder_ids[-1][0]540            processors.append(541                FlaxSuppressTokensAtBeginLogitsProcessor(generation_config.begin_suppress_tokens, begin_index)542            )543        if getattr(generation_config, "forced_decoder_ids", None) is not None:544            forced_decoder_ids = [545                [input_ids_seq_length + i[0] - 1, i[1]] for i in generation_config.forced_decoder_ids546            ]547            processors.append(FlaxForceTokensLogitsProcessor(forced_decoder_ids))548        if generation_config.no_repeat_ngram_size is not None and generation_config.no_repeat_ngram_size > 0:549            processors.append(FlaxNoRepeatNGramLogitsProcessor(generation_config.no_repeat_ngram_size))550        processors = self._merge_criteria_processor_list(processors, logits_processor)551 552        return processors553 554    def _merge_criteria_processor_list(555        self,556        default_list: FlaxLogitsProcessorList,557        custom_list: FlaxLogitsProcessorList,558    ) -> FlaxLogitsProcessorList:559        if len(custom_list) == 0:560            return default_list561        for default in default_list:562            for custom in custom_list:563                if type(custom) is type(default):564                    object_type = "logits processor"565                    raise ValueError(566                        f"A custom {object_type} of type {type(custom)} with values {custom} has been passed to"567                        f" `generate`, but it has already been created with the values {default}. {default} has been"568                        " created by passing the corresponding arguments to generate or by the model's config default"569                        f" values. If you just want to change the default values of {object_type} consider passing"570                        f" them as arguments to `generate` instead of using a custom {object_type}."571                    )572        default_list.extend(custom_list)573        return default_list574 575    def _greedy_search(576        self,577        input_ids: None,578        max_length: Optional[int] = None,579        pad_token_id: Optional[int] = None,580        eos_token_id: Optional[int] = None,581        logits_processor: Optional[FlaxLogitsProcessorList] = None,582        trace: bool = True,583        params: Optional[dict[str, jnp.ndarray]] = None,584        model_kwargs: Optional[dict[str, jnp.ndarray]] = None,585    ):586        # init values587        max_length = max_length if max_length is not None else self.generation_config.max_length588        pad_token_id = pad_token_id if pad_token_id is not None else self.generation_config.pad_token_id589        eos_token_id = eos_token_id if eos_token_id is not None else self.generation_config.eos_token_id590 591        batch_size, cur_len = input_ids.shape592 593        eos_token_id = jnp.array(eos_token_id, dtype=jnp.int32 if eos_token_id is not None else None)594        pad_token_id = jnp.array(pad_token_id, dtype=jnp.int32)595        cur_len = jnp.array(cur_len)596 597        # per batch-item holding current token in loop.598        sequences = jnp.full((batch_size, max_length), pad_token_id, dtype=jnp.int32)599        sequences = lax.dynamic_update_slice(sequences, input_ids, (0, 0))600 601        # per batch-item state bit indicating if sentence has finished.602        is_sent_finished = jnp.zeros((batch_size,), dtype=jnp.bool_)603 604        # For Seq2Seq generation, we only need to use the decoder instead of the whole model in generation loop605        # and pass it the `encoder_outputs`, which are part of the `model_kwargs`.606        model = self.decode if self.config.is_encoder_decoder else self607        # initialize model specific kwargs608        model_kwargs = self.prepare_inputs_for_generation(input_ids, max_length, **model_kwargs)609 610        # initialize state611        state = GreedyState(612            cur_len=cur_len,613            sequences=sequences,614            running_token=input_ids,615            is_sent_finished=is_sent_finished,616            model_kwargs=model_kwargs,617        )618 619        def greedy_search_cond_fn(state):620            """state termination condition fn."""621            has_reached_max_length = state.cur_len == max_length622            all_sequence_finished = jnp.all(state.is_sent_finished)623            finish_generation = jnp.logical_or(has_reached_max_length, all_sequence_finished)624            return ~finish_generation625 626        def greedy_search_body_fn(state):627            """state update fn."""628            model_outputs = model(state.running_token, params=params, **state.model_kwargs)629            logits = model_outputs.logits[:, -1]630 631            # apply min_length, ...632            logits = logits_processor(state.sequences, logits, state.cur_len)633 634            next_token = jnp.argmax(logits, axis=-1)635 636            next_token = next_token * ~state.is_sent_finished + pad_token_id * state.is_sent_finished637            next_is_sent_finished = state.is_sent_finished | (next_token == eos_token_id)638            next_token = next_token[:, None]639 640            next_sequences = lax.dynamic_update_slice(state.sequences, next_token, (0, state.cur_len))641            next_model_kwargs = self.update_inputs_for_generation(model_outputs, state.model_kwargs)642            return GreedyState(643                cur_len=state.cur_len + 1,644                sequences=next_sequences,645                running_token=next_token,646                is_sent_finished=next_is_sent_finished,647                model_kwargs=next_model_kwargs,648            )649 650        # The very first prompt often has sequence length > 1, so run outside of `lax.while_loop` to comply with TPU651        if input_ids.shape[1] > 1:652            state = greedy_search_body_fn(state)653 654        if not trace:655            state = self._run_loop_in_debug(greedy_search_cond_fn, greedy_search_body_fn, state)656        else:657            state = lax.while_loop(greedy_search_cond_fn, greedy_search_body_fn, state)658 659        return FlaxGreedySearchOutput(sequences=state.sequences)660 661    def _sample(662        self,663        input_ids: None,664        max_length: Optional[int] = None,665        pad_token_id: Optional[int] = None,666        eos_token_id: Optional[int] = None,667        prng_key: Optional[jnp.ndarray] = None,668        logits_processor: Optional[FlaxLogitsProcessorList] = None,669        logits_warper: Optional[FlaxLogitsProcessorList] = None,670        trace: bool = True,671        params: Optional[dict[str, jnp.ndarray]] = None,672        model_kwargs: Optional[dict[str, jnp.ndarray]] = None,673    ):674        # init values675        max_length = max_length if max_length is not None else self.generation_config.max_length676        pad_token_id = pad_token_id if pad_token_id is not None else self.generation_config.pad_token_id677        eos_token_id = eos_token_id if eos_token_id is not None else self.generation_config.eos_token_id678        prng_key = prng_key if prng_key is not None else jax.random.PRNGKey(0)679 680        batch_size, cur_len = input_ids.shape681 682        eos_token_id = jnp.array(eos_token_id, dtype=jnp.int32 if eos_token_id is not None else None)683        pad_token_id = jnp.array(pad_token_id, dtype=jnp.int32)684        cur_len = jnp.array(cur_len)685 686        # per batch-item holding current token in loop.687        sequences = jnp.full((batch_size, max_length), pad_token_id, dtype=jnp.int32)688        sequences = lax.dynamic_update_slice(sequences, input_ids, (0, 0))689 690        # per batch-item state bit indicating if sentence has finished.691        is_sent_finished = jnp.zeros((batch_size,), dtype=jnp.bool_)692 693        # For Seq2Seq generation, we only need to use the decoder instead of the whole model in generation loop694        # and pass it the `encoder_outputs`, which are part of the `model_kwargs`.695        model = self.decode if self.config.is_encoder_decoder else self696 697        # initialize model specific kwargs698        model_kwargs = self.prepare_inputs_for_generation(input_ids, max_length, **model_kwargs)699 700        # initialize state701        state = SampleState(702            cur_len=cur_len,703            sequences=sequences,704            running_token=input_ids,705            is_sent_finished=is_sent_finished,706            prng_key=prng_key,707            model_kwargs=model_kwargs,708        )709 710        def sample_search_cond_fn(state):711            """state termination condition fn."""712            has_reached_max_length = state.cur_len == max_length713            all_sequence_finished = jnp.all(state.is_sent_finished)714            finish_generation = jnp.logical_or(has_reached_max_length, all_sequence_finished)715            return ~finish_generation716 717        def sample_search_body_fn(state):718            """state update fn."""719            prng_key, prng_key_next = jax.random.split(state.prng_key)720            model_outputs = model(state.running_token, params=params, **state.model_kwargs)721 722            logits = model_outputs.logits[:, -1]723 724            # apply min_length, ...725            logits = logits_processor(state.sequences, logits, state.cur_len)726            # apply top_p, top_k, temperature727            logits = logits_warper(logits, logits, state.cur_len)728 729            next_token = jax.random.categorical(prng_key, logits, axis=-1)730 731            next_token = next_token * ~state.is_sent_finished + pad_token_id * state.is_sent_finished732            next_is_sent_finished = state.is_sent_finished | (next_token == eos_token_id)733            next_token = next_token[:, None]734 735            next_sequences = lax.dynamic_update_slice(state.sequences, next_token, (0, state.cur_len))736            next_model_kwargs = self.update_inputs_for_generation(model_outputs, state.model_kwargs)737 738            return SampleState(739                cur_len=state.cur_len + 1,740                sequences=next_sequences,741                running_token=next_token,742                is_sent_finished=next_is_sent_finished,743                model_kwargs=next_model_kwargs,744                prng_key=prng_key_next,745            )746 747        # The very first prompt often has sequence length > 1, so run outside of `lax.while_loop` to comply with TPU748        if input_ids.shape[1] > 1:749            state = sample_search_body_fn(state)750 751        if not trace:752            state = self._run_loop_in_debug(sample_search_cond_fn, sample_search_body_fn, state)753        else:754            state = lax.while_loop(sample_search_cond_fn, sample_search_body_fn, state)755 756        return FlaxSampleOutput(sequences=state.sequences)757 758    def _beam_search(759        self,760        input_ids: None,761        max_length: Optional[int] = None,762        pad_token_id: Optional[int] = None,763        eos_token_id: Optional[int] = None,764        length_penalty: Optional[float] = None,765        early_stopping: Optional[Union[bool, str]] = None,766        logits_processor: Optional[FlaxLogitsProcessorList] = None,767        trace: bool = True,768        params: Optional[dict[str, jnp.ndarray]] = None,769        num_return_sequences: Optional[int] = None,770        model_kwargs: Optional[dict[str, jnp.ndarray]] = None,771    ):772        """773        This beam search function is heavily inspired by Flax's official example:774        https://github.com/google/flax/blob/main/examples/wmt/decode.py775        """776 777        def flatten_beam_dim(tensor):778            """Flattens the first two dimensions of a non-scalar array."""779            # ignore scalars (e.g. cache index)780            if tensor.ndim == 0:781                return tensor782            return tensor.reshape((tensor.shape[0] * tensor.shape[1],) + tensor.shape[2:])783 784        def unflatten_beam_dim(tensor, batch_size, num_beams):785            """Unflattens the first, flat batch*beam dimension of a non-scalar array."""786            # ignore scalars (e.g. cache index)787            if tensor.ndim == 0:788                return tensor789            return tensor.reshape((batch_size, num_beams) + tensor.shape[1:])790 791        def gather_beams(nested, beam_indices, batch_size, new_num_beams):792            """793            Gathers the beam slices indexed by beam_indices into new beam array.794            """795            batch_indices = jnp.reshape(796                jnp.arange(batch_size * new_num_beams) // new_num_beams, (batch_size, new_num_beams)797            )798 799            def gather_fn(tensor):800                # ignore scalars (e.g. cache index)801                if tensor.ndim == 0:802                    return tensor803                else:804                    return tensor[batch_indices, beam_indices]805 806            return jax.tree_util.tree_map(gather_fn, nested)807 808        # init values809        max_length = max_length if max_length is not None else self.generation_config.max_length810        pad_token_id = pad_token_id if pad_token_id is not None else self.generation_config.pad_token_id811        eos_token_id = eos_token_id if eos_token_id is not None else self.generation_config.eos_token_id812        length_penalty = length_penalty if length_penalty is not None else self.generation_config.length_penalty813        early_stopping = early_stopping if early_stopping is not None else self.generation_config.early_stopping814        num_return_sequences = (815            num_return_sequences if num_return_sequences is not None else self.generation_config.num_return_sequences816        )817 818        batch_size, num_beams, cur_len = input_ids.shape819 820        eos_token_id = jnp.array(eos_token_id, dtype=jnp.int32 if eos_token_id is not None else None)821        pad_token_id = jnp.array(pad_token_id, dtype=jnp.int32)822        cur_len = jnp.array(cur_len)823 824        # record the prompt length of decoder825        decoder_prompt_len = input_ids.shape[-1]826 827        # per batch,beam-item holding current token in loop.828        sequences = jnp.full((batch_size, num_beams, max_length), pad_token_id, dtype=jnp.int32)829        running_sequences = jnp.full((batch_size, num_beams, max_length), pad_token_id, dtype=jnp.int32)830        running_sequences = lax.dynamic_update_slice(sequences, input_ids, (0, 0, 0))831 832        # per batch,beam-item state bit indicating if sentence has finished.833        is_sent_finished = jnp.zeros((batch_size, num_beams), dtype=jnp.bool_)834 835        # per batch,beam-item score, logprobs836        running_scores = jnp.tile(jnp.array([0.0] + [np.array(-1.0e7)] * (num_beams - 1)), [batch_size, 1])837        scores = jnp.ones((batch_size, num_beams)) * np.array(-1.0e7)838 839        # For Seq2Seq generation, we only need to use the decoder instead of the whole model in generation loop840        # and pass it the `encoder_outputs`, which are part of the `model_kwargs`.841        model = self.decode if self.config.is_encoder_decoder else self842 843        # flatten beam dim844        if "encoder_outputs" in model_kwargs:845            model_kwargs["encoder_outputs"]["last_hidden_state"] = flatten_beam_dim(846                model_kwargs["encoder_outputs"]["last_hidden_state"]847            )848        for kwarg in ["attention_mask", "decoder_attention_mask"]:849            if kwarg in model_kwargs:850                model_kwargs[kwarg] = flatten_beam_dim(model_kwargs[kwarg])851 852        # initialize model specific kwargs853        model_kwargs = self.prepare_inputs_for_generation(flatten_beam_dim(input_ids), max_length, **model_kwargs)854 855        # initialize state856        state = BeamSearchState(857            cur_len=cur_len,858            running_sequences=running_sequences,859            running_scores=running_scores,860            sequences=sequences,861            scores=scores,862            is_sent_finished=is_sent_finished,863            model_kwargs=model_kwargs,864        )865 866        def beam_search_cond_fn(state):867            """beam search state termination condition fn."""868 869            # 1. is less than max length?870            not_max_length_yet = state.cur_len < max_length871 872            # 2. can the new beams still improve?873            # early_stopping == False -> apply heuristic = always get the best score from `cur_len`. See the discussion874            # below for more details.875            # https://github.com/huggingface/transformers/pull/20901#issuecomment-1369845565876            # early_stopping == "never" -> compute the best score from max_length or cur_len, depending on the sign of877            #   length_penalty. Positive length_penalty favors longer sequences, thus we use max_length there.878            if early_stopping == "never" and length_penalty > 0.0:879                best_running_score = state.running_scores[:, :1] / (880                    (max_length - decoder_prompt_len) ** length_penalty881                )882            else:883                best_running_score = state.running_scores[:, :1] / (884                    (state.cur_len - decoder_prompt_len) ** length_penalty885                )886            worst_finished_score = jnp.where(887                state.is_sent_finished, jnp.min(state.scores, axis=1, keepdims=True), np.array(-1.0e7)888            )889            improvement_still_possible = jnp.any(best_running_score > worst_finished_score)890 891            # 3. is there still a beam that has not finished?892            still_open_beam = ~(jnp.all(state.is_sent_finished) & (early_stopping is True))893 894            return not_max_length_yet & still_open_beam & improvement_still_possible895 896        def beam_search_body_fn(state, input_ids_length=1):897            """beam search state update fn."""898            # 1. Forward current tokens899            # Collect the current position slice along length to feed the fast900            # autoregressive decoder model.  Flatten the beam dimension into batch901            # dimension for feeding into the model.902            # unflatten beam dimension903            # Unflatten beam dimension in attention cache arrays904            input_token = flatten_beam_dim(905                lax.dynamic_slice(906                    state.running_sequences,907                    (0, 0, state.cur_len - input_ids_length),908                    (batch_size, num_beams, input_ids_length),909                )910            )911            model_outputs = model(input_token, params=params, **state.model_kwargs)912 913            logits = unflatten_beam_dim(model_outputs.logits[:, -1], batch_size, num_beams)914            cache = jax.tree_util.tree_map(915                lambda tensor: unflatten_beam_dim(tensor, batch_size, num_beams), model_outputs.past_key_values916            )917 918            # adapt logits for FlaxMarianMTModel919            logits = self._adapt_logits_for_beam_search(logits)920 921            # 2. Compute log probs922            # get log probabilities from logits,923            # process logits with processors (*e.g.* min_length, ...), and924            # add new logprobs to existing running logprobs scores.925            log_probs = jax.nn.log_softmax(logits)926            log_probs = logits_processor(927                flatten_beam_dim(state.running_sequences), flatten_beam_dim(log_probs), state.cur_len928            )929            log_probs = unflatten_beam_dim(log_probs, batch_size, num_beams)930            log_probs = log_probs + jnp.expand_dims(state.running_scores, axis=2)931            vocab_size = log_probs.shape[2]932            log_probs = log_probs.reshape((batch_size, num_beams * vocab_size))933 934            # 3. Retrieve top-K935            # Each item in batch has num_beams * vocab_size candidate sequences.936            # For each item, get the top 2*k candidates with the highest log-937            # probabilities. We gather the top 2*K beams here so that even if the best938            # K sequences reach EOS simultaneously, we have another K sequences939            # remaining to continue the live beam search.940            # Gather the top 2*K scores from _all_ beams.941            # Gather 2*k top beams.942            # Recover the beam index by floor division.943            # Recover token id by modulo division and expand Id array for broadcasting.944            # Update sequences for the 2*K top-k new sequences.945            beams_to_keep = 2 * num_beams946            topk_log_probs, topk_indices = lax.top_k(log_probs, k=beams_to_keep)947            topk_beam_indices = topk_indices // vocab_size948            topk_running_sequences = gather_beams(949                state.running_sequences, topk_beam_indices, batch_size, beams_to_keep950            )951            topk_ids = jnp.expand_dims(topk_indices % vocab_size, axis=2)952            topk_sequences = lax.dynamic_update_slice(topk_running_sequences, topk_ids, (0, 0, state.cur_len))953 954            # 4. Check which sequences have ended955            # Update current sequences:956            # Did any of these sequences reach an end marker?957            # To prevent these just finished sequences from being added to the current sequences958            # set of active beam search sequences, set their log probs to a very large959            # negative value.960            did_topk_just_finished = topk_sequences[:, :, state.cur_len] == eos_token_id961            running_topk_log_probs = topk_log_probs + did_topk_just_finished * np.array(-1.0e7)962            # 5. Get running sequences scores for next963            # Determine the top k beam indices (from top 2*k beams) from log probs964            # and gather top k beams (from top 2*k beams).965            next_topk_indices = lax.top_k(running_topk_log_probs, k=num_beams)[1]966            next_running_sequences, next_running_scores = gather_beams(967                [topk_sequences, running_topk_log_probs], next_topk_indices, batch_size, num_beams968            )969 970            # 6. Process topk logits971            # Further process log probs:972            # - add length penalty973            # - make sure no scores can be added anymore if beam is full974            # - make sure still running sequences cannot be chosen as finalized beam975            topk_log_probs = topk_log_probs / ((state.cur_len + 1 - decoder_prompt_len) ** length_penalty)976            beams_in_batch_are_full = jnp.broadcast_to(977                state.is_sent_finished.all(axis=-1, keepdims=True), did_topk_just_finished.shape978            ) & (early_stopping is True)979            add_penalty = ~did_topk_just_finished | beams_in_batch_are_full980            topk_log_probs += add_penalty * np.array(-1.0e7)981 982            # 7. Get scores, sequences, is sentence finished for next.983            # Combine sequences, scores, and flags along the beam dimension and compare984            # new finished sequence scores to existing finished scores and select the985            # best from the new set of beams986            merged_sequences = jnp.concatenate([state.sequences, topk_sequences], axis=1)987            merged_scores = jnp.concatenate([state.scores, topk_log_probs], axis=1)988            merged_is_sent_finished = jnp.concatenate([state.is_sent_finished, did_topk_just_finished], axis=1)989            topk_merged_indices = lax.top_k(merged_scores, k=num_beams)[1]990            next_sequences, next_scores, next_is_sent_finished = gather_beams(991                [merged_sequences, merged_scores, merged_is_sent_finished], topk_merged_indices, batch_size, num_beams992            )993 994            # 8. Update model kwargs.995            # Determine the top k beam indices from the original set of all beams.996            # With these, gather the top k beam-associated caches.997            next_running_indices = gather_beams(topk_beam_indices, next_topk_indices, batch_size, num_beams)998            next_cache = gather_beams(cache, next_running_indices, batch_size, num_beams)999            model_outputs["past_key_values"] = jax.tree_util.tree_map(lambda x: flatten_beam_dim(x), next_cache)1000            next_model_kwargs = self.update_inputs_for_generation(model_outputs, state.model_kwargs)1001 1002            return BeamSearchState(1003                cur_len=state.cur_len + 1,1004                running_scores=next_running_scores,1005                running_sequences=next_running_sequences,1006                scores=next_scores,1007                sequences=next_sequences,1008                is_sent_finished=next_is_sent_finished,1009                model_kwargs=next_model_kwargs,1010            )1011 1012        # Always run first iteration outside of `lax.while_loop` to avoid calling `beam_search_cond_fn`1013        # when `state.cur_len` equals `decoder_prompt_len`. This also helps to comply with TPU when1014        # the very first prompt has sequence length > 1.1015        state = partial(beam_search_body_fn, input_ids_length=input_ids.shape[-1])(state)1016 1017        if not trace:1018            state = self._run_loop_in_debug(beam_search_cond_fn, beam_search_body_fn, state)1019        else:1020            state = lax.while_loop(beam_search_cond_fn, beam_search_body_fn, state)1021 1022        # Account for the edge-case where there are no finished sequences for a1023        # particular batch item. If so, return running sequences for that batch item.1024        none_finished = jnp.any(state.is_sent_finished, axis=1)1025        sequences = jnp.where(none_finished[:, None, None], state.sequences, state.running_sequences)1026        scores = jnp.where(none_finished[:, None], state.scores, state.running_scores)1027 1028        # Take best beams for each batch (the score is sorted in descending order)1029        sequences = flatten_beam_dim(sequences[:, :num_return_sequences, :])1030        scores = flatten_beam_dim(scores[:, :num_return_sequences])1031 1032        return FlaxBeamSearchOutput(sequences=sequences, scores=scores)1033 
Aluode/PerceptionLabPortable · CoolFace