UNK
在自然语言处理(NLP)和大语言模型中,UNK 是 Unknown Token 的缩写,中文译为未知词元,是预训练语言模型在词汇表(Vocabulary) 中专门预留的一个特殊标记。
当模型处理文本时,遇到不在词汇表中的词 / 子词(即 OOV,Out-of-Vocabulary 词),就会将这个词替换为 [UNK],以此避免因未登录词导致的模型报错,保证文本序列处理的连续性。
为什么会出现 UNK?
词汇表大小有限:模型预训练时会构建一个固定大小的词汇表(比如 BERT-base 的词汇表约 30,000 个词),无法覆盖人类语言中所有的词(比如生僻词、专业术语、网络新词、拼写错误词)。
子词切分的兜底策略:现代模型(如 BERT、GPT)会用 WordPiece、Byte-Pair Encoding (BPE) 等子词切分算法,将未登录词切分为更小的子词。但如果切分后仍有子词不在词汇表中,就会用 [UNK] 替代。
典型场景示例
假设模型词汇表中只有 [我]、[喜欢]、[吃]、[苹果]、[UNK] 这些标记
输入句子"我喜欢吃车厘子":词汇表中没有"车厘子"词;子词切分后也无法拆分为已有子词;最终句子会被转换为:[我] [喜欢] [吃] [UNK]
UNK\] 本身没有语义信息,大量出现会降低模型理解和生成的精度 1. 扩大词汇表规模,纳入更多领域专属词汇 2. 优化子词切分算法(如改进 BPE),减少未登录词 3. 针对特定领域微调模型,补充领域词汇 4. 与其他特殊标记的区别 在大模型词汇表中,\[UNK\] 是和 \[PAD\](填充标记)、\[CLS\](分类标记)、\[SEP\](分隔标记)并列的特殊 token,各自分工不同: \[PAD\]:用于补齐不等长的文本序列 \[CLS\]:用于聚合句子语义,做分类任务 \[SEP\]:用于分隔两个句子(如问答任务的问题和上下文) \[UNK\]:专门处理未知词 ## 词嵌入,Word Embedding 大模型处理词嵌入(Word Embedding) 的核心逻辑: 将离散的文本符号(词 / 子词 / 字符)映射为连续的低维稠密向量,让模型能够通过向量运算捕捉语义关联。 该过程分为【基础嵌入层生成】和【上下文感知嵌入增强】两个核心阶段 不同架构的模型(如 BERT、GPT)会在此基础上做针对性优化 词嵌入的核心目标 自然语言是离散的符号系统(比如 "苹果" 是一个独立符号),而深度学习模型只能处理数值向量。 词嵌入的作用 将每个 token 转化为固定维度的向量(比如 768 维) 让语义相似的 token 对应的向量在空间中距离更近(比如 "苹果" 和 "香蕉" 的向量相似度 \> "苹果" 和 "电脑") 为后续的注意力机制、全连接层提供可计算的语义表示 ### 大模型处理词嵌入的完整流程 `/ˌtəʊkənaɪˈzeɪʃn/` #### 第一步:Token 化(Tokenization)------ 离散符号拆分 词嵌入前置步骤,模型先将原始文本拆分为最小处理单元(token),拆分后每个 token 会被映射为一个固定的整数 ID(来自模型词汇表) 常见策略有 3 种: 词级拆分:按空格 / 标点拆分(如英文 I love China → \[I, love, China\]),缺点是易产生大量 OOV(未登录词),已逐渐被淘汰 子词级拆分:主流方案(WordPiece、BPE、SentencePiece),将词拆分为子词(如 unhappiness → \[un, happy, ness\]),兼顾词汇覆盖度和语义完整性 字符级拆分:拆分为单个字符,适合小语种或低资源语言,但语义表达能力弱 低资源语言(Low-Resource Language) 是指在自然语言处理(NLP)和人工智能领域中,缺乏足够高质量数字化数据、工具和研究资源的语言 判断是否为低资源语言,核心不是语言的使用人口多少,而是支撑模型训练与应用的资源丰富度 #### 第二步:基础嵌入层(Embedding Layer)------ 离散→连续的映射 词嵌入的核心转换环节,模型通过一个可训练的嵌入矩阵,将 token 的整数 ID 转化为基础向量 核心原理:嵌入矩阵的维度为 \[词汇表大小, 嵌入维度\](如 BERT-base 是 \[30522, 768\]),每一行对应一个 token 的基础向量 (个人记录:这里的30522是行数,有这么多token;768是列数,表示任何一个token,会用768个数值的向量表示) 计算过程:对于输入 token 的 ID 序列 \[id1, id2, id3\],直接从嵌入矩阵中取出对应行,得到基础嵌入向量序列 \[e1, e2, e3
关键特点:基础嵌入是静态的(预训练阶段学习,微调阶段可更新),仅表示 token 的固有语义,不包含上下文信息(比如 "银行" 的基础向量,无法区分是 "金融银行" 还是 "河岸")
第三步:增强嵌入 ------ 注入位置与类型信息
基础嵌入只包含 token 本身的语义,而大模型需要捕捉位置信息和句子类型信息,因此会在基础嵌入上叠加两种特殊嵌入:
【位置嵌入(Position Embedding)】
告诉模型 token 在序列中的位置,解决 Transformer 对位置不敏感的问题
- BERT:可训练的位置嵌入(每个位置对应一个向量,随模型训练更新)
- GPT:正余弦位置嵌入(固定公式生成,无需训练)
【分段嵌入(Segment Embedding)】
区分不同句子(比如问答任务中的 "问题" 和 "上下文")
BERT 专用,输入两个句子时,第一个句子的 token 加 segment 0 向量,第二个句子的 token 加 segment 1 向量
最终基础表示:token_embedding + position_embedding + segment_embedding(三者维度相同,直接相加)
第四步:上下文感知嵌入 ------ 动态语义增强
大模型词嵌入的关键升级,通过 Transformer 的注意力机制,将基础表示转化为上下文相关的动态嵌入(也叫 Contextual Embedding)
核心逻辑:每个 token 的最终嵌入,不仅包含自身基础语义,还融合序列中其他所有 token 的语义信息(通过注意力权重加权求和)
示例:
我在银行取钱:"银行" 的上下文嵌入会偏向 "金融机构"
河边有个银行:"银行" 的上下文嵌入会偏向 "建筑物"
架构差异:
BERT(纯 Encoder):通过双向注意力生成双向上下文嵌入,每个 token 融合前后文信息;
GPT(纯 Decoder):通过单向注意力生成单向上下文嵌入,每个 token 仅融合前文信息;
T5(Encoder-Decoder):Encoder 生成双向上下文嵌入,Decoder 结合前文和 Encoder 输出,生成带对齐信息的嵌入。
第五步:嵌入的输出与应用
最终的上下文嵌入会作为后续任务的输入,不同任务的使用方式不同:
理解类任务(分类、NER):取 [CLS] token 的最终嵌入作为句子的全局语义表示
生成类任务(续写、翻译):将每个 token 的最终嵌入输入到语言模型头(LM Head),预测下一个 token
匹配类任务(语义相似度):计算两个句子嵌入的余弦相似度
大模型词嵌入的关键优化点
子词嵌入的优势:
相比传统词嵌入(如 Word2Vec),大模型的子词嵌入能处理 OOV 词(拆分为子词后,即使词不在词汇表,子词也大概率存在)
上下文嵌入的动态性:
传统词嵌入是静态的(一个词对应一个固定向量),大模型上下文嵌入是动态的,同一个词在不同语境下向量不同,更符合语言歧义性
嵌入层的训练策略:
预训练阶段,嵌入矩阵与模型其他参数一起训练
微调阶段,可选择冻结嵌入层(减少计算量)或继续更新(适配下游任务)
总结
大模型处理词嵌入的流程可以概括为:
文本 → Token 化 → 基础嵌入(token+位置+分段) → 注意力机制增强 → 上下文感知嵌入 → 下游任务输出
从 "静态基础嵌入" 到 "动态上下文嵌入" 的升级,是大模型词嵌入区别于传统词嵌入的核心标志
OOV
全称是 Out-of-Vocabulary,中文译为未登录词
在模型预定义的词汇表中没有收录的词或子词
Word2Vec - 静态词嵌入的经典
Word2Vec
Word2Vec 是静态词嵌入的经典实现,和大模型的上下文嵌入不同:
它的核心是 "一个词对应一个固定向量",不考虑上下文歧义(比如 "银行" 只有一个向量),但能体现基础的语义相似性
第一步:环境准备
pip install gensim numpy
第二步:
python
from gensim.models import Word2Vec
from gensim.utils import simple_preprocess
# ===================== 1. 准备训练数据 =====================
# 原始文本(模拟小语料库,实际场景会用更大的文本集)
corpus = [
"我 喜欢 吃 苹果",
"我 喜欢 吃 香蕉",
"我 不 喜欢 吃 橘子",
"香蕉 和 苹果 都是 水果",
"橘子 也是 一种 水果",
"猫 喜欢 吃鱼",
"狗 喜欢 啃 骨头"
]
# 数据预处理:将每个句子拆分为词列表(Word2Vec要求输入是"句子列表+每个句子是词列表")
# simple_preprocess 会自动分词、小写(中文需提前分词,这里已分好)
processed_corpus = [simple_preprocess(sentence, min_len=1) for sentence in corpus]
print("预处理后的数据:")
for sent in processed_corpus:
print(sent)
# ===================== 2. 训练 Word2Vec 模型 =====================
# 核心参数说明:
# - vector_size:词向量维度(对应之前说的嵌入维度,这里设为10,简化计算)
# - window:上下文窗口大小(取每个词前后各2个词作为上下文)
# - min_count:忽略出现次数少于1的词(本例数据少,设为1)
# - sg:训练模式,0=CBOW(用上下文预测中心词),1=Skip-gram(用中心词预测上下文)
model = Word2Vec(
sentences=processed_corpus,
vector_size=10,
window=2,
min_count=1,
sg=0 # 用CBOW模式,更适合小语料
)
# 保存/加载模型(可选)
# model.save("my_word2vec.model")
# model = Word2Vec.load("my_word2vec.model")
# ===================== 3. 验证 Word2Vec 核心功能 =====================
# 功能1:获取单个词的向量
print("\n===== 单个词的向量 =====")
apple_vec = model.wv["苹果"]
print("苹果的向量:", apple_vec)
# 功能2:找语义相似的词(核心能力)
print("\n===== 语义相似的词 =====")
# 找和"苹果"最相似的3个词
similar_to_apple = model.wv.most_similar("苹果", topn=3)
print("和苹果最相似的词:", similar_to_apple)
# 找和"喜欢"最相似的3个词
similar_to_like = model.wv.most_similar("喜欢", topn=3)
print("和喜欢最相似的词:", similar_to_like)
# 功能3:语义类比(经典的"国王-男人+女人=女王"逻辑)
print("\n===== 语义类比 =====")
# 尝试:苹果 - 水果 + 动物 = ?(预期接近猫/狗)
# 注:本例语料太小,结果可能不完美,但能体现逻辑
try:
analogy = model.wv.most_similar(positive=["苹果", "动物"], negative=["水果"], topn=2)
print("苹果 - 水果 + 动物 = ", analogy)
except KeyError as e:
print(f"类比失败:缺少词 {e}")
# 功能4:计算两个词的相似度
print("\n===== 词相似度计算 =====")
sim_apple_banana = model.wv.similarity("苹果", "香蕉")
sim_apple_dog = model.wv.similarity("苹果", "狗")
print("苹果 vs 香蕉 的相似度:", sim_apple_banana)
print("苹果 vs 狗 的相似度:", sim_apple_dog)
第三步:运行结果解释(示例)
plaintext
预处理后的数据:
['我', '喜欢', '吃', '苹果']
['我', '喜欢', '吃', '香蕉']
['我', '不', '喜欢', '吃', '橘子']
['香蕉', '和', '苹果', '都是', '水果']
['橘子', '也是', '一种', '水果']
['猫', '喜欢', '吃鱼']
['狗', '喜欢', '啃', '骨头']
===== 单个词的向量 =====
苹果的向量: [0.012, -0.034, 0.056, ...] (共10个数值)
===== 语义相似的词 =====
和苹果最相似的词: [('香蕉', 0.92), ('橘子', 0.88), ('水果', 0.85)]
和喜欢最相似的词: [('吃', 0.90), ('我', 0.87), ('猫', 0.75)]
===== 语义类比 =====
苹果 - 水果 + 动物 = [('猫', 0.78), ('狗', 0.76)]
===== 词相似度计算 =====
苹果 vs 香蕉 的相似度: 0.92
苹果 vs 狗 的相似度: 0.21
苹果和香蕉的相似度远高于苹果和狗,说明 Word2Vec 捕捉到了 "苹果 / 香蕉都是水果" 的语义关联
"喜欢" 和 "吃" 相似度高,因为语料中二者频繁共现,符合 Word2Vec "共现即相关" 的核心逻辑
和大模型嵌入的区别
Word2Vec 的向量是静态的:"喜欢" 在所有语境下都是同一个向量
大模型(如 BERT)的向量是动态的:"喜欢" 在 "我喜欢吃苹果" 和 "猫喜欢吃鱼" 中向量不同
总结
Word2Vec 核心是通过词的共现关系训练静态词向量,一个词对应一个固定向量
核心功能包括获取词向量、找相似词、语义类比,能体现基础的语义关联
早期词嵌入的经典方案,相比大模型的上下文嵌入,缺少对语境歧义的处理能力
共现即相关,出现在相似上下文中的词往往具有相似的含义
BPE分词
BPE 的全称是 Byte-Pair Encoding
中文译为字节对编码,是一种子词切分算法,也是大语言模型(如 GPT、BERT)中主流的 Tokenization 策略之一
核心目标是在词汇表大小和语义完整性之间做平衡,将单词拆分为更小的子词单元:
既解决了传统词级分词的 OOV(未登录词)问题,又避免了字符级分词的语义碎片化
BPE 的核心原理
BPE 的本质是从训练语料中,迭代合并出现频率最高的字符 / 子词对,最终生成一个包含 "字符、子词、完整词" 的混合词汇表
整个过程分为 训练阶段(构建词汇表) 和 推理阶段(切分新词) 两步
训练阶段:构建子词词汇表
假设训练语料只有 3 个单词:low, lower, newest
步骤 1:初始化
将所有单词拆分为单个字符,并在词尾添加特殊标记 (表示词的结束,区分前缀和完整词):
plaintext
low → l o w </w>
lower → l o w e r </w>
newest → n e w e s t </w>
统计所有字符的出现频率:
l:2, o:2, w:2, :3, e:2, r:1, n:1, s:1, t:1
步骤 2:迭代合并高频字符对
设定目标词汇表大小(比如从 9 扩充到 12),每次找出出现次数最多的相邻字符对并合并:
第 1 轮:统计所有相邻对的频率,w 出现 2 次
重新统计:
语料拆分后的相邻对:
l-o(2), o-w(2), w-(1, low), w-e(1, lower), e-r(1), r-(1), n-e(1), e-w(1), w-e(1), e-s(1), s-t(1), t-(1)
最高频对是 l-o(2 次),合并为 lo,词汇表新增 lo
第 2 轮:基于新的拆分,继续找高频对,比如 o-w(2 次),合并为 ow,词汇表新增 ow
第 3 轮:继续合并,比如 e-s(1 次,若没有更高频),直到词汇表达到目标大小
步骤 3:生成最终词汇表
最终词汇表包含原始字符、合并后的子词、完整词,例如:l, o, w, , lo, ow, lower, ...
- 推理阶段:切分新词(OOV 词)
当遇到词汇表中没有的新词时,BPE 会用贪心策略从左到右切分:
从词的开头开始,匹配词汇表中最长的子词
切分后剩余部分重复此过程,直到整个词切分完毕
词尾添加
示例:假设新词是 newer,词汇表中有 ne, ew, er, e, w 等子词
切分过程:newer → ne w e r → 若词汇表有 new,则切分为 new e r
BPE 的核心优势
解决 OOV 问题
即使遇到未见过的词,也能拆分为已有的子词,避免 [UNK] 标记的滥用。比如 unhappiness 可拆分为 un + happy + ness
平衡词汇表大小
相比词级分词(词汇表动辄几十万),BPE 词汇表更小,降低模型嵌入层参数量;相比字符级分词,子词更具语义(ing tion 有意义的后缀)
跨语言友好
不需要针对不同语言设计复杂的分词规则(如中文的 jieba 分词),直接基于字符迭代合并,适合低资源语言
BPE 的变体与应用
原始 BPE 是字符级的,后来衍生出多个变体,被不同模型采用:
WordPiece:BERT 采用的变体,合并策略不是 "频率最高",而是 "合并后能最大程度提升训练数据的似然概率",更适合双向模型
SentencePiece:将空格也视为一个字符,实现无监督分词,不需要提前做分词预处理,适合多语言模型
BPE 与传统分词的区别
分词方式 核心逻辑 优点 缺点
词级分词 按空格 / 词典拆分单词 语义完整 词汇表大,OOV 多
字符级分词 拆分为单个字符 无 OOV,词汇表小 语义碎片化,模型学习难度大
BPE 子词分词 迭代合并高频字符对 平衡词汇表与 OOV,语义完整 训练过程稍复杂