AI大模型探索之路-训练篇10:大语言模型Transformer库-Tokenizer组件实践

系列篇章💥

AI大模型探索之路-训练篇1:大语言模型微调基础认知
AI大模型探索之路-训练篇2:大语言模型预训练基础认知
AI大模型探索之路-训练篇3:大语言模型全景解读
AI大模型探索之路-训练篇4:大语言模型训练数据集概览
AI大模型探索之路-训练篇5:大语言模型预训练数据准备-词元化
AI大模型探索之路-训练篇6:大语言模型预训练数据准备-预处理
AI大模型探索之路-训练篇7:大语言模型Transformer库之HuggingFace介绍
AI大模型探索之路-训练篇8:大语言模型Transformer库-预训练流程编码体验
AI大模型探索之路-训练篇9:大语言模型Transformer库-Pipeline组件实践


目录


前言

在自然语言处理(NLP)的世界里,文本数据的处理和理解是至关重要的一环。为了使得计算机能够理解和处理人类的自然语言,我们需要将原始的、对人类可读的文本转化为机器可以理解的格式。这就是Tokenizer,或者我们常说的分词器,发挥作用的地方。

一、Tokenizer概览

官网API地址:https://huggingface.co/docs/transformers/main_classes/tokenizer
Tokenizer是自然语言处理中的一个核心组件,它的主要功能是将原始文本转换为机器学习模型能够处理的格式。这一过程看似简单,实则包含了许多复杂且精细的步骤。在深度学习中的Transformer架构及其衍生模型中,Tokenizer的工作流程通常包括两个关键步骤:

1)首先,是文本分解。这一步的目的是将原始的、连续的文本分割成更细的粒度单元,这些单元可以是单词级别,也可以是子词级别,甚至是字符级别。这一步骤的目标是将文本分解为可以被模型理解并处理的基本单元。

2)其次,是编码映射。这一步的目标是将这些基本单元转换为模型可以理解的数值形式,最常见的形式是整数序列。这样,我们就可以将这些数值输入到模型中,让模型进行学习和预测。

在接下来的内容中,我们将详细探讨Tokenizer的工作原理,以及如何在实际的自然语言处理任务中使用Tokenizer。

二、Tokenizer的工作原理

Tokenizer的工作原理涉及:
1)文本分解 :将文本分解为更小的单元。
2)词汇表 :使用词汇表将文本单元映射到数值ID。
3)特殊标记 :添加如CLSSEP等特殊标记,以适应模型的特定需求。

在序列标注任务中,特殊标记帮助模型识别序列的开始和结束。

python 复制代码
# 展示特殊标记的添加
sequence = "Here is an example sequence."
encoded_sequence = tokenizer(sequence, add_special_tokens=True)
print(encoded_sequence)

三、Tokenizer的使用方法

Tokenizer的使用流程一般遵循以下步骤:
1)导入Tokenizer库 :从NLP库(例如Hugging Face的transformers)导入Tokenizer类。
2)加载预训练Tokenizer :通过指定模型名称加载预训练的Tokenizer实例。
3)文本转换 :将文本数据输入Tokenizer进行编码转换。
4)获取编码输出 :Tokenizer输出编码后的数据,通常包括:

-输入ID:转换后的整数序列,用于模型输入。

-注意力掩码(Attention Mask):标识哪些输入ID是有效内容,哪些是填充(padding)。

-类别ID(Token Type IDs):在某些任务中区分句子对的两个不同句子。

代码示例:

下面是一个使用Tokenizer的代码示例:

python 复制代码
from transformers import AutoTokenizer

# 加载预训练的Tokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")

# 待处理的文本
text = "Transformers are the core of modern NLP tasks."

# 使用Tokenizer进行编码
encoded_input = tokenizer(text, return_tensors='pt')

# 访问编码结果
input_ids = encoded_input['input_ids']
attention_mask = encoded_input['attention_mask']

Tokenizer的基本使用

python 复制代码
from transformers import AutoTokenizer
sen = "吃葡萄不吐葡萄皮!"

1、加载与保存

1)加载模型

python 复制代码
# 从HuggingFace加载,输入模型名称,即可加载对于的分词器
tokenizer = AutoTokenizer.from_pretrained("uer/roberta-base-finetuned-dianping-chinese")
tokenizer

输出结果:

bash 复制代码
BertTokenizerFast(name_or_path='uer/roberta-base-finetuned-dianping-chinese', vocab_size=21128, model_max_length=1000000000000000019884624838656, is_fast=True, padding_side='right', truncation_side='right', special_tokens={'unk_token': '[UNK]', 'sep_token': '[SEP]', 'pad_token': '[PAD]', 'cls_token': '[CLS]', 'mask_token': '[MASK]'}, clean_up_tokenization_spaces=True),  added_tokens_decoder={
	0: AddedToken("[PAD]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	100: AddedToken("[UNK]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	101: AddedToken("[CLS]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	102: AddedToken("[SEP]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	103: AddedToken("[MASK]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
}

2)保存模型

python 复制代码
# tokenizer 保存到本地
tokenizer.save_pretrained("./roberta_tokenizer")
('./roberta_tokenizer/tokenizer_config.json',
 './roberta_tokenizer/special_tokens_map.json',
 './roberta_tokenizer/vocab.txt',
 './roberta_tokenizer/added_tokens.json',
 './roberta_tokenizer/tokenizer.json')

会自动在同层级目录roberta_tokenizer中存放下载下来的模型

3)从本地加载模型

python 复制代码
# 从本地加载tokenizer
tokenizer = AutoTokenizer.from_pretrained("./roberta_tokenizer/")
tokenizer

输出结果:

bash 复制代码
BertTokenizerFast(name_or_path='./roberta_tokenizer/', vocab_size=21128, model_max_length=1000000000000000019884624838656, is_fast=True, padding_side='right', truncation_side='right', special_tokens={'unk_token': '[UNK]', 'sep_token': '[SEP]', 'pad_token': '[PAD]', 'cls_token': '[CLS]', 'mask_token': '[MASK]'}, clean_up_tokenization_spaces=True),  added_tokens_decoder={
	0: AddedToken("[PAD]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	100: AddedToken("[UNK]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	101: AddedToken("[CLS]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	102: AddedToken("[SEP]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	103: AddedToken("[MASK]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
}

2、句子分词

python 复制代码
tokens = tokenizer.tokenize(sen)
tokens

输出:

bash 复制代码
['吃', '葡', '萄', '不', '吐', '葡', '萄', '皮', '!']

3、查看词典

python 复制代码
tokenizer.vocab

输出如下

查看词典大小

python 复制代码
tokenizer.vocab_size

21128

4、索引转换

1)将词序列转换为id序列

python 复制代码
ids = tokenizer.convert_tokens_to_ids(tokens)
ids

输出:

bash 复制代码
[1391, 5868, 5843, 679, 1402, 5868, 5843, 4649, 106]

2)将id序列转换为token序列

python 复制代码
tokens = tokenizer.convert_ids_to_tokens(ids)
tokens

输出:

bash 复制代码
['吃', '葡', '萄', '不', '吐', '葡', '萄', '皮', '!']

3)将token序列转换为string

python 复制代码
str_sen = tokenizer.convert_tokens_to_string(tokens)
str_sen

输出:

bash 复制代码
'吃 葡 萄 不 吐 葡 萄 皮!'

4)更便捷的实现方式

将字符串转换为id序列,又称之为编码

python 复制代码
ids = tokenizer.encode(sen, add_special_tokens=True)
ids

输出:

bash 复制代码
[101, 1391, 5868, 5843, 679, 1402, 5868, 5843, 4649, 106, 102]

将id序列转换为字符串,又称之为解码

python 复制代码
str_sen = tokenizer.decode(ids, skip_special_tokens=False)
str_sen

输出:

bash 复制代码
'[CLS] 吃 葡 萄 不 吐 葡 萄 皮! [SEP]'

5、填充与截断

1)填充

python 复制代码
ids = tokenizer.encode(sen, padding="max_length", max_length=15)
ids

输出:

bash 复制代码
[101, 1391, 5868, 5843, 679, 1402, 5868, 5843, 4649, 106, 102, 0, 0, 0, 0]

2)截断

python 复制代码
ids = tokenizer.encode(sen, max_length=5, truncation=True)
ids

输出:

bash 复制代码
[101, 1391, 5868, 5843, 102]

6、其他输入部分

python 复制代码
ids = tokenizer.encode(sen, padding="max_length", max_length=15)
ids

输出:

bash 复制代码
[101, 1391, 5868, 5843, 679, 1402, 5868, 5843, 4649, 106, 102, 0, 0, 0, 0]

查看其他部分内容

python 复制代码
attention_mask = [1 if idx != 0 else 0 for idx in ids]
token_type_ids = [0] * len(ids)
ids, attention_mask, token_type_ids

输出:

bash 复制代码
([101, 1391, 5868, 5843, 679, 1402, 5868, 5843, 4649, 106, 102, 0, 0, 0, 0],
 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])

7、快速调用方式

1)简化调用
简化调用 :这是Tokenizer对象的直接调用,它通常是一个简化的方法,提供了基本的编码功能。
参数限制 :此方法的参数选项可能较少,只包括一些常用的参数,如padding和max_length。
适用场景:适用于大多数标准情况,当需要执行常规的编码任务时,可以使用此方法。

python 复制代码
inputs = tokenizer.encode_plus(sen, padding="max_length", max_length=15)
inputs

输出:

bash 复制代码
{'input_ids': [101, 1391, 5868, 5843, 679, 1402, 5868, 5843, 4649, 106, 102, 0, 0, 0, 0], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0]}

2)增强调用
增强功能 :encode_plus方法提供了更多的功能和更细粒度的控制,包括对分词、编码、填充、截断等过程的额外配置。
返回值 :encode_plus方法通常返回一个字典,包含了一系列的输出,如输入ID、注意力掩码、标记类型ID等,这些输出可以直接用于模型的输入。
参数丰富 :此方法允许用户指定更多的参数,如return_tensors(指定返回张量类型)、return_token_type_ids(返回标记类型ID)、return_attention_mask(返回注意力掩码)等。
适用场景:当你需要更细致地控制文本编码过程,或者需要额外的信息(如注意力掩码或标记类型ID)时,使用encode_plus方法。

python 复制代码
inputs = tokenizer(sen, padding="max_length", max_length=15)
inputs

输出:

bash 复制代码
{'input_ids': [101, 1391, 5868, 5843, 679, 1402, 5868, 5843, 4649, 106, 102, 0, 0, 0, 0], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0]}

8、处理batch数据

python 复制代码
sens = ["吃葡萄不吐葡萄皮",
        "不吃葡萄到吐葡萄皮",
        "顺势而为"]
res = tokenizer(sens)
res

输出:

bash 复制代码
{'input_ids': [[101, 1391, 5868, 5843, 679, 1402, 5868, 5843, 4649, 102], [101, 679, 1391, 5868, 5843, 1168, 1402, 5868, 5843, 4649, 102], [101, 7556, 1232, 5445, 711, 102]], 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]]}

批处理可以很大层度提升我们的处理性能

python 复制代码
%%time
# 单条循环处理
for i in range(1000):
    tokenizer(sen)

CPU times: user 45.3 ms, sys: 0 ns, total: 45.3 ms
Wall time: 44.6 ms

python 复制代码
%%time
# 处理batch数据
res = tokenizer([sen] * 1000)

CPU times: user 27.7 ms, sys: 15.6 ms, total: 43.2 ms
Wall time: 7.68 ms

四、Fast/Slow Tokenizer

在Hugging Face的transformers库中,Tokenizer分为两种类型:Fast Tokenizer和Slow Tokenizer。
1)Slow Tokenizer :通常是用Python编写的,速度较慢,但在所有环境中都能保证一致性和可移植性。
2)Fast Tokenizer:使用Rust编写,并通过PyTorch的C++扩展或Python的C扩展提供,速度非常快,尤其是在处理大量数据时。Fast Tokenizers提供了与Slow Tokenizers相同的功能,但速度更快。

选择使用哪种Tokenizer取决于具体的需求。如果对性能要求极高,或者需要处理大量数据,推荐使用Fast Tokenizer。如果需要确保代码的可移植性,或者在性能要求不是非常关键的场景下,可以使用Slow Tokenizer。

在transformers库中,AutoTokenizer类会自动选择Fast Tokenizer (如果可用),以提供最佳性能。如果需要显式选择Tokenizer类型,可以使用模型的特定Tokenizer类,如BertTokenizer或RobertaTokenizer。

fast_tokenizer 使用查看

python 复制代码
sen = "吃葡萄不吐葡萄皮!"
fast_tokenizer = AutoTokenizer.from_pretrained("uer/roberta-base-finetuned-dianping-chinese")
fast_tokenizer

输出结果:

bash 复制代码
BertTokenizerFast(name_or_path='uer/roberta-base-finetuned-dianping-chinese', vocab_size=21128, model_max_length=1000000000000000019884624838656, is_fast=True, padding_side='right', truncation_side='right', special_tokens={'unk_token': '[UNK]', 'sep_token': '[SEP]', 'pad_token': '[PAD]', 'cls_token': '[CLS]', 'mask_token': '[MASK]'}, clean_up_tokenization_spaces=True),  added_tokens_decoder={
	0: AddedToken("[PAD]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	100: AddedToken("[UNK]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	101: AddedToken("[CLS]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	102: AddedToken("[SEP]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	103: AddedToken("[MASK]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
}

slow_tokenizer 使用查看

python 复制代码
slow_tokenizer = AutoTokenizer.from_pretrained("uer/roberta-base-finetuned-dianping-chinese", use_fast=False)
slow_tokenizer
bash 复制代码
BertTokenizer(name_or_path='uer/roberta-base-finetuned-dianping-chinese', vocab_size=21128, model_max_length=1000000000000000019884624838656, is_fast=False, padding_side='right', truncation_side='right', special_tokens={'unk_token': '[UNK]', 'sep_token': '[SEP]', 'pad_token': '[PAD]', 'cls_token': '[CLS]', 'mask_token': '[MASK]'}, clean_up_tokenization_spaces=True),  added_tokens_decoder={
	0: AddedToken("[PAD]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	100: AddedToken("[UNK]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	101: AddedToken("[CLS]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	102: AddedToken("[SEP]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	103: AddedToken("[MASK]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
}

fast_tokenizer 批量执行耗时

python 复制代码
%%time
# 处理batch数据
res = fast_tokenizer([sen] * 10000)

CPU times: user 323 ms, sys: 146 ms, total: 468 ms
Wall time: 172 ms

slow_tokenizer 批量执行耗时

python 复制代码
%%time
# 处理batch数据
res = slow_tokenizer([sen] * 10000)

CPU times: user 1.1 s, sys: 15.8 ms, total: 1.12 s
Wall time: 1.12 s

五、自定义Tokenizer

用户可以根据特定需求定制Tokenizer:
1)自定义词汇表 :创建特定领域的词汇表。
2)自定义规则:添加自定义分词规则以适应特定场景。

实践案例 :在医疗领域的文本处理中,自定义Tokenizer能够识别专业术语。
工具和资源:Hugging Face的transformers库允许用户通过继承和修改现有Tokenizer类来创建自定义Tokenizer。

代码样例:

python 复制代码
from transformers import BertTokenizerFast

class CustomBertTokenizer(BertTokenizerFast):
    def __init__(self, vocab_file, **kwargs):
        super().__init__(vocab_file=vocab_file, **kwargs)
        # 自定义逻辑...

# 假设已有自定义词汇表
custom_tokenizer = CustomBertTokenizer(vocab_file="path_to_vocab.txt")
encoded_custom = custom_tokenizer("Customizing Tokenizer is flexible.", return_tensors="pt")
print(encoded_custom)

六、Tokenizer与模型训练

Tokenizer在模型训练中的作用包括:
1)数据预处理 :将训练数据转换为模型可处理的格式。
2)与模型整合:确保Tokenizer与模型的输入层完全兼容。

实践案例 :在训练一个自定义文本分类模型时,需要确保Tokenizer的输出与模型的输入层匹配。
工具和资源:使用PyTorch或TensorFlow框架,可以方便地将Tokenizer集成到模型训练流程中。

代码样例:

python 复制代码
# 导入必要的类:从transformers库中导入BertForSequenceClassification(用于序列分类的BERT模型),Trainer(训练器类),和TrainingArguments(训练参数类)
from transformers import BertForSequenceClassification, Trainer, TrainingArguments

#初始化模型:使用BertForSequenceClassification类创建一个序列分类模型实例。这个模型是基于BERT的,并且是预训练好的,我们通过from_pretrained方法加载它。num_labels参数指定了分类任务的标签数量。
model = BertForSequenceClassification.from_pretrained("bert-base-uncased", num_labels=2)

# 准备数据集
# ...
#定义训练参数:TrainingArguments类用于定义训练过程中的各种参数,如输出目录output_dir,训练轮数num_train_epochs,每个设备的训练批次大小per_device_train_batch_size,预热步数warmup_steps,权重衰减weight_decay,以及日志目录logging_dir。
training_args = TrainingArguments(
    output_dir="./results",
    num_train_epochs=3,
    per_device_train_batch_size=16,
    warmup_steps=500,
    weight_decay=0.01,
    logging_dir="./logs",
)
# 初始化Trainer:Trainer类负责执行模型的实际训练。我们传入模型实例、训练参数和Tokenizer。train_dataset是一个包含训练数据的PyTorch数据集对象,这里省略了其定义和准备过程。
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    tokenizer=tokenizer
)
#执行训练:调用trainer.train()方法开始训练
trainer.train()

总结

Tokenizer是Transformer模型不可或缺的一部分,它直接影响模型输入的质量和模型的性能。正确选择和使用Tokenizer对于实现高效的NLP任务至关重要。通过上述实践,我们可以看到Tokenizer不仅需要适应特定的模型架构,还要满足特定任务的需求,并考虑到性能优化和可定制性。

🎯🔖更多专栏系列文章:AIGC-AI大模型探索之路

如果文章内容对您有所触动,别忘了点赞、⭐关注,收藏!加入我,让我们携手同行AI的探索之旅,一起开启智能时代的大门!

相关推荐
大刚测试开发实战1 小时前
TestHub V0.2.2版本发布,附更新指南
人工智能
冬奇Lab3 小时前
Agent 系列(21):Harness 测试工程——45 个测试怎么设计,以及它发现了什么 bug
人工智能·llm·agent
冬奇Lab3 小时前
每日一个开源项目(第133篇):EchoBird - 把 AI 工具的安装和部署做成傻瓜操作
人工智能·开源·资讯
程序员龙叔3 小时前
编写高质量 Skill 系列 -- 如何设计需求分析与用例生成的 SKILL
自动化测试·软件测试·python·软件测试工程师·接口测试·性能测试·skill·ai测试
IT_陈寒4 小时前
Redis的SETNX并发问题让我加了三天班
前端·人工智能·后端
用户5191495848456 小时前
Windows 渗透测试载荷加载器 POC 工具集
人工智能·aigc
袋鱼不重6 小时前
我的神奇同事,AI 用多了居然写了个 Open In Codex
前端·后端·ai编程
大树886 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
用户8356290780516 小时前
使用 Python 操作 Word 内容控件
后端·python
量子位6 小时前
刚刚,Fable-5之下,智谱开源的GLM-5.2拿下AI编程第一!
ai编程