在大语言模型(如 GPT)中,单词嵌入(Word Embeddings)是一个极为重要的概念。它不仅是语言模型理解自然语言的基础,也是模型捕获语义关系的关键环节。通过单词嵌入,模型能够将离散的单词表示为高维连续向量,从而更高效地处理和生成自然语言。以下从理论与实践的结合角度,详细解析单词嵌入的概念、实现以及应用。
单词嵌入的定义与直观解释
单词嵌入是将单词映射到向量空间的一种技术。在这种空间中,语义相似的单词往往会被映射到相邻或接近的位置。具体来说,每个单词通过一个向量(通常是高维的浮点数数组)表示。例如:
中文 “苹果”
和 “橘子”
都属于水果类别,它们在嵌入空间中的位置相对较近,而与 “汽车”
的位置较远。
这种表示方式可以捕捉语义、语法等语言特性。模型基于嵌入向量,可以完成更复杂的自然语言任务。
理论基础:如何构建单词嵌入
共现矩阵与分布假设
单词嵌入的构建与分布假设密切相关:一个单词的意义由其上下文决定。例如,在句子 “我喜欢吃苹果”
和 “他讨厌橘子”
中,“苹果” 和 “橘子” 常在相似的上下文中出现,这表明它们语义相近。
基于这种假设,可以构建一个共现矩阵,每行表示一个单词,每列表示其上下文单词,共现值为计数。比如:
喜欢 | 吃 | 他 | 讨厌 | 橘子 | 苹果 | |
---|---|---|---|---|---|---|
苹果 | 3 | 5 | 0 | 0 | 1 | 0 |
橘子 | 2 | 4 | 1 | 3 | 0 | 0 |
尽管这种方式简单直观,但维度过高且稀疏性严重。因此,需要通过降维和优化生成更高效的嵌入。
神经网络方法:Word2Vec
Word2Vec 是一种流行的生成单词嵌入的神经网络模型,主要包括两种训练方法:
- CBOW(Continuous Bag of Words):预测一个单词的上下文中包含哪些单词。
- Skip-gram:给定一个单词,预测其上下文。
以 CBOW 模型为例,假设我们有一个句子 “我喜欢吃苹果”
。
- 输入:上下文单词
“我”
和“吃”
。 - 输出:目标单词
“喜欢”
。
通过训练,模型学会将语义相似的单词映射到相近的向量空间。
以下是一个基于 Python 和 TensorFlow 的简化实现:
import numpy as np
import tensorflow as tf
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import skipgrams
# 示例语料
corpus = ["我 喜欢 吃 苹果", "他 讨厌 橘子"]
# 文本标记化
tokenizer = Tokenizer()
tokenizer.fit_on_texts(corpus)
word2idx = tokenizer.word_index
vocab_size = len(word2idx) + 1
# 生成 skip-gram 数据
sequences = tokenizer.texts_to_sequences(corpus)
data = []
for seq in sequences:
pairs, labels = skipgrams(seq, vocab_size, window_size=2)
data.extend(pairs)
# 构建简单的嵌入模型
embedding_dim = 8
inputs = tf.keras.Input(shape=(2,))
input_target, input_context = tf.unstack(inputs, axis=1)
embedding = tf.keras.layers.Embedding(input_dim=vocab_size, output_dim=embedding_dim)
target_embed = embedding(input_target)
context_embed = embedding(input_context)
dot_product = tf.reduce_sum(target_embed * context_embed, axis=1)
outputs = tf.keras.layers.Activation('sigmoid')(dot_product)
model = tf.keras.Model(inputs=inputs, outputs=outputs)
model.compile(optimizer='adam', loss='binary_crossentropy')
# 模型训练
x_train = np.array(data)
y_train = np.ones(len(data))
model.fit(x_train, y_train, epochs=5)
训练后,可以提取嵌入向量:
word_embeddings = embedding.get_weights()[0]
print(word_embeddings)
嵌入向量的属性分析
嵌入向量有以下几个显著属性:
1. 语义相似性
嵌入向量能够捕捉语义关系。例如,如果 “苹果”
和 “橘子”
的嵌入向量在欧几里得距离上较近,这表明它们的语义相似。
2. 线性可组合性
在嵌入空间中,许多语义关系具有线性可组合性。例如:
向量(“国王”) - 向量(“男人”) + 向量(“女人”) ≈ 向量(“女王”)
。
这种属性可以通过数学运算直接验证:
king = word_embeddings[word2idx['国王']]
man = word_embeddings[word2idx['男人']]
woman = word_embeddings[word2idx['女人']]
queen = king - man + woman
3. 局限性与改进
尽管单词嵌入性能优异,但也存在以下问题:
- 词义模糊性:同一个单词在不同语境下可能有不同含义(如
“苹果”
既可以指水果,也可以指品牌)。 - 上下文独立性:传统嵌入方法无法捕捉动态上下文。
为了解决这些问题,模型如 ELMo 和 BERT 引入了上下文敏感的嵌入方法。
真实世界案例:GPT 模型中的单词嵌入
GPT 模型中的单词嵌入进一步发展,在语料预处理和训练过程中,采用了子词单元(Subword Units)技术。与传统方法相比,这种技术可以更高效地处理低频词和新词。
Byte-Pair Encoding(BPE)
BPE 是 GPT 嵌入的重要组成部分,通过将单词分解为子词单元,缓解了词表膨胀的问题。例如:
- 单词
“喜欢”
被分解为“喜”
和“欢”
。 - 新单词
“机器学习”
可以分解为“机”
、“器”
、“学”
、“习”
。
这种方式不仅减小了嵌入矩阵的尺寸,还增强了模型对新词的泛化能力。
以下是一个简单的 BPE 分解例子:
from collections import Counter
def bpe_encode(corpus, num_merges):
vocab = Counter(" ".join(corpus).split())
for _ in range(num_merges):
pairs = Counter()
for word, freq in vocab.items():
chars = list(word)
for i in range(len(chars)-1):
pairs[chars[i], chars[i+1]] += freq
most_common = max(pairs, key=pairs.get)
vocab = Counter({word.replace("".join(most_common), "".join(most_common)): freq for word, freq in vocab.items()})
return vocab
bpe_vocab = bpe_encode(["我喜欢吃苹果", "他讨厌橘子"], num_merges=10)
print(bpe_vocab)
总结
单词嵌入是语言模型的基石,通过将单词表示为连续向量,可以捕捉语义、语法等多维信息。在实际应用中,嵌入技术经历了从静态方法到动态方法的演进,如 Word2Vec 到 BERT,再到 GPT 模型中的子词嵌入,展现了语言理解的更高水平。通过理论结合实践,我们能够更深刻地理解大语言模型中单词嵌入的核心原理和应用场景。