机器学习--生成学习算法

来源:互联网 发布:单台mysql最大tps 编辑:程序博客网 时间:2024/06/05 21:09

这次的课Ng讲了一种与之前判别类算法不同的学习算法,称为生成学习算法(Generative Learning algorithms)。


一、简介

    比如一个二分类问题,判别一种动物是大象还是狗,特征量有腿长,尾巴长,毛发浓密程度,体型等。有一批数据样本,之前的方法是对所有的数据建立模型,学习特征量和标签之间的关系,对于新输入的一个特征量,通过最优参数奠定的学习模型得出它是属于大象还是狗。而今天所讲的生成学习算法的主要思想是,首先我们来看大象,我们可以对大象的样子建立一个模型,再对狗子建立一个模型,最后为了分类一种新的动物,我们可以将它分别与大象和狗子的模型进行匹配,都算出匹配结果,看看像那个更多一点。即,对p(y|x)直接学习建模的方法是判别类算法,比如逻辑回归;而对p(x|y)和p(y)建模再利用贝叶斯公式算出p(y|x)的成为生成类算法。如果y=0代表动物是大象而y=1代表动物是狗子,那么对p(x|y=0)建模就是对所有大象的特征建模,狗子类似。之后,我们可以用下面的公式来计算p(y|x):


其中分母为:


  但是要注意的是,如果我们只是要求最大的p(y|x)时的y,那么p(x)其实是不需要的,因为:


二、高斯判别分析

2.1.高维正态分布简介

GDA(Gaussian discriminant analysis)是我们接触的第一个生成类学习方法,在这个方法中,我们假设p(x|y)是服从多维正太分布的,分布概率密度为:


其中u为分布的均值向量,为分布的方差阵。

下面几幅图是一些二维正态分布的例子:


均值都是零向量,中间的方差最小,右边的方差最大。方差就反应了数据集的离散程度。这些图形都是对称的,所以协方差矩阵均为对角阵。下面三个例子的协方差矩阵的对角线元素不为0,所以其数据是互相关的:


从左到右协方差矩阵分别为:

2.2.高斯判别分析(GDA)

当我们的分类问题的特征数据是连续的随机变量时,我们可以使用高斯判别分析模型,该模型采用多维正太分布对输入进行建模,对结果依然是采用伯努利分布,模型如下:


具体的分布如下:


这里,我们模型的所有参数为

所以,基于这些模型的似然函数的对数为:


通过极大化概率似然函数(利用梯度下降法或者牛顿法),可以得到模型的最优参数:


下面这幅图还是可以说明的更清楚一点的,对于两组数据,分别对其特征采用高斯分布进行建模,两个分布公用一个协方差矩阵,只是均值不相同。分布等值线最先相交的两个点连成的直线,可以认为是最优分割的直线。当然这个过程是利用贝叶斯公式算出p(y|x)的值。


联想之前讲过的逻辑回归仔细观察我们就会发现,其实GDA与逻辑回归之间有着非常有趣的关系,在高斯判别法中我们将看成是x的函数,即:


写成这种形式,我们就会发现这和之前的逻辑回归具有完全相同的形式,但是哪个效果更好呢?

 我们可以从这个角度来考虑:在GDA中,是假设如果p(x|y)是属于多维高斯分布,则能推出p(y|x)是x的一个逻辑函数,但是反过来却不一定。这就说明GDA的假设比逻辑回归的假设条件更加严谨,如果这些假设合理的话,那么GDA算法将获得比逻辑回归算法更好的拟合结果。并且对于特别庞大的数据集来说,GDA的算法性能也优于逻辑回归。性能的提高意味着稳定性的下降和抗干扰能力的减弱,对于满足泊松分布的p(x|y),虽然p(y|x)也能满足逻辑函数,但是GDA的效果却无法估计,可能好可能坏。

2.3朴素贝叶斯(Naive Bayes)

PS:卧槽听这个名词好久了啊终于学到了好激动,数学定理果然还是以提出它的数学家名字命名的,Naive Bayes(NB)哈哈

在高斯判别分析法中,考虑特征量是连续变量,即x是continuous,real-valued vectors.现在来考虑当输入特征量是离散变量时的学习算法。还是考虑之前那个垃圾邮件分类的例子,这也是文本分类的一个例子。

考虑有一个训练数据集,即有明确标签的垃圾邮件或者非垃圾邮件,为了建立我们的垃圾邮件过滤器,我们得将特征量表示成一个向量,这个向量的长度等于“垃圾”词典中所有词的总数(这个词典的得来有点经验之谈的意思,比如一般商业广告中出现比较多的词语是buy,money,等等乱七八糟的)如第一封邮件:


即出现哪个垃圾词语,就在对应位置置1,其余没出现的位置均为0,以此构成了特征向量。因此,x的维数与词库中词语的总数相等,如果有5000个垃圾词汇,则x维数为5000,特征量则有2的5000次方种可能,从而参数向量也就有个,这样的参数个数太多了。

   为了对p(x|y)建模,我们由此提出了一个非常强的假设。假设一个词语的出现与其余词语出现在给定y的情况下是不相关的,即如果出现了'buy'这个词语,不会增加或减少其他任何词语的出现(这个假设显然是错误的,但是我还没想通为啥错误的假设依然能得出很好的分类效果),这个假设就是贝叶斯定理给出的。用数学表达式表明为:,基于这样的定理,我们可以对p(x|y)建模:


从第一步到第二步遵循的是自然的条件分布概率,从第二步到第三步是基于贝叶斯定理假设。该模型的参数为:




 和之前一样,如果训练集数据为:,则似然概率函数为:


极大化似然概率函数可以得到参各个参数的值为:


这个结果的意义很明显也很朴实,可以看到等于所有垃圾邮件总数中有第j个word出现的垃圾邮件所占的比例。拟合出所有的参数之后,对于一个新的特征x,我们就可以计算p(y|x),


如果特征量不是离散的,可以采用分段的方法将其离散化,再使用多项式分布对其建模,利用贝叶斯定理。下面是一个离散化的例子:


当高斯判别分析的效果不好时(即对特征建模成多维高斯分布不好时),可以尝试将其特征量离散化采用多项式分布对其建模并利用贝叶斯定理,效果会更好。

2.4 Laplace 平滑

贝叶斯分类固然是一个好的分类办法,但是还有一些不足。比如你第一次投论文,那么你可能在投论文之后受到杂志社的回复,回复中可能包括"paper"这个词,但是因为你之前根本没有投过论文,假设paper在词库中的顺序是第35000个,所以:

所以计算p(y|x)时,会出现:



因为乘积项中一项为0,其余均为0,这样就没办法预测了。

更广泛的来说这个问题,如果要估计一个取值范围为多项式随机变量z的值,我们可以令多项式的分布参数为:

,如果直接这样,那么参数可能会等于0,因为也许在训练集中z没有取到过i,采用laplace平滑来解决这个问题:


其中k为z可能的取值个数,所以之前的贝叶斯分类也可以改进为:


Ng上课举了个例子。两个篮球队比赛,A队五次比赛都输给B队,求A队下次赢的概率,应该是1/(5+2)=1/7,这个结果是非常符合常理的。

2.5基于Python的贝叶斯文本分类算法实现

# _*_ coding:utf-8 _*_from numpy import *import reimport randomdef loadDataSet(): #创建样例数据    postingList = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],                   ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],                   ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],                   ['stop', 'posting', 'stupid', 'worthless', 'garbage'],                   ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],                   ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]    classVec = [0, 1, 0, 1, 0, 1]  #1代表脏话    return postingList, classVecdef createVocabList(dataSet):  #创建词库 这里就是直接把所有词去重后,当作词库    vocabSet = set([])    for document in dataSet:        vocabSet = vocabSet | set(document)    return list(vocabSet)def setOfWords2Vec(vocabList, inputSet):  #文本词向量。词库中每个词当作一个特征,文本中就该词,该词特征就是1,没有就是0    returnVec = [0] * len(vocabList)    for word in inputSet:        if word in vocabList:            returnVec[vocabList.index(word)] = 1        else:            print("the word: %s is not in my Vocabulary!" % word)    return returnVecdef trainNB0(trainMatrix, trainCategory):    numTrainDocs = len(trainMatrix)    numWords = len(trainMatrix[0])    pAbusive = sum(trainCategory) / float(numTrainDocs)    p0Num = ones(numWords) #防止某个类别计算出的概率为0,导致最后相乘都为0,所以初始词都赋值1,分母赋值为2.    p1Num = ones(numWords)    p0Denom = 2    p1Denom = 2    for i in range(numTrainDocs):        if trainCategory[i] == 1:            p1Num += trainMatrix[i]            p1Denom += sum(trainMatrix[i])        else:            p0Num += trainMatrix[i]            p0Denom += sum(trainMatrix[i])    p1Vect = log(p1Num / p1Denom)  #这里使用了Log函数,方便计算,因为最后是比较大小,所有对结果没有影响。    p0Vect = log(p0Num / p0Denom)    return p0Vect, p1Vect, pAbusivedef classifyNB(vec2Classify,p0Vec,p1Vec,pClass1): #比较概率大小进行判断,    p1 = sum(vec2Classify*p1Vec)+log(pClass1)    p0 = sum(vec2Classify*p0Vec)+log(1-pClass1)    if p1>p0:        return 1    else:        return 0def testingNB():    listOPosts,listClasses = loadDataSet()    myVocabList = createVocabList(listOPosts)    trainMat=[]    for postinDoc in listOPosts:        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))    p0V,p1V,pAb = trainNB0(array(trainMat),array(listClasses))    testEntry = ['love', 'my', 'dalmation'] # 测试数据    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))    print(testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb))    testEntry = ['stupid', 'garbage'] # 测试数据    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))    print(testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb))if __name__=='__main__':    testingNB()


结果截图:









原创粉丝点击