机器学习实战笔记(三)朴素贝叶斯分类

暑假期间,总结一下以前学过的知识,如有错误,还请指导.

本系列皆是基于<机器学习实战>、李航的<统计学习方法>以及<西瓜书>所记的一些笔记

先提一个最简单的例子:

现在用p1(x,y)表示数据点(x,y)属于类别1的概率,

用p2(x,y)表示数据点(x,y)属于类别2(图中用三角形表示的类别)的概率,
那么对于一个新数据点(x,y),可以用下面的规则来判断它的类别:
如果 p1(x,y) > p2(x,y),那么类别为1。

 如果 p2(x,y) > p1(x,y),那么类别为2
这就是分类的准则,已知参数(x1、x2...xn)求每个类别的概率,然后选择最大的概率,作为该类别

下面是贝叶斯准则:

贝叶斯准则告诉我们如何交换条件概率中
的条件与结果,即如果已知P(x|c),要求P(c|x),那么可以使用下面的计算方法

注意,这里的X代表特征向量,如果用例子来表示,可以看成这样

不过可以使用贝叶斯准则
来交换概率中条件与结果。具体地,应用贝叶斯准则得到:

上面这个公式表示:已知特征向量(x,y),求属于类别Ci的概率,找一个最大的概率,作为类别

即求max(P(ci|x, y)),可以看成有上面等式右边,分子的最大值

这时候还要提及朴素贝叶斯:假设特征之间相互独立

机器学习实战笔记(三)朴素贝叶斯分类_第1张图片

如果将w展开为一个个独立特征,那么就可
以将上述概率写作p(w0,w1,w2..wN|ci)。这里假设所有词都互相独立,该假设也称作条件独立性
假设,它意味着可以使用p(w0|ci)p(w1|ci)p(w2|ci)...p(wN|ci)来计算上述概率,这就极大地
简化了计算的过程。
即求

计算该函数的伪代码如下:

计算每个类别中的文档数目
对每篇训练文档:
      对每个类别:
             如果词条出现在文档中→ 增加该词条的计数值
             增加所有词条的计数值
     对每个类别:
            对每个词条:
                     将该词条的数目除以总词条数目得到条件概率
返回每个类别的条件概率
 

来从代码深入了解吧,其中test1是检测一句话有没有侮辱性言论,test2是垃圾邮件过滤的小demo

#coding=utf-8
import numpy as np


def 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 代表侮辱性言论, 0不是
    return postingList, classVec


def createVocabList(dataSet):
    '''
    :param dataSet: 数据集,由单词单词
    :return: 单词集合,注意,里面没有重复的
    '''
    vocabSet = set([])  # 创建一个空的集合
    for document in dataSet:
        vocabSet = vocabSet | set(document)  # 求集合的并集

    return list(vocabSet)  # 注意,必须要有list,因为集合是无序的,无法通过索引来找元素,所以需要list

def setOfWords2Vec(vocabSet, inputSet):
    '''
    :param vocabSet: 已经在标记过的词条数目
    :param inputSet: 输入的词条
    :return: 返回词条向量,看输入的词条石头在已经标记过的词条中,已经出现过的为1,没有的为0,方便后面统计数目\计算概率
    '''
    resVec = [0] * len(vocabSet)

    for word in inputSet:
        if(word in vocabSet):
            resVec[vocabSet.index(word)] = 1  # 词集模型,遇到一个单词就为1,另外一种情况是词带
        else:
            print("the word: %s is not in my vocabulary!" %word)

    return resVec

def bagOfWords2Vec(vocabSet, inputSet):
    '''
    :param vocabSet: 已经在标记过的词条数目
    :param inputSet: 输入的词条
    :return: 返回词条向量,看输入的词条石头在已经标记过的词条中,已经出现过的为1,没有的为0,方便后面统计数目\计算概率
    '''
    resVec = [0] * len(vocabSet)

    for word in inputSet:
        if(word in vocabSet):
            resVec[vocabSet.index(word)] += 1  # 词袋模型,有的单词会出现多次
        else:
            print("the word: %s is not in my vocabulary!" %word)

    return resVec

def trainNB0(trainMatrix, trainCategory):
    '''
    :param trainMatrix:词向量,既已经有单词转化成特征数字,这里表示0和1
    :param trainCategory:对应的label,这里对应每一行的词向量的label
    :return:p0Vec, 类别为0时,各特征出现的条件概率
            p1Vec,  类别为1时,各特征出现的条件概率
            p1bsuive 类别1的概率,由于是二分类,所以类别0可以看成1-p1
    '''
    numTrainDocs = len(trainMatrix)
    numWords = len(trainMatrix[0])
    p1bsuive = 1.0 * sum(trainCategory) / numTrainDocs  #因为矩阵里只有0和1,所以sum就是统计1的个数
                                                        # 然后就可以得到样本中侮辱性的概率
    # p0Num = np.zeros(numWords)
    # p1Num = np.zeros(numWords)
    # p0Sum = 0.0
    # p1Sum = 0.0

    p0Num = np.ones(numWords)  # 因为小数相乘会变的很小,这里要用log代替,防止出现数学溢出,所以先加1
    p1Num = np.ones(numWords)
    p0Sum = 2.0
    p1Sum = 2.0

    for i in range(numTrainDocs):
        if(trainCategory[i] == 0):  #统计类别为0时,哥哥特征出现的次数
            p0Num += trainMatrix[i]

            p0Sum += sum(trainMatrix[i])# 统计类别为0时,特征总数,作为分母
        else:
            p1Num += trainMatrix[i]
            p1Sum += sum(trainMatrix[i])
    #
    # p0Vec = 1.0 * p0Num / p0Sum
    # p1Vec = 1.0 * p1Num / p1Sum

    p0Vec = np.log(1.0 * p0Num / p0Sum) #  log(A) + log(B) = log(AB)
    p1Vec = np.log(1.0 * p1Num / p1Sum)
    return p0Vec, p1Vec, p1bsuive

def classifyNb(vec2Classify, p0Vec, p1Vec, pClass1):
    '''

    :param vec2Classify: 需要被分类的词向量
    :param p0Vec:   类别为0时,各特征出现的条件概率
    :param p1Vec:   类别为1时,各特征出现的条件概率
    :param pClass1: 类别1的概率,由于是二分类,所以类别0可以看成1-p1
    :return: 预测类别
    '''
    p0 = sum(vec2Classify * p0Vec) + np.log(1-pClass1)
    p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)

    if(p0>p1):
        return 0
    else:
        return 1

def test1():  # 第一个例子,网站留言是否带有侮辱性词汇
    listPosts, listLabels = loadDataSet()  # 加载数据

    myVecabList = createVocabList(listPosts)  # 词集合,里面没有重复的单词,有训练集里面得出
    print myVecabList
    trainMat = []

    for postDoc in listPosts:
        vec = setOfWords2Vec(myVecabList, postDoc)  # 将单词转化成词向量
        trainMat.append(vec)  #最终将所有训练集都变成词向量

    p0Vec, p1Vec, p1bsuive = trainNB0(np.array(trainMat), np.array(listLabels))  # 得出贝叶斯条件概率

    testVec = ['love', 'my', 'dalmation']  # 这是我们的一个测试向量
    vecMat = setOfWords2Vec(myVecabList, testVec) # 同样需要转换成词向量
    # print vecTest
    classRes = classifyNb(np.array(vecMat), p0Vec, p1Vec, p1bsuive)  # 开始测试
    print(testVec,'classified is ',classRes)

    testVec = ['stupid', 'garbage']
    vecMat = setOfWords2Vec(myVecabList, testVec)
    # print vecTest
    classRes = classifyNb(vecMat, p0Vec, p1Vec, p1bsuive)
    print(testVec, 'classified is ', classRes)

def txtParse(bigString):
    import re
    listOfTokens = re.split(r'\W*', bigString)
    return [tok.lower() for tok in listOfTokens if len(tok) > 2]

def spamTest2():
    docList=[]; classList = []; fullText =[]
    for i in range(1,26):
        wordList = txtParse(open('email/spam/%d.txt' % i).read())
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(1)
        wordList = txtParse(open('email/ham/%d.txt' % i).read())
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)

    vocabList = createVocabList(docList) # 词集合,里面没有重复的单词,有训练集里面得出
    trainingSet = range(50); testSet=[]     # 这边是用过创建训练集和测试集,用下标表示
    for i in range(10):
        randIndex = int(np.random.uniform(0,len(trainingSet)))  #随机生成下标
        testSet.append(trainingSet[randIndex])
        del(trainingSet[randIndex])  # 从训练集中删除
    trainMat=[]; trainClasses = []
    for docIndex in trainingSet:#生成训练集的词向量
        trainMat.append(bagOfWords2Vec(vocabList, docList[docIndex]))
        trainClasses.append(classList[docIndex])
    p0V,p1V,pSpam = trainNB0(np.array(trainMat), np.array(trainClasses))
    errorCount = 0
    for docIndex in testSet:        #开始测试
        wordVector = bagOfWords2Vec(vocabList, docList[docIndex])
        if classifyNb(np.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
            errorCount += 1
            print "classification error",docList[docIndex]
    print 'the error rate is: ',float(errorCount)/len(testSet)


if __name__ == '__main__':

    # test1()
    spamTest2()
    print '#########################################################'

 

你可能感兴趣的:(机器学习实战)