NLP文本的表示,从【Bag-of-words】到 【word2vec】

目录

1. Bag-of-words

1.1 类似于one-hot的词袋模型

1.2 TF-IDF(term frequency–inverse document frequency)

2.Bi-gram和N-gram

3.离散表示存在的问题

4.共现矩阵

5.NNLM(Neural Network Language model)

5.1 NNLM的结构

6.CBOW 和 Skip-Gram

6.1 使用层次softmax进行优化

 6.2使用负例采样进行优化


1. Bag-of-words

 

1.1 类似于one-hot的词袋模型

       Bag of words模型最初被用在文本分类中,将文档表示成特征矢量。它的基本思想是假定对于一个文本,忽略其词序和语法、句法,仅仅将其看做是一些词汇的集合,而文本中的每个词汇都是独立的。简单说就是将每篇文档都看成一个袋子(因为里面装的都是词汇,所以称为词袋,Bag of words即因此而来),然后根据袋子里装的词汇对其进行分类。如果文档中猪、马、牛、羊、山谷、土地、拖拉机这样的词汇多些,而银行、大厦、汽车、公园这样的词汇少些,我们就倾向于判断它是一篇描绘乡村的文档,而不是描述城镇的。

基于文本的BoW模型的一个简单例子,首先给出两个简单的文本文档如下:

文档1:       John likes to watch movies. Mary likes too.

文档2:       John also likes to watch football games.

基于上述两个文档中出现的单词,构建如下一个词典 (dictionary):

Vocabulary=  {"John": 1, "likes": 2,"to": 3, "watch": 4, "movies": 5,"also": 6, "football": 7, "games": 8,"Mary": 9, "too": 10}

上面的词典中包含10个单词, 每个单词有唯一的索引, 那么每个文本我们可以使用一个10维的向量来表示。(用整数数字0~n(n为正整数)表示某个单词在文档中出现的次数):

文档1:   [1, 2, 1, 1, 1, 0, 0, 0, 1, 1]

文档2:   [1, 1,1, 1, 0, 1, 1, 1, 0, 0]

从上述的表示中,可以很清楚地看出来,在文档表示过程中并没有考虑关键词的顺序,而是仅仅将文档看成是一些关键词出现的概率的集合(这是Bag-of-words模型的缺点之一),每个关键词之间是相互独立的,这样每个文档可以表示成关键词出现频率的统计集合,类似于直方图的统计表示。

优点:简单,直观

缺点:所有的词一视同仁,没有考虑词的重要度,没有考虑词的上下文,没有考虑词与词之间的关系。

 

1.2 TF-IDF(term frequency–inverse document frequency)

        TF-IDF依旧属于词袋模型,相比与1.1节的,这种方式考虑了词对于一篇文章的重要度。

        TF(Term Frequency,词频)

        TF(Term Frequency,词频)表示一个给定词语t在一篇给定文档d中出现的频率。TF越高,则词语t对文档d来说越重要,TF越低,则词语t对文档d来说越不重要。那是否可以以TF作为文本相似度评价标准呢?答案是不行的,举个例子,常用的中文词语如“我”,“了”,“是”等,在给定的一篇中文文档中出现的频率是很高的,但这些中文词几乎在每篇文档中都具有非常高的词频,如果以TF作为文本相似度评价标准,那么几乎每篇文档都能被命中。

        对于在某一文档 \large d_{j}里的词语 \large t_{i}来说, \large t_{i} 的词频可表示为:

              \large tf_{i,j}=\frac{n_{i,j}}{\sum_{k}n_{k,j}}

      其中 ni,j 是词语 ti 在文档 dj 中的出现次数,分母则是在文件 dj 中所有词语的出现次数之和。

       注意,这里除了文档总词数作为分母,只是做了一个标准化,因为有的文章长,有的文章短,出现100次的不一定算多,出现3次的不一定就少。有时候也用其他作为分母进行标准化。

        IDF(Inverse Document Frequency,逆向文件频率)

IDF(Inverse Document Frequency,逆向文件频率)的主要思想是:如果包含词语t的文档越少,则IDF越大,说明词语t在整个文档集层面上具有很好的类别区分能力。IDF说明了什么问题呢?还是举个例子,常用的中文词语如“我”,“了”,“是”等在每篇文档中几乎具有非常高的词频,那么对于整个文档集而言,这些词都是不重要的。对于整个文档集而言,评价词语重要性的标准就是IDF。             

某一特定词语的IDF,可以由总文件数除以包含该词语的文件数,再将得到的商取对数得到:

               \large idf_{i}=log\frac{|D|}{|\{d_{j}:t_{i}\in d_{j}\}|}

其中 |D| 是语料库中所有文档总数,分母是包含词语 ti 的所有文档数。

有时分母为0,需要平滑,\large idf_{i}=log\frac{|D|}{|\{d_{j}:t_{i}\in d_{j}\}|+1}

 

             \large (TF-IDF)_{i,j}=tf_{i,j}*idf_{i}

    以1.1的例子,来计算两个文档中tf-idf(未平滑)。

import math


d1 = [1, 2, 1, 1, 1, 0, 0, 0, 1, 1]
d2 = [1, 1,1, 1, 0, 1, 1, 1, 0, 0]
assert len(d1) == len(d2)
contain_document_num=[0] * len(d1)

for i in range(len(d1)):
    if(0 != d1[i]):
        contain_document_num[i] += 1
    if(0 != d2[i]):
        contain_document_num[i] += 1
tf_1 = [i/float(sum(d1)) for i in d1]
tf_2 = [i/float(sum(d2)) for i in d2]
idf = [math.log(2 / float(i),math.e) for i in contain_document_num]

tf_idf_1 = [round(tf_1[i] * idf[i],5)  for i in range(len(d1))]
tf_idf_2 = [round(tf_2[i] * idf[i],5) for i in range(len(d2))]

  结果如下:

tf_idf_1 [0.0, 0.0, 0.0, 0.0, 0.08664, 0.0, 0.0, 0.0, 0.08664, 0.08664]
tf_idf_2 [0.0, 0.0, 0.0, 0.0, 0.0, 0.09902, 0.09902, 0.09902, 0.0, 0.0]

 优点:相比one-hot的表示方法,对词语的重要程度的特征能够体现

 缺点:没有考虑词的上下文,没有了考虑顺序,没有考虑词与词之间的关系。不能解决一词多义问题。

2.Bi-gram和N-gram

       N-Gram是一种基于统计语言模型的算法。它的基本思想是将文本里面的内容按照字节进行大小为N的滑动窗口操作,形成了长度是N的字节片段序列。

每一个字节片段称为gram,对所有gram的出现频度进行统计,并且按照事先设定好的阈值进行过滤,形成关键gram列表,也就是这个文本的向量特征空间,列表中的每一种gram就是一个特征向量维度。

该模型基于这样一种假设,第N个词的出现只与前面N-1个词相关,而与其它任何词都不相关,整句的概率就是各个词出现概率的乘积。这些概率可以通过直接从语料中统计N个词同时出现的次数得到。常用的是二元的Bi-Gram和三元的Tri-Gram。

如果我们有一个由 m 个词组成的序列(或者说一个句子),我们希望算得概率 \large p(w_{1},w_{2},\cdots,w_{m}) ,根据链式规则,可得

\large p(w_{1},w_{2},\cdots,w_{m})=p(w_{1})*p(w_{2}|w_{1})*p(w_{3}|w_{2},w_{1})\cdots p(w_{m}|w_{m-1},\cdots ,w_{1})

这个概率显然并不好算,不妨利用马尔科夫链的假设,即当前这个词仅仅跟前面几个有限的词相关,因此也就不必追溯到最开始的那个词,这样便可以大幅缩减上述算式的长度。即

\large p(w_{1},w_{2},\cdots,w_{i})=p(w_{i}|w_{i-1},\cdots ,w_{i-n+1})

这个马尔科夫链的假设为什么好用?我想可能是在现实情况中,大家通过真实情况将n=1,2,3,....这些值都试过之后,得到的真实的效果和时间空间的开销权衡之后,发现能够使用。

下面给出一元模型,二元模型,三元模型的定义:

当 n=1, 一个一元模型(unigram model)即为 :

                                                            \large p(w_{1},w_{2},\cdots,w_{m})=\prod_{i=1}^{m}p(w_{i})

当 n=2, 一个二元模型(bigram model)即为 :

                                                         \large p(w_{1},w_{2},\cdots,w_{m})=\prod_{i=1}^{m}p(w_{i}|w_{i-1})

当 n=3, 一个三元模型(trigram model)即为

                                            \large p(w_{1},w_{2},\cdots,w_{m})=\prod_{i=1}^{m}p(w_{i}|w_{i-2},w_{i-1})

然后下面的思路就很简单了,在给定的训练语料中,利用贝叶斯定理,将上述的条件概率值(因为一个句子出现的概率都转变为右边条件概率值相乘了)都统计计算出来即可。下面会给出具体例子讲解。这里先给出公式:

对于bi-gram model:

                                          \large p(w_{i}|w_{i-1})=\frac{Count(w_{i-1}w_{i})}{Count(w_{i-1})}

对于N-gram model:

                                       \large p(w_{i}|w_{i-1},\cdots ,w_{i-n+1})=\frac{Count(w_{i-n-1},\cdots ,w_{i})}{Count(w_{i-1-n},w_{i-1})}

优点:在较短的时间步考虑了词的顺序

缺点:词表的大小随着N的大小呈指数膨胀。不能解决一词多义的问题。

3.离散表示存在的问题

           无论是词袋模型,还是N-gram都是属于离散表示方法,他们都会存在下列一些问题:

         一、无法衡量词与词之间的关系,在编码的过程中信息就已经丢失。

       二、词表维度随着语料库增长膨胀;

       三、数据稀疏问题。

4.共现矩阵

         局域窗中的Word - Word 共现矩阵可以挖掘语法和语义信息。

        window length设为1(一般设为5~10,使用对称的窗函数(左右window length都为1)。

       以下有文档:

       D1 :   I like deep learning.

       D2 :    I like NLP.

       D3:     I enjoy flying.

D1,D2,D3的共现矩阵
countsIlikeenjoydeeplearningNLPflying.
I02100000
like20010100
enjoy10000010
deep01001000
learning00010001
NLP01000001
flying00100001
.00001110

        如果直接将共现矩阵行(列)作为词向量,会有以下问题:向量维数随着词典大小线性增长,存储整个词典的空间消耗非常大
,一些模型如文本分类模型会面临稀疏性问题,模型会欠稳定。所以通常使用SVD降维,构造低维稠密向量作为词的分布式表示 (25~1000维)!

       将这些词的词向量,第一维度,和第二维度,用图画出来,代码如下:

    

import numpy as np
import matplotlib.pyplot as plt
la = np.linalg
words = ["I","like","enjoy","deep","learning","NLP","flying","."]
X = np.array([
     [0,2,1,0,0,0,0,0],
     [2,0,0,1,0,1,0,0],
     [1,0,0,0,0,0,1,0],
     [0,1,0,0,1,0,0,0],
     [0,0,0,1,0,0,0,1],
     [0,1,0,0,0,0,0,1],
     [0,0,1,0,0,0,0,1],
     [0,0,0,0,1,1,1,0]
])
U,s,Vh = la.svd(X,full_matrices = False)
plt.axis([-0.65,0,-1,1])
for i in range(len(words)):
    plt.text(U[i,0],U[i,1],words[i])
词向量在第一维和第二维度在空间中的位置

        SVD降维可获得稠密的,词向量,但是依旧有一些难以解决的问题。

        一、计算量随语料库和词典增长膨胀太快, 对X(n,n)维的矩阵, 计算量O(n^3)。 而对大型的语料库,n~400k, 语料库大小1~60B token

        二、难以为词典中新加入的词分配词向量

        三、与其他深度学习模型框架差异大

 

5.NNLM(Neural Network Language model)

        直接从语言模型出发, 将模型最优化过程转化为求词向量表示的过程。

        目标函数     {\color{Red} }L(\theta )=\sum_{t}logP(w_{t}|w_{t-1},\cdots ,w_{1})

        一、 使用了非对称前向窗口,窗口长度为n-1;

        二、滑动窗口遍历整个语料库求和,计算量正比于语料库的大小;

        三、概率满足归一化的条件, 这样不同位置t处的概率才能相加, 即\sum_{w_{t}\in vocabulary}P(w_{t}|w_{t-1},\cdots ,w_{t-n+1})=1

5.1 NNLM的结构

NNLM的结构

         如图一个浅层的神经网络,用tanh双曲正切作为激活函数。

          训练完成后,投影矩阵C的每一列,就是一个词向量

6.CBOW 和 Skip-Gram

              如下图,CBOW 通过上下文词汇,预测中间词汇;Skip-gram通过中间词汇,预测上下文词汇。

CBOW和Skip-gram

           以CBOW为例,计算过程如下:

         

 

        对于CBOW,上下文窗口长度为2,其工作过程如下

       一、w_{t-2},w_{t-1},w_{t+1},w_{t+2}是四个词的索引,通过w_I (维度V*N,V是词表大小,N是词向量维度)矩阵,得到四个词向量 v_{t-2}, v_{t-1}, v_{t+1}, v_{t+2}。并求和vec=v_{t-2}+v_{t-1}+v_{t+1}+v_{t+2}。(v.shape = (1*N))。

       二、向量vec和矩阵w_O做矩阵乘法,得到output(output.shape = V*1)。

       三、对output做softmax,反向传播,求导。

      最终得到,w_I (维度V*N)就是词向量。

6.1 使用层次softmax进行优化

       

       当上下文窗口长度为2时,在w_I中,需要更新(4*N个参数),在w_O中需要对(300*V)参数更新。这样不利于更新的效率。

       解决方案,构建一颗哈夫曼树,可以使得每次更新的参数降低。

      如何“沿着霍夫曼树一步步完成”呢?在word2vec中,我们采用了二元逻辑回归的方法,即规定沿着左子树走,那么就是负类(霍夫曼树编码1),沿着右子树走,那么就是正类(霍夫曼树编码0)。判别正类和负类的方法是使用sigmoid函数,即:

                                           P(+)=sigmoid(\theta ^{T}vec)=\frac{a}{1+e^{-\theta^{T}vec }}            

      其中vec是当前内部节点的词向量,而\theta则是我们需要从训练样本求出的逻辑回归的模型参数。      

      使用霍夫曼树有什么好处呢?首先,由于是二叉树,之前计算量为V,现在变成了log_2(V)。第二,由于使用霍夫曼树是高频的词靠近树根,这样高频词需要更少的时间会被找到,这符合我们的贪心优化思想。

      容易理解,被划分为左子树而成为负类的概率为P(-)=1-P(+)。在某一个内部节点,要判断是沿左子树还是右子树走的标准就是看P(−),P(+)谁的概率值大。而控制P(−),P(+)谁的概率值大的因素一个是当前节点的词向量,另一个是当前节点的模型参数\theta

      在“足球”的例子中,假设N=300,V=6。如果不使用层次softmax,那么一次更新的参数量是num1 = 4*300+6*300。使用层次softmax更新的参数量为num2= 4*300+3*300。并且,num1num2的差距随着V变大会越来越大。

 6.2使用负例采样进行优化

       当不进行优化时,相当于再对1个正例结果和V-1个负例结果对应的参数矩阵同时做更新。其实负例样本可以不用那么多。设定一个参数,比如500,从V-1中采样出500作为负例(通常情况下,V>>500),来更新参数,这时每次更新的参数量num = 4*300+501*300

      关于采样,是按照词出现的频率是进行采样。即:

     每一个负样本被采样的概率: P(w_{i})=\frac{Count(i)}{\sum_{n=1}^{|V|}Count(w_{n})},但是实际中,需要取Count(w)^{\frac{3}{4}},即:

     P(w_{i})=\frac{Count(i)^\frac{3}{4}}{\sum_{n=1}^{|V|}Count(w_{n})^\frac{3}{4}}

 

7.总结

     本文回顾了,对文本的表示,从one-hot到word2vec的进化过程和方式,如有错误,请指出,一起交流学习。

            

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值