6.2词向量:迈向NLP领域的第1步台阶

本文涉及的产品
NLP 自学习平台,3个模型定制额度 1个月
NLP自然语言处理_高级版,每接口累计50万次
NLP自然语言处理_基础版,每接口每天50万次
简介: 这篇文章详细介绍了词向量(Word Embedding)的概念、重要性以及如何通过word2vec算法中的CBOW和Skip-gram模型来训练词向量,使其能够捕捉和表达单词的语义信息。

1. 词向量概述

在自然语言处理任务中,词向量(Word Embedding)是表示自然语言里单词的一种方法,即把每个词都表示为一个N维空间内的点,即一个高维空间内的向量。通过这种方法,实现把自然语言计算转换为向量计算。

图1所示的词向量计算任务中,先把每个词(如queen,king等)转换成一个高维空间的向量,这些向量在一定意义上可以代表这个词的语义信息。再通过计算这些向量之间的距离,就可以计算出词语之间的关联关系,从而达到让计算机像计算数值一样去计算自然语言的目的。

图1:词向量计算示意图

因此,大部分词向量模型都需要回答两个问题:

  1. 如何把词转换为向量****?****

自然语言单词是离散信号,比如“香蕉”,“橘子”,“水果”在我们看来就是3个离散的词。

如何把每个离散的单词转换为一个向量?

  1. 如何让向量具有语义信息?

比如,我们知道在很多情况下,“香蕉”和“橘子”更加相似,而“香蕉”和“句子”就没有那么相似,同时“香蕉”和“食物”、“水果”的相似程度可能介于“橘子”和“句子”之间。

那么,我们该如何让词向量具备这样的语义信息?

1.1 如何把词转换为向量

自然语言单词是离散信号,比如“我”、“ 爱”、“人工智能”。如何把每个离散的单词转换为一个向量?通常情况下,我们可以维护一个如图2所示的查询表。表中每一行都存储了一个特定词语的向量值,每一第一个元素都代表着这个词本身,以便于我们进行词和向量的映射(如“我”对应的向量值为 [0.3,0.5,0.7,0.9,-0.2,0.03] )。给定任何一个或者一组单词,我们都可以通过查询这个excel,实现把单词转换为向量的目的,这个查询和替换过程称之为Embedding Lookup。

图2:词向量查询表

上述过程也可以使用一个字典数据结构实现。事实上如果不考虑计算效率,使用字典实现上述功能是个不错的选择。然而在进行神经网络计算的过程中,需要大量的算力,常常要借助特定硬件(如GPU)满足训练速度的需求。GPU上所支持的计算都是以张量(Tensor)为单位展开的,因此在实际场景中,我们需要把Embedding Lookup的过程转换为张量计算,如图3所示。

图3:张量计算示意图

假设对于句子"我,爱,人工,智能",把Embedding Lookup的过程转换为张量计算的流程如下:

  1. 通过查询字典,先把句子中的单词转换成一个ID(通常是一个大于等于0的整数),这个单词到ID映射关系可以根据需求自定义(如 图3 中,我=>1, 人工=>2,爱=>3,...)。
  2. 得到ID后,再把每个ID转换成一个固定长度的向量。假设字典的词表中有5000个词,那么,对于单词“我”,就可以用一个5000维的向量来表示。由于“我”的ID是1,因此这个向量的第一个元素是1,其他元素都是0([1,0,0,…,0]);同样对于单词“人工”,第二个元素是1,其他元素都是0。用这种方式就实现了用一个向量表示一个单词。由于每个单词的向量表示都只有一个元素为1,而其他元素为0,因此我们称上述过程为One-Hot Encoding
  3. 经过One-Hot Encoding后,句子“我,爱,人工,智能”就被转换成为了一个形状为 4×5000的张量,记为 V 。在这个张量里共有4行、5000列,从上到下,每一行分别代表了“我”、“爱”、“人工”、“智能”四个单词的One-Hot Encoding。最后,我们把这个张量 V和另外一个稠密张量 W相乘,其中 W 张量的形状为5000 × 128(5000表示词表大小,128表示每个词的向量大小)。经过张量乘法,我们就得到了一个4×128的张量,从而完成了把单词表示成向量的目的。

1.2 如何让向量具有语义信息

得到每个单词的向量表示后,我们需要思考下一个问题:比如在多数情况下,“香蕉”和“橘子”更加相似,而“香蕉”和“句子”就没有那么相似;同时,“香蕉”和“食物”、“水果”的相似程度可能介于“橘子”和“句子”之间。那么如何让存储的词向量具备这样的语义信息呢?

我们先学习自然语言处理领域的一个小技巧。在自然语言处理研究中,科研人员通常有一个共识:使用一个单词的上下文来了解这个单词的语义,比如:

“苹果手机质量不错,就是价格有点贵。”“这个苹果很好吃,非常脆。”“菠萝质量也还行,但是不如苹果支持的APP多。”

在上面的句子中,我们通过上下文可以推断出第一个“苹果”指的是苹果手机,第二个“苹果”指的是水果苹果,而第三个“菠萝”指的应该也是一个手机。

事实上,在自然语言处理领域,使用上下文描述一个词语或者元素的语义是一个常见且有效的做法。我们可以使用同样的方式训练词向量,让这些词向量具备表示语义信息的能力。

2. word2vec 词向量训练算法

2013年,Mikolov提出的经典word2vec算法就是通过上下文来学习语义信息。word2vec包含两个经典模型:CBOW(Continuous Bag-of-Words)和Skip-gram,如图4所示。

  • CBOW :通过上下文的词向量推理中心词
  • Skip-gram :根据中心词推理上下文

图4:CBOW和Skip-gram语义学习示意图

假设有一个句子“Pineapples are spiked and yellow”,两个模型的推理方式如下:

  • CBOW 中,先在句子中选定一个中心词,并把其它词作为这个中心词的上下文。如 图4 CBOW所示,把“Spiked”作为中心词,把“Pineapples、are、and、yellow”作为中心词的上下文。在学习过程中,使用上下文的词向量推理中心词,这样中心词语义被传递到上下文的词向量中,如“Spiked → pineapple”,从而达到学习语义信息的目的。
  • Skip-gram 中,同样先选定一个中心词,并把其他词作为这个中心词的上下文。如 图4 Skip-gram所示,把“Spiked”作为中心词,把“Pineapples、are、and、yellow”作为中心词的上下文。不同的是,在学习过程中,使用中心词的词向量去推理上下文,这样上下文定义的语义被传入中心词的表示中,如“pineapple → Spiked”, 从而达到学习语义信息的目的。

说明:

一般来说,CBOW比Skip-gram训练速度快,训练过程更加稳定,原因是CBOW使用上下文average的方式进行训练,每个训练step会见到更多样本。

而在生僻字(出现频率低的字)处理上,skip-gram比CBOW效果更好,原因是skip-gram不会刻意回避生僻字(CBOW结构中输入中存在生僻字时,生僻字会被其它非生僻字的权重冲淡)。

2.1 CBOW和Skip-gram的网络结构

CBOW的网络结构如图5所示,其是一个具有3层结构的神经网络:输入层、隐藏层和输出层。下面我们以这句话:“Pineapples are spiked and yellow”为例展开介绍CBOW的网络结构。

图5:CBOW的算法实现

  • 输入层: 一个形状为C×V的one-hot张量,其中C代表上线文中词的个数,通常是一个偶数,我们假设为4;V表示词表大小,我们假设为5000,该张量的每一行都是一个上下文词的one-hot向量表示,比如“Pineapples, are, and, yellow”。
  • 隐藏层: 一个形状为V×N的参数张量W1,一般称为word-embedding,N表示每个词的词向量长度,我们假设为128。输入张量和word embedding W1进行矩阵乘法,就会得到一个形状为C×N的张量。综合考虑上下文中所有词的信息去推理中心词,因此将上下文中C个词相加得一个1×N的向量,是整个上下文的一个隐含表示。
  • 输出层: 创建另一个形状为N×V的参数张量,将隐藏层得到的1×N的向量乘以该N×V的参数张量,得到了一个形状为1×V的向量。最终,1×V的向量代表了使用上下文去推理中心词,每个候选词的打分,再经过softmax函数的归一化,即得到了对中心词的推理概率:

𝑠𝑜𝑓𝑡𝑚𝑎𝑥(Oi)=exp(Oi)∑jexp(Oj)

Skip-gram的网络结构如图6所示,其同样是一个具有3层结构的神经网络:输入层、隐藏层和输出层。

图6:Skip-gram算法实现

  • Input Layer(输入层) :接收一个one-hot张量 V ∈ R 1 × vocab_size作为网络的输入,里面存储着当前句子中心词的one-hot表示。
  • Hidden Layer(隐藏层) :将张量 V乘以一个word embedding张量 W 1 ∈ R vocab_size × embed_size ,并把结果作为隐藏层的输出,得到一个形状为 R 1 × embed_size 的张量,里面存储着当前句子中心词的词向量。
  • Output Layer(输出层) :将隐藏层的结果乘以另一个word embedding张量 W 2 ∈ R embed_size × vocab_size ,得到一个形状为 R 1 × vocab_size 的张量。这个张量经过softmax变换后,就得到了使用当前中心词对上下文的预测结果。根据这个softmax的结果,我们就可以去训练词向量模型。

在实际操作中,使用一个滑动窗口(一般情况下,长度是奇数),从左到右开始扫描当前句子。每个扫描出来的片段被当成一个小句子,每个小句子中间的词被认为是中心词,其余的词被认为是这个中心词的上下文。

下面,我们以Skip-gram模型为例,进一步学习Skip-gram模型的具体实现。

2.2 实现Skip-gram

2.2.1 Skip-gram的理想实现

使用神经网络实现Skip-gram中,模型接收的输入应该有2个不同的tensor:

  • 代表中心词的tensor:假设我们称之为center_words V ,一般来说,这个tensor是一个形状为[batch_size, vocab_size]的one-hot tensor,表示在一个mini-batch中,每个中心词的ID,对应位置为1,其余为0。
  • 代表目标词的tensor:目标词是指需要推理出来的上下文词,假设我们称之为target_words T,一般来说,这个tensor是一个形状为[batch_size, 1]的整型tensor,这个tensor中的每个元素是一个[0, vocab_size-1]的值,代表目标词的ID。

在理想情况下,我们可以使用一个简单的方式实现skip-gram。即把需要推理的每个目标词都当成一个标签,把skip-gram当成一个大规模分类任务进行网络构建,过程如下:

  1. 声明一个形状为[vocab_size, embedding_size]的张量,作为需要学习的词向量,记为W_0 。对于给定的输入 V,使用向量乘法,将 V乘以 W_0 ,这样就得到了一个形状为[batch_size, embedding_size]的张量,记为H=V×W_0 。这个张量 H H 就可以看成是经过词向量查表后的结果。
  2. 声明另外一个需要学习的参数W_1 ,这个参数的形状为[embedding_size, vocab_size]。将上一步得到的 H去乘以W_1 ,得到一个新的tensor O=H×W_1 ,此时的 O是一个形状为[batch_size, vocab_size]的tensor,表示当前这个mini-batch中的每个中心词预测出的目标词的概率。
  3. 使用softmax函数对mini-batch中每个中心词的预测结果做归一化,即可完成网络构建。
2.2.2 Skip-gram的实际实现

然而在实际情况中,vocab_size通常很大(几十万甚至几百万),导致W_0和W_1也会非常大。对于W_0而言,所参与的矩阵运算并不是通过一个矩阵乘法实现,而是通过指定ID,对参数W_0进行访存的方式获取。然而对W_1而言,仍要处理一个非常大的矩阵运算(计算过程非常缓慢,需要消耗大量的内存/显存)。为了缓解这个问题,通常采取负采样(negative_sampling)的方式来近似模拟多分类任务。此时新定义的W_0和W_1均为形状为[vocab_size, embedding_size]的张量。

假设有一个中心词c和一个上下文词正样本t_p。在Skip-gram的理想实现里,需要最大化使用c推理t_p的概率。在使用softmax学习时,需要最大化tpt_p的推理概率,同时最小化其他词表中词的推理概率。之所以计算缓慢,是因为需要对词表中的所有词都计算一遍。然而我们还可以使用另一种方法,就是随机从词表中选择几个代表词,通过最小化这几个代表词的概率,去近似最小化整体的预测概率。比如,先指定一个中心词(如“人工”)和一个目标词正样本(如“智能”),再随机在词表中采样几个目标词负样本(如“日本”,“喝茶”等)。有了这些内容,我们的skip-gram模型就变成了一个二分类任务。对于目标词正样本,我们需要最大化它的预测概率;对于目标词负样本,我们需要最小化它的预测概率。通过这种方式,我们就可以完成计算加速。上述做法,我们称之为负采样。

在实现的过程中,通常会让模型接收3个tensor输入:

  • 代表中心词的tensor:假设我们称之为center_words V ,一般来说,这个tensor是一个形状为[batch_size, vocab_size]的one-hot tensor,表示在一个mini-batch中每个中心词具体的ID。
  • 代表目标词的tensor:假设我们称之为target_words T ,一般来说,这个tensor同样是一个形状为[batch_size, vocab_size]的one-hot tensor,表示在一个mini-batch中每个目标词具体的ID。
  • 代表目标词标签的tensor:假设我们称之为labels L,一般来说,这个tensor是一个形状为[batch_size, 1]的tensor,每个元素不是0就是1(0:负样本,1:正样本)。

模型训练过程如下:

  1. 用 V去查询W_0 ,用T去查询W_1 ,分别得到两个形状为[batch_size, embedding_size]的tensor,记为H_1 和H_2 。
  2. 将这两个tensor进行点积运算,最终得到一个形状为[batch_size]的tensor O = [ O i = ∑ j H 0 [ i , j ] × H 1 [ i , j ] ] i = 1 b a t c h _ s i z e。
  3. 使用sigmoid函数作用在 O上,将上述点积的结果归一化为一个0-1的概率值,作为预测概率,根据标签信息 L训练这个模型即可。

在结束模型训练之后,一般使用W_0作为最终要使用的词向量,用W_0的向量表示。通过向量点乘的方式,计算不同词之间的相似度。

相关文章
|
JavaScript 前端开发 数据安全/隐私保护
Vue3——如何实现页面访问拦截
Vue3——如何实现页面访问拦截
|
1月前
|
人工智能 边缘计算 API
2025大语言模型部署实战指南:从个人开发到企业落地全栈解决方案
本文深度解析了针对2025年大语言模型的四大主流部署框架,适用于不同场景的技术选型。从个人开发者使用的Ollama,支持快速本地部署与量化模型管理;到资源受限设备上的llama.cpp,通过极致优化使老旧硬件焕发新生;再到企业级服务的vLLM,提供高并发生产环境解决方案;以及跨平台开发桥接器LM Studio,作为全栈开发者的瑞士军刀。每种方案根据其特点覆盖了从本地调试、边缘计算到大规模生产的应用场景,旨在帮助技术团队精准匹配最适合的大模型部署方案,以实现效率和成本的最佳平衡。随着大模型应用的增长,选择正确的部署策略对于AI工程化落地至关重要。
WK
|
10月前
|
数据安全/隐私保护
QTextEdit
QTextEdit是Qt框架中的高级文本编辑控件,支持富文本格式、图像、列表和表格的插入,优化处理大型文档,支持HTML和Markdown格式,提供段落和字符级别的格式控制,以及占位文本提示。常用成员函数包括设置和获取文本内容、文本格式设置等。QTextEdit还提供了多种信号和丰富的交互功能,适用于需要处理复杂文本的应用场景。
WK
395 1
|
2月前
|
Web App开发 人工智能 数据可视化
猫头虎 推荐:国产开源AI工具 爱派(AiPy)|支持本地部署、自动化操作本地文件的AI办公神器
爱派(AiPy)是一款国产开源AI工具,支持本地部署与自动化操作,助力数据处理与办公效率提升。基于Python Use理念,AiPy让AI直接控制本地文件,简化繁琐任务,提供高效智能的解决方案,适用于数据工程师、分析师及日常办公用户。
807 0
|
机器学习/深度学习 人工智能 自然语言处理
6.1 自然语言处理综述
这篇文章综述了自然语言处理(NLP)的发展历程、主要挑战、常见任务,并探讨了如何利用深度学习和飞桨框架来解决NLP任务,同时提出了对NLP未来应用的思考。
|
机器学习/深度学习 数据采集 自然语言处理
6.2.3. 使用飞桨实现Skip-gram
这篇文章介绍了如何使用飞桨(PaddlePaddle)实现Skip-gram模型,包括数据下载、处理、网络定义、模型训练以及如何观察模型学习到的词向量效果。
|
SQL JSON 大数据
ElasticSearch的简单介绍与使用【进阶检索】 实时搜索 | 分布式搜索 | 全文搜索 | 大数据处理 | 搜索过滤 | 搜索排序
这篇文章是Elasticsearch的进阶使用指南,涵盖了Search API的两种检索方式、Query DSL的基本语法和多种查询示例,包括全文检索、短语匹配、多字段匹配、复合查询、结果过滤、聚合操作以及Mapping的概念和操作,还讨论了Elasticsearch 7.x和8.x版本中type概念的变更和数据迁移的方法。
ElasticSearch的简单介绍与使用【进阶检索】 实时搜索 | 分布式搜索 | 全文搜索 | 大数据处理 | 搜索过滤 | 搜索排序
|
8月前
|
数据可视化 JavaScript 前端开发
代码可视化平台
这是一个代码可视化工具,旨在简化代码理解过程。用户无需额外配置,直接复制代码即可实时观看执行过程,支持前进后退和动画展示。目前支持JavaScript和Python,未来将扩展更多语言。工具提供了数组、链表、栈、队列、二叉树和哈希表的可视化,并包含辅助函数和自定义注释功能。主要局限在于仅支持单段代码,且执行步数限制为500步。[了解更多](https://staying.fun/zh)
560 20
|
10月前
|
JSON 前端开发 JavaScript
Proxy + Fetch 实现类似于 axios 的基础 API
本项目通过 Proxy 和 Fetch 技术实现了一个类似 axios 的基础 API,支持请求拦截、响应处理等功能,简化了前端网络请求的开发流程,提升了代码的可维护性和扩展性。