对于非 NLP 专业的人来说,要向理解大语言模型的基础其实是非常不容易的。在有了一定的神经网络基础、数学基础之后,算是可以更进一步了,在了解LLM的系列中,大概可以分成几个部分:输入(即本文的tokenize)、计算(Attention)、输出(beam search/top-k)。在本篇中:(a) 通过代码实践观察大模型(GPT2)如何进行 tokenize;(b) 如何查看 token id 列表 (c) 观察模型中所有token的 Embedding Matrix。
这是我的大语言(LLM)学习系列中的一篇,完整的学习路径参考:我的大模型学习路线。
目录
1. 理解 tokenize
1.1 Token ID
这里我们使用如下的提示词,来看看大模型是如何处理的:It’s very hot in summer. Swimming is 。
大模型会使用预先设计好的“tokenize”实现,将上述的句子分解成独立的“Token”,并转换为对应的“Token ID”,而每个Token,都有自己的编码,也就是 Embedding ,这些 Embedding 就最终作为大语言模型的输入。
对于上述的句子,“openai-community/gpt2” 在进行 tokenize 之后对应的 Token 和 Token ID 如下:
| it | ’ | s | very | hot | in | summer | . | Swimming | is | |||
| Token ID | 1026 | 447 | 247 | 82 | 845 | 3024 | 287 | 3931 | 13 | 2451 | 27428 | 318 |
| Token | It | âĢ | Ļ | s | Ġvery | Ġhot | Ġin | Ġsummer | . | ĠSw | imming | Ġis |
“openai-community/gpt2” 使用了较为常见的BPE(Byte Pair Encoding)对句子进行处理,把每个词语按照“subword”进行处理,例如:
- “Swimming”拆分为“Sw”与“imming”
- 这里两个 Token 447、247 组成特殊字符 ‘ (撇号)
- Ġ(U+0120)的作用:表示这是一个新的词语(而不是一个拆分后子词),可以看到 “Swimming”在拆分后的“imming”前面并没有Ġ,表示这是一个拆分后的子词
1.2 词表大小
“openai-community/gpt2” 模型的词表大小为:50257。词表中的前三个 Token 为 ’emb’、 ‘ĠDraft’、 ‘Ġreinvent’,对应的 Token ID 为 24419、 13650、 36608。
1.3 根据 Token ID 打印字符
这里打印了 Token ID 为 0、 1、 2 和 50254、 50255、 50256 的几个字符如下:
| Token ID | Char |
| 0 | ! |
| 1 | “ |
| 2 | # |
| 50254 | Ġinformants |
| 50255 | Ġgazed |
| 50256 | <|endoftext|> |
--- Token ID 转换为 Token 字符 ---
Token ID: 0 | 对应 Token 字符: '!'
Token ID: 1 | 对应 Token 字符: '"'
Token ID: 2 | 对应 Token 字符: '#'
Token ID: 50254 | 对应 Token 字符: 'Ġinformants'
Token ID: 50255 | 对应 Token 字符: 'Ġgazed'
Token ID: 50256 | 对应 Token 字符: '<|endoftext|>'
2. Token Embedding
在大模型的通常是从 Embedding 开始的,即对于所有字符的处理,都是依赖字符对应的“向量”。所以,大致的处理逻辑是这样:一个句子,先切分为 Token,然后根据 Token ID 在“Embedding Matrix”中找到对应的“向量”,把该“向量”组作为输入。
这里,我们观察上述示例句子,根据对应的 Token ID 可以查询到对应的向量。因为这里的向量是768维的,这里仅显示前三个维度的分量,如下:
Token | wte[:3] |
----------------------------------------------
It | [0.039, -0.0869, 0.0662 ,...] |
âĢ | [-0.075, 0.0948, -0.0034,...] |
Ļ | [-0.0223, 0.0182, 0.2631 ,...] |
s | [-0.064, -0.0469, 0.2061 ,...] |
Ġvery | [-0.0553, -0.0348, 0.0606 ,...] |
Ġhot | [0.0399, -0.0053, 0.0742 ,...] |
Ġin | [-0.0337, 0.0108, 0.0293 ,...] |
Ġsummer | [0.0422, 0.0138, -0.0213,...] |
. | [0.0466, -0.0113, 0.0283 ,...] |
ĠSw | [0.0617, 0.0373, 0.1018 ,...] |
imming | [-0.1385, -0.1774, -0.0181,...] |
Ġis | [-0.0097, 0.0101, 0.0556 ,...] |
更为完整的,上述的每个词语对应的向量是一个 1×768 的向量;整个句子 12 个向量,可以理解为一个 12×768 的输入矩阵。对于各 LLM 来说,这通常就是其将要处理的输入。
3. Positional encoding
在“GPT-2”使用了“Learned Positional Embeddings”(注:与 Transformer 论文中固定的Sinusoidal 实现不同)。这是一个 \(L \times d \) 的矩阵,其中,\(L \) 是最大接收字符数,\(d \) 是 Token Embedding 的维度。该矩阵通常随机初始化,最终通过训练确定。在“GPT-2”中,最终训练后的矩阵有如下形式:
pos_emb_layer = model.transformer.wpe
# .weight.data : Embedding Matrix(PyTorch Tensor)
pos_emb_matrix = pos_emb_layer.weight.data
print(pos_emb_matrix[:12,:3])
输入如下:
tensor([[-0.0188, -0.1974, 0.0040],
[ 0.0240, -0.0538, -0.0949],
[ 0.0042, -0.0848, 0.0545],
[-0.0003, -0.0738, 0.1055],
[ 0.0076, -0.0251, 0.1270],
[ 0.0096, -0.0339, 0.1312],
[ 0.0027, -0.0205, 0.1196],
[ 0.0025, -0.0032, 0.1174],
[-0.0012, -0.0018, 0.1110],
[ 0.0049, 0.0021, 0.1178],
[ 0.0016, 0.0062, 0.1004],
[-0.0036, 0.0175, 0.1068]])
示例与代码
环境准备
- 这里依旧使用 🤗 HuggingFace 的 Transformers 库以及 Google 的 Colab 作为环境,硬件为 T4 GPU。这里新建的 Colab 文件链接: AutoTokenizer.ipynb
- 这里使用的测试模型是“openai-community/gpt2”,该模型是由 OpenAI 在 2019 对外发布的。
!pip install transformers torch
from transformers import AutoTokenizer, AutoModelForCausalLM
MODEL_NAME = "openai-community/gpt2"
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
model = AutoModelForCausalLM.from_pretrained(MODEL_NAME)
对句子进行 tokenize
观察句子的tokenize:
text = "It’s very hot in summer. Swimming is"
# 1. 对句子进行 Tokenization
# return_tensors='pt' 表示返回 PyTorch Tensor 格式 (虽然这里我们主要看 IDs)
inputs = tokenizer(text, return_tensors='pt')
# 2. 打印 Tokenization 结果
print(f"--- 原始句子:{text} ---")
# a. 打印 Token ID Tensor
print("Token IDs (Tensor):")
print(inputs['input_ids'])
# b. 将 Token ID 转换回可读的 Token (Word Pieces)
# .squeeze() 是为了去除 batch 维度
tokens = tokenizer.convert_ids_to_tokens(inputs['input_ids'].squeeze().tolist())
print("\nToken List (可读文本 Tokens):")
print(tokens)
# c. 打印 Attention Mask (1 表示是有效 Token,0 表示是 Padding Token)
print("\nAttention Mask:")
print(inputs['attention_mask'])
--------------
--- output ---
--------------
--- 原始句子:It’s very hot in summer. Swimming is ---
Token IDs (Tensor):
tensor([[ 1026, 447, 247, 82, 845, 3024, 287, 3931, 13, 2451,
27428, 318]])
Token List (可读文本 Tokens):
['It', 'âĢ', 'Ļ', 's', 'Ġvery', 'Ġhot', 'Ġin', 'Ġsummer', '.', 'ĠSw', 'imming', 'Ġis']
Attention Mask:
tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])
词表大小查看
查看词表大小,并打印词表中的前20个词:
# 获取词汇表大小
vocab_size = tokenizer.vocab_size
print(f"--- GPT-2 模型支持的 Tokenize 总数 (词汇表大小): {vocab_size} ---")
# 打印所有 Tokenize
# tokenizer.get_vocab() 返回的是一个字典 {token: id}
print("\n--- 打印前 20 个 Token (用于示例): ---")
vocab = tokenizer.get_vocab()
count = 0
for token, id in vocab.items():
if count < 20:
# 使用 repr() 确保特殊字符(如空格 'Ġ')能被清晰展示
print(f"ID: {id:<5} | Token: {repr(token)}")
count += 1
else:
break
--------------
--- output ---
--------------
--- GPT-2 模型支持的 Tokenize 总数 (词汇表大小): 50257 ---
--- 打印前 20 个 Token (用于示例): ---
ID: 24419 | Token: 'emb'
ID: 13650 | Token: 'ĠDraft'
ID: 36608 | Token: 'Ġreinvent'
ID: 36171 | Token: 'Recommended'
ID: 20706 | Token: 'aunting'
ID: 39558 | Token: 'Ġprotagonists'
ID: 49309 | Token: 'raised'
ID: 20589 | Token: 'Ġwicked'
ID: 43074 | Token: 'ĠâĿ'
ID: 22792 | Token: 'ĠTut'
ID: 21620 | Token: 'erate'
...
根据 Token ID 打印字符
# 待查询的 Token ID 列表
target_ids = [0, 1, 2, 50254 ,50255, 50256]
tokens = tokenizer.convert_ids_to_tokens(target_ids)
print(f"--- Token ID 转换为 Token 字符 ---")
for id, token in zip(target_ids, tokens):
# 使用 repr() 确保任何特殊的不可见字符(如空格或控制字符)能被清晰地展示
print(f"Token ID: {id:<5} | 对应 Token 字符: {repr(token)}")
# 额外打印这几个 Token 在词汇表中的 ID,以确认其对应关系
# 这里的 token.id 并不是直接从 1, 2, 3 来的,而是从 tokenizer.get_vocab() 中查到的
# 这是一个辅助验证步骤,确保 Tokenizer 的行为符合预期。
print("\n--- 辅助验证 ---")
for token in tokens:
token_id_check = tokenizer.convert_tokens_to_ids(token)
print(f"Token 字符: {repr(token):<10} | 查验 ID: {token_id_check}")
--------------
--- output ---
--------------
--- Token ID 转换为 Token 字符 ---
Token ID: 0 | 对应 Token 字符: '!'
Token ID: 1 | 对应 Token 字符: '"'
Token ID: 2 | 对应 Token 字符: '#'
Token ID: 50254 | 对应 Token 字符: 'Ġinformants'
Token ID: 50255 | 对应 Token 字符: 'Ġgazed'
Token ID: 50256 | 对应 Token 字符: '<|endoftext|>'
根据 Token ID 查询对应向量
在模型中,“词向量”(准确的应该是Token向量)存储在一个Embedding Matrix 中,可以使用如下的代码获取每个 token 对应 Embedding 后的向量:
target_token = "ĠSw" # 注意前面的特殊符号,确保它是模型词汇表中的 Token
target_id = tokenizer.convert_tokens_to_ids(target_token)
print(target_id)
# embedding_matrix[target_id]
target_embedding = embedding_matrix[target_id]
print(target_embedding[:5].numpy())
--------------
--- output ---
--------------
2451
[ 0.06167513 0.03733223 0.10182938 0.04881619 -0.09597597]
Embedding 层的 dtype: torch.float32
整个模型的默认 dtype: torch.float32
句子到 Embedding 向量
text = "It’s very hot in summer. Swimming is"
inputs = tokenizer(text, return_tensors='pt')
print("Token IDs (Tensor):")
input_ids_tensor = inputs['input_ids']
input_ids_list = input_ids_tensor.squeeze().tolist()
for index, token_int in enumerate(input_ids_list):
token_char = tokenizer.convert_ids_to_tokens(token_int)
print(f"Token ID {token_int:<5} | Token: {repr(token_char):<9} | {embedding_matrix[token_int][:3]}")
--------------
--- output ---
--------------
Token ID 1026 | Token: 'It' | tensor([ 0.0390, -0.0869, 0.0662])
Token ID 447 | Token: 'âĢ' | tensor([-0.0750, 0.0948, -0.0034])
Token ID 247 | Token: 'Ļ' | tensor([-0.0223, 0.0182, 0.2631])
Token ID 82 | Token: 's' | tensor([-0.0640, -0.0469, 0.2061])
Token ID 845 | Token: 'Ġvery' | tensor([-0.0553, -0.0348, 0.0606])
Token ID 3024 | Token: 'Ġhot' | tensor([ 0.0399, -0.0053, 0.0742])
Token ID 287 | Token: 'Ġin' | tensor([-0.0337, 0.0108, 0.0293])
Token ID 3931 | Token: 'Ġsummer' | tensor([ 0.0422, 0.0138, -0.0213])
Token ID 13 | Token: '.' | tensor([ 0.0466, -0.0113, 0.0283])
Token ID 2451 | Token: 'ĠSw' | tensor([0.0617, 0.0373, 0.1018])
Token ID 27428 | Token: 'imming' | tensor([-0.1385, -0.1774, -0.0181])
Token ID 318 | Token: 'Ġis' | tensor([-0.0097, 0.0101, 0.0556])
Embedding Matrix
查看 Embedding Matrix
# model.transformer.wte (Word Token Embeddings)
embedding_layer = model.transformer.wte
# .weight.data : Embedding Matrix(PyTorch Tensor)
embedding_matrix = embedding_layer.weight.data
print(embedding_matrix.shape)
--------------
--- output ---
--------------
torch.Size([50257, 768])
最后
大模型训练的第一步就是对于语料库(corpus)的处理,即将所有的语料转换为大模型训练能够接受的输入,即:tokenize。该过程会将语料库切分为独立的句子,多个句子可以作为一个批次(batch)作为输入进行训练。

























