热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

手把手教你用飞桨做词向量模型SkipGram

飞桨开发者说成员:肥猫、忆臻在做NLP的任务时,一个非常basic的操作就是如何编码自然语言中的符号,例如词、短语,甚至词缀

飞桨开发者说成员:肥猫、忆臻

在做 NLP 的任务时,一个非常 basic 的操作就是如何编码自然语言中的符号,例如词、短语,甚至词缀。目前流行的方法有大约三种:

•  特征工程:这类方法依赖于手工特征,例如tf-idf 同时考虑词频和词的稀缺度;

• 统计方法:统计上常常通过矩阵分解(如SVD、EVD)来建模大规模文档集合;

•  神经网络:目前非常流行通过神经网络端到端的建模语言模型,得到词向量副产品;

今天要讲解的就是SkipGram 模型就属于第三种方法,它的主要思想是利用的词义的分布式表示。除了让您彻底弄懂什么是语言模型以及 SkipGram 的基本原理。我们还会详细的说明如何一步步的用飞桨(PaddlePaddle)实现它。

1.什么是词向量

首先我们需要了解什么是词向量。NLP和图像不太一样,图像的输入本身就是一个有数值特征的矩阵,而 NLP 的输入通常只是一堆自然语言的符号,不方便计算机直接计算。因此,在计算语言学中,我们通常会希望用数值向量来表示这些符号。例如现在我们希望比较词汇“米饭”与“猪肉”和“家具”之间词义的相似性,可以考虑用下面这样的 two-stage 范式。

• 将词汇用向量来表示,例如这里以及;

• 考虑某种数值距离去度量,例如内积。这里有。类似的,同理我们也可以得到;从数值大小可以判断,与“家具”相比,“米饭”和“猪肉”更相似。

从上面这个例子可以看出,使用向量数值表示法最关键的地方在于如何获取词汇的向量的表示,而 SkipGram 就是一个良方。

2.什么是语言模型

词向量一般不是直接获取的,而是某些任务的副产品。它们通常是随机初始化的,然后通过不断的数值优化过程中获得语义信息,例如上述的相似性。因此,训练词向量的办法可以有很多,但是如何高效的获得高质量的词向量很重要,另外任务也应该有一定的可拓展性,例如语料充足,不需要额外标注。

 

语言模型是一个非常好的选择。因为它语料充足,只要有文章,有帖子,那就有数据;同时由于其任务的特殊性,不需要人工进行额外的数据标注(网上有很多称这是无监督,但我觉得不是特别合适,不需要数据标注和无监督概念有所差异)。那么什么是语言模型呢?

语言模型就是用来衡量一句话出现的概率的。例如

•  ;

•  

显然,第二句话一般不会出自于一个神智正常的人之口,因此他的概率很低。而一句话 的概率可以用条件概率分解如下:

这里 表示词的上下文,是对的近似,否则计算复杂度是指数的。其中一种近似就是 n-gram,即

如上图所示的例子,当n=5时, 表示已知前 4 个词,预测下一个词的概率。由于让模型提高 P(s) 的概率等价于让模型提高每个 P(wi|ci)的概率,因此语言模型又可以被理解为已知上下文时中心词不确定性的度量。

3.什么是 SkipGram

经过前两节的解释,相信您对词向量有了很深的认识了。这一小节中我将会介绍 SkipGram,一种有效训练语言模型的方法。

说到 SkipGram,一定有同学会想到 CBOW。实际上 CBOW 更符合常人的思考逻辑,它建模词语上下文的方法很简单,如下图所示:

它从若干文档的文段中随机抽取出5 个连续的词, 然后类似做完形填空,希望模型能够根据上下文 预测。而 SkipGram 则恰恰相反,如下图所示,它是拿用中心词去预测上下文:

虽然看起来 CBOW 更合理,但很多文献指出,用 SkipGram 训出来的词向量效果更好。笔者分析可能存在下面一些原因:

•   SkipGram 用一个中心词去预测上下文,这样相当于对这个中心词的表示要求更高,这就好像一个学生(中心词)同时受到了多个老师(上下文)的教导(这个学习的过程可以被理解为中间的梯度传播),效果肯定比一个老师教导多个学生(因此梯度是均分的,没有区分性,而且由于梯度均分,容易破坏一个窗口中词向量的异构性)效果要好得多;

•   其次,SkipGram 这种强调中心词的结构对某些具有较低频率的生僻词比较友好,因此低频词也可以学到质量较高的向量表示;

但可能是因为 CBOW 的结构相对简单些,经验显示,CBOW 的训练速度要比 SkipGram 快的多,因此两者其实各有优势。

拿上面提到的例子 “Can you please come here ?” 说明 SkipGram 的流程。假设滑动窗口的长度为 5,那么现在窗口 cover 住了片段 “can you please come here”。此时以中心词 please 为输入并度量与上下文 can, you,come, here 的相似度,优化时希望这个值尽量高。

在工程上,实现词向量模型有很多trick,例如概率平滑化,高频词抽样等。但如果做个 demo 不需要考虑太多这些细节。不过无论是 CBOW 还是 SkipGram 都无法规避一个问题,就是过高的词典容量。正常情况下,英语词典的容量在 3000 ~ 4000 上下,因此当训练语料很大时会造成巨大的计算负担。为了权衡质量和效率,目前最常用的方法就是负采样。通俗的来说,就是我不再把整个词典当成负样本了,而是随机抽取若干词作为负样本。实现时,这个随机抽取的数量是一个超参数,大概是 20 ~ 30 之间,这样很明显大大提高了计算效率。另外,也有人指出,用一些重要性采样的技术可以进一步改善效果。

4.用飞桨实现

现在你已经基本了解了什么是SkipGram,下面我们就用强大的飞桨一步一步实现它。

现在你已经基本了解了什么是SkipGram,而实现它需要借助现有的深度学习框架。飞桨是百度自主研发的深度学习框架,功能非常强大,同时支持稠密参数、稀疏参数并行训练;静态网络、动态网络等。而且有非常丰富的中英文文档,非常方便您使用。下面我们就用强大的飞桨一步一步实现它.

首先,我们需要导入一些必要的计算库。

 

# PaddlePaddle 计算引擎.
import paddle
from paddle import fluid# 一些常用的科学计算库.
import numpy as np
import matplotlib.pyplot as plt

然后设置定义一些超参数,用于控制网络结构和训练逻辑。

EMBEDDING_DIM =64 # 词向量维度.
WINDOW_SIZE =5 #滑动窗口大小.
BATCH_SIZE =200 #迭代 batch 大小.
EPOCH_NUM =10 #训练的轮数.
RANDOM_STATE =0 #设置伪随机数种子.

然后就是文本数据,这里使用飞桨自带(会自动下载)的 PTB 数据集,导入如下:

from paddle.dataset import imikolovword_vocab =imikolov.build_dict()
vocab_size =len(word_vocab)# 打印 PTB 数据字典的容量大小.
print("imikolov 字典大小为 "+str(vocab_size))# 类似 Pytorch 的 DataLoader, 用于在训练时做 batch, 很方便.
data_loader =paddle.batch(imikolov.test(word_vocab, WINDOW_SIZE), BATCH_SIZE)

imikolov字典大小为 2074

下面我们需要搭建SkipGram 的网络结构,我们用一个函数打包如下:

def build_neural_network():assertWINDOW_SIZE %2==1medium_num = WINDOW_SIZE //2# 定义输入变量, 是从文本中截取的连续的文本段.var_name_list = [str(i) +"-word"for i inrange(0, WINDOW_SIZE)]word_list = [fluid.layers.data(name=n, shape=[1], dtype="int64")for n in var_name_list]# 取中心词作为输入, 而周围上下文作为输出.input_word = word_list[medium_num]output_context = word_list[:medium_num] + word_list[medium_num +1:]# 将输入输出都做词向量表示, 并且将输出拼起来.embed_input = fluid.layers.embedding(input=input_word, size=[vocab_size, EMBEDDING_DIM],dtype="float32",is_sparse=True,param_attr="input_embedding")embed_output_list = [fluid.layers.embedding(input=w, size=[vocab_size, EMBEDDING_DIM], dtype="float32",is_sparse=True,param_attr="output_embedding")for w in output_context]concat_output = fluid.layers.concat(input=embed_output_list,axis=1)# 用 -log(sigmoid(score)) 作为度量损失函数.var_score =fluid.layers.matmul(embed_input, concat_output, transpose_x=True)avg_loss =0-fluid.layers.mean(fluid.layers.log(fluid.layers.sigmoid(var_score)))# 使用 Adam 优化算法, 并注意需要返回变量定义名.fluid.optimizer.AdamOptimizer().minimize(avg_loss)return avg_loss, var_name_list

类似于 Tensorflow,运行 PaddlePaddle 计算引擎前需要一些热身代码。

# 确定执行的环境, 如果支持 CUDA 可以调用CUDAPlace 函数.
device_place =fluid.CPUPlace()
executor = fluid.Executor(device_place)main_program =fluid.default_main_program()
star_program =fluid.default_startup_program()# 固定伪随机数种子, 一般用于保证论文效果可复现.
main_program.random_seed =RANDOM_STATE
star_program.random_seed =RANDOM_STATE# 定义模型的架构 (之前定义函数输出) 以及模型的输入.
train_loss, tag_list =build_neural_network()
feed_var_list =[main_program.global_block().var(n) for n in tag_list]
data_feeder =fluid.DataFeeder(feed_list=feed_var_list, place=device_place)

下面开始训练模型的流程,将迭代器产生的 batch 不断喂入网络中:

executor.run(star_program)
forepoch_idx inrange(EPOCH_NUM):total_loss, loss_list =0.0, []forbatch_data in data_loader():total_loss +=float(executor.run(main_program, feed=data_feeder.feed(batch_data),fetch_list=[train_loss])[0])loss_list.append(total_loss)print("[迭代轮数{:4d}], 在训练集的损失为{:.6f}".format(epoch_idx, total_loss))

[迭代轮数    0], 在训练集的损失为 75.395110

[迭代轮数    1], 在训练集的损失为 2.346059

[迭代轮数    2], 在训练集的损失为 0.797208

[迭代轮数    3], 在训练集的损失为 0.413886

[迭代轮数    4], 在训练集的损失为 0.254423

[迭代轮数    5], 在训练集的损失为 0.171255

[迭代轮数    6], 在训练集的损失为 0.121907

[迭代轮数    7], 在训练集的损失为 0.090095

[迭代轮数    8], 在训练集的损失为 0.068378

[迭代轮数    9], 在训练集的损失为 0.052923

我们可以将刚才训练过程中的损失用 matplotlib 的库函数画出来。

plt.plot(np.array(range(0, len(loss_list))),loss_list)

好啦,以上就是本次所要分享。总的来说,本节我们主要讲述了什么是词向量,什么是语言模型,SkipGram 算法的内容以及其特性,相对 CBOW 来说它对低频词更友好,而且词向量质量更佳,最后我们还细致的教您一步一步用飞桨实现一个简单的 SkipGram 模型。希望您多多支持,咱们下期再会。

 

想与更多的深度学习开发者交流,请加入飞桨官方QQ群:796771754

如果您想详细了解更多飞桨的相关内容,请参阅以下文档。

官网地址:https://www.paddlepaddle.org.cn

Skip-Gram相关内容参考项目地址:

https://github.com/PaddlePaddle/models/tree/v1.5.1/PaddleRec/word2vec


推荐阅读
  • Android源码中的Builder模式及其作用
    本文主要解释了什么是Builder模式以及其作用,并结合Android源码来分析Builder模式的实现。Builder模式是将产品的设计、表示和构建进行分离,通过引入建造者角色,简化了构建复杂产品的流程,并且使得产品的构建可以灵活适应变化。使用Builder模式可以解决开发者需要关注产品表示和构建步骤的问题,并且当构建流程发生变化时,无需修改代码即可适配新的构建流程。 ... [详细]
  • Python使用Pillow包生成验证码图片的方法
    本文介绍了使用Python中的Pillow包生成验证码图片的方法。通过随机生成数字和符号,并添加干扰象素,生成一幅验证码图片。需要配置好Python环境,并安装Pillow库。代码实现包括导入Pillow包和随机模块,定义随机生成字母、数字和字体颜色的函数。 ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
  • 本文介绍了一个Java猜拳小游戏的代码,通过使用Scanner类获取用户输入的拳的数字,并随机生成计算机的拳,然后判断胜负。该游戏可以选择剪刀、石头、布三种拳,通过比较两者的拳来决定胜负。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • 开源Keras Faster RCNN模型介绍及代码结构解析
    本文介绍了开源Keras Faster RCNN模型的环境需求和代码结构,包括FasterRCNN源码解析、RPN与classifier定义、data_generators.py文件的功能以及损失计算。同时提供了该模型的开源地址和安装所需的库。 ... [详细]
  • Python教学练习二Python1-12练习二一、判断季节用户输入月份,判断这个月是哪个季节?3,4,5月----春 ... [详细]
author-avatar
HenryJuliju
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有