data sample feature extraction && data vector representation - NLP问题中样本数据的向量化表示

catalogue

1. 引言
2. 朴素词频无序定长向量化表示 - bag of words词袋模型
3. n-gram模型
4. Tf–idf算法

 

1. 引言

特征提取的意义在于把复杂的数据,如文本和图像,转化为数字特征,从而在机器学习中使用。
我们本文要讨论的"文本分析"是机器学习算法的主要应用领域。然而,符号文字序列不能直接传递给这些算法,因为他们要求"数值的",且"固定长度"的矩阵特征(向量化表示)而不是可变长度的文本文档。

这就要求我们对样本的原始特征空间进行抽象,将其映射到另一个向量化的定长特征空间中

0x1: 样本数据归一化

我们用一个例子来引出归一化对数据处理的影响,假定为预测房价的例子,自变量为面积,房间数两个,因变量为房价。

那么可以得到的公式为:

首先我们先通过2张图来对比数据是否均一化的最优解寻解过程

未归一化

归一化之后

我们来理解一下上面2张图:

我们在寻找最优解的过程也就是在寻找使得loss function最小的theta1,theta2。上述两幅图代表是的损失函数的等高线,我们可以看出,当数据没有归一化的时候,theta2(面积数)的量纲(0~1000),而theta1(房间数)的量纲(0~10),因为量纲的不同,等高线被拉成了椭圆,这会造成什么影响呢?

这样造成的影响就是在使用梯度下降调整参数,已最小化损失函数的时候

数据没有归一化的表达式,目标函数值为

造成图像的等高线为类似椭圆形状,最优解的寻优过程就是像下图所示:

除此之外我们可以看到,因为600这个值很大,造成只要theta2稍微变动一些,theta2方向的步长就会变化很大,这很不利于模型收敛

回想一下奥科姆剃刀原则和正则化惩罚因子的作用:用尽可能小且平均的参数,而避免使用少量过大且集中的参数,这有利于模型更真实地拟合数据并具备更好的泛化能力

而数据归一化之后,损失函数的表达式可以表示为

其中变量的前面系数几乎一样,则图像的等高线为类似圆形形状,最优解的寻优过程像下图所示

样本的值归一化后,收敛过程也更加平稳了

从上可以看出,数据归一化后,最优解的寻优过程明显会变得平缓,更容易正确的收敛到最优解。 

1. 归一化的定义

归一化用一句话说就是:把数据经过处理后使之限定在一定的范围内。比如通常限制在区间[0, 1]或者[-1, 1] 等等

2. 常用归一化方法

最大-最小标准化

最大-最小标准化是对原始数据进行线性变换,设分别是属性的最小值和最大值,将的一个原始值通过最大-最小标准化映射到区间[0, 1]的值,那么公式如下

Z-score标准化

Z-score标准化是基于原始数据的均值和标准差进行的数据标准化。将属性的原始数据通过Z-score标准化成。Z-score标准化适用于属性的最大值或者最小值未知的情况,或有超出取值范围的离散数据的情况。

其中为均值,为标准差。Z-score标准化得到的结果是所有数据都聚集在0附近,方差为1

3. 在进行数据分析的时候,什么情况下需要对数据进行标准化处理?

主要看模型是否具有伸缩不变性

1. 有些模型(例如SVM)在各个维度进行不均匀伸缩后,最优解与原来不等价(换句话说即该模型对样本中的奇异点很敏感,很容易受到奇异点的影响),对于这样的模型,除非本来各维数据的分布范围就比较接近,否则必须进行标准化,以免模型参数被分布范围较大或较小的数据dominate
2. 有些模型在各个维度进行不均匀伸缩后,最优解与原来等价,例如logistic regression。对于这样的模型,是否标准化理论上不会改变最优解

但是,由于实际求解往往使用迭代算法,如果目标函数的形状太“扁”,迭代算法可能收敛得很慢甚至不收敛。所以对于具有伸缩不变性的模型,最好也进行数据标准化。还要注意的另一点是

如果本来各维的量纲是相同的,最好不要做归一化,以尽可能多地保留信息。
如果本来各维的量纲是不同的,那么直接做PCA没有意义,就需要先对各维分别归一化。

Relevant Link:

https://www.zhihu.com/question/30038463/answer/50491149
https://www.zhihu.com/question/31186681/answer/50929278
https://www.zhihu.com/question/26546711/answer/62085061
https://www.zhihu.com/question/37129350/answer/70592743
https://www.zhihu.com/question/20455227
http://blog.csdn.net/acdreamers/article/details/44664205 

 

2. 朴素词频无序定长向量化表示 - bag of words词袋模型

Bag-of-words model (BoW model) 最早出现在NLP和IR领域. 该模型忽略掉文本的语法和语序, 用一组无序的单词(words)来表达一段文字或一个文档

0x1: BoW model

Bag-of-word是将一个document/sentence进行向量化,所以假设我们有2段独立的sentence

John likes to watch movies. Mary likes too.
John also likes to watch football games.

根据上述两句话中出现的单词, 我们能构建出一个无序字典 (dictionary),字典的key是token化后的单词,字典的value是该词在字典中的索引

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

该字典中包含10个单词, 每个单词有唯一索引, 注意它们的顺序和出现在句子中的顺序没有关联. 根据这个字典, 我们能将上述两句话重新表达为下述两个向量:

[1, 2, 1, 1, 1, 0, 0, 0, 1, 1]
[1, 1, 1, 1, 0, 1, 1, 1, 0, 0]

这两个向量共包含10个元素, 其中第i个元素表示字典中第i个单词在句子中出现的次数. 因此BoW模型可认为是一种统计直方图 (histogram). 在文本检索和处理应用中, 可以通过该模型很方便的计算词频.

可以看到,这是一种one-hot encoding思想,向量化后的特征空间是整个无序字典的size(fix length定长),而单独一个vector的一个slot的值代表了该词在当前sentence中出现的次数(注意:和整体训练集无关,只与当前sentence有关)。例如我们看第一行"John likes to watch movies. Mary likes too."

# John likes to watch movies. Mary likes too

1. 我们按照字典顺序重建出一个vector
{"John": ?, "likes": ?, "to": ?, "watch": ?, "movies": ?, "also": ?, "football": ?, "games": ?, "Mary": ?, "too": ?}

2. 遍历这个vector,根据每个token word在当前sentence中出现的词频来对value赋值
{"John": 1, "likes": 2, "to": 1, "watch": 1, "movies": 1, "also": 0, "football": 0, "games": 0, "Mary": 1, "too": 1}
# John出现了1次;like出现了2次;also没出现

可以看到,在这个体系中,特征和样本有如下定义:

1. 每个 独立令牌(token) 出现频率 (归一化或未归一化) 被当做一个 (特征)feature - 以词频作为映射后特征空间的单个维度
2. 每个 document(文本) 中所有的令牌频率向量被看做一个多元 sample(样本)(特征向量)

因此文本的集合可被表示为矩阵形式,每行一条文本,每列对应每个文本中出现的词令牌(如单个词)的频率

我们称 vectorization(向量化) 是转化文本集合为数值向量的普遍方法。这种特殊思想,包括令牌化,统计频数和归一化,被称为 Bag of Words(词袋子)。文本被词出现频率描述,完全忽略词的相对位置信息。

0x2: sklearn封装实现

sklearn对bag-of-word的词频向量化处理进行了封装,scikit-learn为数值特征提取最常见的方式提供了一系列工具

1. tokenizing: 对每个可能的词令牌分成字符串(word split)并赋予整形的id(索引化),比如使用空格和作为令牌分割依据 
2. counting: 统计每个词令牌在文档(单个文档,不是整体训练集)中的出现次数 
3. normalizing: 在大多数的文档 / 样本中,可以减少重要的次令牌的权重 

CountVectorizer 在单个类中实现了令牌化和出现频数统计:

1. 令牌化字符串,提取至少两个字母的词

from sklearn.feature_extraction.text import CountVectorizer
if __name__ == '__main__':
    vectorizer = CountVectorizer(min_df=1) 
        analyze = vectorizer.build_analyzer()
        print analyze("This is a text document to analyze.")
"" [u'this', u'is', u'text', u'document', u'to', u'analyze']

每个在拟合中被分析器发现的词被指派了一个独一无二的索引,在结果矩阵中表示一列。对于列的翻译可以被如下方式检索

from sklearn.feature_extraction.text import CountVectorizer

if __name__ == '__main__':
    corpus = [
        'This is the first document.',
        'This is the second second document.',
        'And the third one.',
        'Is this the first document?',
    ]
    vectorizer = CountVectorizer(min_df=1)
    X = vectorizer.fit_transform(corpus)
    print vectorizer.get_feature_names()
    print X.toarray()

''''
[u'and', u'document', u'first', u'is', u'one', u'second', u'the', u'third', u'this']
总体样本库中共有9个token word

[[0 1 1 1 0 0 1 0 1]
 [0 1 0 1 0 2 1 0 1]
 [1 0 0 0 1 0 1 1 0]
 [0 1 1 1 0 0 1 0 1]]
每一行代表一个sentence,共4行(4个sentence)
每一列代表一个token词的词频,共9列(9个token word)
''''

对运行的结果我们仔细观察下,第一个sentence和最后一个文本sentence分别表达了陈述和疑问两种句式(token word出现的位置不一样,造成表达意思的不同),但是因为它们包含的词都相同

'This is the first document.',
'Is this the first document?',

[[0 1 1 1 0 0 1 0 1] 
 [0 1 1 1 0 0 1 0 1]]

特别是最后一个字符是询问形式时我们丢失了他的信息,这就是词袋模型最大的缺点:不能捕获词间相对位置信息

从列标到特征名的反转映射储存在向量化类 vectorizer 的属性 vocabulary_ 中

from sklearn.feature_extraction.text import CountVectorizer

if __name__ == '__main__':
    corpus = [
        'This is the first document.',
        'This is the second second document.',
        'And the third one.',
        'Is this the first document?',
    ]
    vectorizer = CountVectorizer(min_df=1)
    X = vectorizer.fit_transform(corpus)
    print vectorizer.get_feature_names()
    print X.toarray()
    print vectorizer.vocabulary_
'' {u'and': 0, u'third': 7, u'this': 8, u'is': 3, u'one': 4, u'second': 5, u'the': 6, u'document': 1, u'first': 2}

因此如果我们训练fit过程和之后编码transform过程使用的样本集不一样,则在训练集里未出现的的词将在将来调用transform方法时被完全忽略:

from sklearn.feature_extraction.text import CountVectorizer

if __name__ == '__main__':
    corpus = [
        'This is the first document.',
        'This is the second second document.',
        'And the third one.',
        'Is this the first document?',
    ]
    vectorizer = CountVectorizer(min_df=1)
    X = vectorizer.fit_transform(corpus)
    print vectorizer.get_feature_names()
    print X.toarray()
    print vectorizer.vocabulary_
    # 对在训练集中未出现的token word进行词频向量化编码
    print vectorizer.transform(['Something completely new.']).toarray()
'' [[0 0 0 0 0 0 0 0 0]]

0x3: 词袋子模型的应用

1. 在监督学习设置( supervised setting )中它能够把快速和可伸缩的线性模型相结合,来训练分类器( document classifiers ) 
Classification of text documents using sparse features
http://sklearn.lzjqsdd.com/auto_examples/text/document_classification_20newsgroups.html#example-text-document-classification-20newsgroups-py

2. 在 unsupervised setting 中它可以为相似文档分类,同时应用聚类方法,比如 K-means  
http://sklearn.lzjqsdd.com/auto_examples/text/document_clustering.html#example-text-document-clustering-py
Clustering text documents using k-means

Relevant Link:

http://www.cnblogs.com/platero/archive/2012/12/03/2800251.html
http://feisky.xyz/machine-learning/resources/github/spark-ml-source-analysis/%E7%89%B9%E5%BE%81%E6%8A%BD%E5%8F%96%E5%92%8C%E8%BD%AC%E6%8D%A2/CountVectorizer.html

 

3. n-gram模型

0x1: 基于已观测结果进行的对未知的推测思想

n-gram最强大的地方是计算一段sentence的词频,并且根据一段已知的sentence预测接下来可能跟随的词的概率,这种推测是基于概率论中的最大似然理论得出的,即根据已知的观测值来推测接下来未知的事件出现的概率。

中国有句古话叫"马后炮",这个词的用途源于说事情都发生了再采取措施,太迟了。但不可否认,我们的认知就是从错误中不断进步,虽然已经做错的不可能变得正确,但"来者尤可追",我们可以根据既往的经验(数据),来判断以后应该采取什么样的措施。这其实就是有监督机器学习的过程。其中涉及的一个问题就是模型中参数的估计

但是为什么会需要参数估计呢?那是因为我们在大多数情况下不可能事先知道一个事务发生的概率P(即概率参数是不可知的),因为我们不是上帝,我们不可能知道在2个小时后的未来,天气是否会下雨!

所以我们只能根据历史上已知的观测结果来推测接下来要发生的事件的概率,这句话就直接引出另一个问题!

历史上已知的观测结果可以理解为一种先验概率,是一种经验,但是这种先验的结果是否足够"客观地"反映了事件真实情况?例如我们抛一枚硬币连续3次都是正面,难道我们就可以说下一次再抛硬币正面朝上的概率是100%?
如果根据已知观测结果计算最大似然概率的确会得出这种结论,但其实我们知道,这种推测是因为观测样本过拟合导致的

但是反过来说,我们也不可能真的计算出一个事物本质的发生概率,因为那是"上帝"决定的事情,我们的算法模型做的事情都是在"推测逼近",通过对已知观测结果的理解和计算,来尽可能地"无限逼近"事物真实的本质概率

再从问题建模的角度来看这个问题,为什么会有参数估计呢?这要源于我们对所研究问题的简化和假设。我们在看待一个问题的时候,经常会使用一些我们所熟知的经典的模型去简化问题,就像我们看一个房子,我们想到是不是可以把它看成是方形一样。如果我们已经知道这个房子是三间平房,那么大体上我们就可以用长方体去描述它的轮廓。这个画房子的问题就从无数的可能性中,基于方圆多少里大家都住平房的经验,我们可以假设它是长方体,剩下的问题就是确定长宽高这三个参数了,问题被简化了。再如学生考试的成绩,根据既往的经验,我们可以假设学生的成绩是正态分布的,那么剩下的问题就是确定分布的期望和方差。所以,之所以要估计参数,是因为我们希望用较少的参数去描述数据的总体分布。而可以这样做的前提是我们对总体分布的形式是知晓的,只需要估计其中参数的值;否则我们要借助非参数的方法了

参数估计的方法有多种,这里我们分析三种基于概率的方法,分别是最大似然估计(Maximum Likelihood)、贝叶斯估计(Bayes)和最大后验估计(Maximum a posteriori)。

我们假设我们观察的变量是,观察的变量取值(样本)为,要估计的参数是的分布函数是(我们用条件概率来显式地说明这个分布是依赖于取值的)。实际中都可以是几个变量的向量(实际问题中往往是高维向量),这里为了说明简单我们不妨认为它们都是标量。

1. 最大似然估计 Maximum Likelihood (ML)

最大似然估计是利用已知的样本的结果,在使用某个模型的基础上,反推最有可能导致这样结果的模型参数值

不谈数学公式,我们先从一个简单的抽球实验的来逐步说明最大似然估计的概念:

例子1:抽球

假设一个袋子装有白球与红球,比例未知,现在抽取10次(每次抽完都放回,保证事件独立性),假设抽到了7次白球和3次红球,在此数据样本条件下,可以采用最大似然估计法求解袋子中白球的比例(最大似然估计是一种“模型已定,参数未知”的方法)。我们知道,一些复杂的问题,是很难通过直观的方式获得答案的,这时候理论分析就尤为重要了,我们可以找到一个"逼近模型"来无限地逼近我们要处理的问题的本质

我们可以定义2次实验中从袋子中抽取白球和红球的概率如下

x1为第一次采样,x2为第二次采样,f为模型, theta为模型参数,X1,X2是独立同分布的

其中theta是未知的,因此,我们定义似然L为:

L为似然的符号

因为目标是求最大似然函数,因此我们可以两边取ln,取ln是为了将右边的乘号变为加号,方便求导(不影响极大值的推导)

两边取ln的结果,左边的通常称之为对数似然

最大似然估计的过程,就是找一个合适的theta,使得平均对数似然的值为最大。因此,可以得到以下公式:

最大似然估计的公式

我们写出拓展到n次采样的情况

最大似然估计的公式(n次采样)

我们定义M为模型(也就是之前公式中的f),表示抽到白球的概率为theta,而抽到红球的概率为(1-theta),因此10次抽取抽到白球7次的概率可以表示为:

10次抽取抽到白球7次的概率

将其描述为平均似然可得:

那么最大似然就是找到一个合适的theta,获得最大的平均似然(求最大极值问题)。因此我们可以对平均似然的公式对theta求导,并另导数为0

求导过程

由此可得,当抽取白球的概率为0.7时,最可能产生10次抽取抽到白球7次的事件

从这个情况再衍生开继续思考,如果我们的实验结果是:前10次抽到的球都是白球,则对对数似然函数进行求导,并另导数为0,得出theta为1,即当取白球的概率是100%时,最有可能10次都抽到白球,显然,这种"推测结果"很容易"偏离真实情况",因为很可能是因为10次都抽到白球这种小概率事件导致我们基于观测值的最大似然推测失真,即产生了过拟合,但是造成这种现象的本质是因为"我们的训练样本未能真实地反映待推测问题的本质",在一个不好的样本集下,要做出正确的预测也就变得十分困难

例子2:正态分布

我们前面说了,事物的本来规律是很复杂的,我们很难用一个百分百准确的模型去描述事物的本质,但是我们用一些类似的通用模型去"尽可能逼近"事物的本质,高斯分布(正态分布)一种非常合理的描述随机事件的概率模型。假如有一组采样值(x1,...,xn),我们知道其服从正态分布,且标准差已知。当这个正态分布的期望为多少时,产生这个采样数据的概率为最大?

这个例子中正态分布就是模型M,而期望就是前文提到的theta

基于n次实验观测值对参数theta预测的的似然函数

正态分布的公式,当第一参数(期望)为0,第二参数(方差)为1时,分布为标准正态分布

把高斯分布函数带入n次独立实验的似然函数中

对上式求导可得,在高斯分布下,参数theta的似然函数的值取决于实验观测结果,这和我们上例中抽球实验是一致的

对这个现象,我们可以继续引申出来思考,根据概率原理我们知道,如果我们的实验次数不断增加,甚至接近无限次,则实验的观测结果会无限逼近于真实的概率分布情况,这个时候最大似然函数的估计就会逐渐接近真实的概率分布,也可以这么理解,样本观测量的增加,会降低似然函数过拟合带来的误差

我们在进行数据建模分析的时候,要考虑的2个比较重要的问题是

1. 要有足够的训练样本,足够的实验观测结果能避免过拟合的产生,让最大似然函数的结果逼近真实情况
2. 要有合理的拟合模型来近似的描述待评估问题的本质,例如高斯分布就是一个描述独立随机事件的很好的模型

2. 贝叶斯估计 Bayes

最大似然估计存在一定的缺陷

1. 最大似然估计属于点估计,只能得到待估计参数的一个值。但是在有的时候我们不仅仅希望知道,我们还希望知道取其它值得概率,即我们希望知道整个在获得观察数据后的分布情况
2. 最大似然估计仅仅根据(有限的)观察数据对总体分布进行估计,在数据量不大的情况下,可能不准确。例如我们要估计人的平均体重,但是抽样的人都是小孩,这样我们得到的平均体重就不能反映总体的分布,而我们应该把“小孩之占总人口20%”的先验考虑进去。这时我们可以用贝叶斯方法

贝叶斯估计和最大似然估计最大的区别我认为在于:贝叶斯估计对假设空间的概率分布有一个预先的假设,而不是完全无脑地信任观测样本数据,它相当于先建立一个初始基线值,然后根据观测样本值去不断修正它,这样修正后的结果具有很好的稳定性,不会随着观测样本的波动而波动

贝叶斯估计,是在给定训练数据D时,确定假设空间H中的最佳假设,最佳假设:一种方法是把它定义为在给定数据D以及H中不同假设的先验概率的有关知识下的最可能假设

贝叶斯理论提供了一种计算假设概率的方法,基于假设的先验概率、给定假设下观察到不同数据的概率以及观察到的数据本身。

先验概率用P(h)表示,它表示了在没有训练数据前假设h拥有的初始概率(训练前的一个初始的先验假设)。先验概率反映了关于h是一正确假设的机会,如果我们没有这一先验知识,可以简单地将每一候选假设赋予相同的先验概率(平均概率也是一种合理的先验假设)。类似地,P(D)表示训练数据D的先验概率,P(D|h)表示假设h成立时D的概率。机器学习中,我们关心的是P(h|D),即给定D时h的成立的概率,称为h的后验概率

贝叶斯公式提供了从先验概率P(h)、P(D)和P(D|h)计算后验概率P(h|D)的方法

p(h|D) = P(D|H) * P(H) / P(D)

我们的目标P(h|D),随着P(h)和P(D|h)的增长而增长,随着P(D)的增长而减少,即如果D独立于h时被观察到的可能性越大,那么D对h的支持度越小,或者说D中包含的对推测出h的有效信息熵越小,即这是一份对我们的推测基本没有帮助的数据

贝叶斯法则

贝叶斯法则又被称为贝叶斯定理、贝叶斯规则,是指概率统计中的应用所观察到的现象对有关概率分布的主观判断(即先验概率)进行修正(训练过程中不断修正)的标准方法。当分析样本大到接近总体数时,样本中事件发生的概率将接近于总体中事件发生的概率

贝叶斯统计中的两个基本概念是先验分布和后验分布:

1. 先验分布: 总体分布参数θ的一个概率分布。贝叶斯学派的根本观点,是认为在关于总体分布参数θ的任何统计推断问题中,除了使用样本所提供的信息外,还必须规定一个先验分布,它是在进行统计推断时不可缺少的一个要素。他们认为先验分布不必有客观的依据,可以部分地或完全地基于主观信念 
2. 后验分布: 根据样本分布和未知参数的先验分布,用概率论中求条件概率分布的方法,求出的在样本已知下,未知参数的条件分布。因为这个分布是在抽样以后才得到的,故称为后验分布。贝叶斯推断方法的关键是任何推断都必须且只须根据后验分布,而不能再涉及样本分布 

在更一般化的情况,假设{Ai}是事件集合里的部分集合,对于任意的Ai,贝叶斯定理可用下式表示:

 贝叶斯公式为利用搜集到的信息对原有判断进行修正提供了有效手段

例如,一座别墅在过去的20年里一共发生过2次被盗,别墅的主人有一条狗,狗平均每周晚上叫3次,在盗贼入侵时狗叫的概率被估计为0.9,问题是:在狗叫的时候发生入侵的概率是多少?

我们假设A事件为狗在晚上叫,B为盗贼入侵,则P(A)=3/7,P(B)=2/(20·365)=2/7300,P(A|B)=0.9,按照公式很容易得出结果:P(B|A)=0.9*(2/7300)/(3/7)=0.00058

另一个例子,现分别有A,B两个容器,在容器A里分别有7个红球和3个白球,在容器B里有1个红球和9个白球,现已知从这两个容器里任意抽出了一个球,且是红球,问这个红球是来自容器A的概率是多少?

假设已经抽出红球为事件B,从容器A里抽出球为事件A,则有:P(B)=8/20,P(A)=1/2,P(B|A)=7/10,按照公式,则有:P(A|B)=(7/10)*(1/2)/(8/20)=0.875

3. 最大后验估计 MAP 

最大后验估计是根据观测样本数据获得对难以观察的量的点的估计,这点与最大似然估计类似,但是最大的不同时,最大后验估计的融入了要估计量的先验分布在其中(算法训练的目的就是寻找到真实的先验分布,这个先验分布本质上就是模型的预测能力,即先验已知模型参数得到对新样本的预测能力)。故最大后验估计可以看做规则化的最大似然估计。

假设x为独立同分布的采样,θ为模型参数,f为我们所使用的模型。那么最大似然估计可以表示为:

现在,假设θ的先验分布为g。通过贝叶斯理论,对于θ的后验分布如下式所示:

后验分布的目标为:

最大后验估计可以看做贝叶斯估计的一种特定形式

举例来说:

假设有五个袋子,各袋中都有无限量的饼干(樱桃口味或柠檬口味),已知五个袋子中两种口味的比例分别是

    樱桃 100%

    樱桃 75% + 柠檬 25%

    樱桃 50% + 柠檬 50%

    樱桃 25% + 柠檬 75%

    柠檬 100%

如果只有如上所述条件,那问从同一个袋子中连续拿到2个柠檬饼干,那么这个袋子最有可能是上述五个的哪一个?

我们知道,最大后验概率MAP是正则化的最大似然概率,我们首先采用最大似然估计来解这个问题,写出似然函数。假设从袋子中能拿出柠檬饼干的概率为p,则似然函数可以写作

由于p的取值是一个离散值,即上面描述中的0,25%,50%,75%,1。我们只需要评估一下这五个值哪个值使得似然函数最大即可,根据最大似然的计算,肯定得到为袋子5。

上述最大似然估计有一个问题,就是没有考虑到模型本身的概率分布(即没有考虑模型本身的复杂度)(结构化风险),下面我们扩展这个饼干的问题。对模型自身的复杂度进行先验估计

拿到袋子1的概率是0.1
拿到袋子2的概率是0.2
拿到袋子3的机率是0.4
拿到袋子4的机率是0.2
拿到袋子5的机率是0.1
# 类高斯分布

那同样上述问题的答案呢?这个时候就变MAP了。我们根据公式

写出我们的MAP函数

根据题意的描述可知,p的取值分别为0,25%,50%,75%,1,g的取值分别为0.1,0.2,0.4,0.2,0.1。分别计算出MAP函数的结果为:

0 * 0 * 0.1 = 0
0.25 * 0.25 * 0.2 = 0.0125
0.5 * 0.5 * 0.4 = 0.1
0.75 * 0.75 * 0.2 = 0.1125
1 * 1 * 0.1 = 0.1

由上可知,通过MAP估计可得结果是从第四个袋子中取得的最高。可以看到,虽然观测结果表明最大似然应该是第5个袋子,但是在加入正则化(模型复杂度)先验后,得到的结果被修正了

最大似然估计是在对被估计量没有任何先验知识的前提下求得的。如果已知被估计参数满足某种分布,则需要用到最大后验估计。举一个例子,假设一个盒子的高度h满足正态分布N(10.5,1),即我们在实验观测前对这个分布已经有了一个大致的认识,在这个前提下,我们要估计正态分布N(h,1)中参数h的值。三次测量结果分别为 X={11,10.5,11.5} cm。要估计h的值,根据贝叶斯理论

这里P(X)是已知事件(与我们要估计的参数无关),可以取值为1

这里P(X|h)符合高斯分布

所以对P(X|h)P(h)求参数h的最大值 h =  ,通过求导计算,可得h = 10.875cm,根据MAP的结果,对新的测量的数据的可能出现概率,则由 N(10.875,1)N(10.875,1)给出

Relevant Link:

https://lagunita.stanford.edu/c4x/Engineering/CS-224N/asset/slp4.pdf
https://guangchun.wordpress.com/2011/10/13/ml-bayes-map/
https://en.wikipedia.org/wiki/N-gram
http://www.jianshu.com/p/f1d3906e4a3e
http://www.cnblogs.com/liliu/archive/2010/11/22/1883702.html
http://www.cnblogs.com/xueliangliu/archive/2012/08/02/2962161.html
http://www.cnblogs.com/stevenbush/articles/3357803.html
http://blog.csdn.net/guohecang/article/details/52313046
http://www.cnblogs.com/burellow/archive/2013/03/19/2969538.html

 

0x2: N词条件下的词序列概率预测模型

为了逐步说明n-gram的算法思想,我们先建立下面这种假设模型(建立在贝叶斯估计上的序列模型)(贝叶斯模型本来就是一种观测预测模型)

第n个词的出现只与前面N-1个词相关,而与其它任何词都不相关,整句的概率就是各个词出现概率的乘积

对于一个句子sentence,假设是由词序列W1,W2,W3,....Wn组成,则P(T)=P(W1W2W3Wn)=P(W1)P(W2|W1)P(W3|W1W2)…P(Wn|W1W2…Wn-1),注意这里的每一个词的出现不是独立随机分布,因为每一个词的出现都和在这个词之前的所有历史序列h都存在先验关系

那么我们怎么得到P(Wn|W1W2…Wn-1)呢?一种简单的估计方法就是最大似然估计(Maximum Likelihood Estimate)了

P(Wn|W1W2…Wn-1) = (C(W1 W2…Wn))/(C(W1 W2…Wn-1))

举一个例子,我们已知历史序列h = its water is so transparent that,我们需要预测接下来跟随的word是the的概率,即P(the|its water is so transparent that)

通过最大似然函数,我们根据已知的实验观测结果预测了未来未知事件的最大似然概率

这种模型虽然非常合理(对真实情况的模拟逼近),但是存在两个比较严重的缺陷

1. 一个缺陷是参数空间过大,不可能实用化, - 一个序列长度为N的参数个数为: N!
2. 另外一个缺陷是数据稀疏严重 - 越长的序列的越不容易出现,整个参数矩阵上会有很多0值

0x3: Ngram(N元模型) - 马尔科夫模型 - 对N词序列模型的参数进行裁剪,用一个稍微不那么准确的近似模型去逼近真实情况

为了解决上一小节提到的2个缺陷,我们引入另一个近似模型:马尔科夫假设

一个词的出现仅仅依赖于它前面出现的有限的一个或者几个词 - 这种模型就大大减少了需要参与计算的先验参数

如果一个词的出现仅依赖于它前面出现的一个词,那么我们就称之为bigram(2-gram)

P(T) = P(W1W2W3…Wn)=P(W1)P(W2|W1)P(W3|W1W2)…P(Wn|W1W2…Wn-1)
          ≈P(W1)P(W2|W1)P(W3|W2)…P(Wn|Wn-1)

我们用一个实际的例子来说明2-gram的计算过程,假设语料库总词数为13,748

可以看到,和词袋模型bag-of-words类似,所有词按照"无须两两组合"的原则构成了一个total(words) * total(words)的matrix笛卡儿积词组频率矩阵,在2-gram模型中所有的组合都在这里了,corpus中未出现的置0

根据这个词频表,我们可以计算一段sentence出现的概率,sentence测概率是之后作其他例如跟随词预测,sentence间相似度预测,文本翻译的基础

P(I want to eat Chinese food)
=P(I) * P(want|I) * P(to|want) * P(eat|to) * P(Chinese|eat) * P(food|Chinese)

= 3437/13748 * P(I want)/P(I) * P(want to)/P(want) * P(eat Chinease)/P(eat) * P(Chinese food)/P(Chinese)
=0.25 * 1087/3437 * 786/1215 * 860/3256 * 19/938 * 120/213
=0.000154171    

谈一下n-gram的词频表数据稀疏和参数爆炸问题

# 假设词表中有20000个词,n-gram在生成词频表的时候要组成一个两两组合的方阵
1. 如果是bigram那么可能的N-gram就有20000 * 20000 = 400000000个
2. 如果是trigram,那么可能的N-gram就有20000 * 20000 * 20000 = 8000000000000个
# 那么对于其中的很多词对的组合,在语料库中都没有出现,根据最大似然估计得到的概率将会是0,这会造成很大的麻烦,在算句子的概率时一旦其中的某项为0,那么整个句子的概率就会为0,最后的结果是,我们的模型只能算完全那些每一个词组都在词频表中出现的sentence,而大部分的句子算得的概率是0

因此,我们要进行数据平滑(data Smoothing),数据平滑的目的有两个

1. 一个是使所有的N-gram概率之和为1
2. 使所有的N-gram概率都不为0.

主要策略是把在训练样本中出现过的事件的概率适当减小,然后把减小得到的概率密度分配给训练语料中没有出现过的事件。实际中平滑算法有很多种,例如:

Add-one平滑
Witten-Bell平滑
Good-Turing平滑
Katz Backoff
Stupid Backoff

数据平滑技术是构造高鲁棒性语言模型的重要手段,且数据平滑的效果与训练语料库的规模有关。训练语料库规模越小,数据平滑的效果越显著;训练语料库规模越大,数据平滑的效果越不显著,甚至可以忽略

0x4: 基于n-gram的机器学习应用

我们已经了解了n-gram的词频生成方法以及sentence概率估计原理,接下来看看n-gram可以用在哪些实际的场景中

1. 基于Ngram模型定义的字符串距离

模糊匹配的关键在于如何衡量两个长得很像的单词(或字符串)之间的“差异”,这种差异通常又称为“距离”。除了可以定义两个字符串之间的编辑距离(通常利用Needleman-Wunsch算法或Smith-Waterman算法),还可以定义它们之间的Ngram距离。

假设有一个字符串S,那么该字符串的Ngram就表示按长度N切分原sentence得到的词段(长度为N),也就是S中所有长度为N的子字符串。设想如果有两个字符串,然后分别求它们的Ngram,那么就可以从它们的共有子串的数量这个角度去定义两个字符串间的Ngram距离。但是仅仅是简单地对共有子串进行计数显然也存在不足,这种方案显然忽略了两个字符串长度差异可能导致的问题。比如字符串girl和girlfriend,二者所拥有的公共子串数量显然与girl和其自身所拥有的公共子串数量相等,但是我们并不能据此认为girl和girlfriend是两个等同的匹配。为了解决该问题,有研究员提出以非重复的Ngram分词为基础来定义Ngram距离,公式表示如下:

|GN(S1)|+|GN(S2)|−2×|GN(S1)∩GN(S2)|

此处,|GN(S1)||GN(S1)|是字符串S1S1的Ngram集合,N值一般取2或者3。以N=2为例对字符串Gorbachev和Gorbechyov进行分段,可得如下结果

Go or rb ba ac ch he ev
Go or rb be ec ch hy yo ov

结合上面的公式,即可算得两个字符串之间的距离是8 + 9 − 2 × 4 = 9。显然,字符串之间的距离越小,它们就越接近。当两个字符串完全相等的时候,它们之间的距离就是0。可以看到,这种公式充分考虑到了字符串的长度区别和相同词组2者的共同作用

2. 利用Ngram模型评估语句是否合理

从统计的角度来看,自然语言中的一个句子S可以由任何词串构成,不过概率P(S)有大有小。例如:

S1 = 我刚吃过晚饭
S2 = 刚我过晚饭吃

显然,对于中文而言S1是一个通顺而有意义的句子,而S2则不是,所以对于中文来说P(S1)>P(S2)

假设我们现在有一个语料库如下,其中<s1><s2>是句首标记,</s2></s1>是句尾标记:

<s1><s2>yes no no no no yes</s2></s1>
<s1><s2>no no no yes yes yes no</s2></s1>

下面我们的任务是来评估如下这个句子的概率:

<s1><s2>yes no no yes</s2></s1>

我们利用trigram来对这句话进行词组分解

所以我们要求的概率就等于:1/2×1×1/2×2/5×1/2×1=0.05

3. 基于Ngram模型的文本分类器

Ngram如何用作文本分类器的呢?只要根据每个类别的语料库训练各自的语言模型,实质上就是每一个类别都有一个概率分布,当新来一个文本的时候,只要根据各自的语言模型,计算出每个语言模型下这篇文本的发生概率,文本在哪个模型的概率大,这篇文本就属于哪个类别了!

4. spam filtering

基于n-gram进行垃圾邮件判断我理解本质上就是在进行文本分类,通过训练"good email"和"spam email"的n-gram词频表,对新来的email分别根据2个词频表计算最大似然概率,根据得出概率最大的那个词频表判断该email属于哪一类

0x5: sklearn封装实现

from sklearn.feature_extraction.text import CountVectorizer

if __name__ == '__main__':
    corpus = [
        'This is the first document.',
        'This is the second second document.',
        'And the third one.',
        'Is this the first document?',
    ]
    bigram_vectorizer = CountVectorizer(ngram_range=(1, 2), token_pattern=r'\b\w+\b', min_df = 1)
    X_2 = bigram_vectorizer.fit_transform(corpus).toarray()
    print X_2
    print bigram_vectorizer.vocabulary_
'''' 
[[0 0 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 1 1 0]
 [0 0 1 0 0 1 1 0 0 2 1 1 1 0 1 0 0 0 1 1 0]
 [1 1 0 0 0 0 0 0 1 0 0 0 1 0 0 1 1 1 0 0 0]
 [0 0 1 1 1 1 0 1 0 0 0 0 1 1 0 0 0 0 1 0 1]]
{u'and': 0, u'the second': 14, u'is': 5, u'this the': 20, u'one': 8, u'and the': 1, u'second': 9, u'first document': 4, u'is the': 6, u'second document': 10, u'the third': 15, u'document': 2, u'the first': 13, u'is this': 7, u'third': 16, u'this': 18, u'second second': 11, u'third one': 17, u'the': 12, u'this is': 19, u'first': 3}

上面输入的结果,我们看第一行的前3个:0 0 1;0号index代表and,1号index代表and the,2号index代表document,0和1号索引都在第一个sentence里未出现,所以g-gram之后的one-hot vector填0,而document出现在第一个sentence的最后一个位置,故填1,可见n-gram并不关注词在样本中的具体位置。n-gram保留的词序列只是它自己的context上下文的词组序列

因为n-gram进行了N*N无序组合(用one-hot的思想抽象成了一个定长的vector),因此矢量化提取的词因此变得很大,同时可以在定位模式时消歧义,同时我们注意到,n-gram本质上还是一种bag-of-words模型,因此n-gram编码后的vector丢失了原始sentence的序列,它只保存了N子序列的词组关系

Relevant Link:

http://blog.csdn.net/lengyuhong/article/details/6022053
https://flystarhe.github.io/2016/08/16/ngram/
http://blog.csdn.net/baimafujinji/article/details/51281816
https://en.wikipedia.org/wiki/Bag-of-words_model
http://www.52nlp.cn/tag/n-gram

 

4. Tf–idf算法

TF-IDF(term frequency–inverse document frequency)是一种用于信息检索与数据挖掘的常用加权技术。用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。主要思想是:如果某个词或短语在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类,也就可以作为上文中所提到的关键字

0x1: 用一个例子引出TF-IDF的加权词频统计思想

假定现在有一篇长文《中国的蜜蜂养殖》,我们准备用计算机提取它的关键词,一个容易想到的思路,就是找到出现次数最多的词。如果某个词很重要,它应该在这篇文章中多次出现。于是,我们进行"词频"(Term Frequency,缩写为TF)统计。在统计结束后我们会发现,出现次数最多的词是 "的"、"是"、"在" 这一类最常用的词。它们叫做"停用词"(stop words),在大多数情况下这对找到结果毫无帮助、我们采取过滤掉停用词的策略

假设我们把它们都过滤掉了,只考虑剩下的有实际意义的词。这样又会遇到了另一个问题,我们可能发现"中国"、"蜜蜂"、"养殖"这三个词的出现次数一样多。这是不是意味着,作为关键词,它们的重要性是一样的?

显然不是这样。因为"中国"是很常见的词(在日常的语料库中出现频率较高),相对而言,"蜜蜂"和"养殖"不那么常见(倾向于专有领域的词)。如果这三个词在一篇文章的出现次数一样多,有理由认为,"蜜蜂"和"养殖"的重要程度要大于"中国",也就是说,在关键词排序上面,"蜜蜂"和"养殖"应该排在"中国"的前面。所以,我们需要一个重要性调整系数,衡量一个词是不是常见词。如果某个词比较少见,但是它在这篇文章中多次出现,那么它很可能就反映了这篇文章的特性,正是我们所需要的关键词

用统计学语言表达,就是在词频的基础上,要对每个词分配一个"重要性"权重

1. 最常见的词("""""")给予最小的权重
2. 较常见的词("中国")给予较小的权重
3. 较少见的词("蜜蜂""养殖")给予较大的权重

这个权重叫做"逆文档频率"(Inverse Document Frequency,缩写为IDF),它的大小与一个词的常见程度成反比

知道了"词频"(TF)和"逆文档频率"(IDF)以后,将这两个值相乘,就得到了一个词的TF-IDF值。某个词对文章的重要性越高,它的TF-IDF值就越大。所以,排在最前面的几个词,就是这篇文章的关键词

这里我们要注意,TF-IDF的本质是将重要的词权重提高,将不重要的词的权重降低,摘要提取只是其中一个应用场景,因为对于一篇文章来说,权重最高的词作为关键摘要也是一种相对合理的策略

下面是这个算法的简要流程

第一步,计算词频

考虑到文章有长短之分,为了便于不同文章的比较,进行"词频"归一化"

第二步,计算逆文档频率

这时,需要一个语料库(corpus),用来模拟语言的总体使用环境

如果一个词越常见,那么分母就越大,逆文档频率就越小越接近0。分母之所以要加1,是为了避免分母为0(即所有文档都不包含该词)。log表示对得到的值取对数

第三步,计算TF-IDF

可以看到,TF-IDF与一个词在文档中的出现次数成正比,与该词在整个语言中的出现次数成反比

以《中国的蜜蜂养殖》为例

1. 假定该文长度为1000个词,"中国""蜜蜂""养殖"各出现20次,则这三个词的"词频"(TF)都为0.02
2. 然后,搜索Google发现,包含""字的网页共有250亿张,假定这就是中文网页总数
3. 包含"中国"的网页共有62.3亿张;包含"蜜蜂"的网页为0.484亿张;包含"养殖"的网页为0.973亿张

则它们的逆文档频率(IDF)和TF-IDF如下

中国
IDF = math.log(250 / 63.3) = 1.37357558871
TF-IDF = IDF * 0.02 = 0.0274715117742

蜜蜂
IDF = math.log(250 / 63.3) = 5.12671977312 
TF-IDF = IDF * 0.02 = 0.102534395462

养殖
IDF = math.log(250 / 63.3) = 4.84190569082 
TF-IDF = IDF * 0.02 = 0.0968381138164

从上表可见,"蜜蜂"的TF-IDF值最高,"养殖"其次,"中国"最低。所以,如果只选择一个词,"蜜蜂"就是这篇文章的关键词

0x2: sklearn封装实现

归一化过程已经实现于类 :class:`TfidfTransformer`中,让我们以下方的词频为例,每一行代表词在该sentence中的词频。第一个词在任何时候都100%显示(因为它在每一行都出现了),其他两个特征只占文档中少于50%的比例。每一行都被正则化,来适用欧几里得标准,每个特征的权重被方法 fit 计算,调用结果被存储在模型参数中:

from sklearn.feature_extraction.text import TfidfTransformer

if __name__ == '__main__':
    transformer = TfidfTransformer()
    counts = [
        [3, 0, 1],
        [2, 0, 0],
        [3, 0, 0],
        [4, 0, 0],
        [3, 2, 0],
        [3, 0, 2]
    ]
    tfidf = transformer.fit_transform(counts)
    print tfidf.toarray()
    print transformer.idf_
''''
[[ 0.85151335  0.          0.52433293]
 [ 1.          0.          0.        ]
 [ 1.          0.          0.        ]
 [ 1.          0.          0.        ]
 [ 0.55422893  0.83236428  0.        ]
 [ 0.63035731  0.          0.77630514]]
[ 1.          2.25276297  1.84729786]

可以看到,第一个词由于在整体语料库中出现频率很高,因此在单个sentence中的词频权重被调整降低了

因为 tf–idf 在特征提取中经常被使用,所以有另一个类: TfidfVectorizer 在单个类中结合了所有类和类中的选择:

from sklearn.feature_extraction.text import TfidfVectorizer

if __name__ == '__main__':
    corpus = [
        'This is the first document.',
        'This is the second second document.',
        'And the third one.',
        'Is this the first document?',
    ]
    vectorizer = TfidfVectorizer(min_df=1)
    tfidf = vectorizer.fit_transform(corpus)
    print vectorizer.vocabulary_
    print tfidf.toarray()
''''

{u'and': 0, u'third': 7, u'this': 8, u'is': 3, u'one': 4, u'second': 5, u'the': 6, u'document': 1, u'first': 2}
[[ 0.          0.43877674  0.54197657  0.43877674  0.          0.        0.35872874  0.          0.43877674]
 [ 0.          0.27230147  0.          0.27230147  0.          0.85322574    0.22262429  0.          0.27230147]
 [ 0.55280532  0.          0.          0.          0.55280532  0.        0.28847675  0.55280532  0.        ]
 [ 0.          0.43877674  0.54197657  0.43877674  0.          0.        0.35872874  0.          0.43877674]]

可以看到,document在整个语料库中都出现,所以权重被动态降低了

Relevant Link:

http://www.ruanyifeng.com/blog/2013/03/tf-idf.html
http://www.cnblogs.com/ybjourney/p/4793370.html
http://www.cc.ntu.edu.tw/chinese/epaper/0031/20141220_3103.html
https://nlp.stanford.edu/IR-book/html/htmledition/tf-idf-weighting-1.html
http://sklearn.lzjqsdd.com/modules/feature_extraction.html

Copyright (c) 2017 LittleHann All rights reserved

posted @ 2017-08-03 15:56 骑着蜗牛逛世界 阅读(...) 评论(...) 编辑 收藏