Qwen2.5等大模型词汇表解析(BPE)

一个词汇表可能会有多种方式生成:

  • BPE(字节对编码):BPE 是从最基础的字符(字节)开始,通过不断合并高频字节对来构建更大的子词单元。在这个过程中,最终形成的子词可能包含各种不同语言、不同类型的字符组合。例如,在处理多语言文本时,可能会出现中文、英文、阿拉伯文等多种字符混合的子词。这些子词的编码范围很广,远远超出了 Latin - 1 所能表示的 256 个字符范围。

  • WordPiece:WordPiece 基于最大似然估计选择子词,它倾向于保留具有语义信息的子词。这样生成的词汇表中可能包含很多常见的词缀、词根等,并且可能会根据语言的特点进行特殊处理。比如英文中的前后缀、中文的偏旁部首等,这些子词的编码也可能无法用 Latin - 1 表示。

  • Unigram Language Model(ULM):ULM 是基于概率统计来选择子词集合的,生成的子词更加灵活多样。它会根据语料库中字符和子词的出现概率进行优化,可能会产生一些特殊的子词组合,同样可能包含超出 Latin - 1 编码范围的字符。

以Qwen-72B为例,其词汇表可能包含:

  • 约20% 的中文字符直接表示

  • 30% 的跨语言BPE合并单元

  • 15% 的ASCII组合

  • 剩余为其他语言及符号的混合表示

一、字节对编码(Byte Pair Encoding)

1、对全部词按照字节编码拆分

  • ASCII 编码:ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是一种基于拉丁字母的字符编码标准,主要用于显示现代英语和其他西欧语言。在 ASCII 编码中,每个字符用 1 个字节(8 位)表示,范围是 0x00 - 0x7F,刚好可以表示 128 个不同的字符,包括英文字母(大写和小写)、数字、标点符号等。例如,字母 'A' 在 ASCII 编码中对应的十进制值是 65,十六进制表示就是 0x41;字母 'B' 对应的十进制值是 66,十六进制表示是 0x42。所以,在 ASCII 编码下,一个英文单词中的每个字母都对应一个单字节。

  • UTF - 8 编码:UTF - 8(8 - bit Unicode Transformation Format)是一种针对 Unicode 的可变长度字符编码,它可以使用 1 到 4 个字节来表示一个字符。对于英文字母和 ASCII 字符,UTF - 8 编码和 ASCII 编码是兼容的,即英文字母仍然用 1 个字节表示。例如,英文单词 "AB" 在 UTF - 8 编码下对应的字节序列(十六进制表示)就是 "41 42"。

  • UTF - 8 是一种变长编码,中文汉字通常用 3 个字节来表示。这是因为中文的字符集非常庞大,需要更多的位来表示。例如,汉字 “你” 在 UTF - 8 编码下对应的字节序列(十六进制表示)是 "E4 BD A0"。所以,一个中文词语或者句子在 UTF - 8 编码下会由多个 3 字节的组合构成。

2、统计词频合并频繁字节对

简单的英文语料库:["low", "lower", "newest", "widest"]

①统计它们的出现频率,得到初始词表:

l

o

w

e

r

n

s

t

i

2

1

3

3

1

1

2

2

1

②统计字节对频率

lo

ow

we

er

ne

ew

ws

st

wi

1

2

1

1

1

1

1

2

1

id

de

1

1

③第一次合并

找出出现频率最高的字节对,这里是 owst(频率都为 2,我们选择 ow 进行合并)。将 ow 合并成一个新的 “单词”,更新语料库和词表。更新后的语料库变为 ["low", "lower", "newest", "widest"] (这里虽然语料库文本不变,但内部表示发生变化),词表更新为:

l

ow

e

r

n

s

t

i

2

2

3

1

1

2

2

1

④不断重复直到词汇数量达到要求

在使用字节对编码(BPE)处理像 “我爱北京” 这样的中文文本时,若采用 UTF - 8 编码,一个汉字通常由 3 个字节表示,“我爱北京” 就会被编码为 12 个字节,后续的词频统计和字节对合并操作是针对这 12 个字节来进行的

在 UTF - 8 编码中,大多数中文字符用 3 个字节来表示。例如,对 “我爱北京” 进行 UTF - 8 编码后,会得到一个 12 字节的序列。假设编码后的十六进制字节序列为(实际值会根据具体字符编码确定):e6 88 91 e7 88 b1 e5 8c 97 e4 ba ac 。在 BPE 编码的初始阶段,会把这 12 个字节中的每个字节当作独立的 “单词”,同时统计每个字节在语料库中的出现频率。其余过程和上面一样

二、BPE词汇保存

a. vocab.json
  • 作用:映射每个Token到唯一的索引。

  • 格式:JSON字典,键为Token的字符串表示,值为对应索引。

  • Token表示

    • 单字节Token:使用Latin-1编码转换为Unicode字符。例如,字节0xDE表示为字符Þ(Unicode U+00DE)。

    • 多字节Token:按合并顺序的字节序列用Latin-1编码为字符串。例如,字节对0xDE 0xAD转换为字符串"Þ­"

    • 特殊Token:直接以字符串形式保存,如"<|endoftext|>"

b.merges.txt
  • 作用:记录BPE训练过程中学到的合并规则。

  • 格式:每行按优先级排列一个合并操作,格式为字节对1 字节对2。例如:

a b ab c

表示先将a和b合并为ab,再将ab与c合并为abc。

实现细节

  • 编码处理:所有Token均以Latin-1编码保存,确保字节到字符的无损转换。例如,0x80对应字符,在JSON中可能转义为\x80

  • 特殊字符处理:不可见字符(如控制字符)在JSON中使用Unicode转义(如\u00DE)。

  • 加载逻辑:分词器加载时,将vocab.json中的字符串按Latin-1编码还原为字节序列,并结合merges.txt重建合并规则。

优势

  • 兼容性:Latin-1编码确保所有字节都能表示为合法字符,避免编码冲突。

  • 压缩性:通过合并高频字节对,显著减少词汇表大小,提升处理效率。

  • 灵活性:支持多语言文本,无需担心未登录字符,因任何字符均可分解为字节处理。

三、 Latin-1(256个奇怪的字符集,内含如何恢复每个token)

Latin-1(ISO/IEC 8859-1)是一种单字节编码,每个字符仅占用一个字节(8位),因此其字符集总共有 256个字符(从0x000xFF)。它并非直接对多字节进行编码,而是通过以下机制实现对多字节序列的间接处理:

  1. Latin-1的编码本质

  • 单字节特性:每个字符严格对应一个字节(范围0x000xFF),无法直接表示多字节字符(如中文)。

  • Unicode兼容性:Latin-1的前256个字符(U+0000到U+00FF)与Unicode一一对应。例如:

    • 0x41 → ASCII字符A(U+0041)

    • 0xDE → 字符Þ(U+00DE)

    • 0xFF → 字符ÿ(U+00FF)

  1. 为何能“处理”多字节序列?

在Qwen等字节级BPE分词器中,多字节序列(如UTF-8)被拆解为单字节,再通过Latin-1编码转换为字符字符串。具体步骤:

  1. 拆解多字节:例如,中文“你”的UTF-8编码为0xE4 0xBD 0xA0,拆分为3个单字节。

  2. Latin-1映射:每个字节独立转换为Latin-1字符:

    1. 0xE4ä(U+00E4)

    2. 0xBD½(U+00BD)

    3. 0xA0 (U+00A0,不换行空格)

  3. 组合字符串:合并为字符串"ä½ ",存入词汇表(如vocab.json)。

  4. 为何总共有256个字符?

  • 字节的数学上限:一个字节为8位二进制数,取值范围为0x00(00000000)到0xFF(11111111),共2⁸=256种可能。

  • Latin-1的设计:它充分利用了单字节的所有256个值,无任何保留或未定义码位,每个值对应唯一字符。

  1. 多字节编码的间接表示

虽然Latin-1本身不支持多字节字符,但通过以下方式间接处理:

  • 分块映射:将多字节序列(如UTF-8)拆分为单字节,每个字节独立映射到Latin-1字符。

  • 无损可逆性:Latin-1编码可逆,str.encode("latin-1")bytes.decode("latin-1")能完全还原原始字节。

  1. 示例:中文“你”的处理

  2. UTF-8编码0xE4 0xBD 0xA0

  3. Latin-1转换 → 字符序列"ä½ "

  4. 为何选择Latin-1而非其他编码?

  • 全覆盖性:Latin-1覆盖所有256个字节值,无遗漏。

  • 无歧义:每个字节对应唯一字符,避免编码冲突。

  • 兼容性:与Unicode无缝对接,支持跨语言处理。

  1. 特殊字符的显示问题

部分Latin-1字符为不可见或控制字符(如0x000x1F),在可视化时需特殊处理:

  • 转义表示:如0x00显示为\x00

  • Unicode名称:如0x03显示为[END OF TEXT]

四、merges.txt有什么用

核心作用:确保训练和推理时对任意文本(包括未见过的文本)按照完全相同的规则进行分词

1、训练与推理的一致性原理

  • 训练阶段:通过统计语料中的高频字节对,生成合并规则(merges.txt),并基于这些规则逐步合并字节,构建最终词汇表(vocab.json)。

  • 推理阶段:面对新文本时,严格按照 merges.txt 中记录的合并顺序和规则,将原始字节流拆分为与训练时一致的Token序列。

2、具体流程对比

(1) 训练阶段
  1. 输入文本 → UTF-8编码为字节流(如“你好” → 0xE4BD A0E5A5BD)。

  2. 统计频率:统计所有相邻字节对的出现频率。

  3. 生成合并规则

    1. 找到最高频的字节对(如 0xE40xBD),合并为新Token 0xE4BD,记录到 merges.txt

    2. 重复合并,直到达到预设词汇表大小。

  4. 生成词汇表:将所有合并后的Token和基础字节存入 vocab.json

(2) 推理阶段
  1. 输入新文本 → UTF-8编码为字节流(如“你好吗” → 0xE4BD A0E5A5BD E59097)。

  2. 应用合并规则

    1. merges.txt 中的顺序,从最后一行(最新合并规则)向第一行(最早合并规则)逆序尝试合并。

    2. 优先合并高频的字节对,生成与训练时一致的Token。

  3. 输出Token序列:即使新文本包含未在训练中出现的部分(如“吗”的字节0xE59097),仍按相同规则拆分。

3、关键示例

场景1:训练时学到的合并规则
  • 训练数据:“你好”(UTF-8字节:0xE4 0xBD 0xA0 0xE5 0xA5 0xBD

  • 生成的 merges.txt

E4 BD # 合并0xE4和0xBD → 0xE4BD E4BD A0 # 合并0xE4BD和0xA0 → 0xE4BDA0(对应“你”) E5 A5 # 合并0xE5和0xA5 → 0xE5A5 E5A5 BD # 合并0xE5A5和0xBD → 0xE5A5BD(对应“好”)

场景2:推理时处理新文本“你好吗”
  • 新文本字节流0xE4 0xBD 0xA0 0xE5 0xA5 0xBD 0xE5 0x90 0x97(“你好吗”)

  • 分词过程

    • 优先应用最新的合并规则

      1. 检查是否有 E5A5 BD → 合并为 0xE5A5BD(“好”)。

      2. 检查是否有 E4BD A0 → 合并为 0xE4BDA0(“你”)。

    • 剩余字节处理

      1. 0xE5 0x90 0x97(“吗”)未在训练中出现,按基础规则拆分:

        • 检查是否有 E5 90 → 若不在merges.txt中,保留为单字节。

        • 最终拆分为 0xE5, 0x90, 0x97(对应3个Token)。

    • 最终Token序列

["你", "好", "吗"] → 实际存储为: ["ä½ ", "好", "é\x90\x97"](假设“吗”未合并)

4、为什么必须严格遵循 merges.txt

(1) 保证Token粒度的对齐
  • 若训练时“你好”被合并为单个Token,而推理时未应用相同规则,拆分为多个单字节Token,会导致:

    • 语义偏差:模型无法识别合并后的语义单元。

    • 性能下降:输入表示与训练时不一致,模型预测失效。

(2) 处理未知文本的鲁棒性
  • 动态拆分能力:即使面对未见过的新字节组合(如“吗”),仍按 merges.txt 的优先级尝试合并,若无法合并则保留为底层字节。

  • 示例

    • 若新文本中出现高频字节对 0xE5 0x90,但 merges.txt 中未记录,则保留为两个单字节Token。

    • 若未来扩展 merges.txt 加入此规则,则合并为新Token。


  1. 特殊情况的处理

(1) 跨语言混合文本
  • 输入:中英文混合句子“Hello 世界”。

  • 处理流程

    • 所有字符统一转为UTF-8字节流。

    • 英文部分(如“Hello”)可能被拆分为单字母或子词(如H e l l o)。

    • 中文部分“世界”按 merges.txt 合并规则处理。

(2) 控制字符或乱码
  • 输入:包含无效UTF-8字节的乱码(如 0xE4 0x12)。

  • 处理

    • merges.txt 规则尝试合并,若无匹配则保留为单字节Token。

    • 解码时可能显示为乱码,但字节级处理保证无损。

五、恢复语义

每个模型在构建词汇表时,会依据特定的分词方法对大量语料进行处理,生成一个包含所有可能 token 的集合,同时为每个 token 分配唯一的编号。这个词汇表是模型进行文本处理的基础,不同模型的词汇表在 token 集合、编码方式等方面可能存在显著差异。

模型自带的分词器(tokenizer)是专门为该模型的分词方法和词汇表量身定制的工具。它精确掌握分词过程中所遵循的规则,包括子词的划分方式、合并顺序以及 token 与编号的映射关系。在将 token 解码还原为原始文本时,分词器能够依据这些规则,准确地将 token 重新组合成原始的文本形式。

若不使用模型自带的分词器,而采用如 Latin - 1 编码这样通用的编码方式进行还原,会面临诸多问题。首先,不同的分词方法和词汇表构建方式导致 token 的编码具有特异性,通用编码方式无法理解这些特定规则,从而难以正确还原 token。其次,在将文本转换为 token 的过程中,不仅要完成字符的拆分,还需要保留文本的语义信息,以确保解码后的文本与原始文本在含义上一致。模型自带的分词器在设计时充分考虑了语义信息的保留,能够结合上下文对多义词、语义关系等进行准确处理。而通用编码方式仅关注字符的编码转换,无法处理这些复杂的语义信息。

import json
from transformers import AutoTokenizer

def read_vocab_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as f:
        vocab = json.load(f)
    return vocab

def decode_all_tokens(vocab, tokenizer):
    decoded_vocab = {}
    for token, token_id in vocab.items():
        try:
            ids = [token_id]
            decoded_token = tokenizer.decode(ids)
            decoded_vocab[token] = decoded_token
        except Exception as e:
            print(f"解码 token '{token}' 时出错: {e}")
    return decoded_vocab

def save_decoded_vocab_to_json(decoded_vocab, output_file_path):
    with open(output_file_path, 'w', encoding='utf-8') as f:
        json.dump(decoded_vocab, f, ensure_ascii=False, indent=4)

def main():
    model_dir = "../qwen0.5"
    vocab_file_path = f"{model_dir}/vocab.json"
    tokenizer = AutoTokenizer.from_pretrained(model_dir)
    vocab = read_vocab_file(vocab_file_path)
    decoded_vocab = decode_all_tokens(vocab, tokenizer)
    output_file_path = "decoded_vocab.json"
    save_decoded_vocab_to_json(decoded_vocab, output_file_path)
    print(f"解码结果已保存到 {output_file_path}")

if __name__ == "__main__":
    main()

### Qwen2.5-Instruct 模型概述 Qwen2.5-14B-Instruct 是一款强大的指令微调大型语言模型,旨在提供高质量的回答和服务于各种自然语言处理任务。该模型经过精心训练,在多个领域展现了卓越的能力,包括但不限于对话生成、文本摘要、机器翻译等[^1]。 对于希望深入了解此模型及其高级特性的用户来说,官方提供了丰富的资源用于进一步探索,如访问 [Qwen2.5 官方博客](https://qwen.ai/blog/) 或查阅详细的 [Qwen2.5 官方文档][^1]。 ### 文档下载与获取途径 为了方便开发者快速上手,Qwen 提供了便捷的方式以获取必要的开发资料和技术指导: - **官方文档**:最全面的技术说明和API指南可直接从官方网站获得。 - **GitHub仓库克隆**:通过命令 `git clone https://github.com/modelscope/qwen` 可轻松复制项目源码至本地环境,便于研究和实践操作[^3]。 ### 使用教程概览 安装与初步使用的具体流程已在专门编写的教程中详细介绍过,确保使用者能够顺利部署并运行Qwen2.5-14B-Instruct实例。按照指引完成设置后,即可体验到这款先进AI工具带来的便利之处。 ```bash # 示例:如何克隆Qwen2.5-Coder模型仓库 $ git clone https://www.modelscope.cn/qwen/Qwen2.5-Coder-7B-Instruct.git ``` 此外,针对特定需求(比如处理极长序列),还有特别优化过的版本可供选择,例如支持百万级别token长度的新一代长上下文模型——Qwen2.5-7B-Instruct-1M 和 Qwen2.5-14B-Instruct-1M,这使得在面对大规模数据集时也能保持高效性能表现[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值