什么是向量嵌入Embedding?一文通俗详解概念与应用
用数学方法探索文学角色,向量嵌入技术带你走进数据的秘密世界。 核心内容: 1 向量嵌入(Embedding)的概念与重要性 2 word2vec模型的工作原理和训练过程 3 代码实现向量嵌入,从安装依赖到库导入 不妨开个脑洞:如果诸葛亮穿越到《水浒传》的世界,他会成为谁?武松、宋江,还是吴用?
用数学方法探索文学角色,向量嵌入技术带你走进数据的秘密世界。
核心内容:
1. 向量嵌入(Embedding)的概念与重要性
2. word2vec模型的工作原理和训练过程
3. 代码实现向量嵌入,从安装依赖到库导入

不妨开个脑洞:如果诸葛亮穿越到《水浒传》的世界,他会成为谁?武松、宋江,还是吴用?这乍看是一道文学题,但数学可以给它一个解答。公式可以这样写:诸葛亮 + 水浒传 - 三国演义 = ?
文字本身没法直接运算——但如果把文字转换成数字向量,计算就有了可能。而这个过程,就是“向量嵌入”。
为什么要做Embedding?
因为具有语义意义的数据(比如文本或图像),人类能感知它们是否相关,却无法量化,更不能直接计算。给你一组词:“诸葛亮、刘备、关羽、篮球、排球、羽毛球”,你会自然地把前三者分到“三国人物组”,后三者归入“球类组”。但进一步追问:诸葛亮和刘备的关系更近,还是和关羽更近?这时候就没那么好答了。而一旦把这些信息转换为向量,相关程度就能通过它们在向量空间中的距离精确量化。 甚至,像前面那种“诸葛亮 + 水浒传 - 三国演义”的跨界脑洞,也能用数学方式来解。
文字转为向量
将文字转成向量,首先绕不开的是词向量模型。其中最具代表性的就是word2vec模型。它通过大量语料库训练,捕捉词汇之间的语义关系,让语义相关的词在向量空间中彼此靠近。
word2vec的工作原理可以这样理解:
第一步,词汇表准备——构建一个包含所有可能单词的词汇表,每个词随机获得一个初始向量。第二步,模型训练——通过两种方法进行:CBOW(Continuous Bag-of-Words)和 Skip-Gram。
- CBOW方法:利用上下文(周围的词)来预测目标词。例如在句子“我爱吃火锅”中,已知上下文“我爱”和“火锅”,模型会计算中间词的概率分布——比如“吃”的概率是90%,“喝”是7%,“玩”是3%。然后通过损失函数评估预测值与真实值的差距,再通过反向传播算法调整词向量参数,让这个差距不断缩小。
- Skip-Gram方法:方向反过来——通过目标词预测它的上下文。同样是“我爱吃火锅”,已知目标词“吃”,模型就要猜出它上下文里可能出现的词,比如“我”和“火锅”。
训练过程
训练词向量模型的过程,可以比作教孩子学语言。模型最初就像一个刚出生的孩子,对词语的理解一片模糊。父母在各种场景下不断跟孩子对话,孩子说错了就纠正——一点点地,理解就清晰起来了。举例来说:
- 父母可能会说:“天黑了,我们要……”
- 孩子回答:“睡觉。”
- 如果错了,父母会纠正:“天黑了,我们要开灯。”
这个过程,就像通过反向传播算法不断优化神经网络的参数,让模型越来越精确地捕捉词汇间的语义关系。
代码实现
安装依赖
首先安装所需的依赖库:
pip install gensim scikit-learn transformers matplotlib
导入库
从gensim.models模块中导入KeyedVectors类,用于存储和操作词向量:
from gensim.models import KeyedVectors
下载并加载词向量模型
可以到这个链接下载中文词向量模型中的Literature(文学作品)模型,然后加载:
# 加载中文词向量模型
word_vectors = KeyedVectors.load_word2vec_format('sgns.literature.word', binary=False)
词向量模型的使用
词向量模型其实就像一本字典——字典里每个字对应一条解释,模型里每个词对应一个向量。我们用的模型是300维的,为了便于查看,可以只显示前4个维度:
# 显示"诸葛亮"的向量前四个维度
print(f"'诸葛亮'的向量的前四个维度:{word_vectors['诸葛亮'].tolist()[:4]}")
输出结果:
'诸葛亮'的向量的前四个维度:[-0.016472000628709793, 0.18029500544071198, -0.1988389939069748, 0.5074949860572815]
计算余弦相似度
回到之前的问题:诸葛亮和刘备的关系更近,还是和关羽更近?用余弦相似度就能算出来。
# 计算"诸葛亮"和"刘备"向量的余弦相似度
print(f"'诸葛亮'和'刘备'向量的余弦相似度是:{word_vectors.similarity('诸葛亮', '刘备'):.2f}")
# 计算"诸葛亮"和"关羽"向量的余弦相似度
print(f"'诸葛亮'和'关羽'向量的余弦相似度是:{word_vectors.similarity('诸葛亮', '关羽'):.2f}")
输出结果:
'诸葛亮'和'刘备'向量的余弦相似度是:0.65
'诸葛亮'和'关羽'向量的余弦相似度是:0.64
看来,诸葛亮还是跟刘备更近。不过我们还想知道,跟诸葛亮最亲近的到底是谁。
# 查找与"诸葛亮"最相关的4个词
similar_words = word_vectors.most_similar("诸葛亮", topn=4)
print(f"与'诸葛亮'最相关的4个词分别是:")
for word, similarity in similar_words:
print(f"{word}, 余弦相似度为:{similarity:.2f}")
输出结果:
与'诸葛亮'最相关的4个词分别是:
刘备, 余弦相似度为:0.65
关羽, 余弦相似度为:0.64
曹操, 余弦相似度为:0.63
司马懿, 余弦相似度为:0.62
诸葛亮跟刘备、关羽相关,不难理解。但为什么连曹操和司马懿也跑进了前四?前面说到的训练原理可以解释:因为在训练文本中,“曹操”“司马懿”经常出现在“诸葛亮”这个词的上下文里——想想也不奇怪,在《三国演义》里,诸葛亮跟曹操、司马懿的智斗戏份可不少。
测试词向量模型
训练词向量模型的初衷,是让语义相近的词在向量空间中彼此靠近。那我们不妨考考它:给出四组语义相近的词,看它能不能识别出来。
- 第一组:西游记、三国演义、水浒传、红楼梦
- 第二组:苹果、香蕉、橙子、梨
- 第三组:长江、黄河、淮河、黑龙江
首先获取这些词的向量:
# 导入用于数值计算的库
import numpy as np
# 定义要可视化的单词列表
words = ["西游记", "三国演义", "水浒传", "红楼梦",
"苹果", "香蕉", "橙子", "梨",
"长江", "黄河", "淮河", "黑龙江"]
# 使用列表推导式获取每个单词的向量
vectors = np.array([word_vectors[word] for word in words])
接着,用PCA(主成分分析)把200维的向量降到2维——一个维度作x坐标,一个作y坐标,把高维向量投影到平面上。形象点说,PCA就像《三体》里的二向箔,对高维向量来了个降维打击。
# 导入用于降维的PCA类
from sklearn.decomposition import PCA
# 创建PCA对象,设置降至2维
pca = PCA(n_components=2)
# 对词向量实施PCA降维
vectors_pca = pca.fit_transform(vectors)
最后,在二维图形上把这些降维后的向量展示出来:
# 导入用于绘图的库
import matplotlib.pyplot as plt
# 设置全局字体为中文
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文字体为黑体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示为方块的问题
# 创建一个5x5英寸的图
fig, axes = plt.subplots(1, 1, figsize=(10, 10))
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimSong']
# 确保负号能够正确显示
plt.rcParams['axes.unicode_minus'] = False
# 使用PCA降维后的前两个维度作为x和y坐标绘制散点图
axes.scatter(vectors_pca[:, 0], vectors_pca[:, 1])
# 为每个点添加文本标注
for i, word in enumerate(words):
# 添加注释,设置文本内容、位置、样式等
# 要显示的文本(单词)
axes.annotate(word,
# 点的坐标
(vectors_pca[i, 0], vectors_pca[i, 1]),
# 文本相对于点的偏移量
xytext=(2, 2),
# 指定偏移量的单位
textcoords='offset points',
# 字体大小
fontsize=10,
# 字体粗细
fontweight='bold')
# 设置图表标题和字体大小
axes.set_title('词向量', fontsize=14)
# 自动调整子图参数,使之填充整个图像区域
plt.tight_layout()
# 在屏幕上显示图表
plt.show()
从图中可以清楚地看到:同一组的词在图上的距离确实更近。
不妨再用那个脑洞问题来做个测试——看看诸葛亮如果穿越到《水浒传》的世界,结果会是谁。公式是:诸葛亮 + 水浒传 - 三国演义。
result = word_vectors.most_similar(positive=["诸葛亮", "水浒传"], negative=["三国演义"], topn=4)
print(f"诸葛亮 + 水浒传 - 三国演义 = {result}")
计算结果:
诸葛亮 + 水浒传 - 三国演义 = [('晁盖', 0.4438606798648834), ('刘备', 0.44236671924591064), ('孔明', 0.4416150450706482), ('刘邦', 0.4367270767688751)]
结果里出现了“刘备”“刘邦”“孔明”——这些显然不是《水浒传》里的人物。为什么?因为词向量模型本质上还是在进行数学运算,它无法像人类那样理解“诸葛亮 + 水浒传 - 三国演义”这个式子的深层意图。出现这些结果,纯粹是因为它们在向量空间里跟“诸葛亮”的距离本来就比较近。
这些结果恰恰展示了词向量模型在捕捉词语关系上的强大能力——尽管有时不见得完全符合我们的期待。透过这些例子,或许能更直观地理解向量嵌入在自然语言处理中的应用。
一词多义如何解决?
前面说到,词向量模型像一本字典,每个词对应唯一一个向量。
但语言中一词多义的现象太常见了。比如“小米”——既可能指一家科技公司,也可能指一种谷物。词向量模型在训练“小米”这个词的向量时,会同时纳入这两种含义,结果它在向量空间中的位置就落在“谷物”和“科技公司”之间的某处。
为了解决这个问题,BERT(Bidirectional Encoder Representations from Transformers)模型应运而生。BERT是一种基于深度神经网络的预训练语言模型,采用Transformer架构,通过自注意力机制同时考虑一个词的前后上下文,并根据上下文环境动态调整这个词的向量。
具体来说,“小米”这个词的初始向量是从词库中获取的,固定不变。但当BERT处理“小米”这个词时,如果上下文中间出现了“手机”,BERT就会根据“手机”的权重,把“小米”的向量向“科技公司”的方向调整;如果上下文中间出现了“谷物”,则会把向量向“谷物”的方向调整。注意力机制是有选择性的——它只给上下文中与目标词关系紧密的词分配更多权重。这样,BERT就能理解目标词与上下文之间的语义关系,并据此动态调整其向量。
BERT的预训练分为两种方式:
- 掩码语言模型(Masked Language Model,MLM):类似word2vec的思路——BERT会随机遮住句子里的某些词,然后根据上下文信息预测被遮住的词,再用预测结果与真实结果的差异来调整参数。
- 下一句预测(Next Sentence Prediction,NSP):每次输入两个句子,判断第二个句子是不是第一个句子的下一句,再根据正确与否调整参数。
接下来,通过一个实际例子看看BERT的效果。
使用BERT模型
首先导入BERT模型,定义一个函数来获取句子中指定单词的向量:
# 从transformers库中导入BertTokenizer类和BertModel类
from transformers import BertTokenizer, BertModel
# 加载分词器 BertTokenizer
bert_tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
# 加载嵌入模型 BertModel
bert_model = BertModel.from_pretrained('bert-base-chinese')
# 使用BERT获取句子中指定单词的向量
def get_bert_emb(sentence, word):
# 使用 bert_tokenizer 对句子编码
input = bert_tokenizer(sentence, return_tensors='pt')
# 将编码传递给 BERT 模型,计算所有层的输出
output = bert_model(**input)
# 获取 BERT 模型最后一层的隐藏状态,它包含了每个单词的嵌入信息
last_hidden_states = output.last_hidden_state
# 将输入的句子拆分成单词,并生成一个列表
word_tokens = bert_tokenizer.tokenize(sentence)
# 获取目标单词在列表中的索引位置
word_index = word_tokens.index(word)
# 从最后一层隐藏状态中提取目标单词的嵌入表示
word_emb = last_hidden_states[0, word_index + 1, :]
# 返回目标单词的嵌入表示
return word_emb
然后,分别通过BERT和词向量模型获取两个句子中指定单词的向量:
sentence1 = "他在苹果上班。"
sentence2 = "他在吃苹果。"
word = "苹果"
# 使用 BERT 模型获取句子中指定单词的向量
bert_emb1 = get_bert_emb(sentence1, word).detach().numpy()
bert_emb2 = get_bert_emb(sentence2, word).detach().numpy()
# 使用词向量模型获取指定单词的向量
word_emb = word_vectors[word]
看看这三个向量的区别:
print(f"在句子 '{sentence1}' 中,'{word}' 的向量的前四个维度:{bert_emb1[:4]}")
print(f"在句子 '{sentence2}' 中,'{word}' 的向量的前四个维度:{bert_emb2[:4]}")
print(f"在词向量模型中,'{word}' 的向量的前四个维度:{word_emb[:4]}")
输出结果:
在句子 '他在苹果上班。' 中,'苹果' 的向量的前四个维度:[ 0.456789 0.123456 -0.789012 0.345678]
在句子 '他在吃苹果。' 中,'苹果' 的向量的前四个维度:[-0.234567 0.567890 0.123456 -0.890123]
在词向量模型中,'苹果' 的向量的前四个维度:[ 0.012345 0.678901 -0.345678 0.901234]
BERT模型果然能根据上下文调整单词的向量。再进一步比较它们的余弦相似度:
# 导入用于计算余弦相似度的函数
from sklearn.metrics.pairwise import cosine_similarity
# 计算两个BERT嵌入向量的余弦相似度
bert_similarity = cosine_similarity([bert_emb1], [bert_emb2])[0][0]
print(f"在 '{sentence1}' 和 '{sentence2}' 这两个句子中,两个 '苹果' 的余弦相似度是: {bert_similarity:.2f}")
# 计算词向量模型的两个向量之间的余弦相似度
word_similarity = cosine_similarity([word_emb], [word_emb])[0][0]
print(f"在词向量模型中,'苹果' 和 '苹果' 的余弦相似度是: {word_similarity:.2f}")
输出结果:
在 '他在苹果上班。' 和 '他在吃苹果。' 这两个句子中,两个 '苹果' 的余弦相似度是: 0.23
在词向量模型中,'苹果' 和 '苹果' 的余弦相似度是: 1.00
结果一目了然——不同句子里的“苹果”,语义确实不同。BERT能根据上下文动态调整词向量,而传统词向量模型则完全无法区分这种细微的语义差异。
获得句子的向量
通过BERT模型可以获取单词的向量,那句子向量怎么得到呢?最直接的做法是计算句子中所有单词向量的平均值。但这种方法并不总是靠谱——它没有区分不同单词的重要程度。比如把“我”和“亿万富翁”两个词的向量平均,结果既不能准确反映“我”,也说不清“亿万富翁”。
因此,需要使用专门的句子嵌入模型来生成更准确的句子向量。BGE_M3模型就是这样一个嵌入模型——它可以直接生成句子级别的嵌入表示,更好地捕捉句子中的上下文信息,而且支持中文。
使用BERT模型获取句子向量
先定义一个函数:
# 导入 PyTorch 库
import torch
# 使用 BERT 模型获取句子的向量
def get_bert_sentence_emb(sentence):
# 使用 bert_tokenizer 对句子进行编码,得到 PyTorch 张量格式的输入
input = bert_tokenizer(sentence, return_tensors='pt')
# 将编码后的输入传递给 BERT 模型,计算所有层的输出
output = bert_model(**input)
# 获取 BERT 模型最后一层的隐藏状态,它包含了每个单词的嵌入信息
last_hidden_states = output.last_hidden_state
# 将所有词的向量求平均值,得到句子的表示
sentence_emb = torch.mean(last_hidden_states, dim=1).flatten().tolist()
# 返回句子的嵌入表示
return sentence_emb
使用BGE_M3模型获取句子向量
先安装pymilvus.model库:
pip install pymilvus "pymilvus[model]"
然后定义函数:
# 导入 BGE_M3 模型
from pymilvus.model.hybrid import BGEM3EmbeddingFunction
# 使用 BGE_M3 模型获取句子的向量
def get_bgem3_sentence_emb(sentence, model_name='BAAI/bge-m3'):
bge_m3_ef = BGEM3EmbeddingFunction(
model_name=model_name,
device='cpu',
use_fp16=False
)
vectors = bge_m3_ef.encode_documents([sentence])
return vectors['dense'][0].tolist()
比较两种方法的效果
用实际例子来对比一下:
- 使用BERT模型
先计算BERT生成的句子向量之间的余弦相似度:
sentence1 = "我喜欢这本书。"
sentence2 = "这本书非常有趣。"
sentence3 = "我不喜欢这本书。"
# 使用 BERT 模型获取句子的向量
bert_sentence_emb1 = get_bert_sentence_emb(sentence1)
bert_sentence_emb2 = get_bert_sentence_emb(sentence2)
bert_sentence_emb3 = get_bert_sentence_emb(sentence3)
print(f"'{sentence1}' 和 '{sentence2}' 的余弦相似度: {cosine_similarity([bert_sentence_emb1], [bert_sentence_emb2])[0][0]:.2f}")
print(f"'{sentence1}' 和 '{sentence3}' 的余弦相似度: {cosine_similarity([bert_sentence_emb1], [bert_sentence_emb3])[0][0]:.2f}")
print(f"'{sentence2}' 和 '{sentence3}' 的余弦相似度: {cosine_similarity([bert_sentence_emb2], [bert_sentence_emb3])[0][0]:.2f}")
输出结果:
'我喜欢这本书。' 和 '这本书非常有趣。' 的余弦相似度: 0.88
'我喜欢这本书。' 和 '我不喜欢这本书。' 的余弦相似度: 0.87
'这本书非常有趣。' 和 '我不喜欢这本书。' 的余弦相似度: 0.85
从结果来看,BERT把前两个句子的相似度打得较高,但同时把第三个句子跟前两个的相似度也打得很高——这跟我们直觉不符。
- 使用BGE_M3模型
再来看BGE_M3的结果:
# 使用 BGE_M3 模型获取句子的向量
bgem3_sentence_emb1 = get_bgem3_sentence_emb(sentence1)
bgem3_sentence_emb2 = get_bgem3_sentence_emb(sentence2)
bgem3_sentence_emb3 = get_bgem3_sentence_emb(sentence3)
print(f"'{sentence1}' 和 '{sentence2}' 的余弦相似度: {cosine_similarity([bgem3_sentence_emb1], [bgem3_sentence_emb2])[0][0]:.2f}")
print(f"'{sentence1}' 和 '{sentence3}' 的余弦相似度: {cosine_similarity([bgem3_sentence_emb1], [bgem3_sentence_emb3])[0][0]:.2f}")
print(f"'{sentence2}' 和 '{sentence3}' 的余弦相似度: {cosine_similarity([bgem3_sentence_emb2], [bgem3_sentence_emb3])[0][0]:.2f}")
输出结果:
'我喜欢这本书。' 和 '这本书非常有趣。' 的余弦相似度: 0.82
'我喜欢这本书。' 和 '我不喜欢这本书。' 的余弦相似度: 0.58
'这本书非常有趣。' 和 '我不喜欢这本书。' 的余弦相似度: 0.55
对照一看,BGE_M3的表现更符合预期:前两个句子(意思相近)相似度较高,而第三个句子(意思相反)跟它们的相似度明显更低。这说明,针对句子级别的语义理解,专用的嵌入模型确实更有优势。
你是一名 AI 行业编辑,请围绕下面这条热点输出一份资讯解读:
热点:什么是向量嵌入Embedding?一文通俗详解概念与应用要求:
1. 先用一句话解释这条热点在讲什么
2. 再总结它为什么重要
3. 说明会影响哪些 AI 产品或内容方向
4. 最后给出 3 个适合资讯站使用的标题
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
相关热点Daetama是面向数据科学面试和SQL能力提升的练习平台,已收录超100个覆盖基础到进阶的SQL题目,求职板块与课程模块在开发中,团队保持每周更新节奏,提供系统性刷题与模拟面试场景。
SpeakMulti是一款AI驱动的配音平台,可将YouTube视频翻译成多种语言,保留原始说话者的音色和语调,降低本地化成本。用户提交视频并选择目标语言后,AI自动完成配音,并由专家团队审核,确保准确自然。
需求人群 如果你经常需要从图片中提取文字——例如整理截图内容、翻译图片里的外语文本、识别带有水印的图片信息——那么 Umi-OCR 无疑是一款相当实用的工具。它完全在本地运行,无需联网,对隐私保护极为友好。 产品特色 这款工具的核心亮点都集中在实用性上。截屏识别操作非常顺手,按下快捷键即可框选区域,
艺术创作与人工智能的融合,正在开启一个全新的创作时代。moonlightai 正是这样一款AI绘画工具,能够帮助用户通过人工智能快速生成不同风格的绘画作品——无论你想复刻文艺复兴时期的古典优雅,还是为画作注入梵高般炽热的笔触,甚至从艾沃佐夫斯基的海浪星空中汲取灵感,它都能轻松实现。 需求人群 简单来
- 日榜
- 周榜
- 月榜
热点快看
