NLP_词的向量表示Word2Vec 和 Embedding

文章目录

  • 词向量
  • Word2Vec:CBOW模型和Skip-Gram模型
  • 通过nn.Embedding来实现词嵌入
  • Word2Vec小结


词向量

下面这张图就形象地呈现了词向量的内涵:把词转化为向量,从而捕捉词与词之间的语义和句法关系,使得具有相似含义或相关性的词语在向量空间中距离较近。
在这里插入图片描述
我们把语料库中的词和某些上下文信息,都“嵌入”了向量表示中。

将词映射到向量空间时,会将这个词和它周围的一些词语一起学习,这就使得具有相似语义的词在向量空间中靠得更近。这样,我们就可以通过向量之间的距离来度量词之间的相似性了。

Word2Vec:CBOW模型和Skip-Gram模型

稠密向量中的元素大部分为非零值。稠密向量通常具有较低的维度,同时能够捕捉到更丰富的信息。Word2Vec就是一种典型的稠密向量表示。稠密向量能够捕捉词与词之间的语义和语法关系,使得具有相似含义或相关性的词在向量空间中距离较近。

在自然语言处理中,稠密向量通常更受欢迎,因为它们能够捕捉到更多的信息,同时计算效率更高。下图直观地展示了二者的区别。
在这里插入图片描述

通过Word2Vec学习得到的向量可以捕捉到词与词之间的语义和语法关系。而且,这个算法比以前的方法更加高效,能够轻松地处理大规模的文本数据。因此,Word2Vec迅速流行起来。

具体来说,Word2Vec有两种主要实现方式:CBOW(Continuous Bag of Words,有时翻译为“连续词袋”)模型和Skip-Gram(有时翻译为“跳字”)模型,如下图所示。CBOW模型通过给定上下文词(也叫“周围词”)来预测目标词(也叫“中心词”);而Skip-Gram模型则相反,通过给定目标词来预测上下文词。这两个模型都是通过训练神经网络来学习词向量的。在训练过程中,我们通过最小化预测词和实际词之间的损失来学习词向量。当训练完成后,词向量可以从神经网络的权重中提取出来。
在这里插入图片描述

通过nn.Embedding来实现词嵌入

在PyTorch中,nn.Embedding是nn中的一个模块,它用于将离散的索引(通常是单词在词汇表中的索引)映射到固定大小的向量空间。在自然语言处理任务中,词嵌入是将单词表示为高维向量的一种常见方法。词嵌入可以捕捉单词之间的相似性、语义关系等。在训练过程中,嵌入层会自动更新权重以最小化损失函数,从而学习到有意义的词向量。

嵌入层的构造函数接收以下两个参数。

  • num_embeddings :词汇表的大小,即唯一单词的数量。
  • embedding_dim:词嵌入向量的维度。
    使用嵌入层有以下优点。
  • 更简洁的代码:与线性层相比,嵌入层提供了更简洁、更直观的表示词嵌入的方式。这使得代码更容易理解和维护。
  • 更高的效率:嵌入层比线性层更高效,因为它不需要进行矩阵乘法操作。它直接从权重矩阵中查找对应的行(嵌入向量),这在计算上更高效。
  • 更容易训练:嵌入层不需要将输入转换为One-Hot编码后的向量。我们可以直接将单词索引作为输入,从而减少训练的计算复杂性。
# 定义一个句子列表,后面会用这些句子来训练 CBOW 和 Skip-Gram 模型
sentences = ["Kage is Teacher", "Mazong is Boss", "Niuzong is Boss",
             "Xiaobing is Student", "Xiaoxue is Student",]
# 将所有句子连接在一起,然后用空格分隔成多个单词
words = ' '.join(sentences).split()
# 构建词汇表,去除重复的词
word_list = list(set(words))
# 创建一个字典,将每个词映射到一个唯一的索引
word_to_idx = {word: idx for idx, word in enumerate(word_list)}
# 创建一个字典,将每个索引映射到对应的词
idx_to_word = {idx: word for idx, word in enumerate(word_list)}
voc_size = len(word_list) # 计算词汇表的大小
print(" 词汇表:", word_list) # 输出词汇表
print(" 词汇到索引的字典:", word_to_idx) # 输出词汇到索引的字典
print(" 索引到词汇的字典:", idx_to_word) # 输出索引到词汇的字典
print(" 词汇表大小:", voc_size) # 输出词汇表大小

在这里插入图片描述

# 生成 Skip-Gram 训练数据
def create_skipgram_dataset(sentences, window_size=2):
    data = [] # 初始化数据
    for sentence in sentences: # 遍历句子
        sentence = sentence.split()  # 将句子分割成单词列表
        for idx, word in enumerate(sentence):  # 遍历单词及其索引
            # 获取相邻的单词,将当前单词前后各 N 个单词作为相邻单词
            for neighbor in sentence[max(idx - window_size, 0): 
                        min(idx + window_size + 1, len(sentence))]:
                if neighbor != word:  # 排除当前单词本身
                    # 将相邻单词与当前单词作为一组训练数据
                    data.append((neighbor, word))
    return data
# 使用函数创建 Skip-Gram 训练数据
skipgram_data = create_skipgram_dataset(sentences)
# 打印未编码的 Skip-Gram 数据样例(前 3 个)
print("Skip-Gram 数据样例(未编码):", skipgram_data[:3])

在这里插入图片描述

# 定义 One-Hot 编码函数
import torch # 导入 torch 库
def one_hot_encoding(word, word_to_idx):    
    tensor = torch.zeros(len(word_to_idx)) # 创建一个长度与词汇表相同的全 0 张量  
    tensor[word_to_idx[word]] = 1  # 将对应词的索引设为 1
    return tensor  # 返回生成的 One-Hot 向量
# 展示 One-Hot 编码前后的数据
word_example = "Teacher"
print("One-Hot 编码前的单词:", word_example)
print("One-Hot 编码后的向量:", one_hot_encoding(word_example, word_to_idx))
# 展示编码后的 Skip-Gram 训练数据样例
print("Skip-Gram 数据样例(已编码):", [(one_hot_encoding(context, word_to_idx), 
          word_to_idx[target]) for context, target in skipgram_data[:3]])

在这里插入图片描述

# 定义 Skip-Gram 模型
import torch.nn as nn # 导入 neural network
class SkipGram(nn.Module):
    def __init__(self, voc_size, embedding_size):
        super(SkipGram, self).__init__()
        # 从词汇表大小到嵌入大小的嵌入层(权重矩阵)
        self.input_to_hidden = nn.Embedding(voc_size, embedding_size)  
        # 从嵌入大小到词汇表大小的线性层(权重矩阵)
        self.hidden_to_output = nn.Linear(embedding_size, voc_size, bias=False) 
    def forward(self, X):
        hidden_layer = self.input_to_hidden(X)  # 生成隐藏层:[batch_size, embedding_size]
        output_layer = self.hidden_to_output(hidden_layer)  # 生成输出层:[batch_size, voc_size]
        return output_layer  
embedding_size = 2 # 设定嵌入层的大小,这里选择 2 是为了方便展示
skipgram_model = SkipGram(voc_size, embedding_size)  # 实例化 Skip-Gram 模型
print("Skip-Gram 模型:", skipgram_model)

在这里插入图片描述

# 训练 Skip-Gram 类
learning_rate = 0.001 # 设置学习速率
epochs = 1000 # 设置训练轮次
criterion = nn.CrossEntropyLoss()  # 定义交叉熵损失函数
import torch.optim as optim # 导入随机梯度下降优化器
optimizer = optim.SGD(skipgram_model.parameters(), lr=learning_rate)  
# 开始训练循环
loss_values = []  # 用于存储每轮的平均损失值
for epoch in range(epochs):
    loss_sum = 0 # 初始化损失值
    for context, target in skipgram_data:        
        X = torch.tensor([word_to_idx[target]], dtype=torch.long)  # # 输入是中心词
        y_true = torch.tensor([word_to_idx[context]], dtype=torch.long)  # 目标词是周围词
        y_pred = skipgram_model(X)  # 计算预测值
        loss = criterion(y_pred, y_true)  # 计算损失
        loss_sum += loss.item() # 累积损失
        optimizer.zero_grad()  # 清空梯度
        loss.backward()  # 反向传播
        optimizer.step()  # 更新参数
    if (epoch+1) % 100 == 0: # 输出每 100 轮的损失,并记录损失
        print(f"Epoch: {epoch+1}, Loss: {loss_sum/len(skipgram_data)}")  
        loss_values.append(loss_sum / len(skipgram_data))
# 绘制训练损失曲线
import warnings
warnings.filterwarnings("ignore")
import matplotlib.pyplot as plt
%matplotlib inline
from matplotlib.font_manager import FontProperties
font = FontProperties(fname='SimHei.ttf', size = 15)
# 绘制二维词向量图
#plt.rcParams["font.family"]=['SimHei'] # 用来设定字体样式
#plt.rcParams['font.sans-serif']=['SimHei'] # 用来设定无衬线字体样式
#plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号
plt.plot(range(1, epochs//100 + 1), loss_values) # 绘图
plt.title(' 训练损失曲线 ', FontProperties = font) # 图题
plt.xlabel(' 轮次 ', FontProperties = font) # X 轴 Label
plt.ylabel(' 损失 ', FontProperties = font) # Y 轴 Label
plt.show() # 显示图

在这里插入图片描述

# 输出 Skip-Gram 习得的词嵌入
print("Skip-Gram 词嵌入:")
for word, idx in word_to_idx.items(): # 输出每个词的嵌入向量
    print(f"{word}: {skipgram_model.input_to_hidden.weight[idx].detach().numpy()}")

在这里插入图片描述

# 绘制二维词向量图
#plt.rcParams["font.family"]=['SimHei'] # 用来设定字体样式
#plt.rcParams['font.sans-serif']=['SimHei'] # 用来设定无衬线字体样式
#plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号
fig, ax = plt.subplots() 
for word, idx in word_to_idx.items():
    # 获取每个单词的嵌入向量
    vec = skipgram_model.input_to_hidden.weight[idx].detach().numpy() 
    ax.scatter(vec[0], vec[1]) # 在图中绘制嵌入向量的点
    ax.annotate(word, (vec[0], vec[1]), fontsize=12) # 点旁添加单词标签
plt.title(' 二维词嵌入 ', FontProperties = font) # 图题
plt.xlabel(' 向量维度 1', FontProperties = font) # X 轴 Label
plt.ylabel(' 向量维度 2', FontProperties = font) # Y 轴 Label
plt.show() # 显示图

在这里插入图片描述

此外,因为nn.Embedding 是一个简单的查找表,所以input_to_hidden. weight的维度为[voc_size,embedding_size]。因此,当打印和可视化权重时,需要使用weight[idx] 来获取权重。

这个向量蕴含在 PyTorch的嵌入层中,可以通过embedding_size参数来调整它的维度。此处嵌入层的维度是2,但刚才说过,处理真实语料库时,嵌入层的维度一般来说有几百个,这样才可以习得更多的语义知识。其实,几百维的词向量,对于动辄拥有上万,甚至十万、百万个词的词汇表(《辞海》的词条数,总条目数近13万)来说,已经算是很“低”维、很稠密了。

所以,词向量或者说词嵌入的学习过程就是,通过神经网络来习得包含词的语义信息的向量,这个向量通常是几维到几百维不等,然后可以降维进行展示,以显示词和词之间的相似程度。如图所示。
在这里插入图片描述

这些词向量捕捉了词与词之间的关系之后,具有相似含义或用法的词在向量空间中会靠得更近。我们可以使用这些词向量作为其他自然语言处理任务(如文本分类、文本相似度比较、命名实体识别等)的输入特征。

Word2Vec之后的许多词嵌入方法,如G1oVe (Global Vectors for Word Representation)和fastText,也都是这样使用的。我们可以拿到别人已经训练好的词向量(G1oVe和fastText都提供现成的词向量供我们下载)作为输入,来完成我们的下游NLP任务;也可以利用PyTorch 的nn.Embedding,来针对特定语料库从头开始词嵌入的学习,然后再把学到的词向量(也就是经过nn.Embedding的参数处理后的序列信息)作为输入,完成下游NLP任务。

Word2Vec小结

Word2Vec对整个自然语言处理领域产生了巨大的影响。后来的许多词嵌入方法,如GloVe 和 fastText 这两种被广泛应用的词向量,都受到了Word2Vec的启发。如今,Word2Vec已经成为词嵌入领域的基石。它的出现使得更复杂的NLP任务,如文本分类、情感分析、命名实体识别、机器翻译等,处理起来更轻松。这主要是因为 Word2Vec 生成的词向量能够捕捉到单词之间的语义和语法关系。
然而,Word2Vec仍然存在一些局限性。

  • (1)词向量的大小是固定的。Word2Vec这种“在全部语料上一次习得,然后反复使用”的词向量被称为静态词向量。它为每个单词生成一个固定大小的向量,这限制了模型捕捉词义多样性的能力。在自然语言中,许多单词具有多种含义,但 Word2Vec无法为这些不同的含义生成多个向量表示。
  • (2)无法处理未知词汇。Word2Vec只能为训练过程中出现过的单词生成词向量。对于未知或低频词汇,Word2Vec无法生成合适的向量表示。虽然可以通过拼接词根等方法来解决这个问题,但这并非Word2Vec 本身的功能。

值得注意的是,Word2Vec本身并不是一个完善的语言模型,因为语言模型的目标是根据上下文预测单词,而Word2Vec主要关注生成有意义的词向量。尽管 CBOW和 Skip-Gram 模型在训练过程中学习了单词之间的关系,但它们并未直接对整个句子的概率分布进行建模。而后来的模型,如基于循环神经网络、长短期记忆网络和 Transformer 的模型,则通过对上下文进行建模,更好地捕捉到了语言结构,从而成为更为强大的语言模型。


学习的参考资料:
(1)书籍
利用Python进行数据分析
西瓜书
百面机器学习
机器学习实战
阿里云天池大赛赛题解析(机器学习篇)
白话机器学习中的数学
零基础学机器学习
图解机器学习算法

动手学深度学习(pytorch)

(2)机构
光环大数据
开课吧
极客时间
七月在线
深度之眼
贪心学院
拉勾教育
博学谷
慕课网
海贼宝藏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/374933.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024牛客寒假算法基础集训营1——H

输入 3 4 11 1 8 1 4 1 5 1 1 4 11 5 8 1 4 1 5 1 1 4 0 2 0 0 0 3 0 4 1 输出 3 6 5 思路&#xff1a; 考虑二进制&#xff0c;有点像数位dp 本题考虑集合划分&#xff0c;累加最大值即可 代码如下&#xff1a; #include<bits/stdc.h> using namespace std;void solv…

2024三掌柜赠书活动第八期:Web3与DAO:下一代互联网演进逻辑

目录 前言关于Web3和DAO关于《Web3与DAO&#xff1a;下一代互联网演进逻辑》编辑推荐内容简介作者简介精彩书评图书目录书中前言/序言《Web3与DAO&#xff1a;下一代互联网演进逻辑》全书速览结束语 前言 随着区块链技术的崛起&#xff0c;Web3和DAO成为了当前互联网领域炙手…

【达梦数据库】使用DBeaver管理达梦数据库

使用DBeaver管理达梦数据库 Step1 安装相关程序 达梦8数据库DBeaver社区版 Step2 新建驱动 类型参数驱动名称DM8驱动类型Generic类名dm.jdbc.driver.DmDriverURL模板jdbc:dm://{host}:{port}默认端口5236默认数据库默认用户SYSDBA Step3 连接服务

docker部署笔记系统flatnotes

效果 安装 创建目录 mkdir -p /opt/flatnotes/data && cd /opt/flatnotes/ chmod -R 777 /opt/flatnotes/ 创建并启动容器(可以自己修改账户和密码) docker run -d \ --restart unless-stopped \ --name flatnotes \ -p "10040:8080" \ -v "/dat…

vue3 之 组合式API—生命周期函数

vue3的生命周期API 生命周期函数基本使用 1️⃣导入生命周期函数 2️⃣执行生命周期函数 传入回调 <scirpt setup> import { onMounted } from vue onMounted(()>{// 组件挂载完毕mounted执行了 }) </script>执行多次 生命周期函数是可以执行多次的&#xff…

【pikachu csrf】

cxrf 个人理解getPOST 个人理解 当被攻击用户登陆访问网站时&#xff0c;在保持登陆状态时点击小黑子&#xff08;黑客&#xff09;搭建的恶意链接而导致用户受到攻击。 举个例子 我去攻击网站&#xff0c;但是我找不到漏洞&#xff0c;这个时候我注册一个账号&#xff0c;发现…

C++——stack与queue与容器适配器

1.stack和queue的使用 1.1stack的使用 栈这种数据结构我们应该挺熟了&#xff0c;先入后出&#xff0c;只有一个出口(出口靠栈顶近)嘛 stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类&#xff0c;这些容器类应该支持以操作&#xff1a; empty&#xff1…

【leetcode题解C++】77.组合 and 216.组合总和III and 17.电话号码的字母组合

77. 组合 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1a; [[2,4],[3,4],[2,3],[1,2],[1,3],[1,4], ] 示例 2&#xff1a; 输入&#xff1a…

Linux系统调试课:ftrace跟踪器介绍

文章目录 一、什么是frace跟踪器?二、Ftrace 配置三、Ftrace 文件系统四、Ftrace 初体验五、函数跟踪六、Ftrace function_graph七、函数 Profiler沉淀、分享、成长,让自己和他人都能有所收获!😄 一、什么是frace跟踪器? 操作系统内核对应用开发工程师来说就像一个黑盒,…

页面单跳转换率统计案例分析

需求说明 页面单跳转化率 计算页面单跳转化率&#xff0c;什么是页面单跳转换率&#xff0c;比如一个用户在一次 Session 过程中访问的页面路径 3,5,7,9,10,21&#xff0c;那么页面 3 跳到页面 5 叫一次单跳&#xff0c;7-9 也叫一次单跳&#xff0c; 那么单跳转化率就是要统计…

2024年【上海市安全员C3证】考试题库及上海市安全员C3证报名考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年【上海市安全员C3证】考试题库及上海市安全员C3证报名考试&#xff0c;包含上海市安全员C3证考试题库答案和解析及上海市安全员C3证报名考试练习。安全生产模拟考试一点通结合国家上海市安全员C3证考试最新大纲…

刘诗诗与吴奇隆共度甜蜜亲子时光,力破离婚传闻,恩爱如初。

♥ 为方便您进行讨论和分享&#xff0c;同时也为能带给您不一样的参与感。请您在阅读本文之前&#xff0c;点击一下“关注”&#xff0c;非常感谢您的支持&#xff01; 文 |猴哥聊娱乐 编 辑|徐 婷 校 对|侯欢庭 最近&#xff0c;备受瞩目的狗仔队摄影师刘大锤&#xff0c;成…

删除.git的影响、git分支切换时注意事项

一、删除.git的影响 master分支文件 dev分支文件 删除.git后 文件为删除.git前分支的文件状态。 二、git分支切换时注意事项 情景&#xff1a;如果我在分支A&#xff0c;想要跳转到分支B。 git的规矩是&#xff0c;在那个分支上进行的提交&#xff0c;就算哪个分支上的工作…

代码随想录算法训练营第37天(贪心算法06 ● 738.单调递增的数字 ● 968.监控二叉树 ● 总结

贪心算法 part06 738.单调递增的数字解题思路不熟悉的基础语法知识 968.监控二叉树 &#xff08;可以跳过&#xff09;解题思路 总结 738.单调递增的数字 题目链接&#xff1a; 738.单调递增的数字 文章/视频链接: 738.单调递增的数字 解题思路 一旦出现strNum[i - 1] > …

ubuntu开机报错/dev/nume0n1p2:clean

一、前提 1、当你平时用的图站或者linux系统出现这个问题&#xff0c;首先看看你的显卡有没有换位置。 我的就是项目电脑&#xff0c;同事换了显卡位置&#xff0c;我不知道&#xff0c;当我在这个基础上继续做的时候&#xff0c;出了问题。 2、当你是第一次装显卡&#xff…

肯尼斯·里科《C和指针》第12章 使用结构和指针(1)链表

只恨当时学的时候没有读到这本书&#xff0c;&#xff0c;&#xff0c;&#xff0c;&#xff0c;&#xff0c; 12.1 链表 有些读者可能还不熟悉链表&#xff0c;这里对它作一简单介绍。链表(linked list)就一些包含数据的独立数据结构&#xff08;通常称为节点&#xff09;的集…

[HTML]Web前端开发技术15(HTML5、CSS3、JavaScript )表格,bordercolorlight,frame,valign,rowspan,colspan——喵喵画网页

希望你开心&#xff0c;希望你健康&#xff0c;希望你幸福&#xff0c;希望你点赞&#xff01; 最后的最后&#xff0c;关注喵&#xff0c;关注喵&#xff0c;关注喵&#xff0c;佬佬会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#xff0c;你对我真的…

DataX详解和架构介绍

系列文章目录 一、 DataX详解和架构介绍 二、 DataX源码分析 JobContainer 三、DataX源码分析 TaskGroupContainer 四、DataX源码分析 TaskExecutor 五、DataX源码分析 reader 六、DataX源码分析 writer 七、DataX源码分析 Channel 文章目录 系列文章目录DataX是什么&#xff…

探索C语言结构体:编程中的利器与艺术

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C语言学习 贝蒂的主页&#xff1a;Betty‘s blog 1. 常量与变量 1. 什么是结构体 在C语言中本身就自带了一些数据类型&#x…

前端实现标题滚动点击导航

效果图 右边滚动的html代码 <div class"right-box"><el-tabs v-model"isScrollNow" tab-position"right" class"updateTab" tab-click"scrollTo"style"height: fit-content;"><el-tab-pane label…