大模型中提到的 token 是什么东西

2025/04/16 LLM 共 3855 字,约 12 分钟

目录

1. 写在前面

在自然语言处理(NLP)领域,特别是在使用大型预训练模型(如BERT、GPT等)时,我们经常会遇到“token”这个词。理解什么是 token 以及如何生成它们对于理解和应用这些模型至关重要。

2. Token 的定义和作用

2.1 Token 的基本概念

在 NLP 中,一个 token 可以被定义为文本中的最小单位,可以是:

  • 单词(如 “hello”)
  • 子词(如 “unhappiness” → “un”, “happiness”)
  • 字符(如 “h”, “e”, “l”, “l”, “o”)

例如,Hello, world!这句话可以被分割成两个tokens:Hello,world!

2.2 Token 在大模型中的作用

在大规模的语言模型中,比如 Transformer 架构的 BERT 或 GPT 系列,每个输入序列首先会被转换成一系列 tokens。这是因为模型的内部结构是基于对序列的处理来构建的,而不仅仅是单个字符或者单词。通过这种方式,模型可以更好地捕捉到句子之间的语义关系和上下文信息。

3. 如何生成 Tokens?

以 Transformer 模型中(如 BERT)为例,通常使用 WordPiece 算法进行子词划分(Subword Tokenization),这是一种将单词拆分成更小单元的方法。例如:

  1. 输入句子:Hello, world!
  2. Tokenization(子词划分): 可能拆分为:["Hello", ",", "world", "!"]

  3. 转为 ID:
    • 假设词表:{"Hello": 5, ",": 6, "world": 7, "!": 8}
    • 结果:[5, 6, 7, 8]
  4. 添加特殊 Token(如 BERT): [CLS] + Tokens + [SEP][101, 5, 6, 7, 8, 102]

  5. 最终张量: 直接输入 ID 或通过嵌入层转为向量。

4. Token 如何转为张量?

需要两步:

  • step1: Token 转 ID:用词表(Vocabulary)将 Token 映射为整数
  • step2: ID 转张量:将整数转换为向量(如词嵌入)或直接作为标量输入模型

下面以一个具体例子说明:

示例1:

假设有一个 prompt: I love NLP,有一个词表:{"<PAD>": 0, "<UNK>": 1, "I": 2, "love": 3, "NLP": 4}

其中:

  • <PAD>:填充符号
  • <UNK>:未知词
  1. Tokenization: 将 句子拆分为 tokens:["I", "love", "NLP"]
  2. Token -> ID: 用词表映射每个 token 到一个整数:[2, 3, 4]
  3. ID -> 张量

    • 标题形式:直接作为整数输入(形状 [3])tensor([2, 3, 4])
    • One-Hot 编码:
      • “I” (ID=2) → [0, 0, 1, 0, 0]
      • “love” (ID=3) → [0, 0, 0, 1, 0]
      • 最终张量(形状 [3, 5],5 是词表大小):
         tensor([
           [0, 0, 1, 0, 0],  # "I"
           [0, 0, 0, 1, 0],  # "love"
           [0, 0, 0, 0, 1]   # "NLP"
         ])
        
    • 词嵌入(Embedding):
      • 通过嵌入层将 ID 映射为低维度向量(如:维度=3)
         # 假设嵌入矩阵:
         embedding_matrix = [
           [0.0, 0.0, 0.0],  # <PAD>
           [0.1, 0.2, 0.3],  # <UNK>
           [0.4, 0.5, 0.6],  # "I"
           [0.7, 0.8, 0.9],  # "love"
           [1.0, 1.1, 1.2]   # "NLP"
         ]
        
      • 输入 [2, 3, 4] 会转换为(形状 [3, 3]):
          tensor([
          [0.4, 0.5, 0.6],  # "I"
          [0.7, 0.8, 0.9],  # "love"
          [1.0, 1.1, 1.2]   # "NLP"
        ])
        

        示例2:

        使用 HuggingFace Tokenizer transformers 库的示例: 使用 BertTokenizer 分词器对 I love NLP but bugs 进行分词:

from transformers import AutoTokenizer

# 加载预训练分词器
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")

# 分词并转为张量
inputs = tokenizer("I love NLP but bugs", return_tensors="pt")
print("BERT Tokenizer 输出:")
print("Input IDs:", inputs["input_ids"])  # Token IDs
print("Attention Mask:", inputs["attention_mask"])  # 非填充部分标记为1

输出:

BERT Tokenizer 输出:
Input IDs: tensor([[  101,  1045,  2293,  17953,  2021, 12471,   102]])  # 包含[CLS]和[SEP]
Attention Mask: tensor([[1, 1, 1, 1, 1, 1, 1]])

5. 词表

词表是通过统计训练数据中所有 Token 的频率生成的,下面使用 HuggingFace 的 transformers 库加载 GPT-2 分词器并查看词表:

  • 代码
from transformers import GPT2Tokenizer

# 加载 GPT-2 分词器
tokenizer = GPT2Tokenizer.from_pretrained("gpt2")

# 获取词表(字典格式:token → id)
vocab = tokenizer.get_vocab()

# 词表大小
print("词表大小:", len(vocab))  # 输出: 50257

# 打印前10个token和ID
print("\n前10个token示例:")
for idx, (token, token_id) in enumerate(vocab.items()):
    if idx >= 10:
        break
    print(f"Token: {token:<20} ID: {token_id}")

# 打印一些特殊token
special_tokens = {
    "bos_token": tokenizer.bos_token,       # 句子开始(GPT-2实际未显式使用)
    "eos_token": tokenizer.eos_token,       # 句子结束
    "unk_token": tokenizer.unk_token,       # 未知词
    "pad_token": tokenizer.pad_token,       # 填充符
}
print("\n特殊Token:")
for key, value in special_tokens.items():
    print(f"{key}: {value}")

# 随机采样一些有趣的token
random_tokens = ["!", "Hello", "ĠNLP", "ĠðŁĺ", "Ġtokenization", "ĠĠĠ", "512", "Ġ[", "Ġ]"]
print("\n随机token示例:")
for token in random_tokens:
    if token in vocab:
        print(f"Token: {token:<20} ID: {vocab[token]}")
  • 输出
词表大小: 50257

前10个token示例:
Token:  !                   ID: 0
Token: "                   ID: 1
Token: #                   ID: 2
Token: $                   ID: 3
Token: %                   ID: 4
Token: &                   ID: 5
Token: '                   ID: 6
Token: (                   ID: 7
Token: )                   ID: 8
Token: *                   ID: 9

特殊Token:
bos_token: None
eos_token: <|endoftext|>
unk_token: <|endoftext|>
pad_token: None

随机token示例:
Token: !                   ID: 0
Token: Hello               ID: 15496
Token: ĠNLP                ID: 4234
Token: ĠðŁĺ               ID: 12736
Token: Ġtokenization      ID: 38325
Token: ĠĠĠ                ID: 220
Token: 512                 ID: 40237
Token: Ġ[                 ID: 27
Token: Ġ]                 ID: 28
  1. 词表结构:
    • 普通字符:如 !, ", # 等符号。
    • 子词:以 Ġ 开头的 token(表示前面有空格,例如 ĠNLP 对应 “ NLP”)。
    • 数字和字母:如 512, Hello
    • 特殊符号:如 <|endoftext|>(GPT-2 的句子结束符)。
  2. 特殊标记:
    • GPT-2 没有显式的 bos_token(句子开始符),但用 <|endoftext|> 作为 eos_token
    • 空格会被编码为 Ġ(例如 “ Hello” → ĠHello)。
  3. 编码特性:
    • 使用 字节级 BPE(Byte Pair Encoding)分词,可以处理任意文本。
    • 生僻字符(如表情符号)会被拆分为多个子词。

另外,也可以通过直接查询指定 token 的 ID:

text = "NLP"
tokens = tokenizer.tokenize(text)
ids = tokenizer.encode(text)
print(f"文本: {text}")
print(f"Token化: {tokens}")  # 输出: ['ĠN', 'LP']
print(f"Token ID: {ids}")     # 输出: [4234, 2271]

6. 参考

  • https://huggingface.co/docs/tokenizers/index

Search

    Table of Contents