【大模型开发】将vocab解码
【大模型开发】将vocab解码
在这篇博客中【大模型】tokenizer 中编码过程,说明了tokenizer分词编码过程。
在这里,我在简单叙述一下,vocab.json
文件中存储的是一个字典,key是token分词标记,value是这个token对应的代码点。
举个例子:词汇你好
;在Qwen 的词汇表中使用一个token表示的,按理来说,我们可以直接在vocab.json
文件中直接表示为"你好": 108386
;但似乎目前的大模型都不是这样设计的。而是按照以下的方法对应的:
对你好
使用utf-8编码,得到b'\xe4\xbd\xa0\xe5\xa5\xbd
;然后根据【大模型】tokenizer 中编码过程中提到的映射集合,进行映射:\xe4
->ä
;\xbd
->½
;\xa0
->ł
;\xe5
->å
;\xa5
-> ¥
; \xbd
->½
;然后组合得到ä½łå¥½
;因此,你好
这一词汇,在vocab.json
中是这样表示的:ä½łå¥½:108386
。
(目前,我还没确切地搞懂为什么要这样进行表示和编码…,我觉得可能是因为解析时间和压缩的优势?)
因此,要解析vocab.json
文件中的内容,进行逆向解码即可。
先获得Qwen2.5
的词汇表vocab
;这是一个字典,key是token,所以我们对每一个key进行解码;使用一个关系映射表进行解码即可。
import json
from transformers import AutoTokenizer
def bytes_to_unicode():
"""
Returns list of utf-8 byte and a mapping to unicode strings. We specifically avoids mapping to whitespace/control characters the bpe code barfs on.
The reversible bpe codes work on unicode strings. This means you need a large # of unicode characters in your vocab if you want to avoid UNKs. When you're at something like a 10B token dataset you end up needing around 5K for decent coverage. This is a significant percentage of your normal, say, 32K bpe vocab. To avoid that, we want lookup tables between utf-8 bytes and unicode strings. """ bs = (
list(range(ord("!"), ord("~") + 1)) + list(range(ord("¡"), ord("¬") + 1)) + list(range(ord("®"), ord("ÿ") + 1))
)
cs = bs[:]
n = 0
for b in range(2**8):
if b not in bs:
bs.append(b)
cs.append(2**8 + n)
n += 1
cs = [chr(n) for n in cs]
return dict(zip(bs, cs))
def get_reverse_map(forward_map):
"""
根据正向映射生成反向映射
返回字典:{unicode_char: byte_value}
""" return {v: k for k, v in forward_map.items()}
def bytes_to_unicode_str(byte_sequence):
return ''.join([forward_map[b] for b in byte_sequence])
def unicode_str_to_bytes(unicode_str):
return bytes([reverse_map[c] for c in unicode_str])
# 生成映射表
forward_map = bytes_to_unicode()
reverse_map = get_reverse_map(forward_map)
if __name__ == '__main__':
# 加载 Qwen tokenizer
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-7B-Instruct")
# 加载词汇
vocab = tokenizer.vocab
decoded_data = dict()
# 进行逆向映射
for k, v in vocab.items():
recovered_k = unicode_str_to_bytes(k)
try:
recovered_text = recovered_k.decode('utf-8')
decoded_data[str(recovered_text)] = v
except UnicodeDecodeError:
decoded_data[str(recovered_k)] = v
with open(r"./Qwen/vocab_decoded.json", "w", encoding='utf-8') as f:
f.write(json.dumps(decoded_data, ensure_ascii=False))
以下是解码后的一些输出,Qwen2.5
中的词汇表是一个多语言的词汇表;有一些词汇感觉有一点不能理解,例如)))\n\n\n
;不排除我可能对某些token没有进行很好的解码。
"führer": 142884, "皤": 121912, " freaking": 73240, " mono": 39674, " whom": 8711, "',...\n": 56268, " Wildcats": 96850, "你好": 108386, "_ot": 65614, "のではない": 127841, " disclosure": 27857, "῟": 149627, " pesquisa": 94630, "-wh": 55332, "===": 8707, "cade": 32796, "UIS": 28521, "引领": 104353, "_Tr": 21038, "_stub": 62781, ")))\n\n\n": 83809, " bytecode": 75129, "จำ": 124355, "NEWS": 68578, "_ent": 27425, ".exports": 7435, "狗狗": 109032, "鬣": 122119, " Alumni": 75426, "'>{": 63220, "ё": 44022, "朦胧": 116728, "GridColumn": 64144, "b'\\xb7\\xb8'": 32926, "逆转": 112229, " Abd": 29638, " Ünivers": 133696, " 너무": 133267, " Injection": 53811, "Nat": 65214, "manda": 35545,