「词嵌入」在NLP中扮演什么角色?一文搞懂其背后原理


「词嵌入」

  原文来源:DATASCIENCE

  作者:Ruslana Dalinina

 http://dy.163.com/v2/article/detail/D0PTK8F00511C9QL.html

  介绍

  相信大家都清楚,自然语言处理(NLP)所涉及的广阔领域使得我们能够理解从在线评论到音频录制这样大量语言数据的模式。但在数据科学家真正挖掘出一个NLP问题之前,他或她必须为此奠定基础,从而帮助模型对即将遇到的不同语言单位有所了解。

  词嵌入(Word embeddings)是一组广泛应用于预测NLP建模的特征工程技术,特别是在深度学习应用中的使用更为显著。词嵌入是将词的稀疏向量表示转换为密集、连续的向量空间,使你能够识别单词和短语之间的相似性,而这一点很大程度上依赖于它们的语境。

  在本文中,我将详细阐述一下词嵌入背后的原理,并演示如何使用这些技术对来自亚马逊的500000份评论数据创建相似词汇的集群。你可以下载数据集(https://www.kaggle.com/snap/amazon-fine-food-reviews)来进行追踪。

  词嵌入:它们是如何运作的?

  在典型的词袋(bag-of-words)模型中,每个单词都被认为是一个唯一的标记,与其他单词毫无关联。例如,即使“salt(盐)”和“seasoning(调味料)”平时都是高频地出现在相同的语境或是句子中,也将被分配一个唯一的ID。词嵌入是一系列特征工程技术,它将稀疏的词向量映射到基于周围语境的连续空间中。话句话说,你可以将此过程视为将高维向量词表示嵌入到一个较低维的空间中。

  而这样的向量表示比较单词或短语提供了便利属性。例如,如果“salt”和“seasoning”出现在相同的语境中,那么模型将表明“salt”在概念上更接近于“seasoning”,而不是“chair(椅子)”。

  现如今有几种用于构建词嵌入表示的模型。谷歌的word2vec是最为广泛使用的实现之一,而这主要得益于它的训练速度和性能。Word2vec是一个预测模型,这意味着它并不是像LDA(latent Dirichlet allocation)那样需要进行单词计数,而是被训练为从邻近词的语境中预测目标词。该模型首先使用独热编码(one-hot-encoding)对每个单词进行编码,然后使用权重矩阵将其馈送到隐藏层;该过程的输出则是目标单词。词嵌入向量实际上是该拟合模型的权重。为了更好地进行说明,这里有一个简单的视觉图:

  

  实际上,Word2vec有两种“风格”的词嵌入模型:连续的词袋(CBOW)和跳格式。CBOW实现会在目标单词周围出现一个滑动窗口,以便于做出预测。而另一方面,跳格式模型则是截然相反的——它会预测出给定目标单词的语境周边的单词。有关跳格式模型的更多信息,请查阅此篇学术论文(https://arxiv.org/abs/1310.4546)。

  入门

  为什么有那么多人对词嵌入如此感兴趣呢?这主要是因为词嵌入在深度学习的多种任务中有着非常广泛的应用,如情绪分析、语法分析、命名称识别等。除此之外,它们还可以用于:

  通过基于语境的保存单词与单词之间的相似性,提供一种更为复杂的方法来表示数字空间中的单词。

  提供单词或短语之间的相似度的衡量标准。

  用作分类任务中的特征。

  提高模型性能。

  使用词嵌入可以发现许多有趣的关系,最著名的例子莫过于此了:king - man + woman = queen。所以,请继续在Amazon Fine Foods评论数据集中进行词嵌入操作吧!

  可以先从越多语料库开始,我将使用一个面向DataScience.com客户的模块,该模块被称之为“客户手册”,其中包含常见文本处理和建模任务的代码,例如删除或阻止不良字符。它也可以用于主题建模和意见挖掘任务。

  

# imports

  %matplotlib inline

  import os

  import pandas as pd

  import numpy

  import matplotlib.pyplot as plt

  import string

  import re

  from gensim import corpora

  from gensim.models import Phrases

  from nltk.corpus import stopwords

  from nltk.stem.porter import PorterStemmer

  from ds_voc.text_processing import TextProcessing

  # sample for speed

  raw_df = raw_df.sample(frac=0.1, replace=False)

  print raw_df.shape

  # grab review text

  raw = list(raw_df['Text'])

  print len(raw)

  先标注我们的样本,并做一般的清理步骤:删除不好的字符,过滤单词和干扰:

  # word2vec expexts a list of list: each document is a list of tokens

  te = TextProcessing()

  cleaned = [te.default_clean(d) for d in raw]

  sentences = [te.stop_and_stem(c) for c in cleaned]

  现在我们已经准备好适应一个模型了。这里我们使用gensim实现:

  from gensim.models import Word2Vec

  model = Word2Vec(sentences=sentences, # tokenized senteces, list of list of strings

  size=300, # size of embedding vectors

  workers=4, # how many threads?

  min_count=20, # minimum frequency per token, filtering rare words

  sample=0.05, # weight of downsampling common words

  sg = 0, # should we use skip-gram? if 0, then cbow

  iter=5,

  hs = 0

  )

  X = model[model.wv.vocab]

  瞧!这很简单。现在,你可以问这个模型有什么问题?回想一下,在相似情景下发生的词将被认为是相似的,从而形成“集群”。 从简单的例子开始,通过检查模型认为类似于“花生”的什么样的词语:

  

print (model.most_similar('peanut'))

  [(u'butter', 0.9887357950210571), (u'fruit', 0.9589880108833313), (u'crunchi', 0.9448184967041016), (u'potato', 0.9327490329742432), (u'textur', 0.9302218556404114), (u'nut', 0.9176014065742493), (u'tasti', 0.9175000190734863), (u'sweet', 0.9135239124298096), (u'appl', 0.9122942686080933), (u'soft', 0.9103059768676758)]

  不错,最相似的标记当然是“黄油”,其他的标记也很直观。然而,“水果”和“苹果”可能是客户提到果酱和花生酱时扩展的结果。让我们再举几个例子:

  

print (model.most_similar('coffee'))

  [(u'k', 0.8691866397857666), (u'starbuck', 0.862629771232605), (u'keurig', 0.85813969373703), (u'decaf', 0.8456668853759766), (u'blend', 0.840221643447876), (u'bold', 0.8374124765396118), (u'cup', 0.8330360651016235), (u'brew', 0.8262926340103149), (u'espresso', 0.8225802183151245), (u'roast', 0.812541127204895)]

  print (model.most_similar('spice'))

  [(u'refresh', 0.9925233721733093), (u'caramel', 0.9756978750228882), (u'pepper', 0.9739495515823364), (u'cherri', 0.9737452268600464), (u'slightli', 0.9729464054107666), (u'cinnamon', 0.9727376699447632), (u'lemon', 0.9724155068397522), (u'blueberri', 0.9717040061950684), (u'sour', 0.971449613571167), (u'cocoa', 0.9712052345275879)]

  请注意,即使“焦糖苹果香料”是一个受欢迎的组合,但与“香料”最相似的词语是“刷新”和“焦糖”。你可以扩展类比,不仅可以在上下文中包含多个词,还可以排除某些词。我对富含蛋白质的零食很感兴趣,但我不想服用蛋白质补充剂:

  

print (model.most_similar(['snack', 'protein'], negative=['supplement']))

  这是模型得出来的结果:

 

 [(u'chip', 0.7655218839645386), (u'bar', 0.7496042251586914), (u'potato', 0.7473998069763184), (u'peanut', 0.741823136806488), (u'feel', 0.7318717241287231), (u'cereal', 0.7217452526092529), (u'nut', 0.716484546661377), (u'butter', 0.7104200124740601), (u'healthi', 0.7084594964981079), (u'low', 0.7055443525314331)]

  该模型发现,“薯条”,“酒吧”,“花生”,“坚果”和“健康”等词汇与“零食”和“蛋白质”相同处在同一词群中,但与“补充品”的词群不同。

  当然,这些都是简单的例子,但别忘了我们在一个小样本的评论中训练了该模型。我建议你扩展数据集,以构建更强大的词嵌入。

  可视化词向量

  为了可视化所得到的单词嵌入,我们可以使用降维技术t-SNE,它可以将所得到的嵌入向量投影到二维中:

  

# visualize food data

  from sklearn.manifold import TSNE

  tsne = TSNE(n_components=2)

  X_tsne = tsne.fit_transform(X)

  plt.rcParams['figure.figsize'] = [10, 10]

  plt.scatter(X_tsne[:, 0], X_tsne[:, 1])

  plt.show()

  

  我们可以看到一些集群形成。我们来标记每一个点,看看所得到的集群。你还可以使用散景库(bokeh library)https://bokeh.pydata.org/en/latest/进行交互式绘图,以便进行缩放。

  

from bokeh.plotting import figure, show

  from bokeh.io import push_notebook, output_notebook

  from bokeh.models import ColumnDataSource, LabelSet

  def interactive_tsne(text_labels, tsne_array):

  '''makes an interactive scatter plot with text labels for each point'''

  # define a dataframe to be used by bokeh context

  bokeh_df = pd.DataFrame(tsne_array, text_labels, columns=['x','y'])

  bokeh_df['text_labels'] = bokeh_df.index

  # interactive controls to include to the plot

  TOOLS="hover, zoom_in, zoom_out, box_zoom, undo, redo, reset, box_select"

  p = figure(tools=TOOLS, plot_width=700, plot_height=700)

  # define data source for the plot

  source = ColumnDataSource(bokeh_df)

  # scatter plot

  p.scatter('x', 'y', source=source, fill_alpha=0.6,

  fill_color="#8724B5",

  line_color=None)

  # text labels

  labels = LabelSet(x='x', y='y', text='text_labels', y_offset=8,

  text_font_size="8pt", text_color="#555555",

  source=source, text_align='center')

  p.add_layout(labels)

  # show plot inline

  output_notebook()

  show(p)

  其中一些更具直观的意义,如左下角包含decaf,bean和french的集群:

  

  附加提示

  我们还可以通过在模型中添加二进制和/或部分语音标签来改进词嵌入。在同一个单词可能具有多重含义的情况下,词性标注可能很有用。例如,seasoning可以是名词也可以是动词,并且根据词性的不同而具有不同的含义。

sent_w_pos = [nltk.pos_tag(d) for d in sentences]

  sents = [[tup[0]+tup[1] for tup in d] for d in sent_w_pos]

  model_pos = Word2Vec(sentences=sents,

  size=300,

  workers=4,

  min_count=20,

  sample=0.05,

  sg = 0,

  hs=0,

  X = model_pos[model_pos.wv.vocab]
  我们还可以在我们的模型中添加二进制对象,以便将频繁的单词对分组:  

bigrams = Phrases(sentences)

  model = Word2Vec(sentences=bigrams[sentences],

  结论

  现在,你对词嵌入有了一些基本的了解,并可以使用gensim中的word2vec生成向量。这个有用的技术应该在你的NLP工具箱中,因为它可以在各种建模任务中派上用场。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值