大模型tokenizer重构流程
大模型tokenizer层再训练(选取Qwen7B试验,重构token层)
最近公司可能想训练一个蛋白质大模型,需要了解一下大模型tokenizer重构,之后可能要训练,这里做了一定的总结。
文章目录
- 1. 首先查看Qwen2.5 7B基本信息
- 2. 重构tokenizer整体流程
- 2.1 步骤一:准备语料
- 2.2 步骤二:训练 tokenizer
- 分词算法
- BPE流程
- 构建tokenizer
- 2.3 步骤三:替换原模型目录中的 3 个 tokenizer 文件
- 2.4 步骤四:加载 tokenizer,验证
- 2.5 步骤五:加载、重构、保存模型
- 加载模型 + 验证当前 embedding 尺寸
- resize_token_embeddings(new_vocab_size) 调整维度
- 保存新的模型权重 + tokenizer
- 2.6 步骤六:检查
- 3. 构建tokenizer核心:选择分词算法
- 3.1 Word-level Tokenizer(词级分词器)
- 3.2 Character-level Tokenizer(字符级)
- 3.3 Byte-level Tokenizer(字节级)
- 3.4 BPE (Byte Pair Encoding)
- 3.5 WordPiece
- 3.6 Unigram Language Model(概率模型)
- 3.7 SentencePiece(分词框架)
- 3.8 Tiktoken(OpenAI 专用)
1. 首先查看Qwen2.5 7B基本信息
✅ Tokenizer 加载成功!📌 类型: <class 'transformers.models.qwen2.tokenization_qwen2_fast.Qwen2TokenizerFast'>
📦 所使用的 tokenizer 文件: /home/liuzhao/models/Qwen/Qwen2___5-7B
🧱 词表大小(vocab size): 151643
🔢 特殊 token id: [PAD]: <|endoftext|> -> id: 151643[BOS]: None -> id: None[EOS]: <|endoftext|> -> id: 151643[UNK]: None -> id: None🧪 示例 token 编码:
Decoded: 你好,蛋白质'ä½łå¥½' -> 108386'ï¼Į' -> 3837'èĽĭçĻ½è´¨' -> 107151
Sliding Window Attention is enabled but not implemented for `sdpa`; unexpected results may be encountered.
Loading checkpoint shards: 100%|█████████████████████████████████████████████████████████████████████████████| 4/4 [00:08<00:00, 2.00s/it]✅ 模型加载成功!
📐 Embedding 层形状: torch.Size([152064, 3584])
🔢 Embedding 参数数量: 544997376📊 模型结构摘要:
Qwen2ForCausalLM((model): Qwen2Model((embed_tokens): Embedding(152064, 3584)(layers): ModuleList((0-27): 28 x Qwen2DecoderLayer((self_attn): Qwen2Attention((q_proj): Linear(in_features=3584, out_features=3584, bias=True)(k_proj): Linear(in_features=3584, out_features=512, bias=True)(v_proj): Linear(in_features=3584, out_features=512, bias=True)(o_proj): Linear(in_features=3584, out_features=3584, bias=False))(mlp): Qwen2MLP((gate_proj): Linear(in_features=3584, out_features=18944, bias=False)(up_proj): Linear(in_features=3584, out_features=18944, bias=False)(down_proj): Linear(in_features=18944, out_features=3584, bias=False)(act_fn): SiLU())(input_layernorm): Qwen2RMSNorm((3584,), eps=1e-06)(post_attention_layernorm): Qwen2RMSNorm((3584,), eps=1e-06)))(norm): Qwen2RMSNorm((3584,), eps=1e-06)(rotary_emb): Qwen2RotaryEmbedding())(lm_head): Linear(in_features=3584, out_features=152064, bias=False)
)📦 模型总参数量: 7,615,616,512
✅ 分析结果
🔹 Tokenizer 分析:
| 项目 | 值 | 说明 |
|---|---|---|
| Tokenizer 类 | Qwen2TokenizerFast | 使用 transformers 中快速实现,基于 tokenizer.model(SentencePiece) |
| 词表大小 | 151643 | 表示 tokenizer 可用的 token 数量 |
| [PAD] token | `< | endoftext |
| [EOS] token | `< | endoftext |
| [BOS] token | None | 未定义,模型可能默认不需要 |
| [UNK] token | None | 未定义,可能使用 fallback 机制 |
| 示例编码 | token 输出乱码 | 因为使用了 byte-level BPE,按字节切分后再 BPE,token 看似乱码但是正确的 UTF-8 字节表示 |
🔹 模型结构分析:
| 项目 | 值 | 说明 |
|---|---|---|
| Embedding 大小 | [152064, 3584] | vocab size(包括 special tokens)为 152064,hidden size 为 3584 |
| 层数 | 28 | Transformer decoder 层数 |
| 隐藏维度(hidden size) | 3584 | 词向量维度,也是注意力/FFN 输入输出维度 |
| Attention 参数 | KV projection 输出维度为 512 | 使用 多头注意力 + 低秩投影(multi-query or grouped attention)优化推理速度 |
| FFN维度(MLP) | 18944 | 表示采用大容量前馈网络,约为 hidden size 的 5.3 倍 |
| LayerNorm 类型 | Qwen2RMSNorm | 自定义 RMSNorm,代替标准 LayerNorm |
| Rotary Embedding | 有 | 支持 RoPE,用于 position encoding |
| 输出层(lm_head)维度 | [3584, 152064] | 最终投影到 vocab 空间用于生成预测 |
2. 重构tokenizer整体流程
| 步骤 | 操作 | 工具/说明 |
|---|---|---|
| 1 | 准备生物医药语料(每行一个样本) | protein.txt |
| 2 | 使用 HuggingFace tokenizers 训练 tokenizer | 输出 tokenizer.json / vocab.json / tokenizer_config.json |
| 3 | 替换原模型目录中的 3 个 tokenizer 文件 | cp *.json Qwen2.5-7B/ |
| 4 | 加载 tokenizer,验证 vocab size、分词逻辑 | AutoTokenizer.from_pretrained(…, trust_remote_code=True) |
| 5 | 加载模型,并执行 resize_token_embeddings(),重构embedding以及输出层,保存模型 | 自动调整 embedding + lm_head |
| 6 | 检查 shape:model.get_input_embeddings().weight.shape 和 model.lm_head.weight.shape | 应该都是 (padded_vocab_size, hidden_size) |
2.1 步骤一:准备语料
由于现在没有语料数据,暂时构建伪数据,尝试跑通流程:
/home/liuzhao/tokenizer_construct/fack_data/protein_mock.txt
MVHLTPEEKSAVTALWGKVNVDEVGGEALGRLLVVYPWTQ
MKVIFLGAVIGTILLISYGIR
MGDVEKGKKIFIMKCSQCHTVEKGGKHKTGPNLHGLFGRK
GAVAGVAGAAAKAAAKAAAKAA
MSTAVLGVLIFVGLVGMAAWTSRI
MAAAKAAAEAAAKAKAAAEAA
MEFVHLVYFGLVKGL
MNSFEMLRILGIIAASTLSLTI
MKAVLAGLVAVGLTVLAAAGA
MGGAVILFVLIGTFTALLAG
这 10 条伪序列涵盖了常见氨基酸字母(A、V、G、L、M、K、T、S、F、E…),不包含任何非法字符,适合 BPE/ByteLevel 分词训练。
2.2 步骤二:训练 tokenizer
分词算法
分词算法(Tokenization Algorithm)就是把原始文本(如“你好”或蛋白质序列)转换成模型能理解的离散符号(token)序列的过程。
它是大模型理解输入的第一步,决定了:
• 模型看到的“词”是啥?
• 每个 token 的 ID 是多少?
• 最终 embedding 层输入长什么样?
✅ 常见的分词算法一览(用于大语言模型 LLM)
| 分词算法 | 所属模型/特点 | 是否可训练 | 优点 | 缺点 |
|---|---|---|---|---|
| Word-level | 古早模型(Word2Vec) | ❌ 预定义词表 | 简单直观 | 无法处理新词、词表爆炸 |
| Character-level | GPT-Char、汉字模型 | ❌ 固定单字 | 不丢信息,适合小语种 | 序列过长,不压缩 |
| Byte-level | GPT2、Qwen2 | ❌ 固定每个字节 | 跨语言统一处理 | 不压缩子词关系 |
| BPE(Byte Pair Encoding) | GPT2、Qwen2、LLama | ✅ 可训练 | 平衡词频、可学习 | 初始复杂,需 merge 文件 |
| Unigram LM(SentencePiece) | T5、mT5 | ✅ 可训练 | 支持多种粒度 | 训练慢、实现复杂 |
| WordPiece | BERT | ✅ 可训练 | 词干拼接,较紧凑 | 不如 BPE 高效 |
| Tiktoken | GPT-3.5 / 4(OpenAI) | ❌ 固定预定义 | 极度高效 | 封闭,无法训练 |
✅ 举例:
用蛋白质序列构建自己的 tokenizer,然后替换 Qwen2.5 的原生 tokenizer。
🧬 你的输入语料:
MVHLTPEEKSAVTALWGK...
这是蛋白质序列,由一串大写字母组成,每个字母表示一种氨基酸。
🧩 如果用不同分词算法会变成什么?
| 分词算法 | 分词结果 | 特点 |
|---|---|---|
| Character-level | [‘M’, ‘V’, ‘H’, ‘L’, ‘T’, …] | 每个氨基酸是一个 token |
| Byte-level BPE(你现在用的) | [‘M’, ‘V’, ‘H’, ‘L’, ‘T’, ‘PE’, ‘EKS’, …] | 高频组合被合并为 token,压缩效果更好 |
| Word-level | 整句作为一个 token(不适用) | 无法处理蛋白质 |
| Tiktoken | [‘MV’, ‘HL’, ‘TP’, ‘EE’, …](预定义) | 可能会错误分割 |
| SentencePiece(Unigram) | [‘MVHL’, ‘TP’, ‘EEKS’] | 基于语言概率构建,合并子序列更灵活 |
✅ 那么,分词算法的作用就是:
把一条「原始蛋白质序列」分解成适合模型使用的 token 序列,再将每个 token 映射成一个 ID,再送入 embedding 层中。
✅ ByteLevel BPE 有什么特别的?
ByteLevel BPE = Byte-level + BPE 合体
| 特性 | 说明 |
|---|---|
| ByteLevel | 文本按字节处理,支持各种字符集(中英文、特殊字符、符号、换行、空格) |
| BPE(可训练) | 高频子词自动 merge,词表更精简,更适合短词/序列建模 |
| 优势 | 对蛋白质这种「结构性、定长、重复性高」的字符序列特别友好 |
所以我们说:
✅ ByteLevel BPE 是当前 LLM 中最通用、兼容性最强、训练效率高的分词方法之一。
✅ 选 ByteLevel BPE 的理由:
| 原因 | 说明 |
|---|---|
| 蛋白质本身是字符序列 | 天然适合 ByteLevel |
| 子串重复率高(如 AVAVAV) | BPE merge 后可以变成一个 token,提升压缩效率 |
| 不需要复杂语义模型 | 不像自然语言,要解析句法 |
| 想替换大模型(Qwen2.5)的 tokenizer | Qwen2 就是用 ByteLevel BPE,所以兼容性满分 ✅ |
✅ 最终总结:
分词算法是模型输入的第一道大门,不同算法决定了模型“看到的单位”是词、子词、字节还是字符;而 ByteLevel BPE 是一种兼具高效、灵活、可训练的主流方法,适用于你构建蛋白质 tokenizer 的任务。
BPE流程
完整可视化 BPE 的合并(merge)过程,包括:
- 初始化状态;
- 每轮统计 + 合并;
- 最终得到词表(vocab)和合并规则(merges)。
✅ 示例文本语料:
我们用这个简单但有重复的英文短语做语料(共 4 条):
low lower lowest lowly
🧾 Step 1:初始化为字符级 token + <\w> 结尾标记
每个词都拆成字符 + 特殊符号 表示词结束:
| 原始词 | 初始 token 序列 |
|---|---|
| low | l o w |
| lower | l o w e r |
| lowest | l o w e s t |
| lowly | l o w l y |
🔁 Step 2:统计频率最高的相邻 token 对
我们数一下所有相邻字符对出现的次数:
| Token Pair | 出现次数 |
|---|---|
| (l, o) | 4 ✅ |
| (o, w) | 4 ✅ |
| (w, ) | 1 |
| (w, e) | 2 |
| (e, r) | 1 |
| (e, s) | 1 |
| (s, t) | 1 |
| (w, l) | 1 |
| (l, y) | 1 |
🔨 Step 3:合并频次最高的 pair
我们选中 (‘l’, ‘o’) → “lo”,进行第一次 merge。
更新所有词:
| 更新后 token | 合并 |
|---|---|
| lo w | l+o ✅ |
| lo w e r | l+o ✅ |
| lo w e s t | l+o ✅ |
| lo w l y | l+o ✅ |
🔁 Step 4:继续迭代统计 + 合并
第 2 次 merge:
pair: (‘lo’, ‘w’) → “low”
更新后:
| 更新后 token |
|---|
| low |
| low e r |
| low e s t |
| low l y |
第 3 次 merge:
pair: (‘low’, ‘e’) → “lowe”
更新:
low </w>
lowe r </w>
lowe s t </w>
low l y </w>
第 4 次 merge:
pair: (‘lowe’, ‘r’) → “lower”
更新:
low </w>
lower </w>
lower s t </w>
low l y </w>
第 5 次 merge:
pair: (‘lower’, ‘s’) → “lowers”
更新:
low </w>
lower </w>
lowers t </w>
low l y </w>
第 6 次 merge:
pair: (‘lowers’, ‘t’) → “lowest”
更新:
low </w>
lower </w>
lowest </w>
low l y </w>
第 7 次 merge:
pair: (‘low’, ‘l’) → “lowl”
然后再合并 (‘lowl’, ‘y’) → “lowly”
最终分词表:
| 原词 | 最终 token 序列 |
|---|---|
| low → low | |
| lower → lower | |
| lowest → lowest | |
| lowly → lowly |
🎉 每个词都被压缩成一个 token(理想情况),这就是 BPE 的“学习高频组合”的本事。
📦 最终 vocab(词表)示意:
{"l": 0,"o": 1,"w": 2,"e": 3,"r": 4,"s": 5,"t": 6,"y": 7,"</w>": 8,"lo": 9,"low": 10,"lowe": 11,"lower": 12,"lowers": 13,"lowest": 14,"lowl": 15,"lowly": 16
}
🪄 最终 merges.txt:
l o
lo w
low e
lowe r
lower s
lowers t
low l
lowl y
每一行就是一轮 merge 的规则,按顺序合并。
🔎 总结一下流程:
| 步骤 | 说明 |
|---|---|
| 初始化 | 将词拆成字符,加词尾标记 |
| 多轮统计 + 合并 | 每轮合并出现频率最高的相邻 token |
| 构建 merges.txt | 合并记录即为 tokenizer 的“知识” |
| 应用时分词 | 遇到相同子串时就能直接编码为 token,提高效率 |
构建tokenizer
import os
import json
from tokenizers.implementations import ByteLevelBPETokenizer
from transformers import GPT2TokenizerFast# === 配置路径 ===
input_file = "/home/liuzhao/tokenizer_construct/fack_data/protein_mock.txt"
output_dir = "/home/liuzhao/tokenizer_construct/new_tokenizer"
vocab_size = 512 # 小语料建议设置小一点# === Step 1: 训练 ByteLevelBPE Tokenizer ===
tokenizer = ByteLevelBPETokenizer()# 特殊 token(你可以根据自己需求添加)
special_tokens = ["<|endoftext|>", "<unk>", "<pad>"]# 训练 tokenizer,生成 vocab.json + merges.txt
tokenizer.train(files=input_file,vocab_size=vocab_size,min_frequency=1,special_tokens=special_tokens,
)# 创建输出目录
os.makedirs(output_dir, exist_ok=True)# 保存 vocab.json / merges.txt
tokenizer.save_model(output_dir)# === Step 2: 写入 tokenizer_config.json,指定 tokenizer 类型为 GPT2 ===
tokenizer_config = {"model_type": "gpt2", # 关键!明示为 GPT2-style,避免 tiktoken 报错"unk_token": "<unk>","eos_token": "<|endoftext|>","pad_token": "<pad>"
}with open(os.path.join(output_dir, "tokenizer_config.json"), "w") as f:json.dump(tokenizer_config, f)# === Step 3: 使用 GPT2TokenizerFast 加载 + 保存 HuggingFace 格式 ===
wrapped_tokenizer = GPT2TokenizerFast.from_pretrained(output_dir)
wrapped_tokenizer.save_pretrained(output_dir)# === Step 4: 打印验证信息 ===
print(f"✅ Tokenizer 构建成功,已保存至 {output_dir}")
print(f"🧱 词表大小: {wrapped_tokenizer.vocab_size}")
print(f"🔤 示例分词: {wrapped_tokenizer.tokenize('MVHLTPEEKS')}")
输出:
(tokenizer) root@420GP-252:/home/liuzhao/tokenizer_construct# python build_tokenizer.py
[00:00:00] Pre-processing files (0 Mo) █████████████████████████████████████████████████████████████████████ 100%
[00:00:00] Tokenize words █████████████████████████████████████████████████████████████████████ 14 / 14
[00:00:00] Count pairs █████████████████████████████████████████████████████████████████████ 14 / 14
[00:00:00] Compute merges █████████████████████████████████████████████████████████████████████ 183 / 183
✅ Tokenizer 构建成功,已保存至 /home/liuzhao/tokenizer_construct/new_tokenizer
🧱 词表大小: 259
🔤 示例分词: ['M', 'V', 'H', 'L', 'T', 'P', 'E', 'E', 'K', 'S']
2.3 步骤三:替换原模型目录中的 3 个 tokenizer 文件
将生成的文件替换原来Qwen的tokenizer文件。
2.4 步骤四:加载 tokenizer,验证
# check_tokenizer_replacement.pyfrom transformers import AutoTokenizer
import os# 替换后的模型目录
tokenizer_dir = "/home/liuzhao/models/Qwen/Qwen2.5-7B-change"# 加载 tokenizer(关键:信任 remote_code)
tokenizer = AutoTokenizer.from_pretrained(tokenizer_dir,trust_remote_code=True
)# 打印基本信息
print("✅ Tokenizer 加载成功!\n")
print(f"📌 类型: {type(tokenizer)}")
print(f"📦 路径: {tokenizer.name_or_path}")
print(f"🧱 词表大小: {tokenizer.vocab_size}")# 分词验证(用你熟悉的蛋白质序列)
test_text = "MVHLTPEEKS"
tokens = tokenizer.tokenize(test_text)
token_ids = tokenizer.convert_tokens_to_ids(tokens)
decoded = tokenizer.decode(token_ids)print(f"\n🔤 分词结果: {tokens}")
print(f"🔢 Token IDs: {token_ids}")
print(f"🔁 解码还原: {decoded}")
输出:
(tokenizer) root@420GP-252:/home/liuzhao/tokenizer_construct# python check_tokenizer_replacement.py
✅ Tokenizer 加载成功!📌 类型: <class 'transformers.models.gpt2.tokenization_gpt2_fast.GPT2TokenizerFast'>
📦 路径: /home/liuzhao/models/Qwen/Qwen2.5-7B-change
🧱 词表大小: 259🔤 分词结果: ['M', 'V', 'H', 'L', 'T', 'P', 'E', 'E', 'K', 'S']
🔢 Token IDs: [47, 56, 42, 46, 54, 50, 39, 39, 45, 53]
🔁 解码还原: MVHLTPEEKS
2.5 步骤五:加载、重构、保存模型
第 5 步:模型结构调整阶段。
✅ 第五步任务总目标:
让模型结构(特别是 embedding 层和输出层 lm_head)的维度,匹配你新 tokenizer 的词表大小(vocab_size),确保 forward 时不会报错或 shape mismatch。
🎯 第五步重新拆解为 3 个子任务(建议逐步执行):
加载模型 + 验证当前 embedding 尺寸
| 项 | 内容 |
|---|---|
| ✅ 检查当前 embedding 层 Embedding(num_embeddings, hidden_size) 的维度 | |
| ✅ 检查 lm_head 的输出维度是否一致 | |
| ✅ 打印当前 vocab_size 与 tokenizer vocab 是否对齐 |
✅ 示例输出结构检查:
print(model.model.embed_tokens.weight.shape)
print(model.lm_head.out_features)
resize_token_embeddings(new_vocab_size) 调整维度
model.resize_token_embeddings(new_vocab_size)
| 调整的内容 | 说明 |
|---|---|
| model.model.embed_tokens | embedding 层的 token 数变成新 vocab size(如 259) |
| model.lm_head | 输出维度也会同步调整(transformers 自动处理) |
保存新的模型权重 + tokenizer
• 保存为 Qwen2.5-7B-with-protein-tokenizer/
• 之后可以重新加载、微调或推理
model.save_pretrained(output_dir)
tokenizer.save_pretrained(output_dir)
resize_model_embedding.py
import os
from transformers import AutoTokenizer, AutoModelForCausalLM# === 路径配置 ===
model_path = "/home/liuzhao/models/Qwen/Qwen2.5-7B-change"
output_path = "/home/liuzhao/models/Qwen/Qwen2.5-7B-with-protein-tokenizer"# === Step 1: 加载 tokenizer 和模型 ===
print("📦 加载 tokenizer 和模型...")
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_path, trust_remote_code=True)print("✅ 加载完成!")
print(f"🔢 当前 tokenizer.vocab_size: {tokenizer.vocab_size}")
print(f"📐 原始 embedding 层形状: {model.model.embed_tokens.weight.shape}")
print(f"📐 原始 lm_head 输出形状: {model.lm_head.out_features}")# === Step 2: 调整 embedding 和输出层大小 ===
print("\n🔧 执行 resize_token_embeddings ...")
model.resize_token_embeddings(tokenizer.vocab_size)# 再次打印新形状
print(f"✅ 新 embedding 层形状: {model.model.embed_tokens.weight.shape}")
print(f"✅ 新 lm_head 输出形状: {model.lm_head.out_features}")# === Step 3: 保存新模型和 tokenizer ===
print(f"\n💾 保存模型和 tokenizer 到: {output_path}")
os.makedirs(output_path, exist_ok=True)
model.save_pretrained(output_path)
tokenizer.save_pretrained(output_path)print("\n🎉 完成!模型已保存并可用于推理或微调。")
输出:
(tokenizer) root@420GP-252:/home/liuzhao/tokenizer_construct# python resize_model_embedding.py
📦 加载 tokenizer 和模型...
Sliding Window Attention is enabled but not implemented for `sdpa`; unexpected results may be encountered.
Loading checkpoint shards: 100%|█████████████████████████████████████████████████████████████████████| 4/4 [00:05<00:00, 1.42s/it]
✅ 加载完成!
🔢 当前 tokenizer.vocab_size: 259
📐 原始 embedding 层形状: torch.Size([152064, 3584])
📐 原始 lm_head 输出形状: 152064🔧 执行 resize_token_embeddings ...
✅ 新 embedding 层形状: torch.Size([259, 3584])
✅ 新 lm_head 输出形状: 259💾 保存模型和 tokenizer 到: /home/liuzhao/models/Qwen/Qwen2.5-7B-with-protein-tokenizer🎉 完成!模型已保存并可用于推理或微调。
2.6 步骤六:检查
首先检查tokenizer以及模型:
✅ Tokenizer 加载成功!📌 类型: <class 'transformers.models.gpt2.tokenization_gpt2_fast.GPT2TokenizerFast'>
📦 所使用的 tokenizer 文件: /home/liuzhao/models/Qwen/Qwen2.5-7B-with-protein-tokenizer
🧱 词表大小(vocab size): 259
🔢 特殊 token id: [PAD]: <pad> -> id: 2[BOS]: <|endoftext|> -> id: 0[EOS]: <|endoftext|> -> id: 0[UNK]: <unk> -> id: 1🧪 示例 token 编码:
Decoded: 你好,蛋白质'ä' -> 163'½' -> 124'ł' -> 257'å' -> 164'¥' -> 101'½' -> 124'ï' -> 174'¼' -> 123'Į' -> 237'è' -> 167'Ľ' -> 252'ĭ' -> 236'ç' -> 166'Ļ' -> 250'½' -> 124'è' -> 167'´' -> 115'¨' -> 104
Sliding Window Attention is enabled but not implemented for `sdpa`; unexpected results may be encountered.
Loading checkpoint shards: 100%|█████████████████████████████████████████████████████████████████████| 6/6 [00:05<00:00, 1.13it/s]✅ 模型加载成功!
📐 Embedding 层形状: torch.Size([259, 3584])
🔢 Embedding 参数数量: 928256📊 模型结构摘要:
Qwen2ForCausalLM((model): Qwen2Model((embed_tokens): Embedding(259, 3584)(layers): ModuleList((0-27): 28 x Qwen2DecoderLayer((self_attn): Qwen2Attention((q_proj): Linear(in_features=3584, out_features=3584, bias=True)(k_proj): Linear(in_features=3584, out_features=512, bias=True)(v_proj): Linear(in_features=3584, out_features=512, bias=True)(o_proj): Linear(in_features=3584, out_features=3584, bias=False))(mlp): Qwen2MLP((gate_proj): Linear(in_features=3584, out_features=18944, bias=False)(up_proj): Linear(in_features=3584, out_features=18944, bias=False)(down_proj): Linear(in_features=18944, out_features=3584, bias=False)(act_fn): SiLU())(input_layernorm): Qwen2RMSNorm((3584,), eps=1e-06)(post_attention_layernorm): Qwen2RMSNorm((3584,), eps=1e-06)))(norm): Qwen2RMSNorm((3584,), eps=1e-06)(rotary_emb): Qwen2RotaryEmbedding())(lm_head): Linear(in_features=3584, out_features=259, bias=False)
)📦 模型总参数量: 6,527,478,272
test_model_with_protein_tokenizer.py
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch# === 路径配置 ===
model_path = "/home/liuzhao/models/Qwen/Qwen2.5-7B-with-protein-tokenizer"# === Step 6.1: 加载 tokenizer 和模型 ===
print("📦 加载 tokenizer 和模型...")
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_path, trust_remote_code=True)
model.eval() # 推理用print("✅ 加载完成!\n")# === Step 6.2: 打印模型结构参数 ===
print(f"📌 Tokenizer 类型: {type(tokenizer)}")
print(f"🧱 词表大小: {tokenizer.vocab_size}")
print(f"📐 Embedding 层形状: {model.model.embed_tokens.weight.shape}")
print(f"📊 Transformer 层数: {len(model.model.layers)}")
print(f"📐 输出层形状 (lm_head): {model.lm_head.weight.shape}\n")# === Step 6.3: 简单推理测试 ===
test_input = "MVHLTPEEKSAVTALWGKVNV" # 一段蛋白质序列
inputs = tokenizer(test_input, return_tensors="pt")print("🔍 输入 token IDs:", inputs["input_ids"][0].tolist())# 推理(限制最大长度防止 OOM)
with torch.no_grad():outputs = model.generate(input_ids=inputs["input_ids"],max_new_tokens=20,do_sample=False)# 解码输出
decoded = tokenizer.decode(outputs[0])
print("\n🧪 推理结果:")
print(decoded)
输出:
(tokenizer) root@420GP-252:/home/liuzhao/tokenizer_construct# CUDA_VISIBLE_DEVICES=5 python test_model_with_protein_tokenizer.py
📦 加载 tokenizer 和模型...
Sliding Window Attention is enabled but not implemented for `sdpa`; unexpected results may be encountered.
Loading checkpoint shards: 100%|█████████████████████████████████████████████████████████████████████| 6/6 [00:05<00:00, 1.03it/s]
✅ 加载完成!📌 Tokenizer 类型: <class 'transformers.models.gpt2.tokenization_gpt2_fast.GPT2TokenizerFast'>
🧱 词表大小: 259
📐 Embedding 层形状: torch.Size([259, 3584])
📊 Transformer 层数: 28
📐 输出层形状 (lm_head): torch.Size([259, 3584])🔍 输入 token IDs: [47, 56, 42, 46, 54, 50, 39, 39, 45, 53, 35, 56, 54, 35, 46, 57, 41, 45, 56, 48, 56]
The attention mask and the pad token id were not set. As a consequence, you may observe unexpected behavior. Please pass your input's `attention_mask` to obtain reliable results.
Setting `pad_token_id` to `eos_token_id`:151643 for open-end generation.
The attention mask is not set and cannot be inferred from input because pad token is same as eos token. As a consequence, you may observe unexpected behavior. Please pass your input's `attention_mask` to obtain reliable results.🧪 推理结果:
MVHLTPEEKSAVTALWGKVNVGVGVGVGVGVGVGVGVGVGV
3. 构建tokenizer核心:选择分词算法
通过构建tokenizer的整体流程来看,核心其实是第二步:训练tokenizer。这一步设计到分词算法的选择。
✅ 常用分词算法总览表(用途 × 原理 × 特性 × 模型支持)
| 分词算法 | 是否可训练 | 是否支持 OOV | 压缩能力 | 粒度 | 原理简介 | 常用模型/场景 |
|---|---|---|---|---|---|---|
| Word-level | ❌ 否 | ❌ 差 | ❌ 差 | 词 | 按空格分词,每个词是一个 token | Word2Vec(早期)、GloVe |
| Character-level | ❌ 否 | ✅ 好 | ❌ 差 | 字/字符 | 每个字符是一个 token,无需词表 | 拼音、蛋白质、语音转写 |
| Byte-level | ❌ 否 | ✅ 很好 | ❌ 差 | 字节(UTF-8) | 每个字符拆成 1-4 字节,再用 Byte 作为 token | OpenAI GPT 系列(GPT-3/4)、tiktoken |
| BPE (Byte Pair Encoding) | ✅ 是 | ✅ 良好 | ✅ 高 | 子词 | 高频字符对合并,构造合成词单元 | GPT2、LLama、Qwen、CodeGen |
| WordPiece | ✅ 是 | ✅ 良好 | ✅ 一般 | 子词 | 最大匹配 + 子词合并 | BERT、ALBERT |
| Unigram Language Model | ✅ 是 | ✅ 最好 | ✅ 很高 | 子词 | 训练一个概率模型,保留最优子词集 | T5、mT5、XLM-R、SentencePiece |
| SentencePiece (框架) | ✅ 是 | ✅ 强 | ✅ 强 | 子词/字节 | 支持 Unigram/BPE/Char/Byte 等算法 | Google 系列、跨语种任务 |
| Tiktoken | ❌ 否 | ✅ 固定词表 | ✅ 非常高 | 字节 | OpenAI 内部算法,压缩+速度双优 | GPT-3.5、GPT-4(不可训练) |
3.1 Word-level Tokenizer(词级分词器)
原理:按空格或分隔符将文本切分为完整词语。
优点:
直观易理解
缺点:
词表庞大,难以覆盖 OOV(未登录词)
无法泛化:如“play”和“played”是两个不同词
常见模型:Word2Vec、GloVe(早期传统方法)
3.2 Character-level Tokenizer(字符级)
原理:将每个字符(如汉字、英文字母、氨基酸等)当作一个 token。
优点:
完全无 OOV 问题
极细粒度,适合结构化序列(如蛋白质)
缺点:
序列太长,建模成本高
应用:蛋白质序列、拼音建模、小语种处理
3.3 Byte-level Tokenizer(字节级)
原理:将文本按 UTF-8 字节编码,每个字节为一个 token,共 256 种。
优点:
无语言依赖,支持所有字符
与 GPT2、OpenAI 模型兼容性好
缺点:
不会做词级压缩(每个 token 都很短)
代表实现:GPT2、Qwen2、OpenAI 的 Tiktoken
3.4 BPE (Byte Pair Encoding)
原理:
从字符出发,按频率合并最常出现的 token 对,逐步构造子词
优点:
可训练
高压缩比,子词有语义组合能力
高效通用,适用于各种语种和符号结构
缺点:
只考虑合并频率,缺少语言建模概率
应用模型:GPT2、LLama、Qwen、CodeGen、RoBERTa(BPE with space)
3.5 WordPiece
原理:
按最大匹配方式将词拆解成子词,子词带 ## 前缀表示后缀部分
优点:
控制好拆分与组合平衡,稳定性强
缺点:
构建过程比 BPE 更复杂
常见模型:BERT、ALBERT、DistilBERT、Electra
3.6 Unigram Language Model(概率模型)
原理:
从一大批子词候选集中,选择能最优压缩文本概率的子词集合
每个词有多种拆分路径,选择最优的
优点:
最灵活可控、最适配多语种场景
缺点:
构建耗时较长,训练复杂
常见模型:T5、mT5、XLM-R、Google 系列
3.7 SentencePiece(分词框架)
本质是框架,支持以下算法:
✅ Unigram(默认)
✅ BPE
✅ Character
✅ Byte
支持无空格语言(如中文、日文)
常见于 Google / multilingual 模型
3.8 Tiktoken(OpenAI 专用)
• 原理:压缩优化过的 UTF-8 字节映射 + 快速查表
• 特点:
• 封闭系统,无法训练
• 提供极高推理效率和兼容性
• 常见模型:GPT-3.5、GPT-4(不可替换)
支持自训练的分词算法有哪些?
| 可自训练? | 算法 |
|---|---|
| ✅ 可以 | BPE, Unigram, WordPiece, SentencePiece |
| ❌ 固定 | Char-level, Byte-level, Tiktoken |
相关文章:
大模型tokenizer重构流程
大模型tokenizer层再训练(选取Qwen7B试验,重构token层) 最近公司可能想训练一个蛋白质大模型,需要了解一下大模型tokenizer重构,之后可能要训练,这里做了一定的总结。 文章目录 1. 首先查看Qwen2.5 7B基本…...
JAVA线程安全的集合类分类
1. 传统同步集合类(早期实现,性能较低) Vector 动态数组实现,所有方法通过 synchronized 同步锁保证线程安全。 Stack 继承自 Vector,实现后进先出(LIFO)堆栈,同步锁机…...
ISIS-1 ISIS概述
前面几章我们介绍了OSPF的基础工作原理以及怎样交互LSA形成LSDB链路状态数据库的 这一章我们来介绍另一个链路状态路由协议,ISIS路由协议 一、概述 ISIS(Intermediate System to Intermediate System,中间系统到中间系统)是由ISO(International Organization for Standardiza…...
茱元游戏TV2.9.3 | 适配多设备的经典街机游戏集合
茱元游戏TV是一款专为TV端设计的游戏软件,同时适配手机、投影仪和车机等多种设备。尽管其兼容性一般,仅支持安卓9.0以上系统,但它提供了丰富的经典街机游戏资源,非常适合8090后怀旧游玩。注意,游戏需先下载才能玩&…...
RTD2525BE《HDMI转EDP,DP转EDP》显示器芯片
一、产品概述 瑞昱RTD2525BE是一款专为高端显示设备设计的多接口转换芯片,支持HDMI 2.0与DisplayPort(DP)1.4双输入,并高效转换为嵌入式DisplayPort(eDP)输出。该芯片集成先进信号处理技术,支持…...
SvelteKit 最新中文文档教程(10)—— 部署 Cloudflare Pages 和 Cloudflare Workers
前言 Svelte,一个语法简洁、入门容易,面向未来的前端框架。 从 Svelte 诞生之初,就备受开发者的喜爱,根据统计,从 2019 年到 2024 年,连续 6 年一直是开发者最感兴趣的前端框架 No.1: Svelte …...
springboot使用阿里限流框架-sentinel
当前项目源码 控制台下载 启动bin中的看板服务:账号密码:sentinel/sentinel 官方文档地址 项目引入依赖 <!-- sentinel注解支持 --> <dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-annotation-aspectj<…...
鸿蒙特效教程10-卡片展开/收起效果
鸿蒙特效教程10-卡片展开/收起效果 在移动应用开发中,卡片是一种常见且实用的UI元素,能够将信息以紧凑且易于理解的方式呈现给用户。 本教程将详细讲解如何在HarmonyOS中实现卡片的展开/收起效果,通过这个实例,你将掌握ArkUI中状…...
Qt在模块依靠情况下资源文件名称和资源名称的使用限制
概述 在Qt中使用添加资源文件的时候,对于资源文件名称的定义,往往是较为随意的。 但是当涉及到Qt库依赖的时候,则可能需要遵守一定的规则,否则可能出现文件找不到或者错误加载的问题。 环境 环境名称Qt 版本系统版本LinuxQt 5.…...
MTK Android12-Android13 设置系统默认语言
Android 系统,默认语言 文章目录 需求:场景 参考资料实现方案实现思路编译脚本熟悉-平台熟悉mssi_64_cnkernel-4.19 解决方案修改文件-实现方案 源码分析PRODUCT_LOCALES 引用PRODUCT_DEFAULT_LOCALE 定义get-default-product-locale 方法定义PRODUCT_DE…...
【003安卓开发方案调研】之ReactNative技术开发安卓
基于2025年最新行业动态和搜索资料,以下是针对国内使用React Native(RN)开发安卓应用的深度分析: 一、技术成熟度评估 1. 核心架构升级 新架构全面普及:2024年起,React Native的 新架构(Fabri…...
CSS3学习教程,从入门到精通,CSS3 浮动与清除浮动语法知识点及案例代码(14)
CSS3 浮动与清除浮动语法知识点及案例代码 一、浮动基础 浮动语法 选择器 {float: left|right|none|initial|inherit; }left:元素向左浮动。right:元素向右浮动。none:默认值,元素不浮动。initial:使用默认值。inhe…...
贪心算法——思路与例题
贪心算法:当我们分析一个问题时,我们往往先以最优的方式来解决问题,所以顾名思义为贪心。 例题1 题目分析:这题利用贪心算法来分析,最优解(可容纳人数最多时)一定是先考虑六人桌,然…...
网络华为HCIA+HCIP 防火墙
防火墙部署模式 路由模式 有路由器的功能 路由器干的活 他都得干 透明模式 旁挂模式 IDS 端口镜像 VPN...
WordPress超级菜单插件UberMenu v3.78汉化版
一、插件介绍 UberMenu 是一款功能强大的 WordPress 超级菜单插件,能够帮助站长创建响应式、可自定义的多级菜单。该插件支持动态内容加载、图标、图片、搜索框等丰富功能,并且兼容大多数 WordPress 主题。 UberMenu v3.78 经过完整汉化,适用于中文站点用户,让操作更加直观…...
SQL中体会多对多
我们可以根据学生与课程多对多关系的数据库模型,给出实际的表数据以及对应的查询结果示例,会用到JOINLEFT JOIN两种连接 1. 学生表(students) student_idstudent_name1张三2李四3王五 2. 课程表(courses)…...
23种设计模式-备忘录(Memento)设计模式
备忘录设计模式 🚩什么是备忘录设计模式?🚩备忘录设计模式的特点🚩备忘录设计模式的结构🚩备忘录设计模式的优缺点🚩备忘录设计模式的Java实现🚩代码总结🚩总结 🚩什么是…...
2024年3月全国计算机等级考试真题(二级C语言)
😀 第1题 下列叙述中正确的是 A. 矩阵是非线性结构 B. 数组是长度固定的线性表 C. 对线性表只能作插入与删除运算 D. 线性表中各元素的数据类型可以不同 题目解析: A. 矩阵是非线性结构 错误。矩阵通常是二维数组,属…...
计算机网络基础之三种交换技术及其性能分析
一. 交换技术基础 1. 三种交换技术 电路交换:用于电话网络报文交换:用于电报网络分组交换:用于现代计算机网络 2. 人类历史上的通信网络 #mermaid-svg-AeGvrkUbCkicFOIo {font-family:"trebuchet ms",verdana,arial,sans-serif;…...
使用python爬取网络资源
整体思路 网络资源爬取通常分为以下几个步骤: 发送 HTTP 请求:使用requests库向目标网站发送请求,获取网页的 HTML 内容。解析 HTML 内容:使用BeautifulSoup库解析 HTML 内容,从中提取所需的数据。处理数据ÿ…...
【MySQL】索引 事务
目录 一、索引 概念 作用 使用场景 使用 查看索引 创建索引 删除索引 背后的数据结构 二、事务 为什么使用事务 事务的概念 使用 开启事务 执行多条 SQL 语句 回滚或提交:rollback/commit; 事务的基本特性 原子性 一致性 持久性 隔离性 脏读 …...
平台与架构:深度解析与开发实践
平台与架构:深度解析与开发实践 1. 什么是平台与架构? 平台(Platform):指操作系统或运行环境,例如 linux、windows、darwin(macOS)、android 等。架构(Architecture&…...
【Springboot+JPA】存储过程Procedure使用及踩坑记录
SpringbootJPA存储过程调用 存储过程实现1.表结构2.上报数据分页查询2.1先查出总条数2.1.1 创建存储过程2.1.2 实体类声明存储过程2.1.3 仓库方法绑定存储过程2.1.4 服务调用存储过程 2.2返回分页数据2.2.1 创建存储过程2.2.2 实体类声明存储过程2.2.3 仓库方法绑定存储过程2.2…...
<template>标签的作用,在构建可复用 UI 片段时如何应用?
大白话标签的作用,在构建可复用 UI 片段时如何应用 <template>标签的作用 在前端开发里,<template>标签可是个超棒的工具。它就像是一个“代码仓库”,可以把一些 HTML 代码片段存起来,而且这些代码片段在页面刚加载…...
Android Compose框架的值动画(animateTo、animateDpAsState)(二十二)
深入剖析 Android 框架的值动画(animateTo、animateDpAsState) 一、引言 在构建富有交互性和吸引力的 Android 应用界面时,动画起着至关重要的作用。值动画作为 Android 动画体系中的重要组成部分,能够为各种 UI 元素的属性变化…...
Gunicorn+Eventlet无法收到SocketIO发送的消息
GunicornEventlet无法收到Socketio发送的消息 介绍问题分析**1. 确保正确安装依赖库****2. 检查 Gunicorn 启动命令****3. 配置 Flask-SocketIO 的异步模式****4. 检查库版本兼容性****5. 确认 emit 的调用方式****6. 客户端连接检查** 如何使用多个workers?**1. 为什么不能直…...
【江协科技STM32】软件SPI读写W25Q64芯片(学习笔记)
SPI通信协议及S为5Q64简介:【STM32】SPI通信协议&W25Q64Flash存储器芯片(学习笔记)-CSDN博客 STM32与W25Q64模块接线: SPI初始化: 片选SS、始终SCK、MOSI都是主机输出引脚,输出引脚配置为推挽输出&…...
基于 Vue 3 的PDF和Excel导出
以下是基于 Vue 3 Composition API 的完整实现,包括 PDF 和 Excel 导出。 一、PDF 导出 (Vue 3) 安装依赖 在项目中安装相关库: npm install html2canvas jspdf Vue 3 代码实现 <template><div><div ref"pdfContent" cla…...
Git+Fork 入门介绍
git 分区理解 fork安装 从路径下去拿软件时,注意先拉到本地。经验来看,fork直接安装会出不可思议的问题。 fork操作 安装,注意设置好名字,如果之前安装的同学,名字没有写好,重新安装设置好名字。 clone操…...
Windows系统安装Node.js和npm教程【成功】
0.引言——Node.js和npm介绍 项目描述Node.js基于Chrome V8引擎的JavaScript运行环境,使JavaScript可用于服务器端开发。采用单线程、非阻塞I/O及事件驱动架构,适用于构建Web服务器、实时应用和命令行工具等npmNode.js的包管理器与大型软件注册表。拥有…...
