1.4自然语言的分布式表示-word2vec实操

news2024/12/23 19:04:59

文章目录

  • 0写在前面
  • 1数据准备
  • 2CBOW模型结构的实现
  • 3交叉熵损失函数的前向计算
    • 3.1关于cross_entropy_error的计算
    • 3.2关于softmax

0写在前面

  1. 代码都位于:nlp;
  2. 其他相关内容详见专栏:深度学习自然语言处理基础_骑着蜗牛环游深度学习世界的博客-CSDN博客;

1数据准备

  1. 输入是上下文,目标是中间的单词

  2. 因此对于下图所示的小的语料库来说,可以构建上下文以及对应的目标词:

    1. 对语料库中的所有单词都执行该操作(两端的单词 除外)

    在这里插入图片描述

  3. 接下来需要将单词转换为神经网络能够处理的输入

    1. 需要根据前面所学习的内容,从语料库构建单词-ID之间的映射【使用nlp\2-基于计数的方法.py中写好的preprocess方法】

    2. 根据构建的映射,将输入和输出从单词转换为索引;由于这里不是一个单词了,而是所有的输入输出,因此需要写一个函数来统一处理;

      def create_contexts_targets(corpus, window_size=1):
          '''
      
          :param corpus: 序列化的语料库列表
          :param window_size: 上下文大小;主要用于去除掉不同时具备上下文的开头或者末尾的单词
          :return:
          '''
          target = corpus[window_size:-window_size]  # 获取需要预测的词列表;开头和末尾是不算的,因为他们不同时具有上下文
          contexts = []
          # 从第一个有上下文的单词开始,到最后一个有上下文的单词结束
          for i in range(window_size, len(corpus) - window_size):
              cs = []  # 当前单词的上下文列表
              for t in range(-window_size, window_size + 1):
                  if t == 0:
                      # 跳过单词本身
                      continue
                  cs.append(corpus[i + t])
              contexts.append(cs)
          return np.array(contexts), np.array(target)
      

      在这里插入图片描述

    3. 然后将单词索引转换为独热编码;注意维度的变化;

      在这里插入图片描述

      def convert_one_hot(corpus, vocab_size):
          '''转换为one-hot表示
      
          :param corpus: 单词ID列表(一维或二维的NumPy数组)
          :param vocab_size: 词汇个数
          :return: one-hot表示(二维或三维的NumPy数组)
          '''
          N = corpus.shape[0]  # 获取数据的数量;即输入的个数(输出的个数)
      
          if corpus.ndim == 1:
              # 维度数量为1,即是目标词数组
              one_hot = np.zeros((N, vocab_size), dtype=np.int32)
              for idx, word_id in enumerate(corpus):
                  one_hot[idx, word_id] = 1
      
          elif corpus.ndim == 2:
              # 维度数量为2,即上下文数组
              C = corpus.shape[1]  # 窗口的大小(上下文的大小)
              one_hot = np.zeros((N, C, vocab_size), dtype=np.int32)
              for idx_0, word_ids in enumerate(corpus):
                  # word_ids是某个目标词对应的上下文ID向量
                  for idx_1, word_id in enumerate(word_ids):
                      one_hot[idx_0, idx_1, word_id] = 1
      
          return one_hot
      

2CBOW模型结构的实现

  1. 构建SimpleCBOW

    1. 初始化:初始化权重、构建两个输入层,一个输出层,一个损失计算层;并将所有的权重和梯度整理到一起;

      class SimpleCBOW():
          def __init__(self, vocab_size, hidden_size):
              '''
              :param vocab_size: 输入侧和输出侧神经元的个数
              :param hidden_size: 中间层神经元个数
              '''
              V, H = vocab_size, hidden_size
              # 乘上0.01使得初始化的权重是一些比较小的浮点数
              W_in = 0.01 * np.random.randn(V, H).astype('f')  # 维度为[V,H]
              W_out = 0.01 * np.random.randn(H, V).astype('f')  # 维度为[H,V]
              # 构建层
              self.in_layer_0 = MatMul(W_in)  # 输入层
              self.in_layer_1 = MatMul(W_in)  # 输入层
              self.out_layer = MatMul(W_out)  # 输出层
              self.loss_layer = SoftmaxWithLoss()  # 损失计算层
      
              # 将所有的权重的参数和梯度数据存在一个变量中
              layers = [self.in_layer_0, self.in_layer_1, self.out_layer]
              self.params, self.grads = [], []
              for layer in layers:
                  self.params += layer.params
                  self.grads += layer.grads
      
              # 将单词的分布式表示记录为这个模型类的成员变量;这样模型训练好之后权重被更新
              # "在 Python 中,对象和变量实际上是对内存中的值的引用。当你创建一个变量并将其设置为某个对象时,你实际上是在创建一个指向该对象的引用"
              self.wordvec = W_in
      
    2. 实现CBOW模型的前向计算(关于这里的损失计算,后面再专门讲)

      1. 这里由于contexts的维度为[6,2,7],因此不再是一个单词的上下文向量;因此传入输入层进行计算时是一次性计算了6条数据的全连接结果;因而我们可以发现,矩阵天然可以支持批量数据的处理;
      2. 也就是说,输入是[6,7],输入层的权重维度为[7,3],得到中间层是[6,3],不再是原来的[1,3]
      def forward(self, contexts, target):
          '''
      
          :param contexts: 输入
          :param target: 真实标签;[6,7]
          :return:损失值
          '''
          # 输入层到中间层
          h0 = self.in_layer_0.forward(contexts[:, 0])  # 结果是[6,3]
          h1 = self.in_layer_1.forward(contexts[:, 1])
          h = 0.5 * (h0 + h1)
          # 中间层到输出层
          score = self.out_layer.forward(h)  # 输出的维度是[6,7]
          # 计算损失
          loss = self.loss_layer.forward(score, target)  # 将得分与真实标签传入损失计算函数;score在计算损失时会被施加softmax转换为概率的
          return loss
      
    3. 梯度反向传播;计算过程为【关于反向传播的细节,需要深入去了解】:

      1. 先计算损失函数的导数,从而得到损失函数输入的梯度
      2. 然后是输出层矩阵乘法的导数
      3. 然后0.5h的导数计算;这个直接对这个y=0.5h求导,对h求导;梯度是0.5;因此根据梯度的链式传递法则,在传过来的时候是梯度需要乘上0.5
      4. 然后是两个输入层结果相加的操作,这一步的梯度是分别原样传递到各个分支;简答理解:
        1. y=x1+x2;当对x1求导时,x2是当做常数的;x2同理;
        2. 因此相加操作处的梯度就直接分别传递给两个输入层
      5. 最后,两个输入层再传播梯度【这里传递梯度的计算公式在之前有分享一个博客:【深度学习】7-矩阵乘法运算的反向传播求梯度_矩阵梯度公式-CSDN博客】
      def backward(self, dout=1):
          ds = self.loss_layer.backward(dout)
          da = self.out_layer.backward(ds)
          da = da * 0.5
          self.in_layer_0.backward(da)  # 输入层计算完最终梯度之后会将梯度保存在梯度列表里面;因此这里就不需要返回值了
          self.in_layer_1.backward(da)
      

3交叉熵损失函数的前向计算

当调用SimpleCBOW类的forward方法之后,当执行到self.loss_layer.forward语句时,将进入到SoftmaxWithLoss类的forward函数,进行损失的计算;

  1. 首先,将模型输出的得分通过softmax函数转换为了概率分布;维度保持不变,依然是[6,7]

  2. 这里的真实标签是独热编码形式,因此根据独热编码提取正确的解标签(即,每条数据要预测单词的真实ID);此时标签变成一个维度:(6,)

  3. 然后进行交叉损失的计算,并返回损失值。

    def forward(self, x, t):
        self.t = t  # 维度为[6,7];是独热编码的形式
        self.y = softmax(x)  # 首先将得分转换为概率;维度为[6,7]
    
        # 在监督标签为one-hot向量的情况下,转换为正确解标签的索引
        # 在监督标签为one-hot向量的情况下,它与模型输出的得分维度相同
        if self.t.size == self.y.size:
            self.t = self.t.argmax(axis=1)  # 从独热编码转换为标签;变成一个维度:(6,)
    
        loss = cross_entropy_error(self.y, self.t)
        return loss
    

3.1关于cross_entropy_error的计算

  1. 代码如下;接下来对其进行解释;

    def cross_entropy_error(y, t):
        '''
        :param y: 模型输出;已经转换为概率了
        :param t: 真实标签;不再是独热编码
        :return:
        '''
        if y.ndim == 1: #1-1
            # 默认不执行;这种情况是当批处理大小为1时可能进行的
            t = t.reshape(1, t.size)
            y = y.reshape(1, y.size)
        # 在监督标签为one-hot-vector的情况下,转换为正确解标签的索引
        # 当前示例下,监督标签已经在上一步转换成了真实标签索引了
        if t.size == y.size: #1-2
            t = t.argmax(axis=1)
        batch_size = y.shape[0]
        return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size
    
  2. 我们这个小例子输入的数据维度是[6,7],是包含多条数据的;如果只有一条数据,则可能存在维度是1的情况;当这种情况发生时,要先增加一个维度;对标签也是如此;因此需要执行reshape(1, t.size)reshape(1, y.size)

  3. 执行#1-2时存在以下情况:

    1. 真实标签和模型输出一般是[6,7],即两个维度;那么在SimpleCBOW类的forward方法中,真实标签就会从独热编码变成真实标签ID数组,真实标签的维度就已经是一维的了;此时#1-2就不会执行了;

    2. 如果是一条数据的情况,模型输出一般还是[1,7]两个维度;真实标签数组(独热编码形式)维度可能是[1,7],也可能是(7,);如果是[1,7],则和多条数据时的情况一样;如果是(7,),则在SimpleCBOW类的forward方法中不会将标签从独热编码转换为ID索引,那么在cross_entropy_error这里,最好将#1-1改成如下的样子;然后通过执行#1-2将真实标签从独热编码转换为ID索引;

      if t.ndim == 1: #1-1
          # 默认不执行;这种情况是当批处理大小为1时可能进行的
          t = t.reshape(1, t.size)
      
      # 以下是一个维度变换的小例子
      import numpy as np
      c = np.array([1, 0, 0, 0, 0, 0, 0]).astype('f')
      print(c.size) #7
      print(c.shape) # (7,)
      print(c.reshape(1,c.size)) # c:[[1. 0. 0. 0. 0. 0. 0.]]
      
  4. 然后计算交叉熵损失

    1. 本例子要预测的单词所属类别是7(因为对于每个待预测的单词,有7种可能),因此是一个多分类问题;对于多分类问题,交叉熵损失公式为: L = − ∑ [ y i ∗ l o g ( p i ) ] L=-\sum[y_i*log(p_i)] L=[yilog(pi)];对于一次处理多条数据的情况,一般累加每个样本的值,然后求和再平均;​

    2. 如何理解这个损失:假设这里的真实标签y是独热编码的形式,模型预测如果很准确,则独热编码中元素1就拥有更大的概率;概率越大,log函数值越接近0,取相反数之后,值越接近0就相当于是值越小;我们是期望这个值越小的,因为这意味着正确解标签具有更大的概率,模型就更准确;

    3. 实际代码计算时,我们不需要完全按照公式来一步步计算;而是直接取正确解标签在模型预测输出中对应位置的概率来计算就可以了;

      1. y是二维的,[6,7];通过y[np.arange(batch_size), t]y中选择对应的行和列,即选择了每条数据真实解标签对应的概率值;不能写成y[:, t]
      2. y[np.arange(batch_size), t]的结果为(6,),其中是每个样本的正确解标签的概率值;对每个值计算对数;
      3. +1e-7防止对数里的值为0
      4. 然后对每个样本的对数值求和,并求平均,作为损失值;这个损失值应该越小越好;
    4. 这样就能把损失值计算出来了:

      在这里插入图片描述

3.2关于softmax

  1. 代码如下:

    def softmax(x):
        if x.ndim == 2:
            x = x - x.max(axis=1, keepdims=True)
            x = np.exp(x)
            x /= x.sum(axis=1, keepdims=True)
        elif x.ndim == 1:
            x = x - np.max(x)
            x = np.exp(x) / np.sum(np.exp(x))
    
        return x
    
  2. 我们的输入一般是二维的;即[6,7]

  3. 执行x - x.max可以将数值约束到负数;这样指数的值就在0、1之间;可以有效防止数值过大溢出的问题;

  4. 然后使用np.expx的每个元素计算指数;得到新的x

  5. 然后用当前x的某个值除以x的和得到概率;

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

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

相关文章

家有老人小孩,室内灰尘危害大!资深家政教你选对除尘空气净化器

哈喽,各位亲爱的朋友们!今天我们来聊聊每次大扫除时最让人头疼的问题——灰尘。你有没有发现,两天不打扫,桌子上就能积上一层灰;阳光一照,地板上的灰尘都在跳舞;整理被子的时候,空气…

ONLYOFFICE 文档 8.1 现已发布:功能全面的 PDF 编辑器、幻灯片版式等等

最新版本的 ONLYOFFICE 在线编辑器已经发布,整个套件带来了30多个新功能和432个 bug 修复。阅读本文了解全部更新。 什么是 ONLYOFFICE 文档 ONLYOFFICE 文档是一套功能强大的文档编辑器,支持编辑处理文本文档、电子表格、演示文稿、可填写的表单、PDF&…

基 CanMV 的 C 开发环境搭建(Linux,Ubuntu篇)

不论是使用 CanMV 提供的基于 C 语言和 FreeRTOS 的应用开发方式开发应用程序或是编译 CanMV 固件,都需要搭建基于 CanMV 的 C 开发环境,用于编译 CanMV 源码。 1. 开发环境搭建说明 CanMV 提供了基于 C 语言和 FreeRTOS 的应用开发…

IO-LiNK简介

什么是IO-Link? IO-Link( IEC 61131-9 )是一种开放式标准串行通信协议,允许支持 IO-Link 的传感器、设备进行双向数据交换,并连接到主站。 IO-Link 主站可以通过各种网络,如现场总线进行传输。每个 IO-L…

ComfyUI 作者辞职搞开源

ComfyUI 作者发文表示,已从 Stability AI,并与其它开源开发者合作成立了一个致力于迭代和改进 ComfyUI 的开源组织:Comfy Org 目前其生态已经有: node 管理器node registrycomfy 命令行工具自动化测试文档 作者表示&#xff0c…

CCAA:认证通用基础(理解、掌握、应用合格评定功能法的基本概念)

5.合格评定技术 一、合格评定功能法 5.1合格评定功能法 合格评定被视为是一项对与标准相关的规定要求满足程度的一系列技术评价与证明的活动。当需要表明某客体(或特定的对象)是否满足规定要求时使用合格评定功能法所作出的证实能够使之更为切实可 信,可增加使用…

【React Native】measureInWindow在安卓上无法正确获取View在屏幕上的布局信息

问题描述: 在React Native中,我们可以使用measureInWindow的方式去获取一个View在屏幕中的位置信息: 下面这个Demo中,我们写了一个页面HomePage和一个列表项组件ListItemA,我们期望每过5s监测一次列表中每一项在屏幕中…

uniapp 实人认证

首先Dcloud创建云服务空间,开启一键登录并充值 下一步 1. 右键项目 》 创建uniCloud云开发环境 》右键uniCloud》关联云服务空间 2. cloudfunctions右键 新建云函数,任意命名(例:veify),然后右键项目》管…

33.获取入口点

上一个内容:32.双击列表启动目标游戏 前置知识 25.入口点注入(查看pe头)、32.双击列表启动目标游戏 以它的代码为基础进行修改 效果图: 代码实现:原理通过读文件流的方式把文件加载到内存中然后解析pe结构 void CWnd…

ReF:斯坦福提出的新型语言模型微调方法

随着预训练语言模型(LMs)在各种自然语言处理(NLP)任务中的广泛应用,模型微调成为了一个重要的研究方向。传统的全参数微调方法虽然有效,但计算成本高昂,尤其是在大型模型上。为了解决这一问题&a…

CSS样式、选择器、盒子模型

标题 文章目录 一、CSS样式内联样式内部样式外部样式 二、选择器三、颜色四、盒子模型(内边距padding、边框border、外边框margin) 一、CSS样式 可分为:内联样式、内部样式、外部样式 优先级: 内联样式 >内部样式 >外部样式…

2024-6-20 Windows AndroidStudio SDK(首次加载)基础配置,SDK选项无法勾选,以及下载失败的一些解决方法

2024-6-20 Windows AndroidStudio SDK(首次加载)基础配置,SDK选项无法勾选,以及下载失败的一些解决方法 注意:仅仅是SDK这种刚安装时的配置的下载,不要和开源库的镜像源扯到一起!!!! 最近想玩AndroidStudio的JNI开发, 想着安装后…

机器人阻抗控制相关文献学习(阻抗实现)

机器人阻抗是一个描述机器人与环境交互时动态特性的概念。 定义: 阻抗在机器人领域中,通常用来描述机器人与其环境之间的相互作用。当机器人与环境接触时,环境对机器人施加一个作用力,而机器人也会对环境施加一个反作用力。这个反…

【机器学习】从理论到实践:决策树算法在机器学习中的应用与实现

📝个人主页:哈__ 期待您的关注 目录 📕引言 ⛓决策树的基本原理 1. 决策树的结构 2. 信息增益 熵的计算公式 信息增益的计算公式 3. 基尼指数 4. 决策树的构建 🤖决策树的代码实现 1. 数据准备 2. 决策树模型训练 3.…

Studying-代码随想录训练营day15| 222.完全二叉树的节点个数、110.平衡二叉树、257.二叉树的所有路径、404.左叶子之和

第十五天,二叉树part03💪,编程语言:C 目录 257.完全二叉树的节点个数 110.平衡二叉树 257.二叉树的所有路径 404.左叶子之和 总结 257.完全二叉树的节点个数 文档讲解:代码随想录完全二叉树的节点个数 视频讲解…

Mistral AI 发布 Codestral-22B,精通 80+ 编程语言,22B 参数超越 70B Code Llama

前言 大型语言模型 (LLM) 在代码生成领域展现出巨大的潜力,但现有的模型在支持的编程语言数量、生成速度和代码质量方面仍存在局限性。法国 AI 独角兽 Mistral AI 近期发布了其首款代码生成模型 Codestral-22B,宣称在多项指标上超越了 GPT-4 和 Llama3&…

计算机网络:应用层 - 万维网 HTTP协议

计算机网络:应用层 - 万维网 & HTTP协议 万维网 WWW统一资源定位符 URL 超文本传输协议 HTTP非持续连接持续连接非流水线流水线 代理服务器HTTP报文 万维网 WWW 万维网是一个大规模的、联机式的信息储藏所。万维网用链接的方法能非常方便地从互联网上的一个站点…

企业的差旅费用还能更节省吗?

对于多数企业而言,差旅成本是仅次于人力资源成本的第二大可控成本。 差旅成本除了差旅产品采购费用、差旅服务费用这些显性成本外,还有预订时间消耗、审批环节、报销流程、票据核查等隐性成本。 据调研数据显示:企业对于专业差旅管理的认知度…

测试服务器端口是否打开,服务器端口开放异常的解决方法

在进行服务器端口开放性的测试时,我们通常使用网络工具来验证目标端口是否响应特定的协议请求。常用的工具包括Telnet、Nmap、nc(netcat)等。这些工具可以通过发送TCP或UDP数据包到指定的IP地址和端口,然后分析返回的数据包&#…

「Python-docx 专栏」docx 获取页面大小、设置页面大小(纸张大小)

本文目录 前言一、docx纸张大小介绍1、document.xml① 关于 document.xml 的一些知识点② 纸张大小在哪里③ 纸张大小都有啥④ EMU对应的尺寸列表二、获取docx纸张大小1、完整代码2、运行效果图三、python为docx设置纸张大小1、完整代码2、效果图前言 今天的这边文章,我们来说…