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

【13】Transformers快速入门:Transformers 分词器 (Tokenizer) 实战?


课程模块:Transformers 分词器 (Tokenizer) 实战

目标: 理解为什么需要分词器,掌握常见分词策略的区别,学会加载、保存分词器,并能熟练使用分词器对文本进行编码和解码。

核心概念: 分词 (Tokenization), 编码 (Encoding), 解码 (Decoding), 词表 (Vocabulary), 子词切分 (Subword Tokenization), AutoTokenizer, from_pretrained, save_pretrained, tokenize, convert_tokens_to_ids, encode, decode, 特殊 Token ([CLS], [SEP], [UNK])


1. 为什么需要分词器?—— 让文本“说”模型能懂的语言

想象一下,你是一位精通多种语言的翻译官(模型),但你的大脑只能处理数字(神经网络)。现在,有人递给你一段英文文本(比如 “I love transformers!”),你需要把它翻译成法语。但问题来了:你的“数字大脑”看不懂英文单词!

这就是分词器 (Tokenizer) 的作用!它就像一位专业的文本预处理助手,负责:

  1. “切分”文本: 把一串连续的文本(句子、段落)拆分成更小的、有意义的片段,称为 Tokens。这就像把一整块面包切成方便入口的小片。
  2. “翻译”成数字: 把这些 Tokens 转换成模型能理解的数字——Token IDs。这就像给每个面包片贴上一个唯一的编号标签。

这个过程合起来就叫编码 (Encoding)文本 -> Tokens -> Token IDs

核心任务: 分词器是连接人类语言和模型数字世界的桥梁! 没有它,再强大的模型也无法理解我们输入的文字。


2. 怎么“切”?—— 揭秘分词策略

就像切面包可以用刀、锯齿刀或面包刀一样,分词也有不同的策略,各有优劣:

  • 策略一:按词切分 (Word-based)

    • 做法: 最简单粗暴!直接按空格、标点把文本切成一个个单词。
      text = "Jim Henson was a puppeteer"
      tokens = text.split()  # ['Jim', 'Henson', 'was', 'a', 'puppeteer']
      
    • 优点: 直观,容易理解。
    • 致命缺点:
      • 词表爆炸: 每个单词(包括不同形式如 “dog”, “dogs”, “running”)都算作不同的 token,词表会变得巨大无比(几十万甚至上百万),模型难以处理。
      • 无法处理未知词: 遇到词表里没有的词(比如拼写错误、罕见词、新词),只能用 [UNK] (unknown) 表示,丢失信息。想象一下,翻译官遇到不认识的词只能说“我不知道”,这翻译就没法做了!
      • 忽略词间关系: “dog” 和 “dogs” 明明有关联,却被当作完全不同的东西。
  • 策略二:按字符切分 (Character-based)

    • 做法: 精细到极致!把文本切成一个个字符(字母、标点、甚至空格)。
      text = "Dog"
      tokens = list(text)  # ['D', 'o', 'g'] (英文) 或 ['狗'] (中文单字)
      
    • 优点:
      • 词表超小: 英文就几十个字符,中文几千个常用字。
      • 几乎无 [UNK] 字符组合总能拼出新词。
    • 致命缺点:
      • 失去语义单元: 单个字符(尤其是英文字母)通常没有明确含义。'D''o''g' 单独看谁知道是“狗”?模型需要从更零散的碎片中学习语义,难度剧增。
      • 序列超长: 一个单词变成多个 token,一个句子就变成超长序列,模型处理效率低。想象翻译官要处理“D-o-g”三个信息点才能理解“狗”,太累了!
  • 策略三(明星策略):按子词切分 (Subword Tokenization)

    • 核心思想: 高频词保留,低频词拆分! 找一个平衡点。
    • 做法:
      • 高频词(如 “the”, “is”, “dog”)直接作为独立的 token。
      • 低频词或复杂词(如 “annoyingly”, “tokenization”)被拆分成更小的、有意义的子词(如 “annoying” + “ly”, “token” + “ization”)。
    • 优点(完美解决前两种的问题):
      • 词表大小适中: 通常在几万到十几万,既不会爆炸,也能覆盖绝大多数文本。
      • 极少 [UNK] 即使遇到新词,也能拆分成已知的子词组合理解其意(比如 “Bumblebee” 可能拆成 “Bumble” + “bee”)。
      • 保留语义和效率: 子词通常有意义(如 “ly” 表示副词,“ization” 表示名词化),模型更容易学习。同时,一个长词只用几个 token 表示,序列长度合理。
      • 跨语言友好: 尤其擅长处理像土耳其语、芬兰语这种靠拼接词根形成复杂长词的“黏着语”。
    • Transformer 模型的最爱: BERT、GPT、T5 等主流模型都使用子词切分策略(如 Byte-Pair Encoding - BPE, WordPiece, SentencePiece)。
    • 例子: “Let’s do tokenization!” 可能被切分为 ['Let', "'", 's', 'do', 'token', 'ization', '!']。你看,“tokenization” 被优雅地拆成了常见的 “token” 和 “ization”。

结论: 子词切分是现代 Transformer 模型的标准分词策略! 它聪明地平衡了词表大小、语义保留和处理效率。


3. 请出助手:加载与保存分词器

和加载模型一样,transformers 库提供了两种加载分词器的方式:

  • 方式一:指名道姓 (BertTokenizer, GPT2Tokenizer 等)
    from transformers import BertTokenizer
    bert_tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
    
  • 方式二:智能匹配 (AutoTokenizer - 强烈推荐!)
    from transformers import AutoTokenizer
    # 加载 BERT 分词器
    tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
    # 想换 GPT-2?只需改名字!
    gpt2_tokenizer = AutoTokenizer.from_pretrained("gpt2")
    
    为什么推荐 AutoTokenizerAutoModel 一样,它让代码更灵活!切换模型时,只需改 checkpoint 名字,分词器代码无需改动。

加载来源:

  • 云端 Hugging Face Hub (需网络): from_pretrained("checkpoint_name")
  • 本地目录 (推荐): from_pretrained("./path/to/tokenizer/")

保存分词器:
保存和模型一样简单,使用 .save_pretrained()

tokenizer.save_pretrained("./my_saved_tokenizers/bert-base-cased/")

保存了什么?
保存目录下会生成几个关键文件:

  1. tokenizer_config.json 分词器的“说明书”。记录了它是哪种类型的分词器(如 WordPiece),以及它的配置参数(比如特殊 token 是什么)。
  2. vocab.txt (或 vocab.json 等): 词表文件! 这是核心!里面列出了所有 token,一行一个 token。行号就是该 token 的 ID(从 0 开始)。模型就是靠这个文件把 token 变成数字 ID 的。
  3. special_tokens_map.json 特殊 Token 的“花名册”。记录了 [UNK] (未知词), [CLS] (分类), [SEP] (分隔), [PAD] (填充), [MASK] (掩码) 等特殊 token 对应的实际字符串是什么(比如 [UNK] 可能对应 "<unk>")。

重要提示: 分词器文件和模型文件是分开的!通常你需要同时保存(或下载)模型对应的分词器。


4. 实战操作:编码与解码文本

现在,让我们请出 AutoTokenizer 助手,看看它如何把文本变成数字,以及如何把数字变回文本。

  • 步骤一:加载分词器

    from transformers import AutoTokenizer
    tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")  # 例子用 BERT
    
  • 步骤二:分词 (Tokenize) - 切成 Tokens

    sequence = "Using a Transformer network is simple"
    tokens = tokenizer.tokenize(sequence)
    print(tokens)  # 输出:['Using', 'a', 'Transform', '##er', 'network', 'is', 'simple']
    

    观察:

    • “Transformer” 被切成了 'Transform''##er'。这是 BERT 使用的 WordPiece 算法的标志:
      • '##' 表示这个 token 是前面 token 的一部分(后缀),需要和前面的 token 拼接起来才能组成完整单词。
    • 分词器自动处理了大小写(“Using” 保持大写,因为用的是 bert-base-cased)。
  • 步骤三:映射为 ID (Convert Tokens to IDs)

    ids = tokenizer.convert_tokens_to_ids(tokens)
    print(ids)  # 输出:[7993, 170, 13809, 23763, 2443, 1110, 3014]
    

    原理: 分词器拿着每个 token (如 'Using'),去查它的词表文件 vocab.txt,找到 'Using' 在第几行(比如第 7993 行),那么它的 ID 就是 7993。

  • 步骤四:一步到位编码 (Encode) - 更常用!

    encoded_output = tokenizer(sequence)  # 或者 tokenizer.encode(sequence, ...)
    print(encoded_output)
    # 输出:
    # {
    #   'input_ids': [101, 7993, 170, 13809, 23763, 2443, 1110, 3014, 102],
    #   'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0],
    #   'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]
    # }
    

    tokenizer() 的强大之处:

    • 自动完成了分词 + 映射 ID
    • 自动添加了模型需要的特殊 token
      • 101 ([CLS]):通常放在序列开头,用于分类任务。
      • 102 ([SEP]):通常放在序列结尾,或用于分隔两个句子(如问答)。
    • 返回了模型需要的所有输入
      • input_ids:Token IDs 列表,是模型最主要的输入。
      • token_type_ids (或 segment_ids):用于区分句子。例如,第一个句子全为 0,第二个句子全为 1。单句任务通常全为 0。
      • attention_mask:告诉模型哪些位置是真实的 token (1),哪些是填充的位置 (0)。这里没有填充,所以全是 1。
    • 这是实际编码文本时最常用的方法!
  • 步骤五:解码 (Decode) - 数字变回文本

    # 解码我们之前得到的 ID 列表 (注意:这个列表没有包含 [CLS] 和 [SEP])
    decoded_text = tokenizer.decode([7993, 170, 13809, 23763, 2443, 1110, 3014])
    print(decoded_text)  # 输出:Using a Transformer network is simple# 解码包含 [CLS] 和 [SEP] 的完整 ID 列表
    decoded_text_with_special = tokenizer.decode([101, 7993, 170, 13809, 23763, 2443, 1110, 3014, 102])
    print(decoded_text_with_special)  # 输出:[CLS] Using a Transformer network is simple [SEP]
    

    解码的魔法:

    • 它不仅仅是把 ID 变回 token 字符串。
    • 智能地合并了那些被切分的子词!比如把 'Transform''##er' 无缝拼接成 “Transformer”。
    • 保留了特殊 token(如 [CLS], [SEP])。在实际任务中(如文本生成),你可能需要在解码后手动去除这些特殊 token。
    • 解码是生成类任务(如翻译、摘要、对话)的关键步骤!

课程总结:

  • 分词器是桥梁: 将文本 (str) 编码 (encode) 成模型能吃的数字 (input_ids),将模型生成的数字解码 (decode) 回人类可读的文本。
  • 分词策略: 子词切分 (Subword) 是主流(如 BPE, WordPiece),平衡词表大小、语义保留和处理效率。
  • 加载分词器: 首选 AutoTokenizer.from_pretrained(),来源可以是 Hub checkpoint 名或本地路径。
  • 保存分词器: 使用 tokenizer.save_pretrained(),生成 tokenizer_config.json, vocab.txt, special_tokens_map.json 等关键文件。
  • 编码文本:
    • 了解过程:text -> tokens (tokenize) -> input_ids (convert_tokens_to_ids)
    • 实战首选: tokenizer(text)tokenizer.encode(),一步到位得到包含 input_ids, token_type_ids, attention_mask 的字典。
    • 注意:模型会自动添加特殊 token (如 [CLS], [SEP])。
  • 解码文本: 使用 tokenizer.decode(input_ids),它能合并子词并处理特殊 token。
  • 词表 (vocab.txt): 核心文件!定义了 token 到 ID 的映射关系(行号即 ID)。

提示:

  1. 动手实验:AutoTokenizer 加载不同的模型(如 'bert-base-cased', 'gpt2', 't5-small'),对同一句话调用 tokenize()tokenizer(),观察它们的分词策略和输出差异。
  2. 探索词表: 下载或保存一个分词器,打开它的 vocab.txt 文件看看里面有什么。尝试查找一些常见词和特殊 token 的 ID。
  3. 理解解码: 尝试用 decode() 函数解码一些你自己构造的 ID 列表(可以从 vocab.txt 里挑几个 ID),观察输出结果。特别注意含有 ## 前缀的 token 是如何被合并的。
  4. 思考: 为什么 tokenizer() 返回的字典里,token_type_idsattention_mask 也是必要的?它们分别解决了什么问题?(提示:多句子任务、处理不同长度的序列)
http://www.dtcms.com/a/330233.html

相关文章:

  • 哈希表之两个数组的交集(leetcode349)
  • 智能合约开发全流程实战指南
  • 【LeetCode】4. 寻找两个正序数组的中位数
  • 芯伯乐300kHz降压DC/DC转换器XBL4005:4.5V~40V宽电压范围,5A大电流高效输出
  • 三伍微电子GSR2406 IoT FEM 2.4G PA 射频前端模组芯片
  • 深入解析C语言嵌套结构体的内存管理与操作实践
  • linux_网络层-ip协议
  • [系统架构设计师]信息安全技术基础知识(三)
  • SpringBoot3+ Elasticsearch8 Spring-data-Elasticsearch使用
  • 多模态数据集分级方案设计与实现
  • 容器基础镜像制作
  • ETLCloud批流一体化体现在哪
  • 【Python】Python 函数基本介绍(详细版)​
  • 版图设计学习2_掌握PDK中的层定义(工艺文档精读)
  • DAY39打卡
  • 【运维进阶】管理变量和事实
  • 哥斯拉--安装、使用
  • graf示教界面技术累积
  • 数据结构摘星题库800题笔记 第2章线性表
  • [TG开发]简单的回声机器人
  • Linux信号量和信号
  • 淘汰人工巡检!企业配电室无线测温实战:0布线+240点位同步监控
  • @进程管理工具 - Glances工具详细指南
  • 20250813测试开发岗(凉)面
  • 《探索C++ set与multiset容器:深入有序唯一性集合的实现与应用》
  • 网络存储技术:数据存储架构的演进与全景解析
  • 计算机网络——协议
  • 基于SpringBoot+Vue的智能消费记账系统(AI问答、WebSocket即时通讯、Echarts图形化分析)
  • Python 类元编程(元类基础知识)
  • 推荐系统论文分享之多任务模型--PLE(二)