万物皆可embedding

不是做NLP方向对这方面之前也并不了解,但本科学习过文本挖掘课程,在数据挖掘的时候偶尔会涉及到对本文特征的处理,只好趁着机会再学习总结下。

embedding

简单来说,embedding就是用一个低维的向量表示一个物体,可以是一个词,或是一个item(商品,或是一个电影等等)。这个embedding向量的性质是能使距离相近的向量对应的物体有相近的含义,比如 Embedding(复仇者联盟)和Embedding(钢铁侠)之间的距离就会很接近,但 Embedding(复仇者联盟)和Embedding(乱世佳人)的距离就会远一些。

除此之外Embedding甚至还具有数学运算的关系,比如Embedding(马德里)-Embedding(西班牙)+Embedding(法国)≈Embedding(巴黎)

从另外一个空间表达物体,甚至揭示了物体间的潜在关系,上次体会这样神奇的操作还是在学习傅里叶变换的时候,从某种意义上来说,Embedding方法甚至具备了一些本体论的哲学意义。

言归正传,Embedding能够用低维向量对物体进行编码还能保留其含义的特点非常适合深度学习。在传统机器学习模型构建过程中,我们经常使用one hot encoding对离散特征,特别是id类特征进行编码,但由于one hot encoding的维度等于物体的总数,比如阿里的商品one hot encoding的维度就至少是千万量级的。这样的编码方式对于商品来说是极端稀疏的,甚至用multi hot encoding对用户浏览历史的编码也会是一个非常稀疏的向量。而深度学习的特点以及工程方面的原因使其不利于稀疏特征向量的处理。因此如果能把物体编码为一个低维稠密向量再喂给DNN,自然是一个高效的基本操作。

Embedding可以说是神经网络最基础但是最重要的部分。在自然语言处理中(NLP),数字化文本中的词汇是首先需要做的步骤。在这篇文章中,我们将抛开one-hot encoding这种无脑的Embedding形式,介绍一下从词上下文角度来计算词向量的方法(CBOW)。

用一张图回顾一下CBOW对词语出现概率的假设:

如图所示,CBOW的核心思想就是通过上下文来预测中心词,也就是通过”今天“和”真好“来计算输出”天气“的概率。而中间的那个节点就是样本空间到结果空间的映射函数。聪明的同学在说到映射函数的时候是不是就想到最基本的神经网络结构了?没错,最基础的词汇Embedding正是用到了神经网络。问题来了,既然需要利用神经网络来训练词向量,那么输入和输出是什么呢,怎么寻找训练集呢?别着急,再给你们举个带有输入输出的例子'':

原始文档:['我','今天‘,’不开心']输入输出:X = ['我','不开心'] Y = ['今天']

看到这里是不是恍然大悟,训练词向量所需要的Y就是随机从文档中选择的词,X呢就是这个词周围的上下文。 OK,训练数据问题解决了,但是,不是说好是词向量么,向量中的数字是怎么得到的呢,另外,怎么才能训练出预先定义好维数的向量呢?这两个问题看上去更加的高大上了,但其实,呵呵呵。

首先,向量中数字的问题。请您再回看一遍上面那个丑丑的图,然后再回想一下上一篇文章讲的CBOW的概念。从神经网络的角度出发,既然是特征映射,无脑的使用加权平均公式我们可以将上面那张图转换为:这里的y就是融合了上下文特征的值(特征实在是不怎么值钱),而词汇在等式里里可以用onehot编码表示,多个词用平均或者拼接都可以。OK,做到这一步其实已经完成一半了,这时候我们从CBOW的角度出发,看看”天气“这个词是怎么出来的:你可能这时候有疑问,CBOW指的是概率啊,跟神经网络有什么关系,别着急,回想一下之前说过的激活函数,我们对中间的那个节点进行激活,随便用什么函数都行(Softmax,Sigmoid),只要条件允许并且其值域在[0,1]范围内都行。这样我们就完美地把y转换为输出的概率了。剩下的就是神经网络老掉牙的反向传播调整权重,略过略过。最终我们会得到一个最优的权重a,这个就是”今天“这个词的嵌入值了。解决了值的问题,剩下的就是维数的问题。这个就更简单了,再仔细看那张丑图,中间有一个节点对应最终的一个权重,如果把这个节点增加到100个,是不是就对应了100个权重了?是不是超级简单。至于增加了100个怎么计算,请自行百度全连接神经网络,我就不废话了。

word2vec

对word的vector表达的研究早已有之,但让embedding方法空前流行,我们还是要归功于google的word2vec。我们简单讲一下word2vec的原理.

既然我们要训练一个对word的语义表达,那么训练样本显然是一个句子的集合。假设其中一个长度为T的句子为。这时我们假定每个词都跟其相邻的词的关系最密切,换句话说每个词都是由相邻的词决定的(CBOW模型的动机),或者每个词都决定了相邻的词(Skip-gram模型的动机)。如下图,CBOW的输入是周边的词,预测的输出是  ,而Skip-gram则反之,经验上讲Skip-gram的效果好一点,所以本文从Skip-gram模型出发讲解模型细节。

640?wx_fmt=jpeg

那么为了产生模型的正样本,我们选一个长度为2c+1(目标词前后各选c个词)的滑动窗口,从句子左边滑倒右边,每滑一次,窗口中的词就形成了我们的一个正样本。

有了训练样本之后我们就可以着手定义优化目标了,既然每个词  都决定了相邻词,基于极大似然,我们希望所有样本的条件概率之积最大,这里我们使用log probability。我们的目标函数有了:

640?wx_fmt=jpeg

接下来的问题是怎么定义 640?wx_fmt=png ,作为一个多分类问题,最简单最直接的方法当然是直接用softmax函数(关于softmax https://blog.csdn.net/bitcarmanlee/article/details/82320853),我们又希望用向量640?wx_fmt=png表示每个词w,用词之间的距离640?wx_fmt=png表示语义的接近程度,那么我们的条件概率的定义就可以很直观的写出。

640?wx_fmt=jpeg

看到上面的条件概率公式,很多同学可能会习惯性的忽略一个事实,就是我们用 640?wx_fmt=png 去预测 640?wx_fmt=png ,但其实这二者的向量表达并不在一个向量空间内。

就像上面的条件概率公式写的一样,和分别是词w的输出向量表达和输入向量表达。那什么是输入向量表达和输出向量表达呢?我们画一个word2vec的神经网络架构图就明白了。

640?wx_fmt=jpeg

根据640?wx_fmt=png的定义,我们可以把两个vector的乘积再套上一个softmax的形式转换成上面的神经网络架构。在训练过程中我们就可以通过梯度下降的方式求解模型参数了。那么上文所说的输入向量表达就是input layer到hidden layer的权重矩阵640?wx_fmt=png,而输出向量表达就是hidden layer到output layer的权重矩阵640?wx_fmt=png

那么到底什么是我们通常意义上所说的词向量 640?wx_fmt=png呢?

其实就是我们上面所说的输入向量矩阵640?wx_fmt=png中每一行对应的权重向量。于是这个权重矩阵自然转换成了word2vec的lookup table。

640?wx_fmt=jpeg

当然在训练word2vec的过程中还有很多工程技巧,比如用negative sampling或Hierarchical Softmax减少词汇空间过大带来的计算量,对高频词汇进行降采样避免对于这些低信息词汇的无谓计算等。在具体实现的时候最好参考Google的原文 Distributed Representations of Words and Phrases and their Compositionality

从word2vec到item2vec

在word2vec诞生之后,embedding的思想迅速从NLP领域扩散到几乎所有机器学习的领域,我们既然可以对一个序列中的词进行embedding,那自然可以对用户购买序列中的一个商品,用户观看序列中的一个电影进行embedding。而广告、推荐、搜索等领域用户数据的稀疏性几乎必然要求在构建DNN之前对user和item进行embedding后才能进行有效的训练。

具体来讲,如果item存在于一个序列中,item2vec的方法与word2vec没有任何区别。而如果我们摒弃序列中item的空间关系,在原来的目标函数基础上,自然是不存在时间窗口的概念了,取而代之的是item set中两两之间的条件概率。

具体可以参考item2vec的原文 Item2Vec:Neural Item Embedding for Collaborative Filtering

但embedding的应用又远不止于此,事实上,由于我们也可以把输出矩阵的列向量当作item embedding,这大大解放了我们可以用复杂网络生成embedding的能力。读过我专栏上一篇文章 YouTube深度学习推荐系统的十大工程问题 的同学肯定知道,YouTube在serve其candidate generation model的时候,只将最后softmax层的输出矩阵的列向量当作item embedding vector,而将softmax之前一层的值当作user embedding vector。在线上serving时不用部署整个模型,而是只存储user vector和item vector,再用最近邻索引进行快速搜索,这无疑是非常实用的embedding工程经验,也证明了我们可以用复杂网络生成user和item的embedding。

(KDD 2018 best paper Real-time Personalization using Embeddings for Search Ranking at Airbnb 也介绍了Airbnb的embedding最佳实践)

在文本进行embedding处理过程中,常用python的gensim模块

Gensim(http://pypi.python.org/pypi/gensim)是一款开源的第三方Python工具包,用于从原始的非结构化的文本中,无监督地学习到文本隐层的主题向量表达。 主要用于主题建模和文档相似性处理,它支持包括TF-IDF,LSA,LDA,和word2vec在内的多种主题模型算法。Gensim在诸如获取单词的词向量等任务中非常有用。

1.tf-idf

在文本处理中,TF-IDF可以说是一个简单粗暴的东西。它可以用作特征抽取,关键词筛选等。

  

  

  

以网页搜索“核能的应用”为例,关键字分成“核能”、“的”、“应用”。根据直觉,我们知道,包含这三个词较多的网页比包含它们较少的网页相关性强。但是仅仅这样,就会有漏洞,那就是文本长的比文本短的关键词数量要多,所以相关性会偏向长文本的网页。所以我们需要归一化,即用比例代替数量。用关键词数除以总的词数,得到我们的“单文本词频(Term Frequency)”最后的TF为各个关键词的TF相加。这样还不够,还是有漏洞。像“的”、“和”等这样的常用字,对衡量相关性没什么作用,但是几乎所有的网页都含有这样的字,所以我们要忽略它们。于是就有了IDF(Inverse Document Frequency)

原理非常简单,结合单词的词频和包含该单词的文档数,统计一下,计算TF和IDF的乘积即可。

除了gensim,sklearn里面也提供TF-IDF。主要用到了两个函数:CountVectorizer()和TfidfTransformer()。CountVectorizer是通过fit_transform函数将文本中的词语转换为词频矩阵,矩阵元素weight[i][j] 表示j词在第i个文本下的词频,即各个词语出现的次数;通过get_feature_names()可看到所有文本的关键字,通过toarray()可看到词频矩阵的结果。TfidfTransformer也有个fit_transform函数,它的作用是计算tf-idf值。

2.word2vec

使用Gensim训练Word2vec十分方便,训练步骤如下:

        1)将语料库预处理:一行一个文档或句子,将文档或句子分词(以空格分割,英文可以不用分词,英文单词之间已经由空格分割,中文预料需要使用分词工具进行分词,常见的分词工具有StandNLP、ICTCLAS、Ansj、FudanNLP、HanLP、结巴分词等);

        2)将原始的训练语料转化成一个sentence的迭代器,每一次迭代返回的sentence是一个word(utf8格式)的列表。可以使用Gensim中word2vec.py中的LineSentence()方法实现;

        3)将上面处理的结果输入Gensim内建的word2vec对象进行训练即可:

from gensim.models import Word2Vec 
 
sentences = word2vec.LineSentence('./in_the_name_of_people_segment.txt') 
# in_the_name_of_people_segment.txt 分词之后的文档
 
model = Word2Vec(sentences , size=100, window=5, min_count=1, workers=4)

 在gensim中,word2vec 相关的API都在包gensim.models.word2vec中。和算法有关的参数都在类gensim.models.word2vec. Word2Vec中。算法需要注意的参数有:

class Word2Vec(utils.SaveLoad):
    def __init__(
            self, sentences=None, size=100, alpha=0.025, window=5, min_count=5,
            max_vocab_size=None, sample=1e-3, seed=1, workers=3, min_alpha=0.0001,
            sg=0, hs=0, negative=5, cbow_mean=1, hashfxn=hash, iter=5, null_word=0,
            trim_rule=None, sorted_vocab=1, batch_words=MAX_WORDS_IN_BATCH):

sentences:可以是一个list,对于大语料集,建议使用BrownCorpus,Text8Corpus或lineSentence构建。
size:是指词向量的维度,默认为100。这个维度的取值一般与我们的语料的大小相关,如果是不大的语料,比如小于100M的文本语料,则使用默认值一般就可以了。如果是超大的语料,建议增大维度。大的size需要更多的训练数据,但是效果会更好. 推荐值为几十到几百。
window:窗口大小,即词向量上下文最大距离,这个参数在我们的算法原理篇中标记为c。window越大,则和某一词较远的词也会产生上下文关系。默认值为5。在实际使用中,可以根据实际的需求来动态调整这个window的大小。如果是小语料则这个值可以设的更小。对于一般的语料这个值推荐在[5,10]之间。个人理解应该是某一个中心词可能与前后多个词相关,也有的词在一句话中可能只与少量词相关(如短文本可能只与其紧邻词相关)。
min_count: 需要计算词向量的最小词频。这个值可以去掉一些很生僻的低频词,默认是5。如果是小语料,可以调低这个值。可以对字典做截断, 词频少于min_count次数的单词会被丢弃掉。
negative:即使用Negative Sampling时负采样的个数,默认是5。推荐在[3,10]之间。这个参数在我们的算法原理篇中标记为neg。
cbow_mean: 仅用于CBOW在做投影的时候,为0,则算法中的为上下文的词向量之和,为1则为上下文的词向量的平均值。在我们的原理篇中,是按照词向量的平均值来描述的。个人比较喜欢用平均值来表示,默认值也是1,不推荐修改默认值。
 iter: 随机梯度下降法中迭代的最大次数,默认是5。对于大语料,可以增大这个值。
alpha: 是初始的学习速率,在训练过程中会线性地递减到min_alpha。在随机梯度下降法中迭代的初始步长。算法原理篇中标记为η,默认是0.025。
min_alpha: 由于算法支持在迭代的过程中逐渐减小步长,min_alpha给出了最小的迭代步长值。随机梯度下降中每轮的迭代步长可以由iter,alpha, min_alpha一起得出。这部分由于不是word2vec算法的核心内容,因此在原理篇我们没有提到。对于大语料,需要对alpha, min_alpha,iter一起调参,来选择合适的三个值。
max_vocab_size: 设置词向量构建期间的RAM限制,设置成None则没有限制。
sample: 高频词汇的随机降采样的配置阈值,默认为1e-3,范围是(0,1e-5)。
seed:用于随机数发生器。与初始化词向量有关。
workers:用于控制训练的并行数。

gensim  word2vec实战 https://blog.csdn.net/qq_27586341/article/details/90025288

模型出来了,我们可以用来做什么呢?这里给出三个常用的应用。

1.  第一个是最常用的:找出某一个词向量最相近的词集合

代码如下:

req_count = 5
for key in model.wv.similar_by_word('沙瑞金'.decode('utf-8'), topn =100):
    if len(key[0])==3:
        req_count -= 1
        print key[0], key[1]
        if req_count == 0:
            break;
我们看看沙书记最相近的一些3个字的词(主要是人名)如下:

高育良 0.967257142067
李达康 0.959131598473
田国富 0.953414440155
易学习 0.943500876427
祁同伟 0.942932963371
2. 第二个应用:看两个词向量的相近程度

这里给出了书中两组人的相似程度:

print model.wv.similarity('沙瑞金'.decode('utf-8'), '高育良'.decode('utf-8'))
print model.wv.similarity('李达康'.decode('utf-8'), '王大路'.decode('utf-8'))
 输出如下:

0.961137455325
0.935589365706
3. 第三个应用:找出不同类的词

这里给出了人物分类题:

print model.wv.doesnt_match(u"沙瑞金 高育良 李达康 刘庆祝".split())
word2vec也完成的很好,输出为"刘庆祝"。

发布了10 篇原创文章 · 获赞 2 · 访问量 1789

猜你喜欢

转载自blog.csdn.net/weixin_41814051/article/details/104393633