自然语言处理: 第二章Word2Vec

news2024/11/26 16:49:12

一. 理论基础

  1. 维度很高(与语料库有关),计算复杂
  2. 稀疏性,浪费计算效率,只有一个元素是1 其他都是0
  3. 缺乏语义信息,无法衡量语义相似度
  4. 无法处理未知单词


而在One-Hot的基础上,Word2Vec 是一种分布式表达字/词的方式,其是以一种能够刻画语义之间相似度并且纬度较低的稠密向量表示。如下图,利用Word2vec的表示的话,其在特征空间拥有相似语义的向量,距离更加接近或者说相似度更高,并且赋予了向量语义的信息, 比如 : 爸爸 - 儿子 ≈ 妈妈 - 女儿 。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CykDpolr-1686836480800)(image/word2vec/1686833893211.png)]



那么Word2vec如何实现的呢? 主要有两种实现方式:

  1. CBOW(Continuous Bag of Words)模型,通过滑动窗口的词去预测中心词 , 其中窗口中的词经过embedding层求一个sum(mean)然后再去预测中心词 。由于CBOW在训练过程中关注于根据上下文词预测目标词,它倾向于对频繁出现的词汇表现更好,因为这些词对整体上下文的贡献更大
  2. Skip-Gram 模型 , 通过中心词去预测滑动窗口的词( 每个词与中心词都组成一个pair 然后构建网络去预测) 。在训练过程中关注于预测上下文词。因此,它通常在处理罕见词汇和具有更大窗口大小的数据集上表现更好

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GfqECyH8-1686836480802)(image/word2vec/1686834123606.png)]



下面以CBOW做一个图解 ,:

  1. 得到一个目标句子 : 【‘我’,‘喜欢’,‘咖哥’,‘老师’】
  2. 现在设定是窗口是2 所以目前的中心词是’咖哥’ , 目标是’老师’
  3. 将输入词’咖哥’做One-Hot 编码
  4. 输入一个线性层,得到一个embedding 向量(其权重矩阵应该是 vocab_size * embedding大小)
  5. 然后将embedding 向量再通过一个线形层映射回原字典大小的向量坐Softmax , 与目标单词’老师’ 做交叉熵 更新网络参数
  6. 经过多次迭代后,取4中的权重每一行作为每个单词的表达embedding ,也就是我们求得Word2Vec (好像也有一种做法是把同样5中的权重(embedding * vocab_size)每一列作为embedding 或者将前面的4和5的embedding做一个mean)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1ikpJXqu-1686836480803)(image/word2vec/1686834884796.png)]



二.代码实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qptFrjLr-1686836480804)(image/word2vec/1686835548880.png)]

代码实现的流程如上图, 可以分成6步 ,下面主要演示了下关键步骤代码:



  • 构建语料库, 利用类实现,通过输入语料库句子 和设定窗口值进行初始化
class CBOW():
    def __init__(self ,  sentences , window_size = 3 ,) -> None:
        self.window_size = window_size
        self.sentences = sentences
        # 将所有句子连接在一起,然后用空格分隔成词汇
        words = ' '.join(sentences).split()
        # 构建词汇表,去除重复的词
        self.word_list = list(set(words))
        # 创建一个字典,将每个词汇映射到一个唯一的索引
        self.word_to_idx = {word: idx for idx, word in enumerate(self.word_list)}
        # 创建一个字典,将每个索引映射到对应的词汇
        self.idx_to_word = {idx: word for idx, word in enumerate(self.word_list)}
        self.voc_size = len(self.word_list) # 计算词汇表的大小
        print("词汇表:", self.word_list) # 输出词汇表
        print("词汇到索引的字典:", self.word_to_idx) # 输出词汇到索引的字典
        print("索引到词汇的字典:", self.idx_to_word) # 输出索引到词汇的字典


  • 生成skipgram数据 , 这里分了两种情况 第一种 get_embedding_ENdataset是生成One-Hot编码的训练数据 , 对应着网络输入一个linear 而第二种是 get_embedding_dataset 是直接生成idx 而对应着网络输入一个embedding层
    def _encoding(self, word):
        tensor = torch.zeros(self.voc_size) 
        tensor[self.word_to_idx[word]] = 1
        return tensor

    def get_embedding_ENdataset(self):
        data = self._get_unembedding_dataset() # 拿到为编码的数据
        self.cbow_data = [(self.word_to_idx[target] , [self._encoding(content) for content in context_words]) for target , context_words in data]
        print("CBOW数据样例(已编码):", self.cbow_data[0])
        return self.cbow_data

    def get_embedding_dataset(self):
        data = self._get_unembedding_dataset() # 拿到为编码的数据
        self.cbow_data = [(self.word_to_idx[target] , [self.word_to_idx[content] for content in context_words]) for target , context_words in data]
        print("CBOW数据样例(已编码):", self.cbow_data[0])
        return self.cbow_data


  • 定义CBOW网络, 这里对比了两种网络,一种是输入是linear 一种输入是embedding,其实整体词袋模型是一个只有一个隐藏层的softmax网络。
class CBOW_onehot(nn.Module):
    def __init__(self, voc_size, embedding_size):
        super(CBOW_onehot, self).__init__()
        # 从词汇表大小到嵌入大小的线性层(权重矩阵)
        self.input_to_hidden = nn.Linear(voc_size, 
                                         embedding_size, bias=False)  
        # 从嵌入大小到词汇表大小的线性层(权重矩阵)
        self.hidden_to_output = nn.Linear(embedding_size, 
                                          voc_size, bias=False)  

    def forward(self, X): # X: [num_context_words, voc_size]
        # 生成嵌入:[num_context_words, embedding_size]
        embeddings = self.input_to_hidden(X)  
        # 计算隐藏层,求嵌入的均值:[embedding_size]
        hidden_layer = torch.mean(embeddings, dim=0)  
        # 生成输出层:[1, voc_size]
        output_layer = self.hidden_to_output(hidden_layer.unsqueeze(0)) 
        return output_layer
  
class CBOW_embedding(nn.Module):
    def __init__(self, voc_size, embedding_size):
        super(CBOW_embedding, 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): # X: [num_context_words, voc_size]
        # 生成嵌入:[num_context_words, embedding_size]
        embeddings = self.input_to_hidden(X)  
        # 计算隐藏层,求嵌入的均值:[embedding_size]
        hidden_layer = torch.mean(embeddings, dim=0)  
        # 生成输出层:[1, voc_size]
        output_layer = self.hidden_to_output(hidden_layer.unsqueeze(0)) 
        return output_layer


  • 训练模型 , 这里定义了training 函数, 其中参数第一个是不同的网络,lr是学习率, epoch 是迭代次数 , 而flag对应的是不同模型对应着不同的输入数据,需要声明一下。
    def training(self , cbow_model:nn.Module , lr = 0.001 , epochs = 1000 , flag = False):
        assert self.cbow_data is not None
        criterion = nn.CrossEntropyLoss()  # 定义交叉熵损失函数
        optimizer = optim.SGD(cbow_model.parameters(), lr= lr)

        # 开始训练循环
        loss_values = []  # 用于存储每轮的平均损失值
        for epoch in range(epochs):
            loss_sum = 0
            for target, context_words in self.cbow_data:
                # 将上下文词转换为One-hot向量并堆叠
                # flag == 1 indicates embedding , else onehot
                X = torch.tensor(context_words) if flag else torch.stack(context_words).float()

                y_pred = cbow_model(X)  # 计算预测值
                y_true = torch.tensor(target).reshape(1)
                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(self.cbow_data)}")  
                loss_values.append(loss_sum / len(self.cbow_data))



三.结果

loss 曲线

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O4G4fOvx-1686836480804)(image/word2vec/1686836242167.png)]

特征空间表征,可以看到意思相近的词位置靠的更加接近:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LjMv0BLw-1686836480805)(image/word2vec/1686836273582.png)]

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java|注解之定义注解

Java语言使用interface语法来定义注解(Annotation),它的格式如下: public interface Report {int type() default 211;String level() default "211";String value() default "211"; } 注解的参数类似无参数…

华为OD机试真题 JavaScript 实现【比赛评分】【2023 B卷 100分】,附详细解题思路

一、题目描述 一个有N个选手参加比赛&#xff0c;选手编号为1~N&#xff08;3<N<100&#xff09;&#xff0c;有M&#xff08;3<M<10&#xff09;个评委对选手进行打分。打分规则为每个评委对选手打分&#xff0c;最高分10分&#xff0c;最低分1分。 请计算得分最…

Android 音视频开发核心知识点笔记整合

很多开发者都知道Android音视频开发这个概念&#xff0c;音视频开发不仅需要掌握图像、音频、视频的基础知识&#xff0c;并且还需要掌握如何对它们进行采集、渲染、处理、传输等一系列的开发和应用&#xff0c;因此&#xff0c;音视频开发是一门涉及到很多内容的领域。 随着5G…

开战在即!与全球伙伴一起打造你的数据应用,TiDB Future App Hackathon 2023 来啦!

2023 TiDB Future App Hackathon 来啦&#xff01;本届 Hackathon 的主题为&#xff1a;Code, Innovate & Build Amazing Data Applications —— 释放你的创造力、构建突破性的应用、在全球范围内寻找你的队友、体验最新最 in 的 Serverless 技术&#xff0c;更有 总计 $3…

马原第一章复习1.

一.物质的存在方式 《德法年鉴》 完成从唯物到唯心 从革命主义等到共产主义的过度 为创立马克思理论提供了根本前提《德意志形态》 首次阐述了历史唯物主义的基本观点《共产党宣言》标志着马克思主义的公开问世 也是第一个无产阶级政党的党纲《资本论》阐述剩余价值学说 解释生…

【推荐】Oracle Live SQL——在线 Oracle SQL 测试工具

最近回答了几个 CSDN “学习”功能里“问答”区的一些专业相关问题&#xff0c;回答过程中采用严谨的方式&#xff0c;在 Oracle Live SQL 上进行验证测试。这个很好用的 Oracle APEX 应用我使用好几年了&#xff0c;虽然近年来已转行 MySQL 和国产数据库领域&#xff0c;但仍然…

链表与顺序表的区别以及扩展计算机硬件的存储体系

好久没有更新文章了&#xff0c;在忙学校的事情时我还是比较怀念大家一直以来对我的关注和鼓励&#xff0c;接下来我会继续更新数据结构相关的文章&#xff0c;也请大家多多支持&#xff0c;十分感谢。正文来了&#xff1a; 首先说明一点&#xff0c;我在举例和比较时所使用的是…

【2023,学点儿新Java-02】计算机硬件与软件 | CPU、内存、硬盘概览 | 科学使用键盘——“指法” | 软件——计算机的灵魂 | 人机交互方式

前情回顾&#xff1a; 【2023&#xff0c;学点儿新Java-01】从查看本机 jdk版本 开始 | Java基础全程脉络图、Java工程师全程技术路线、Java职业晋升路线图 我们见到的太阳 是八分钟前的太阳&#xff0c;见到的月亮 是一点三秒之前的月亮&#xff0c;见到一英里之外的建筑&…

【Docker 安装 Zipkin】—— 每天一点小知识

&#x1f4a7; D o c k e r 安装 Z i p k i n \color{#FF1493}{Docker 安装 Zipkin} Docker安装Zipkin&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390; &#x1f433; 《数据结构与算法》…

解决vue打包一次部署到不同的服务器的问题

1. 问题描述 在工作的时候&#xff0c;往往碰到同一套vue前端代码程序需要部署到很多的服务器上&#xff0c;每次更改完程序都需要打包部署到各个服务器上&#xff0c;因为每个服务器的访问地址和端口都不一样&#xff0c;如果用的若依自带的框架&#xff0c;需要每次都需要打…

Spring Security --- 自定义登录逻辑

目录 UserDetailsService详解 返回值 方法参数 异常 PasswordEncoder密码解析器详解 接口介绍 内置解析器介绍 BCryptPasswordEncoder简介 代码演示 自定义登录逻辑 编写配置类 自定义逻辑 UserDetailsService详解 当什么也没有配置的时候&#xff0c;账号和密码是…

Vue3:计算属性、监听器

computed 计算属性 计算属性是指 基于现有状态派生 (演变) 出新的状态&#xff0c;现有状态发生变化&#xff0c;派生状态重新计算。 computed 接收回调函数作为参数&#xff0c;基于回调函数中使用的响应式数据进行计算属性的创建&#xff0c;回调函数的返回值就是基于现有状态…

软件测试什么样的技术栈才能进入大厂

我们知道&#xff0c;能在一线大厂工作是大多数人的目标&#xff0c;不仅薪酬高&#xff0c;技能提升快&#xff0c;而且能得到公司影响力背书&#xff0c;将来就算跳槽也能带来光环加持。 最近疫情的影响&#xff0c;网上也爆出了一些裁员新闻&#xff0c;可以说这个疫情确实…

【深入浅出密码学】RSA

RSA密码体制 引言&#xff1a; RSA加密的本意并不是为了取代对称密码&#xff0c;而且它比诸如AES的密码要慢很多&#xff0c;因为RSA当中涉及许多数学计算&#xff0c;RSA通常和类似AES的对称密码一起使用&#xff0c;真正用来加密大量数据的是对称密码。而RSA主要保护对称密…

Linux0.11内核源码解析-block_dev.c

目录 block_dev.c 文件的作用 int block_write(int dev, long * pos, char * buf, int count) block_dev.c 文件的作用 block_dev.c 文件的作用 block_dev.c 文件就包含两个函数&#xff0c;分别是block_read和block_write函数&#xff0c;提供给read和write系统调用 块读写…

STM32开发踩坑——基于CubeMx+Gcc移植正点原子3.5‘TFTLCD(开发环境:正点F103精英版+3.5‘TFTLCD)

成立这个专栏的目的是&#xff0c;记录自己嵌入式开发遇到的问题&#xff0c;与成功的解决方法&#xff0c;方便自己回顾。 最近在学习王维波老师的《STM32Cube高效开发教程》&#xff0c;王老师移植的是普中科技的驱动&#xff0c;而我手动移植了一下正点原子的lcd驱动&#…

【Java高级语法】(三)泛型:关于泛型最全面的讲解来了~

Java高级语法详解之泛型 :one: 概念:two: 优势:three: 使用3.1 泛型类3.2 泛型接口3.3 泛型方法 :four: 通配符&#xff08;Wildcards&#xff09;4.1 无界通配符&#xff08;Unbounded Wildcard&#xff09;4.2 上限通配符&#xff08;Upper Bounded Wildcard&#xff09;4.3 …

aardio - 【库】http访问网页

为了简化http访问操作&#xff0c;提高速度&#xff0c;丰富功能&#xff0c;特封装了此库&#xff0c;可以根据需要进行选择。 本库带一个dll&#xff0c;所以建议优先选择使用 inet.http 库&#xff1a; 如果使用 inet.http库&#xff0c;直接 inet.http.get() 速度较慢。 大…

理解3ds max中的容器的概念

实验一&#xff1a; 在场景中创建一个容器 把这个容器保存为一个文件&#xff0c;在文件夹中可看到此容器文件&#xff0c;其大小为892KB&#xff0c;同时可看到生成一个同名的lock类型文件。 将场景中的某一个物体&#xff08;面加多一点的&#xff09;添加到容器中&#x…

框架---面经

Spring 循环依赖 概念 多个实体之间相互依赖并形成闭环的情况就叫做"循环依赖”&#xff0c;也叫做”循环引用。 三级缓存解决循环依赖的原理 循环依赖的解决方案--- Feild注入单例&#xff08;AutoWired&#xff09; 直接在类的成员变量上使用Autowired注解&#xf…