您的位置: 首页 - 站长

wordpress本地怎么迁移到服务器廊坊短视频优化公司

当前位置: 首页 > news >正文

wordpress本地怎么迁移到服务器,廊坊短视频优化公司,网站如何使用cdn,天推广人的网站目录 简介输入程序model.generate()输入参数1. 创建生成参数对象 generation_config2. 初始化部分输入参数3. 定义模型输入4. 定义其他模型参数5. 准备 input_ids6. 准备 max_length7. 确定生成模式8. 准备 logits 处理器9. 准备 stopping 处理器10. 执行生成 self._sample1. 先… 目录 简介输入程序model.generate()输入参数1. 创建生成参数对象 generation_config2. 初始化部分输入参数3. 定义模型输入4. 定义其他模型参数5. 准备 input_ids6. 准备 max_length7. 确定生成模式8. 准备 logits 处理器9. 准备 stopping 处理器10. 执行生成 self._sample1. 先拿出一些变量2. 根据要求初始化一些变量3. 再设置一些变量4. 正式进行生成 备注 简介 本文将使用几个输入案例详细解读transformers库中 使用环境参考transformers4.41.1 torch2.0.1cu117 输入程序 本文使用Qwen2.5-1.5B-Instruct模型进行实验下面程序为输入的一个样例.generate()中的参数为实验中debug使用的作为参考这里就不删掉了。 from transformers import AutoModelForCausalLM, AutoTokenizer device cuda # the device to load the model ontomodel AutoModelForCausalLM.from_pretrained(Qwen2.5-1.5B-Instruct,torch_dtypetorch.bfloat16,device_mapcuda ) tokenizer AutoTokenizer.from_pretrained(Qwen2.5-1.5B-Instruct) tokenizer.padding_side left text1 [{role: system, content: 你是一个人工智能助手},{role: user, content: 介绍一下你自己} ] text1 tokenizer.apply_chat_template(text1,tokenizeFalse,add_generation_promptTrue ) text2 [{role: system, content: 你是一个人工智能助手},{role: user, content: 写一个谜语} ] text2 tokenizer.apply_chat_template(text2,tokenizeFalse,add_generation_promptTrue ) model_inputs tokenizer([text1, text2], truncationTrue, paddingTrue, return_tensorspt).to(device)generated_ids model.generate(# model_inputs.input_ids,# temperature0.1,# top_k10,# top_p0.7,# penalty_alpha1.3,max_new_tokens64,# return_dict_in_generateTrue,# output_scoresTrue,# output_logitsTrue,no_repeat_ngram_size2,# num_beam_groups3,# num_beams6,do_sampleTrue,# diversity_penalty0.5,model_inputs, ) generated_ids1 [output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids) ]response tokenizer.batch_decode(generated_ids1, skip_special_tokensTrue) print(response) model.generate() 输入参数 进入.generate()中最先涉及的就是输入参数的问题以下是函数的定义。 def generate(self,inputs: Optional[torch.Tensor] None,generation_config: Optional[GenerationConfig] None,logits_processor: Optional[LogitsProcessorList] None,stopping_criteria: Optional[StoppingCriteriaList] None,prefix_allowed_tokens_fn: Optional[Callable[[int, torch.Tensor], List[int]]] None,synced_gpus: Optional[bool] None,assistant_model: Optional[PreTrainedModel] None,streamer: Optional[BaseStreamer] None,negative_prompt_ids: Optional[torch.Tensor] None,negative_prompt_attention_mask: Optional[torch.Tensor] None,kwargs,) - Union[GenerateOutput, torch.LongTensor]:在代码中可以看到在函数入口显式的定义了很多参数。他们的具体含义如下 inputstensor 形式的 token_id通常先准备文本形式的提示词和输入使用tokenizer转化为对应 id这里维度通常为 [batch_size, seq_len]generation_config一个用 GenerationConfig 类创建的对象存储着模型生成的超参数可以提前创建该对象并传入 .generate()logits_processor高级功能logits_processor 可以在每个 step 的输出概率计算完成后对分数进行进一步的干预改变输出的概率分布从而影响生成的结果例如最常见的重复惩罚就是使用 logits_processor 完成的。不懂的话可以看后面如何具体实现的stopping_criteria高级功能允许用户通过 stopping_criteria 自定义生成停止条件不懂的话可以看后面如何具体实现的prefix_allowed_tokens_fnsynced_gpus DeepSpeed ZeRO Stage-3 多GPU时使用ZeRO-3包括优化器状态梯度权重并行优化而推理阶段只使用权重并行此时需要将 synced_gpus 设置成 Ture。.否则如果一个 GPU 在另一个 GPU 之前完成生成整个系统就会挂起因为其余 GPU 尚未从最先完成的 GPU 接收到权重分片。transformers4.28 在生成时检测到多个 GPU 会自动设置 synced_gpusTruetransformers4.28 需要手动设置本文代码环境transformers4.41.1 assistant_model高级功能辅助生成模型另一个词表完全相同的小模型有些token使用辅助模型生成更快streamer流式输出控制器现在的大模型平台都是一个字一个字显示出来的这就是流式输出否则的话会等所有生成完成再显示出来。这个可以自定义流式输出的方式negative_prompt_ids负面提示一些前沿研究会用到不用管negative_prompt_attention_mask负面提示的 attention_maskkwargs 以上输入都太高大上了只有 inputs 会每次传入其他的对于常规输出根本用不到其实 inputs 也可以不用输入通过tokenizer()得到model_inputs后使用model_inputs方式也可以传入回想一下别人的代码会看到这里经常传入 temperature0.7, top_k20, max_new_tokens512等参数都是通过**kwargs传入进来的其实传入的这些都是输入参数 generation_config 的属性可以进入对应类中看一下有哪些属性from transformers.generation.configuration_utils import GenerationConfig你可以创建该对象并覆盖某些参数也可以通过参数形式在调用.generate()时传进来在后面会将传入的这些参数覆盖掉generation_config中对应的属性

  1. 创建生成参数对象 generation_config 刚说完**kwargs正式代码的第一部分就是整合输入的这些参数如果输入 kwargs 中存在某些字段则进行替换。 先别着急第一行先验证模型是否与 .generate()兼容运行了如下代码 self._validate_model_class()进入对应函数看到如下代码第一个函数其实就看第一行 if 判断的结果.can_generate()放到了下面第二个函数其实不在一个文件中以下博文使用同样方式里面主要判断当前模型有没有重写生成函数如果重写了就不能执行默认的.generate()了即执行了一堆东西弹出异常下面一堆主要用来提出是哪一种异常方便调试修改 def _validate_model_class(self):Confirms that the model class is compatible with generation. If not, raises an exception that points to theright class to use.# 验证能不能用.generate()生成应该很少不可以的吧if not self.can_generate():generate_compatible_mappings [MODEL_FOR_CAUSAL_LM_MAPPING,MODEL_FOR_CAUSAL_IMAGE_MODELING_MAPPING,MODEL_FOR_VISION_2_SEQ_MAPPING,MODEL_FOR_SEQ_TO_SEQ_CAUSAL_LM_MAPPING,MODEL_FOR_SPEECH_SEQ_2_SEQ_MAPPING,]generate_compatible_classes set()for model_mapping in generate_compatible_mappings:supported_models model_mapping.get(type(self.config), defaultNone)if supported_models is not None:generate_compatible_classes.add(supported_models.name)exception_message (fThe current model class ({self.class.name}) is not compatible with .generate(), as it doesnt have a language model head.)if generate_compatible_classes:exception_message f Please use one of the following classes instead: {generate_compatible_classes}raise TypeError(exception_message)def can_generate(cls) - bool:Returns whether this model can generate sequences with .generate().Returns:bool: Whether this model can generate sequences with .generate().# Detects whether prepare_inputs_for_generation has been overwritten, which is a requirement for generation.# Alternativelly, the model can also have a custom generate function.if GenerationMixin in str(cls.prepare_inputs_for_generation) and GenerationMixin in str(cls.generate):return Falsereturn True如果输入了 tokenizer将其从 kwargs 中取出这个后面会用于生成停止的判定条件 tokenizer kwargs.pop(tokenizer, None)进入正题将输入的生成参数覆盖掉默认生成参数这里只从 **kwargs 中取出默认生成参数中有的 key其余没有的 key 放回 model_kwargs 中 generation_config, model_kwargs self._prepare_generation_config(generation_config, **kwargs)具体实现为如下代码每一行的具体功能标了注释 里面有几处判断 is_torchdynamo_compiling()这里说明一下 is_torchdynamo_compiling()判断当前代码是否使用了 TorchDynamo 优化TorchDynamo 是一种为 PyTorch 设计的即时JIT编译器通过在运行时拦截 Python 代码、优化它并编译成高效的机器代码来解决这一问题 def _prepare_generation_config(self, generation_config: Optional[GenerationConfig], **kwargs: Dict) - Tuple[GenerationConfig, Dict]:# 如果 model.generate() 没有传入 generation_config就是用 model 自身创建的默认 generation_configif generation_config is None:# 首先声明这是一个即将弃用的策略# 第一行 is_torchdynamo_compiling() 判断是否用了 TorchDynamo 计算图只有在使用 TorchDynamo 时它才会设置为 True # 参考 https://pytorch.org/docs/stable/_modules/torch/compiler.html#is_dynamo_compiling# 以前用户会在模型创建时指定生成参数与现在的通用推理方式不同为了适配这种遗留问题做了三个判断检测是否是这种情况# 注意这里的 self 是 model 本身即 Qwen2ForCausalLM 类# 1. generation_config 是从 model config 里创建的在那里面会设置 _from_model_configTrue# 2. generation config 自创建以来没有修改过即在创建模型时创建的 generation config这里用哈希值判断有没有修改# 3. 用户必须在 model config 中设置生成参数这里创建了一个默认参数字典只要 model config 中有一个参数值与默认值不同就返回 Trueif (not is_torchdynamo_compiling()and self.generation_config._from_model_configand self.generation_config._original_object_hash hash(self.generation_config)and self.config._has_non_default_generation_parameters()):new_generation_config GenerationConfig.from_model_config(self.config)if new_generation_config ! self.generation_config:warnings.warn(You have modified the pretrained model configuration to control generation. This is a deprecated strategy to control generation and will be removed soon, in a future version. Please use and modify the model generation configuration (see https://huggingface.co/docs/transformers/generation_strategies#default-text-generation-configuration ))self.generation_config new_generation_configgeneration_config self.generation_config# 如果使用了计算图则必须传入 generation_config 参数设置因为 torch.compile 无法使用 copy.deepcopy()if is_torchdynamo_compiling():# 这里检测使用 kwargs 传入了那些参数然后弹出异常用于提示model_kwargs kwargsgenerate_attributes_in_kwargs [key for key, value in kwargs.items() if getattr(generation_config, key, None) ! value]if len(generate_attributes_in_kwargs) 0:raise ValueError(torch.compile exception: all generation configuration attributes must be passed within a fgeneration_config instance passed to generate (found: {generate_attributes_in_kwargs}).)else:# 深拷贝出来一份目前还没找到为什么要深拷贝这里拷贝完进行替换了上一级函数也替换了有网友知道可以说一下generation_config copy.deepcopy(generation_config)# 这里的 .update() 不是 dict 默认的是自己实现的它将 generation_config 里面存在的属性进行替换不存在的 return 给 model_kwargsmodel_kwargs generation_config.update(kwargs)return generation_config, model_kwargs这里验证剩余的输入参数如果 key 输入错误也会在这里被发现 self._validate_model_kwargs(model_kwargs.copy())这里审核了很多东西防止各种可能的错误并弹出对应异常来提示 def _validate_model_kwargs(self, model_kwargs: Dict[str, Any]):# 如果模型不支持cache类但是传入的past_key_values却是cache类就弹出异常if isinstance(model_kwargs.get(past_key_values, None), Cache) and not self._supports_cache_class:raise ValueError(f{self.class.name} does not support an instance of Cache as past_key_values. Please check the model documentation for supported cache formats.)# 如果是encoder-decoder模型移除不需要的属性if self.config.is_encoder_decoder:for key in [decoder_input_ids]:model_kwargs.pop(key, None)unused_model_args []# 取出模型输入需要用到的所有参数model_args set(inspect.signature(self.prepare_inputs_for_generation).parameters)if kwargs in model_args or model_kwargs in model_args:model_args | set(inspect.signature(self.forward).parameters)# Encoder-Decoder 模型还需要一些额外的编码器参数if self.config.is_encoder_decoder:base_model getattr(self, self.base_model_prefix, None)# allow encoder kwargsencoder getattr(self, encoder, None)# MusicgenForConditionalGeneration has text_encoder and audio_encoder.# Also, it has base_model_prefix encoder_decoder but there is no self.encoder_decoder# TODO: A better way to handle this.if encoder is None and base_model is not None:encoder getattr(base_model, encoder, None)if encoder is not None:encoder_model_args set(inspect.signature(encoder.forward).parameters)model_args | encoder_model_args# allow decoder kwargsdecoder getattr(self, decoder, None)if decoder is None and base_model is not None:decoder getattr(base_model, decoder, None)if decoder is not None:decoder_model_args set(inspect.signature(decoder.forward).parameters)modelargs | {fdecoder{x} for x in decoder_model_args}# allow assistant_encoder_outputs to be passed if were doing assisted generatingif assistant_encoder_outputs in model_kwargs:model_args | {assistant_encoder_outputs}# 如果传入了不需要用到的参数就弹出异常for key, value in model_kwargs.items():if value is not None and key not in model_args:unused_model_args.append(key)if unused_model_args:raise ValueError(fThe following model_kwargs are not used by the model: {unused_model_args} (note: typos in the generate arguments will also show up in this list))2. 初始化部分输入参数 最先验证一下是否用了 DeepSpeed ZeRO Stage-3 多GPU具体说明在输入参数 synced_gpus 里如果没用 DeepSpeed 或单卡的话不用管此时synced_gpusFalse if synced_gpus is None:if is_deepspeed_zero3_enabled() and dist.get_world_size() 1:synced_gpus Trueelse:synced_gpus False接下来初始化 logits 处理器和停止条件具体说明看输入参数。以 logits_processor 为例这是一个 LogitsProcessorList: List 对象也就是一个继承的list的类可以往里面放很多单个的处理器LogitsProcessorList 复写了call函数调用的时候会循环执行自己的单处理器从而实现 logits 的修改。stopping_criteria 是同样原理。 logits_processor logits_processor if logits_processor is not None else LogitsProcessorList() stopping_criteria stopping_criteria if stopping_criteria is not None else StoppingCriteriaList()检测一些 attention_mask 的事情主要判断需不需要以及传没传入如果没传入后续会自己创建 # inspect.signature(self.forward).parameters.keys() 获取 self.forward 的所有输入参数这里判断模型是否需要 attention_mask 参数 accepts_attention_mask attention_mask in set(inspect.signature(self.forward).parameters.keys())

    这里如果通过判断模型有没有 encoder 决定是否需要 attention_mask与 accepts_attention_mask 为双保险判断

    requires_attention_mask encoder_outputs not in model_kwargs

    检测输入 kwargs 中是否传入了 attention_mask

    kwargs_has_attention_mask model_kwargs.get(attention_mask, None) is not None3. 定义模型输入 首先需要拓展一下从这里初次判断模型的种类是 encoder-decoder 类型还是 decoder-only 类型两者有很多不同例如输入 id 的 key 名称 里面验证了几点 若传入了 inputs就不要在 kwargs 中再次定义 input_ids若 inputs 为 None且 model_kwargs 不包含 input_ids 或 input_ids 也为 None则创建一个 [batch_sie, 1] 大小的tensor里面的值都为 bos_token_id inputs_tensor, model_input_name, model_kwargs self._prepare_model_inputs(inputs, generation_config.bos_token_id, model_kwargs )输入有三种方式inputsmodel.generate()的输入参数、input_ids放在kwargs中的输入id、inputs_embeds通常在encoder-decoder 模型中使用可以传入编码器输出的embedding这个部分就是来回确认是否传错 def _prepare_model_inputs(self,inputs: Optional[torch.Tensor] None,bos_token_id: Optional[torch.Tensor] None,model_kwargs: Optional[Dict[str, torch.Tensor]] None, ) - Tuple[torch.Tensor, Optional[str], Dict[str, torch.Tensor]]:This function extracts the model-specific inputs for generation.# 1.有一些 encoder-decoder 模型的输入有不同的名称这里首先确认名称if (self.config.is_encoder_decoderand hasattr(self, encoder)and self.encoder.main_input_name ! self.main_input_name):input_name self.encoder.main_input_nameelse:input_name self.main_input_name# 从 model_kwargs 中去掉 input_name: None 的键值对model_kwargs {k: v for k, v in model_kwargs.items() if v is not None or k ! input_name}# 2.这里确保 model.generate() 输入参数中的 inputs 和 kwargs 中的 input_name 只输入一个inputs_kwarg model_kwargs.pop(input_name, None)if inputs_kwarg is not None and inputs is not None:raise ValueError(finputs: {inputs} were passed alongside {input_name} which is not allowed. fMake sure to either pass {inputs} or {input_name}…)elif inputs_kwarg is not None:inputs inputs_kwarg# 3.如果 input_name ! inputs_embeds 这里确保 input_name 和 inputs_embeds 只输入一个if input_name input_ids and inputs_embeds in model_kwargs:# 如果是 decoder-only 模型先看看模型 .forward() 函数的参数中是否包含 inputs_embeds如果不包含就弹出异常if not self.config.is_encoder_decoder:has_inputs_embeds_forwarding inputs_embeds in set(inspect.signature(self.prepare_inputs_for_generation).parameters.keys())if not has_inputs_embeds_forwarding:raise ValueError(fYou passed inputs_embeds to .generate(), but the model class {self.class.name} doesnt have its forwarding implemented. See the GPT2 implementation for an example (https://github.com/huggingface/transformers/pull/21405), and feel free to open a PR with it!)# In this case, input_ids is moved to the model_kwargs, so a few automations (like the creation of# the attention mask) can rely on the actual model input.model_kwargs[input_ids] self._maybe_initialize_input_ids_for_generation(inputs, bos_token_id, model_kwargsmodel_kwargs)else:if inputs is not None:raise ValueError(You passed inputs_embeds and input_ids to .generate(). Please pick one.)inputs, input_name model_kwargs[inputs_embeds], inputs_embeds# 4. 如果 inputs 还是 None尝试用 BOS token 创建 input_idsinputs self._maybe_initialize_input_ids_for_generation(inputs, bos_token_id, model_kwargs)return inputs, input_name, model_kwargs一些小操作取 batch_size 和设备类型 # 取第一个维度 batch_size 大小 batch_size inputs_tensor.shape[0]

    device 类型

    device inputs_tensor.device处理 bos_token_id、eos_token_id、pad_token_id、decoder_start_token_id将其转化为 tensor torch.long 类型 self._prepare_special_tokens(generation_config, kwargs_has_attention_mask, devicedevice)这里面的具体操作就是各种安全确认 def _prepare_special_tokens(self,generation_config: GenerationConfig,kwargs_has_attention_mask: Optional[bool] None,device: Optional[Union[torch.device, str]] None, ):# 将 token 数字转化成 tensordef _tensor_or_none(token, deviceNone):if device is None:device self.deviceif token is None or isinstance(token, torch.Tensor):return tokenreturn torch.tensor(token, devicedevice, dtypetorch.long)# encoder-decoder 模型在输出时需要 decoder_start_token_id如果没传入就用 bos_token_idif self.config.is_encoder_decoder:generation_config.decoder_start_token_id self._get_decoder_start_token_id(generation_config.decoder_start_token_id, generation_config.bos_token_id)# 将这些特殊 id 转化成 tensor 形式bos_token_id _tensor_or_none(generation_config.bos_token_id, devicedevice)eos_token_id _tensor_or_none(generation_config.eos_token_id, devicedevice)pad_token_id _tensor_or_none(generation_config.pad_token_id, devicedevice)decoder_start_token_id _tensor_or_none(generation_config.decoder_start_token_id, devicedevice)# 这里讲 eos_token_id 弄成至少一维的格式用于后面统一方式取数if eos_token_id is not None and eos_token_id.ndim 0:eos_token_id eos_token_id.unsqueeze(0)# 如果没设置 pad_token_id就用 eos_token_id 填充if pad_token_id is None and eos_token_id is not None:# attention mask 和 pad_token_id 如果都不设置就会弹出警告可能有未知的异常发生if kwargs_has_attention_mask is not None and not kwargs_has_attention_mask:logger.warning(The attention mask and the pad token id were not set. As a consequence, you may observe unexpected behavior. Please pass your inputs attention_mask to obtain reliable results.)pad_token_id eos_token_id[0]logger.warning(fSetting pad_token_id to eos_token_id:{pad_token_id} for open-end generation.)# 一些安全检查if self.config.is_encoder_decoder and decoder_start_token_id is None:raise ValueError(decoder_start_token_id or bos_token_id has to be defined for encoder-decoder generation.)if eos_token_id is not None and (torch.is_floating_point(eos_token_id) or (eos_token_id 0).any()):logger.warning(feos_token_id should consist of positive integers, but is {eos_token_id}. Your generation will not stop until the maximum length is reached. Depending on other flags, it may even crash.)# 将处理好的特殊 token 传回去generation_config.bos_token_id bos_token_idgeneration_config.eos_token_id eos_token_idgeneration_config.pad_token_id pad_token_idgeneration_config.decoder_start_token_id decoder_start_token_id检测 tokenizer 是否 padding 左对齐 tokenizer 默认对其方式是右对齐需设置 tokenizer.padding_side “left”decoder-only 在批次生成时必须左对齐具体理论这里不具体讲解 if not self.config.is_encoder_decoder and not is_torchdynamo_compiling():# 检查是否批次大于1且每个样本最后一个元素是否为 pad_token_id如果有则认为当前是右对齐弹出警告if (generation_config.pad_token_id is not Noneand batch_size 1and len(inputs_tensor.shape) 2and torch.sum(inputs_tensor[:, -1] generation_config.pad_token_id) 0):logger.warning(A decoder-only architecture is being used, but right-padding was detected! For correct generation results, please set padding_sideleft when initializing the tokenizer.)4. 定义其他模型参数 首先又是模型种类区分如果是 encoder-decoder 类型并传入了编码器的 embedding 表示则自动认为这是输入缓存关于kv cache的原理这里不展开否则设置接收 generation_config.use_cache默认是Trueif not self.config.is_encoder_decoder and model_input_name inputs_embeds:model_kwargs[use_cache] True else:model_kwargs[use_cache] generation_config.use_cacheattention_mask的检查如果没传入就默认创建# 如果没有传入 attention_mask则使用各种方式生成 attention_mask if not kwargs_has_attention_mask and requires_attention_mask and accepts_attention_mask:model_kwargs[attention_mask] self._prepare_attention_mask_for_generation(inputs_tensor, generation_config.pad_token_id, generation_config.eos_token_id)# 如果是 encoder-decoder 模型将 encoder_outputs 放到 model_kwargs 中 if self.config.is_encoder_decoder and encoder_outputs not in model_kwargs:model_kwargs self._prepare_encoder_decoder_kwargs_for_generation(inputs_tensor, model_kwargs, model_input_name, generation_config)5. 准备 input_ids 先准备 input_ids如果是 encoder_decoder 模型传入了 decoder_input_ids 就直接用如果没传入就用 decoder_start_token_id 创建一个这里就不展开了。如果是 decoder-only 模型直接取 input_idsif self.config.is_encoder_decoder:input_ids, model_kwargs self._prepare_decoder_input_ids_for_generation(batch_sizebatch_size,model_input_namemodel_input_name,model_kwargsmodel_kwargs,decoder_start_token_idgeneration_config.decoder_start_token_id,deviceinputs_tensor.device,) else:# 之前 _prepare_model_inputs 函数中处理了 model_input_name# if input_name input_ids and inputs_embeds in model_kwargs:# inputs, input_name model_kwargs[inputs_embeds], inputs_embeds# 这种情况下 model_input_nameinputs_embeds所以这里再次尝试取出 input_idsinput_ids inputs_tensor if model_input_name input_ids else model_kwargs.pop(input_ids)这里顺便定义了一下流失输出# 流式输出时使用即一个字一个字的显示 if streamer is not None:streamer.put(input_ids.cpu())6. 准备 max_length 先能取的取出来这里解释一下变量 max_length: 包括输入和输出所有 token 的最大长度max_new_tokens: 除输入之外新生成 token 的最大长度 input_ids_length input_ids.shape[-1]

    查看是否设置了最大长度和最小长度这里区分 max_length 和 max_new_tokens

    has_default_max_length kwargs.get(max_length) is None and generation_config.max_length is not None has_default_min_length kwargs.get(min_length) is None and generation_config.min_length is not None然后正式处理这个长度多种检查保证不会出错 generation_config self._prepare_generated_length(generation_configgeneration_config,has_default_max_lengthhas_default_max_length,has_default_min_lengthhas_default_min_length,model_input_namemodel_input_name,inputs_tensorinputs_tensor,input_ids_lengthinput_ids_length, )def _prepare_generated_length(self,generation_config,has_default_max_length,has_default_min_length,model_input_name,input_ids_length,inputs_tensor, ):# max_length 和 max_new_tokens 只传入一个就行都传则 max_new_tokens 优先并弹出警告min 同理if generation_config.max_new_tokens is not None:if not has_default_max_length and generation_config.max_length is not None:logger.warning(fBoth max_new_tokens ({generation_config.max_new_tokens}) and max_length(f{generation_config.max_length}) seem to have been set. max_new_tokens will take precedence. Please refer to the documentation for more information. (https://huggingface.co/docs/transformers/main/en/main_classes/text_generation))# max_new_tokens 优先的方式就是用 max_new_tokens input_ids_length 重新赋值 max_lengthgeneration_config.max_length generation_config.max_new_tokens input_ids_length# 到这步如果 model_input_name inputs_embeds 说明同时传入了 inputs_embeds 和 input_ids# 此时将 max_length 扣除掉 inputs_tensor 的长度elif (model_input_name inputs_embedsand input_ids_length ! inputs_tensor.shape[1]and not self.config.is_encoder_decoder):generation_config.max_length - inputs_tensor.shape[1]# 最小长度相同的做法if generation_config.min_new_tokens is not None:if not has_default_min_length:logger.warning(fBoth min_new_tokens ({generation_config.min_new_tokens}) and min_length(f{generation_config.min_length}) seem to have been set. min_new_tokens will take precedence. Please refer to the documentation for more information. (https://huggingface.co/docs/transformers/main/en/main_classes/text_generation))generation_config.min_length generation_config.min_new_tokens input_ids_lengthelif (model_input_name inputs_embedsand input_ids_length ! inputs_tensor.shape[1]and not self.config.is_encoder_decoder):generation_config.min_length max(generation_config.min_length - inputs_tensor.shape[1], 0)return generation_config在这里顺便处理一下 cache 相关 # 不支持同时传递“cache_implementation”生成时使用的缓存类和“past_key_values”缓存对象 if generation_config.cache_implementation is not None and model_kwargs.get(past_key_values) is not None:raise ValueError(Passing both cache_implementation (used to initialize certain caches) and past_key_values (a Cache object) is unsupported. Please use only one of the two.)

    当前版本 transformers NEED_SETUP_CACHE_CLASSES_MAPPING 只有 {static: StaticCache}

    elif generation_config.cache_implementation in NEED_SETUP_CACHE_CLASSES_MAPPING:# 这里的 self 是模型类例如 Qwen2ForCausalLM# 检查模型是否支持 cache_implementation若不支持则弹出警告if not self._supports_cache_class:raise ValueError(This model does not support the cache_implementation argument. Please check the following issue: https://github.com/huggingface/transformers/issues/28981.)if generation_config.cache_implementation static:# 检查模型是否支持 cache_implementationstatic若不支持则弹出警告if not self._supports_static_cache:raise ValueError(This model does not support cache_implementationstatic. Please check the following issue: https://github.com/huggingface/transformers/issues/28981)# 如果是 static就用这种方式创建一个 Cache 对象model_kwargs[past_key_values] self._get_static_cache(batch_size, generation_config.max_length)最后又检查了长度相关反反复复 self._validate_generated_length(generation_config, input_ids_length, has_default_max_length)def _validate_generated_length(self, generation_config, input_ids_length, has_default_max_length):Performs validation related to the resulting generated length# 1. max_new_tokens 和 max_length 都没设置会弹出警告提醒if has_default_max_length and generation_config.max_new_tokens is None and generation_config.max_length 20:# 这里的 20 是默认 max_lengthwarnings.warn(fUsing the model-agnostic default max_length ({generation_config.max_length}) to control the generation length. We recommend setting max_new_tokens to control the maximum length of the generation.,UserWarning,)# 输入长度直接超过 max_length 弹出异常if input_ids_length generation_config.max_length:input_ids_string decoder_input_ids if self.config.is_encoder_decoder else input_idsraise ValueError(fInput length of {input_ids_string} is {input_ids_length}, but max_length is set tof {generation_config.max_length}. This can lead to unexpected behavior. You should consider increasing max_length or, better yet, setting max_new_tokens.)# 2. 最小长度也是同理这里做一些提醒信息min_length_error_suffix ( Generation will stop at the defined maximum length. You should decrease the minimum length and/or increase the maximum length.)if has_default_max_length:min_length_error_suffix (f Note that max_length is set to {generation_config.max_length}, its default value.)# 如果单独设置了 min_length 且 min_length max_length当然要弹出异常if generation_config.min_length is not None and generation_config.min_length generation_config.max_length:warnings.warn(fUnfeasible length constraints: min_length ({generation_config.min_length}) is larger thanf the maximum possible length ({generation_config.max_length}). min_length_error_suffix,UserWarning,)# 如果设置了 min_new_tokens就重新计算 min_length若大于 max_length 就弹出警告if generation_config.min_new_tokens is not None:min_length generation_config.min_new_tokens input_ids_lengthif min_length generation_config.max_length:warnings.warn(fUnfeasible length constraints: min_new_tokens ({generation_config.min_new_tokens}), when fadded to the prompt length ({input_ids_length}), is larger thanf the maximum possible length ({generation_config.max_length}). min_length_error_suffix,UserWarning,)7. 确定生成模式 先直接确定生成模式通过一堆条件判断当前属于那种生成模式各种生成模式的解释单独放到后面的博客中。 generation_mode generation_config.get_generation_mode(assistant_model)def get_generation_mode(self, assistant_model: Optional[PreTrainedModel] None) - GenerationMode:Returns the generation mode triggered by the [GenerationConfig] instance.Arg:assistant_model (PreTrainedModel, optional):The assistant model to be used for assisted generation. If set, the generation mode will beassisted generation.Returns:GenerationMode: The generation mode triggered by the instance.# TODO joao: find out a way of not depending on external fields (e.g. assistant_model), then make this a# property and part of the reprif self.constraints is not None or self.force_words_ids is not None:generation_mode GenerationMode.CONSTRAINED_BEAM_SEARCHelif self.num_beams 1:if self.do_sample is False:if (self.top_k is not Noneand self.top_k 1and self.penalty_alpha is not Noneand self.penalty_alpha 0):generation_mode GenerationMode.CONTRASTIVE_SEARCHelse:generation_mode GenerationMode.GREEDY_SEARCHelse:generation_mode GenerationMode.SAMPLEelse:if self.num_beam_groups 1:generation_mode GenerationMode.GROUP_BEAM_SEARCHelif self.do_sample is True:generation_mode GenerationMode.BEAM_SAMPLEelse:generation_mode GenerationMode.BEAM_SEARCH# Assisted generation may extend some generation modesif assistant_model is not None or self.prompt_lookup_num_tokens is not None:if generation_mode in (greedy_search, sample):generation_mode GenerationMode.ASSISTED_GENERATIONelse:raise ValueError(Youve set assistant_model, which triggers assisted generate. Currently, assisted generate is only supported with Greedy Search and Sample.)return generation_mode其他操作比如流式输出不支持束搜索输入和模型要放到相同的设备上 if streamer is not None and (generation_config.num_beams 1):raise ValueError(streamer cannot be used with beam search (yet!). Make sure that num_beams is set to 1.)if self.device.type ! input_ids.device.type:warnings.warn(You are calling .generate() with the input_ids being on a device type differentf than your models device. input_ids is on {input_ids.device.type}, whereas the modelf is on {self.device.type}. You may experience unexpected behaviors or slower generation. Please make sure that you have put input_ids to thef correct device by calling for example input_ids input_ids.to({self.device.type}) before running .generate().,UserWarning,)8. 准备 logits 处理器 根据生成参数的不同将对应的 logits_processor 放到类 LogitsProcessorList 中LogitsProcessorList 是一个 list 类型的类在调用的时候会顺序执行里面的每一个子对象。 比如重复惩罚系数不是默认值就加入重复惩罚类RepetitionPenaltyLogitsProcessor 最小序列长度不是默认值就设置最小长度类MinLengthLogitsProcessor prepared_logits_processor self._get_logits_processor(generation_configgeneration_config,input_ids_seq_lengthinput_ids_length,encoder_input_idsinputs_tensor,prefix_allowed_tokens_fnprefix_allowed_tokens_fn,logits_processorlogits_processor,deviceinputs_tensor.device,model_kwargsmodel_kwargs,negative_prompt_idsnegative_prompt_ids,negative_prompt_attention_masknegative_prompt_attention_mask, )9. 准备 stopping 处理器 与上面类似的操作将对应的处理器放到类 StoppingCriteriaList 中它也是一个list类型的类在调用的时候会顺序执行里面的每一个子对象。 比如最大长度停止的处理器 MaxLengthCriteria eos_token_id停止的处理器 EosTokenCriteria prepared_stopping_criteria self._get_stopping_criteria(generation_configgeneration_config, stopping_criteriastopping_criteria, tokenizertokenizer, kwargs )10. 执行生成 这里就根据第 7 步创建的生成模式执行不同函数本文仅以 SAMPLE模式为例。 首先加载 logits warper这里和之前加载的logits processor是类似的区别在于 logits warper 里面是采样时才需要运行的处理器logits processor 是通用的处理器每种生成模式都需要用到的 prepared_logits_warper (self._get_logits_warper(generation_config) if generation_config.do_sample else None )为了生成结果的多样性生成参数中的generation_config.num_return_sequences可以控制一条输入要有几个输出如果不止一个输出就需要将这个批次的数据赋值出来几份同步进行生成。 input_ids, model_kwargs self._expand_inputs_for_generation(input_idsinput_ids,expand_sizegeneration_config.num_return_sequences,is_encoder_decoderself.config.is_encoder_decoder,model_kwargs, )def _expand_inputs_for_generation(expand_size: int 1,is_encoder_decoder: bool False,input_ids: Optional[torch.LongTensor] None,model_kwargs, ) - Tuple[torch.LongTensor, Dict[str, Any]]:def _expand_dict_for_generation(dict_to_expand):for key in dict_to_expand:if (key ! cache_positionand dict_to_expand[key] is not Noneand isinstance(dict_to_expand[key], torch.Tensor)):dict_to_expand[key] dict_to_expand[key].repeat_interleave(expand_size, dim0)return dict_to_expand# 将 input_ids 复制出来几份if input_ids is not None:input_ids input_ids.repeat_interleave(expand_size, dim0)# 将 model_kwargs 中的 attention mask 也复制出来几份model_kwargs _expand_dict_for_generation(model_kwargs)# 如果是 encoder-decoder 模型则将 encoder_outputs 也复制出来几份if is_encoder_decoder:if model_kwargs.get(encoder_outputs) is None:raise ValueError(If is_encoder_decoder is True, make sure that encoder_outputs is defined.)model_kwargs[encoder_outputs] _expand_dict_for_generation(model_kwargs[encoder_outputs])return input_ids, model_kwargs接下来就进入正式的生成和采样了 result self._sample(input_ids,logits_processorprepared_logits_processor,logits_warperprepared_logits_warper,stopping_criteriaprepared_stopping_criteria,generation_configgeneration_config,synced_gpussynced_gpus,streamerstreamer,model_kwargs, )self._sample 输入参数就不说了都是之前处理过的传进来

  2. 先拿出一些变量 pad_token_id generation_config.pad_token_id # pad 值 output_attentions generation_config.output_attentions # 是否输出 attentions output_hidden_states generation_config.output_hidden_states # 是否输出 hidden states output_scores generation_config.output_scores # 是否输出 scores output_logits generation_config.output_logits # 是否输出 logits return_dict_in_generate generation_config.return_dict_in_generate # 是否返回 dict has_eos_stopping_criteria any(hasattr(criteria, eos_token_id) for criteria in stopping_criteria) # 检测有没有 eos 停止条件 do_sample generation_config.do_sample # 是否使用采样

    如果设置 do_sample要求 logits_warper 必须是特定的数据类型

    if do_sample is True and not isinstance(logits_warper, LogitsProcessorList):raise ValueError(do_sample is set to True, logits_warper must be a LogitsProcessorList instance (it is f{logits_warper}).)2. 根据要求初始化一些变量 如果想输出下面中间变量值必须设置 return_dict_in_generateTrue然后想输出哪个就指定哪个为True

    这里先将对应值初始化为元组类型

    scores () if (return_dict_in_generate and output_scores) else None raw_logits () if (return_dict_in_generate and output_logits) else None decoder_attentions () if (return_dict_in_generate and output_attentions) else None cross_attentions () if (return_dict_in_generate and output_attentions) else None decoder_hidden_states () if (return_dict_in_generate and output_hidden_states) else None# 如果是 encoder-decoder 模型从 model_kwargs 里取出 encoder 的 attentions 和 hidden states if return_dict_in_generate and self.config.is_encoder_decoder:encoder_attentions model_kwargs[encoder_outputs].get(attentions) if output_attentions else Noneencoder_hidden_states (model_kwargs[encoder_outputs].get(hidden_states) if output_hidden_states else None)3. 再设置一些变量 记录有哪些序列完成生成了以及计算一下缓存或者输入序列的长度是多少确定生成第一个字符的位置编码索引。 batch_size input_ids.shape[0]

    是否 batch 内所有序列都生成完成的判断标志位

    this_peer_finished False

    创建一个跟踪每个序列是否完成生成的变量

    unfinished_sequences torch.ones(batch_size, dtypetorch.long, deviceinput_ids.device)

    初始化位置序号如果用了 cache就从 cache 里取没有就根据 input_ids 长度创建

    prefilling 阶段确定输入的长度

    model_kwargs self._get_initial_cache_position(input_ids, model_kwargs)def _get_initial_cache_position(self, input_ids, model_kwargs):if not model_kwargs.get(use_cache, True):model_kwargs[cache_position] Nonereturn model_kwargspast_length 0# 如果输入了 past_key_values 则根据 past_key_values 确定缓存序列的长度if past_key_values in model_kwargs:if isinstance(model_kwargs[past_key_values], Cache):past_length model_kwargs[past_key_values].get_seq_length()else:past_length model_kwargs[past_key_values][0][0].shape[2]# 如果输入 inputs_embeds 则根据这个确定if inputs_embeds in model_kwargs:cur_len model_kwargs[inputs_embeds].shape[1]else:# 都没有就根据 input_ids 确定cur_len input_ids.shape[-1]# 创建输入序列的位置索引model_kwargs[cache_position] torch.arange(past_length, cur_len, deviceinput_ids.device)return model_kwargs4. 正式进行生成 首先进入一个循环来生成知道所有序列都生成完成。这个函数可以只关注this_peer_finished变量如果都完成会变成True从而退出循环。 while self._has_unfinished_sequences(this_peer_finished, synced_gpus, deviceinput_ids.device):然后开始准备模型的输入这里的prepare_inputs_for_generation函数是模型内部的本文是在 qwen2\modeling_qwen2.py中。 model_inputs self.prepare_inputs_for_generation(input_ids, **model_kwargs)def prepare_inputs_for_generation(self, input_ids, past_key_valuesNone, attention_maskNone, inputs_embedsNone, **kwargs ):# 忽略被 kv cache 覆盖的 tokenif past_key_values is not None:if isinstance(past_key_values, Cache):cache_length past_key_values.get_seq_length()past_length past_key_values.seen_tokensmax_cache_length past_key_values.get_max_length()else:cache_length past_length past_key_values[0][0].shape[2]max_cache_length None# 特殊情况如果 atten mask 的长度小于 input_ids就将没有 mask 的地方一起输入进去常见于 encoder-decoder 模型生成第一个字符的时候if attention_mask is not None and attention_mask.shape[1] input_ids.shape[1]:input_ids input_ids[:, -(attention_mask.shape[1] - past_length) :]# 在每次新生成token的时候都会触发input_ids 只存储最新的一个token配合 kv cache 快速生成# 这里每次进来的 input_ids 是全部已有的 token输入输出截取最新的一个放到了 model_inputs 中不影响外层的 input_ids 值elif past_length input_ids.shape[1]:input_ids input_ids[:, past_length:]# 如果超出了 kv cache 的最大长度就丢掉最远的部分if (max_cache_length is not Noneand attention_mask is not Noneand cache_length input_ids.shape[1] max_cache_length):attention_mask attention_mask[:, -max_cache_length:]position_ids kwargs.get(position_ids, None)# 如果输入了 attention mask 但没有输入 position ids则根据 atten mask 创建if attention_mask is not None and position_ids is None:# 动态创建position_ids以进行批量生成# .cumsum(-1) 沿着 atten mask 最后一个维度进行累加求和生成0,1,2,3…# 如果 atten mask 前几个是0则生成是 -1,-1,-1,0,1,2,3…position_ids attention_mask.long().cumsum(-1) - 1# 将 atten mask 等于0的地方填充为1原来为-1用于处理padding部分的位置编码# 由于是左填充所以都是最左边的位置编码为1position_ids.maskedfill(attention_mask 0, 1)# 每次生成 position_ids 也只取最新的一个if past_key_values:position_ids position_ids[:, -input_ids.shape[1] :]# inputs_embeds 只在生成第一个 token 时使用if inputs_embeds is not None and past_key_values is None:model_inputs {inputs_embeds: inputs_embeds}else:model_inputs {input_ids: input_ids}# 都输入给 model_inputs 返回model_inputs.update({position_ids: position_ids,past_key_values: past_key_values,use_cache: kwargs.get(use_cache),attention_mask: attention_mask,})return model_inputs然后运行模型生成下一个 token生成之后取出最新生成的token经过 logits_processor和logits_warper采样。

    进入模型内部生成下一个token

    outputs self(**model_inputs,return_dictTrue,output_attentionsoutput_attentions,output_hidden_statesoutput_hidden_states, )if synced_gpus and this_peer_finished:continue # dont waste resources running the code we dont need# 取出最后一个token.logits维度为batch_size, seq_len, vocab_size next_token_logits outputs.logits[:, -1, :]# 经过前面的处理器进行分数调整 next_token_scores logits_processor(input_ids, next_token_logits) if do_sample:next_token_scores logits_warper(input_ids, next_token_scores)如果需要输出某些变量这里进行处理将最新结果存储进去 if return_dict_in_generate:if output_scores:scores (next_token_scores,)if output_logits:raw_logits (next_token_logits,)if output_attentions:decoder_attentions ((outputs.decoder_attentions,) if self.config.is_encoder_decoder else (outputs.attentions,))if self.config.is_encoder_decoder:cross_attentions (outputs.cross_attentions,)if output_hidden_states:decoder_hidden_states ((outputs.decoder_hidden_states,)if self.config.is_encoder_decoderelse (outputs.hidden_states,))如果进行采样先将原始得分概率化然后从候选 token 中按给定概率随机采样一个 token 如果贪婪搜索则直接选取原始得分最大的那个 token。 if do_sample:probs nn.functional.softmax(next_token_scores, dim-1)next_tokens torch.multinomial(probs, num_samples1).squeeze(1) else:next_tokens torch.argmax(next_token_scores, dim-1)再更新一些变量

    如果生成完成了就将新生成的 token 替换成 pad_token_id提醒后面这个已经生成完成

    if has_eos_stopping_criteria:next_tokens next_tokens * unfinished_sequences pad_token_id * (1 - unfinished_sequences)# update generated ids, model inputs, and length for next step

    输入的 input_ids 和 model_kwargs 都要更新

    input_ids torch.cat([input_ids, next_tokens[:, None]], dim-1)

    流式输出将最新生成的 token 输出到 streamer

    if streamer is not None:streamer.put(next_tokens.cpu())model_kwargs self._update_model_kwargs_for_generation(outputs,model_kwargs,is_encoder_decoderself.config.is_encoder_decoder, )最后判断有没有生成完毕stopping_criteria是之前加载的结束生成的处理器这里返回的数量是序列对应每个序列是否生成完毕例如[True, False]取反之后跟 unfinished_sequences 与操作得到最终每个序列是否生成完毕。 this_peer_finished 只在所有序列都生成完毕才为 True unfinished_sequences unfinished_sequences ~stopping_criteria(input_ids, scores) this_peer_finished unfinished_sequences.max() 0最后先结束流失输出在处理需要返回的一些中间变量返回的结果使用类似GenerateDecoderOnlyOutput的类它继承了ModelOutput一个模型输出结果的基类除了取数时可以用索引、切片或字符串类似字典的 key其他时候与python 的字典无异。 if streamer is not None:streamer.end()if return_dict_in_generate:if self.config.is_encoder_decoder:return GenerateEncoderDecoderOutput(sequencesinput_ids,scoresscores,logitsraw_logits,encoder_attentionsencoder_attentions,encoder_hidden_statesencoder_hidden_states,decoder_attentionsdecoder_attentions,cross_attentionscross_attentions,decoder_hidden_statesdecoder_hidden_states,past_key_valuesmodel_kwargs.get(past_key_values),)else:return GenerateDecoderOnlyOutput(sequencesinput_ids,scoresscores,logitsraw_logits,attentionsdecoder_attentions,hidden_statesdecoder_hidden_states,past_key_valuesmodel_kwargs.get(past_key_values),) else:return input_ids到这里就全部结束啦 备注 本篇文章是拆解 transformers 源码逐行解析的由于博文太长难免有错误或者遗漏博文创作周期很长很可能某部分当时懒得写了。如有不对也请指出我及时改正谢谢。