概述
最近研究了一下 transformers 的源码,通过 debug 的方式一步步调试代码,了解了transformers 加载模型的完整流程。本文将根据自己的调试过程详细介绍 transformers 加载模型的原理,接下来我将分成下面几个部分介绍 transformers 源码:
- AutoTokenizer 详解
- AutoModelForCausalLM 详解
- Trainer 详解
本文是transformers源码的第一篇 -- AutoTokenizer 详解, 通过本文的学习,你将有如下收获:
- 了解 AutoTokenizer 如何自动加载 tokenizer
- 了解 tokenizer 如何进行编码(encode)
- 了解 tokenizer 如何进行解码(decode)
- 了解 tokenizer 如何使用对话模板(chat_template)
- 了解如何自定义 tokenizer 并集成到 transformers 中
在介绍具体内容之前,先说明一下本文的讲解思路,我将以 Qwen/Qwen-7B 进行讲解,为什么选择 Qwen-7B
,因为其在模型文件目录中自定义了 tokenization_qwen.py 和 modeling_qwen.py 文件,也就是说它没有将 tokenizer 和 model 的加载集成到 transformers 框架中,这样的话可以帮助我们理解如何自定义 tokenizer 和 model,当我们了解了 tokenizer 的自定义加载过程,也就很容易实现将自定义的加载过程集成到 transformers 框架中。
本文将从 huggingface 中的 Qwen/Qwen-7B 的快速使用代码为入口进行调试讲解,快速开始的代码示例如下:
python
from transformers import AutoModelForCausalLM, AutoTokenizer
from transformers.generation import GenerationConfig
# Note: The default behavior now has injection attack prevention off.
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen-7B", trust_remote_code=True)
# use bf16
# model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen-7B", device_map="auto", trust_remote_code=True, bf16=True).eval()
# use fp16
# model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen-7B", device_map="auto", trust_remote_code=True, fp16=True).eval()
# use cpu only
# model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen-7B", device_map="cpu", trust_remote_code=True).eval()
# use auto mode, automatically select precision based on the device.
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen-7B", device_map="auto", trust_remote_code=True).eval()
# Specify hyperparameters for generation. But if you use transformers>=4.32.0, there is no need to do this.
# model.generation_config = GenerationConfig.from_pretrained("Qwen/Qwen-7B", trust_remote_code=True)
inputs = tokenizer('蒙古国的首都是乌兰巴托(Ulaanbaatar)\n冰岛的首都是雷克雅未克(Reykjavik)\n埃塞俄比亚的首都是', return_tensors='pt')
inputs = inputs.to(model.device)
pred = model.generate(**inputs)
print(tokenizer.decode(pred.cpu()[0], skip_special_tokens=True))
# 蒙古国的首都是乌兰巴托(Ulaanbaatar)\n冰岛的首都是雷克雅未克(Reykjavik)\n埃塞俄比亚的首都是亚的斯亚贝巴(Addis Ababa)...
transformers 懒加载原理
首先将 Qwen-7B 下载到本地,由于在国内访问 huggingface 比较慢,可以通过 modelscope下载对应的模型,这里我已经将模型下载到了本地,然后通过下面的代码进行调试:
python
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("/Users/xiniao/models/Qwen/Qwen-7B", trust_remote_code=True)
当执行 from transformers import AutoTokenizer
这行代码时,导入包的顺序如下:
-
Python 首先会解析
transformers
这个模块的路径,找到对应的模块文件。如果我们通过pip install transformers
安装,所以会在python安装目录下的site-packages
包中找到 transformers 包;如果下载 transformers 源码,然后通过pip install -e .
安装,则会从本地的 transformers 路径中找到 transformers 包(可以通 sys.path 查看包路径)。 -
然后 Python 会执行
transformers
模块的代码,这包括执行该包下的__init__.py文件初始化操作、执行一些代码,以及定义相关的类和函数。 -
最后 Python 会从
transformers
模块中导入AutoTokenizer
对象,并将其添加到当前模块的命名空间中,使得在当前模块中可以直接使用AutoTokenizer
。
Python 基础知识
在Python中当导入一个包时,会执行该包下的__init__.py文件。这是因为在Python中,目录下如果包含了__init__.py文件,Python就会将这个目录视为一个包,而不仅仅是一个普通的目录。
init.py文件的主要作用有以下几个方面:
-
定义包的初始化内容:init.py中可以包含一些初始化的代码,比如定义包的公共变量、常量、函数等,确保在导入包的时候能够进行一些初始化操作。
-
控制包的导入行为:init.py文件可以用来控制包的导入行为,可以在其中定义__all__变量来控制在使用import *时导入的内容,也可以在其中进行一些导入时的特殊处理。
-
作为包的标识:init.py文件的存在表示该目录是一个包,有助于Python解释器正确识别和处理包的导入。
所以,当导入一个包时,Python会首先执行该包下的__init__.py文件,以便进行一些初始化操作,并标识该目录为一个包。
查看 transformers 源码,在 __init__.py
文件中,有如下代码:
python 基础知识
当使用 import 语句导入模块时,Python会按照以下步骤检查 sys.modules 中是否已经存在对应的模块:
-
首先,Python会根据 import 语句中指定的模块名在 sys.modules 中查找对应的键。如果该模块名已经存在于 sys.modules 中,说明该模块已经被导入过了。
-
如果找到了对应的模块名,Python会直接使用 sys.modules 中已经缓存的模块对象,而不会重新导入模块。
-
如果未找到对应的模块名,Python会继续执行模块的实际导入操作,并在导入完成后将模块对象添加到 sys.modules 中,以便后续的导入操作可以直接使用缓存的模块对象。
总之,当使用 import 语句导入模块时,Python会先在 sys.modules 中检查是否已经存在对应的模块名,如果存在则直接使用缓存的模块对象,否则才会进行模块的实际导入操作。这个机制可以提高模块导入的效率,并避免重复导入相同的模块。
因为在__init__.py文件中设置了 sys.modules,所以会从 _LazyModule
中导入对应的模块,接下来看一下_LazyModule
是如何根据名称导入模块的,核心代码如下:
python 基础知识
在Python中,调用 getattr()
函数不会直接触发 __getattr__
方法的调用,因为这两者是不同的概念。getattr()
函数用于获取对象的属性值,而 __getattr__
方法用于处理对象属性的访问行为。
然而,在一些情况下,通过 getattr()
获取对象的属性时,如果该属性不存在,会间接触发 __getattr__
方法的调用。具体来说,当使用 getattr()
获取对象的属性时,如果该属性不存在,Python 会尝试从对象的 __getattr__
方法中查找该属性。
下面是一个示例说明了这种情况:
python
class Example:
def __getattr__(self, name):
print(f'Attribute {name} is not found')
obj = Example()
getattr(obj, 'attribute1') # 调用getattr()获取对象属性,会触发__getattr__方法的调用
在上面的示例中,由于attribute1
属性不存在,getattr(obj, 'attribute1')
会触发Example
类中的 __getattr__
方法,并输出 Attribute attribute1 is not found
。
因此,尽管 getattr()
函数本身并不直接触发 __getattr__
方法的调用,但在获取对象的属性时,如果该属性不存在,Python 会尝试从对象的 __getattr__
方法中查找该属性,从而间接触发 __getattr__
方法的调用。
我们看一下 auto 包中的__init__.py
文件做了什么,如下图所示:
在 auto 包中的 init .py 文件中定义了一个 _import_structure
字典,该字典的key对应 auto 包下面的 python 文件名(模块名),字典的 value 对应 python 文件中定义的变量名、函数名、类名,这个字典是用来实现 python 的懒加载的,也就是在python运行过程中可以根据名称导入对应的模块,在后面将详细介绍如何实现的。
通过前面的介绍,总结一下 from transformers import AutoTokenizer
是如何通过懒加载的方式导入 AutoTokenizer
。当从 transformers
模块中导入AutoTokenizer
的时候,会从 sys.modules
字典中找 key 为 transformers
的模块,因为在 transformers
包的 __init__.py
文件中设置了 sys.modules['transformers'] = _LazyModule
并且将懒加载的字典 _import_structure
作为参数传入给_LazyModule
中,所以导入AutoTokenizer
时会调用 _LazyModule
的 __getattr__(name)
方法,传入的name ='AutoTokenizer'
, 执行__getattr__(name)
方法会从 import_structure
字典配置中找到 AutoTokenizer
对应的模块名 models.auto
,然后通过 importlib.import_module("." + module_name, self.__name__)
方式导入 transformers.models.auto
模块,在使用importlib.import_module
导入模块时会执行 transformers.models.auto
模块中的 __init__.py
代码,和 transformers
模块中的 __init__.py
类似,会将 transformers.models.auto
模块设置到 sys.modules
中,导入模块后会调用 getattr(module, name)
获取 transformers.models.auto
的属性 AutoTokenizer
,如果获取不到,则会继续调用 __getattr__(name)
方法从 transformers.models.auto
模块的 __init__.py
文件中配置的 _import_structure
字典中找到 AutoTokenizer
对应的模块 tokenization_auto
,再次通过importlib.import_module("." + module_name, self.__name__)
导入transformers.models.auto
模块中 tokenization_auto
模块,再次调用 getattr(module, name)
获取tokenization_auto
模块中 AutoTokenizer
类,这次可以获取到了,则将 AutoTokenizer
类通过 setattr(self, name, value)
方法设置到 transformers
的AutoTokenizer
属性中,最后返回从 transfomers.auto.models.tokenization_auto
中导入的 AutoTokenizer
类。
用一个简单的流程图描述一下上面的过程:
也许你看完上面描述的这个流程有点绕,建议通过debug的方式,在 _LazyModule
类的 __getattr__(self, name: str)
方法中打上断点进行调试验证,如下图所示:
通过前面的介绍,我们可以知道从 tranformers 模块中导入其他模块对象都是一样的加载流程。
一句话总结 transformers 的懒加载机制:在各个模块的__init__.py文件中定义要导入的类、方法、变量的名称和模块名称的映射,然后将映射字典设置到_LazyModel中,最后将模块名称和_LazyModel设置到sys.modules字典中,导入包的时候会在_LazyModel中通过importlib.import_module
方法递归导入,直到找到对应的模块或者找不到对应的模块而报错。
如何自动加载 tokenizer
前面介绍了如何从 from transformers import AutoTokenizer
导入 AutoTokenizer
,接下来介绍使用 AutoTokenizer.from_pretrained
方法加载 tokenizer。
ini
from transformers import AutoTokenizer
if __name__ == '__main__':
model_path = '/Users/xiniao/models/Qwen/Qwen-7B'
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
print(tokenizer)
AutoTokenizer.from_pretrained
方法的内容比较多,这里只介绍核心流程:
get_tokenizer_config
方法的内容如下:
在 transformers 框架中定义的 Slow tokenizers 和 Fast tokenizers 配置文件的名称如下,在调用 save_pretrained()
保存 tokenizer 时,会保存下面的配置文件,后面会介绍 Slow tokenizers 和 Fast tokenizers的区别
我们可以查看 qwen-7b 中的 tokenizer_config.json
文件的内容如下:
通过前面源码截图的粗略介绍,接下来总结一下 transformers 使用 AutoTokenizer.from_pretrained
加载tokenizer的原理。
AutoTokenizer 首先会读取指定模型目录下的 tokenizer_config.json 文件内容,然后从读取的字典中获取 tokenizer_class 的值,如果 tokenizer_class 不存在,则会从 config.json 文件中读取 tokenizer_class 的值。
然后从tokenizer_config.json配置的字典中读取 auto_map 中的 AutoTokenizer 的值,如果这个值存在,则表示模型自定义了 tokenizer,也就是说需要从模型目录中查找对应的python文件执行文件的代码,这个存在安全问题,所以transformers中强制在加载自定义内容时必须 trust_remote_code=True
否则会报错。
接下来程序会解析自定义的AutoTokenizer对应的字符串值:tokenization_qwen.QWenTokenizer
通过 .
切分字符串,前面的 tokenization_qwen
是python文件的名称(模块名称),后面的 QWenTokenizer
是tokenization_qwen
文件中的类名。在解析自定义 tokenizer 的时候,会做如下操作:
- 将模型目录下的
tokenization_qwen.py
文件复制到~/.cache/huggingface/modules/transformers_modules/Qwen-7B
文件夹中。复制本地文件以避免在 sys.path 中放置太多文件夹,当文件是新的或自上次复制以来已更改时,会进行此复制。为什么将tokenization_qwen.py
复制到~/.cache/huggingface/modules/transformers_modules/Qwen-7B
文件夹中任然能够正确的导入模块,因为在执行复制操作之前,程序执行了init_hf_modules()
方法,该方法会将~/.cache/huggingface/modules
添加到sys.path
中,所以能够正确导入对应的模块。
自定义的 QWenTokenizer
实际上是在 get_class_in_module()
方法中导入的,如下所示:
自定义的 QWenTokenizer
类在哪里实例化的呢?查看源码我们知道自定义的 QWenTokenizer
类继承自 PreTrainedTokenizer
类,PreTrainedTokenizer
类继承自 PreTrainedTokenizerBase
类,如果 QWenTokenizer
没有重写 from_pretrained()
类方法,那么调用 QWenTokenizer.from_pretrained
类方法,则会调用父类 PreTrainedTokenizerBase
中的from_pretrained
类方法,实际上实例化 tokenizer
的代码如下所示:
在实例化 tokenizer 的时候,如何找到词表文件的,实际上是在自定义的 tokenizer 的 vocab_files_names
属性中指定的,如下图所示:
在自定义的 QWenTokenizer
的类属性中指定了 vocab_files_names
的值,所以才能正确加载词表:
经过上面的步骤,终于来到了自定义 tokenizer 的实例化方法中,前面的一系列操作都是为了获取 tokenizer 的配置内容(也就是 tokenizer_config.json
文件中的内容)和词表文件的路径,接下来进行初始化操作,在初始化过程中,主要完成如下内容:
- 调用父类
PreTrainedTokenizer
的__init__()
方法,在其构造方法中主要实现了将tokenizer自定义配置内容中的added_tokens_decoder
添加到_added_tokens_decoder
实例属性中。实现了以统一的方式在所有的tokenizer中添加token,因此我们不必特殊处理各种基础词典结构(BPE、sentencepiece...)的特定词汇扩充方法。
模型 gemma-2b 中的 tokenizer_config.json
的配置如下:
- 调用父类
PreTrainedTokenizer
的父类PreTrainedTokenizerBase
的__init__()
方法,在其构造方法中主要实现了将tokenizer自定义的配置内容设置成它的属性值,如果没有配置的内容,则设置默认值。
- 在
PreTrainedTokenizerBase
中会调用其父类SpecialTokensMixin
的__init__()
方法,在SpecialTokensMixin
的__init__()
方法中会将tokenizer_config.json
配置字典的值设置到specail_token
中,如下图所示:
- 在自定义的
QWenTokenizer
构造方法中加载词表文件对应的 tokenizer,以及设置其他自定义的特殊token。
到这里已经介绍完了创建自定义的 QWenTokenizer
实例的基本流程,因为是通过文字和截图的方式描述的,整个过程可能还是有点不清晰,建议自己通过debug的方式过一遍,这样对整个流程就会更加清晰一点,如果后续有时间,我将录制一个视频简单讲解一下。
默认情况下,AutoTokenizer 类首先加载 Fast tokenizer
Fast tokenizer 和 Slow tokenizer 的区别
Fast tokenizer 和 Slow tokenizer 的区别:
Slow tokenizer 是在 Transformer 库中用Python编写的。
Fast tokenizer 是在 Tokenizers 库中用Rust编写的。
Fast tokenizer 和 Slow tokenizer 分别对 Drug Review Dataset 的分词速度比较如下:
当标记一个句子时,Fast tokenizer 和 Slow tokenizer 分词速度差异不明显,事实上 Fast tokenizer 可能更慢!只有当同时并行标记大量文本时,你才能清楚地看到差异。
tokenizer 如何进行编码
前面粗略地介绍了使用 AutoTokenizer.from_pretrained()
加载自定义 tokenizer 实例的过程,接下来介绍一下 tokenizer 如何进行编码的。
python
from transformers import AutoTokenizer, TensorType
if __name__ == '__main__':
model_path = '/Users/xiniao/models/Qwen/Qwen-7B'
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
print(tokenizer)
inputs = tokenizer('蒙古国的首都是乌兰巴托(Ulaanbaatar)\n冰岛的首都是雷克雅未克(Reykjavik)\n埃塞俄比亚的首都是', return_tensors=TensorType.PYTORCH)
print(inputs)
prompt = tokenizer.decode(inputs['input_ids'][0])
print(prompt)
当调用 tokenizer('prompt')
时,实际的调用方法栈如下图:
调用 tokenizer(['prompt1', 'prompt2'])
时,实际的调用方法栈如下图:
接下来我们重点看一下tokenizer是怎样对输入文本进行编码的,主要实现是在 _encode_plus()
和 _batch_encode_plus()
方法中,在它们的方法中定义了一个函数 get_input_ids()
,在 get_input_ids()
方法中调用了 tokenize()
和 convert_tokens_to_ids()
方法分别进行分词和将分词后的 token 转换为字典表对应的 id,这两个方法通常可以在自定义的 tokenizer 中实现。
因为 tokenize()
和 convert_tokens_to_ids()
方法在 PreTrainedTokenizer
中本质上调用的是 _tokenize()
和 _convert_tokens_to_ids()
方法,并且这两个方法在PreTrainedTokenizer
中都没有实现,所以在自定义的 tokenizer 中需要实现_tokenize()
和 _convert_tokens_to_ids()
方法。
可以看到 QWenTokenizer
中实现了 _tokenize()
和 _convert_tokens_to_ids()
方法,并且重写了tokenize()
和 convert_tokens_to_ids()
方法:
总结:通过前面的介绍我们简单总结一下 tokenizer 如何进行编码的,主要是在自定义的 tokenizer 中实现了 tokenize()
和 convert_tokens_to_ids()
或者 _tokenize()
和 _convert_tokens_to_ids()
方法 ,这两个方法是用来对输入的文本分词和转换为字典表的 id ,然后返回对应的 id 列表。在获取到输入文本对应的 id 列表后,在prepare_for_model()
方法中需要对这个 id 列表进行截断(truncate)和 填充(pad),最后构造为模型推理和训练需要的字典参数返回。
关于tokenizer 编码过程中截断(truncate)和 填充(pad)这里不再详细介绍,详细的内容可以查看huggingface 对应的文档 Padding and truncation
tokenizer 如何进行解码
前面简单介绍了 tokenizer 如何对输入文本进行编码,接下来介绍一下如何使用 tokenizer.decode()
对输入的 token_id 列表进行解码,还原为原来的文本内容:
python
from transformers import AutoTokenizer, TensorType
if __name__ == '__main__':
prompts = ['蒙古国的首都是乌兰巴托(Ulaanbaatar)', '冰岛的首都是雷克雅未克(Reykjavik)', '埃塞俄比亚的首都是']
model_path = '/Users/xiniao/models/Qwen/Qwen-7B'
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
print(tokenizer)
inputs = tokenizer(prompts, return_tensors=TensorType.PYTORCH, padding=True)
print(inputs)
prompt = tokenizer.decode(inputs['input_ids'][0])
print(prompt)
通过debug的方式查看源码如下,实际调用 PreTrainedTokenizerBase
类的 decode()
:
在 QWenTokenizer
中实现了 _decode()
方法,所以对于 QWenTokenizer
来说,会调用自定义的_decode()
方法完成解码。
对于没有实现 _decode()
方法的 tokenizer 来说,会按照如下的调用过程完成解码:
- 调用
PreTrainedTokenizer
类的_decode()
方法 - 调用
convert_ids_to_tokens()
方法,如果子类重写了该方法,则调用子类重写的该方法 - 调用
_convert_id_to_token()
方法(在convert_ids_to_tokens()
内部),如果子类重写了该方法,则调用子类重写的该方法 - 调用
convert_tokens_to_string()
方法,如果子类重写了该方法,则调用子类重写的该方法
在 LlamaTokenizer
中重写了 _convert_id_to_token()
和 convert_tokens_to_string()
来完成自定义的解码。
如果我们需要自定义解码逻辑,重写下面介绍的几个方法即可:
-
_decode():将 token_id 列表还原为字符串
-
convert_ids_to_tokens():将 token_id 列表还原为 token 列表
-
_convert_id_to_token():在 在
convert_ids_to_tokens()
内部调用 -
convert_tokens_to_string():将 token 列表还原为字符串列表
tokenizer 如何使用对话模板
tokenizer 对话模板详细使用可以查看 huggingface 官方文档:Templates for Chat Models
大语言模型最常用的功能是聊天,我们在使用模型进行chat的时候,需要使用对话模版,不同的模型有不同的对话模板,推理时的对话模型需要和模型训练的对话模板保持一致。
在引入chat_template
之前,聊天模板的处理是在模型类级别进行硬编码的,通过手动的方式拼接不同模型的对话模版,为了解决手动拼接对话模版的问题,transformers 的 PreTrainedTokenizerBase
类引入了一个chat_template
属性,该属性配置 jinja
模版,通过该模版可以自动将具有" role
"和" content
"键的 Conversation
对象或字典列表转换为对话模版字符串。
为了向后兼容,如果模型没有设置chat_template
,但其模型类有default_chat_template
属性,则调用tokenizer.apply_chat_template()
将使用类default_chat_template
配置的模板,可以通过tokenizer.default_chat_template
属性来查看tokenizer的默认模板是什么。
下面使用一个例子来说明如何使用 tokenizer 的对话模板:
python
from transformers import AutoTokenizer, TensorType
if __name__ == '__main__':
model_path = '/Users/xiniao/models/Qwen/Qwen-7B'
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
tokenizer.chat_template = "{% for message in messages %}{% if loop.first and messages[0]['role'] != 'system' %}{{ '<|im_start|>system\nYou are a helpful assistant<|im_end|>\n' }}{% endif %}{{'<|im_start|>' + message['role'] + '\n' + message['content']}}{% if (loop.last and add_generation_prompt) or not loop.last %}{{ '<|im_end|>' + '\n'}}{% endif %}{% endfor %}{% if add_generation_prompt and messages[-1]['role'] != 'assistant' %}{{ '<|im_start|>assistant\n' }}{% endif %}"
chat = [
{"role": "user", "content": "你好,你好吗?"},
{"role": "assistant", "content": "我很好,需要什么帮助吗?"},
{"role": "user", "content": "我想了解聊天模板的工作原理!"},
]
chat_template = tokenizer.apply_chat_template(chat, tokenize=False)
print(chat_template)
# 输出内容:
# <|im_start|>system
# You are a helpful assistant<|im_end|>
# <|im_start|>user
# 你好,你好吗?<|im_end|>
# <|im_start|>assistant
# 我很好,需要什么帮助吗?<|im_end|>
# <|im_start|>user
# 我想了解聊天模板的工作原理!
如上代码所示,通过给 tokenizer 设置 chat_template
属性,然后调用 apply_chat_template()
方法就可以自动生成对话模版字符串。接下来说明一下 apply_chat_template()
方法的作用和各个参数的含义:
方法的作用:
将Conversation对象或带有"role
"和"content
"键的字典列表转换为token id列表或者对话模版字符串,该方法旨在与聊天模型一起使用,并将读取 tokenizer 的 chat_template
属性以确定在转换时要使用的格式和控制标记,当chat_template
为 None
时,它将退回到类级别指定的 default_chat_template
。
方法的参数说明:
conversation (Union[List[Dict[str, str]], "Conversation"])
: 表示对话的内容,可以是一个Conversation
对象或者是一个包含了字典的列表,每个字典包含了 "role" 和 "content" 键来表示对话的历史。这个参数用于表示到目前为止的聊天记录。chat_template (str, optional)
: 表示对话的模板,是一个字符串。如果不传入这个参数,将会使用模型的默认对话模板。add_generation_prompt (bool, optional)
: 表示是否在提示(prompt)的末尾添加指示开始助手消息的标记(token)。这在你想要生成模型的回复时非常有用。注意,这个参数将被传递给对话模板,因此对话模板必须支持这个参数才能起作用。tokenize (bool, defaults to True)
: 表示是否对输出进行分词(tokenization)。如果为False
,输出将是一个字符串。padding (bool, defaults to False)
: 表示是否对序列进行填充(padding)到最大长度。如果tokenize
为False
,则此参数无效。truncation (bool, defaults to False)
: 表示是否对序列进行截断(truncation)到最大长度。如果tokenize
为False
,则此参数无效。max_length (int, optional)
: 表示填充或截断的最大长度(以标记数计算)。如果未指定,将使用分词器的max_length
属性作为默认值。return_tensors (str or TensorType, optional)
: 如果设置,将返回特定框架(framework)的张量。如果tokenize
为False
,此参数无效。可接受的值包括:
-
'tf'
: 返回 TensorFlowtf.Tensor
对象。'pt'
: 返回 PyTorchtorch.Tensor
对象。'np'
: 返回 NumPynp.ndarray
对象。'jax'
: 返回 JAXjnp.ndarray
对象。
return_dict (bool, optional, defaults to False)
: 表示是否返回具有命名输出的字典。如果tokenize
为False
,此参数无效。**tokenizer_kwargs
: 附加的关键字参数,用于传递给分词器。
设置tokenizer 的 chat_template
的方式和优先级(从高到低)如下:
- 在
apply_chat_template()
方法的chat_template
参数中设置 - 在
tokenizer
的chat_template
属性中设置 - 在
tokenizer_config.json
配置文件中chat_template
属性中设置 - 在
tokenizer
的default_chat_template
属性中设置
参考文章