zoukankan      html  css  js  c++  java
  • 朴素贝叶斯-学习笔记

    朴素贝叶斯

    标签(空格分隔): 机器学习


    朴素贝叶斯算法适合给文档分类。

    词向量

    先对文档分词,所有文档中所有不同的词构成词汇表。每个文档根据词汇表能形成一个词向量,1表示对应维度的词条出现在文档中,0表示词条未出现。

    这种想法推广开来,发现向量是新时代的数字
    比如一个可能发生的事件集合,可以表示为一个向量,每个维度对应一个事件发生的概率。

    问题描述

    任给一个文档,怎样确定其分类?
    每个文档对应一个词向量(vec w),需要确定(p(c_i|vec w))的最大值,即:已知这个文档的条件下,把这个文档归为哪个类的概率最大?

    朴素贝叶斯

    [分类(vec w) = p(c_i|vec w)_{max}=frac{p(vec w|c_i)p(c_i)}{p(vec w)} ]

    其中(vec w)表示词向量。对于任意一个分类(c_i)(p(vec w))是相同的,因而只需要确定(p(vec w|c_i))(p(c_i))

    (p(c_i)):从训练数据中,用简单除法得到。
    (p(vec w|c_i)):根据朴素贝叶斯假设,特征向量(vec w)(c_i)条件下相互独立,也就是:给定目标值时属性之间相互条件独立

    [p(vec w|c_i)=Pi p(w_j|c_i) ]

    而对于(p(w_j|c_i)),只需要在扫描训练数据时,依据(c_i)过滤数据项,在这些被过滤出的数据中(都是(c_i)类的),用简单统计的结果相除,就得到在(j)维上的概率权值(e(w_j|c_i))。为什么不是(p(w_j|c_i))?因为最终要计算的是一个未知分类的向量(vec x)的分类,需要将(vec x)与概率权值向量(vec e)对应位相乘,这才得到(p(w_j|c_i))序列。(vec x)中的元素非1即0,如果元素为1,那么(p(w_j|c_i))就取(e(w_j|c_i)),否则取0。

    简单优化

    条件概率乘积为0

    (p(vec w|c_i)=Pi p(w_j|c_i))这个公式中,一旦(p(w_j|c_i))等于0,整个结果就是0。但是对于(vec w),你怎样判断(Pi p(w_j|c_i)=0)的所有(c_i),该如何判断哪个(c_i)更好?万一所有的(c_i)对应的(p(vec w|c_i)=Pi p(w_j|c_i))都等于0,该怎样确定最终的分类?因此,要避免某个(p(w_j|c_i))等于0。一个办法是使用拉普拉斯平滑:将所有词的出现数初始化为1,并将分母初始化为2。对应《机器学习实战》中文版P62的代码:

    p0Num = onew(numWords)
    p1Num = ones(numWords)
    p0Denom = 2.0
    p1Denom = 2.0
    

    条件概率乘积下溢

    相乘的都是概率,都是小于1的数,乘不了几次就得到非常小的数字,产生下溢,干扰比较。一个方法是用取对数。没错,就是这么老套的办法,但是管用,因为(forall x in (0,1)),x越小,(log x)的绝对值越大,或者说负的程度越大。对于(p(vec w|c_i)=Pi p(w_j|c_i))取对数,就是一堆负数相加。这种情形下对于任意的(vec x)是很方便的。

    当然,公式中还有一个(p(c_i)),也需要对数处理后相加。

    代码

    使用分类器

    在训练阶段我们只计算(e(vec w|c_i)),这其实就是训练出来的分类器。一旦完成训练,就可以使用分类器:

    def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
        """
        @param vec2Classify: 需要被分类的词向量
        @param p0Vec: 第0类对应的词概率向量
        @param p1Vec: 第1类对应的词概率向量
        @param 
        """
        p1=sum(vec2Classify*p1Vec) + np.log(pClass1)
        p0=sum(vec2Classify*p0Vec) + np.log(1.0-pClass1)
        if p1>p0:
            return 1
        else:
            return 0
    

    训练模型

    使用训练数据,训练出分类器,也就是获取(e(vec w|c_i))(e(c_i))。同时修正了《机器学习实战》P61的一个bug,即对于(p1Vect和p0Vect的计算,分母应当是训练数据对应类的向量总数,而不是元素总和)

    def trainNB0(trainMatrix, trainCategory):
        """
        朴素贝叶斯分类器训练函数:计算P(vec w|c_1)和P(vec w|c_2)
        @param trainMatrix:训练矩阵
        @param trainCategory: 和trainMatrix配套使用的向量,表示每个trainMatrix[i]所属类别
        
        """
        numTrainDocs = len(trainMatrix)  #文档总数
        numWords=len(trainMatrix[0])  #向量维数:不重复的单词数量
        pAbusive=sum(trainCategory)/float(numTrainDocs)
        p0Num=np.ones(numWords)
        p1Num=np.ones(numWords)
        p0Denom=2.0
        p1Denom=2.0
        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 = p1Num/p1Denom  ##书上这句是错的
        #p0Vect = p0Num/p0Denom  ##这句也是错的
        
        num_c1 = sum(trainCategory)
        num_c0 = len(trainCategory) - num_c1
        p1Vect = log(p1Num / num_c1)   #这才正确的是p(vec w|c_1)
        p0Vect = log(p0Num / num_c0)   #这才正确的是p(vec w|c_0)
        
        return p0Vect, p1Vect, pAbusive #pAbusive就是p(c_1)
    

    理论上到这里就算出了基本的表达式,可以用来比较了。

    总结

    感觉《机器学习实战》第四章朴素贝叶斯对于取对数的说明不够清楚,使用分类器时的向量相乘更是没有任何说明,让人一头雾水。不过章节后面的垃圾邮件分类和词语倾向性的例子还不错~

    Greatness is never a given, it must be earned.
  • 相关阅读:
    转:客制FORM调用会计科目弹性域/根据科目取得CODE_COMBINATION_ID
    设计模式——“signleton”
    javascript部分知识点
    Java多线程初学者指南(9):为什么要进行数据同步
    tomcat报错org.springframework.web.context.ContextLoaderListener找不到
    ibatis知识点
    毕业快一年
    (转)Spring AOP的底层实现技术
    JavaWeb项目中引入spring框架
    Spring的核心机制依赖注入
  • 原文地址:https://www.cnblogs.com/zjutzz/p/4607574.html
Copyright © 2011-2022 走看看