机器学习实战之朴素贝叶斯

2017/11/26 posted in  MachineLearning

朴素贝叶斯就是利用先验知识来解决后验概率,因为训练集中我们已经知道了每个单词在类别0和1中的概率,即p(w|c),
我们就是要利用这个知识去解决在出现这些单词的组合情况下,类别更可能是0还是1,即p(c|w)。
如果说之前的训练样本少,那么这个p(w|c)就更可能不准确,所以样本越多我们会觉得这个p(w|c)越可信。

import os
import sys
from numpy import *
sys.path.append(os.getcwd())
import bayes
# 返回实验样本和类别标签(侮辱类和非侮辱类)
listOPosts,listClasses=bayes.loadDataSet()
# 创建词汇表
# 将实验样本里面的词汇进行去重
def createVocabList(dataSet):
    vocabSet=set([])
    for document in dataSet:
        vocabSet=vocabSet|set(document)
    return list(vocabSet)
myVocabList=createVocabList(listOPosts)
# 将词汇转成特征向量
# 也就是将每一行样本转成特征向量
# 向量的每一元素为1或者0,分别表示词汇表中的单词在输入文档中是否出现
def setOfWordsVec(vocabList,inputSet):
    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 returnVec

训练算法:从词向量计算概率

朴素贝叶斯分类器训练函数

# 输入为文档矩阵以及由每篇文档类别标签所构成的向量
def trainNB0(trainMatrix,trainCategory):
    # 文档总数,有几篇文档,在这里也就是有几个一维数组
    numTrainDocs=len(trainMatrix)
    # 每篇文档里面的单词数,也就是一维数组的长度
    numWords=len(trainMatrix[0])
    # 因为就只有0和1两个分类,将类别列表求和后,就是其中一个类别的个数
    # 然后numTrainDocs也就是文档总数,这样相除后就是这个类别的概率了
    pAbusive=sum(trainCategory)/float(numTrainDocs)
    # 以下两行,初始化概率
    p0Num=ones(numWords);p1Num=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=log(p1Num/p1Denom)
    p0Vect=log(p0Num/p0Denom)
    return p0Vect,p1Vect,pAbusive
# 所有文档的特征向量
trainMat=[]
# 将文档的每一行,转成词向量,然后追加到trainMat中
for postinDoc in listOPosts:
    trainMat.append(bayes.setOfWords2Vec(myVocabList,postinDoc))
p0V,p1V,pAb=trainNB0(trainMat,listClasses)
pAb
0.5
p0V
array([-2.56494936, -2.56494936, -2.56494936, -3.25809654, -3.25809654,
       -2.56494936, -2.56494936, -2.56494936, -3.25809654, -2.56494936,
       -2.56494936, -2.56494936, -2.56494936, -3.25809654, -3.25809654,
       -2.15948425, -3.25809654, -3.25809654, -2.56494936, -3.25809654,
       -2.56494936, -2.56494936, -3.25809654, -2.56494936, -2.56494936,
       -2.56494936, -3.25809654, -2.56494936, -3.25809654, -2.56494936,
       -2.56494936, -1.87180218])
p1V
array([-3.04452244, -3.04452244, -3.04452244, -2.35137526, -2.35137526,
       -3.04452244, -3.04452244, -3.04452244, -2.35137526, -2.35137526,
       -3.04452244, -3.04452244, -3.04452244, -2.35137526, -2.35137526,
       -2.35137526, -2.35137526, -2.35137526, -3.04452244, -1.94591015,
       -3.04452244, -2.35137526, -2.35137526, -3.04452244, -1.94591015,
       -3.04452244, -1.65822808, -3.04452244, -2.35137526, -3.04452244,
       -3.04452244, -3.04452244])
# 朴素贝叶斯分类函数
def classifyNB(vec2classify,p0Vec,p1Vec,pClass1):
    #元素相乘
    p1=sum(vec2classify*p1Vec)+log(pClass1)
    p0=sum(vec2classify*p0Vec)+log(pClass1)
    if p1>p0:
        return 1
    else:
        return 0
def testingNB():
    listOPosts,listClasses=bayes.loadDataSet()
    myVocabList=createVocabList(listOPosts)
    trainMat=[]
    for postinDoc in listOPosts:
        trainMat.append(setOfWordsVec(myVocabList,postinDoc))

    p0V,p1V,pAb=trainNB0(array(trainMat),array(listClasses))

    testEntry=['love','my','dalmation']
    thisDoc=array(setOfWordsVec(myVocabList,testEntry))
    print testEntry,'classified as : ',classifyNB(thisDoc,p0V,p1V,pAb)

    testEntry=['stupid','garbage']
    thisDoc=array(setOfWordsVec(myVocabList,testEntry))
    print testEntry,'classified as : ',classifyNB(thisDoc,p0V,p1V,pAb)
testingNB()
['love', 'my', 'dalmation'] classified as :  0
['stupid', 'garbage'] classified as :  1

使用朴素贝叶斯过滤垃圾邮件

  • 收集数据:提供文本文件
  • 准备数据:将文本文件解析成词条向量
  • 分析数据:检查词条确保解析的正确性
  • 训练算法:使用我们之前建立的trainNB0()函数
  • 测试算法:使用classifyNB()
mySent='This book is the best book on Python or M.L. I have ever laid eyes upon.'
# 文件解析及完整的垃圾邮件测试函数
def textParse(bigString):
    import re
    listOfTokens=re.split(r'\W*',bigString)
    return [tok.lower() for tok in listOfTokens if len(tok)>2]
def spamTest():
    docList=[];classList=[];fullText=[]
    for i in range(1,26):
        # 导入邮件文本,并解析成词条
        wordList=textParse(open('email/spam/%d.txt' %i).read())
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(1)

        wordList=textParse(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(random.uniform(0,len(trainingSet)))
        testSet.append(trainingSet[randIndex])
        del(trainingSet[randIndex])

    # 生成测试集的特征向量
    trainMat=[];trainClasses=[]
    for docIndex in trainingSet:
        trainMat.append(setOfWordsVec(vocabList,docList[docIndex]))
        trainClasses.append(classList[docIndex])

    p0V,p1V,pSpam=trainNB0(trainMat,trainClasses)

    # 测试集,测试错误率
    errorCount=0
    for docIndex in testSet:
        wordVector=setOfWordsVec(vocabList,docList[docIndex])
        if classifyNB(wordVector,p0V,p1V,pSpam)!=classList[docIndex]:
            errorCount+=1
    print 'the error rate is : ',float(errorCount)/len(testSet)
spamTest()
the error rate is :  0.2

使用朴素贝叶斯分类器从个人广告中获取区域倾向

  • 收集数据:从RSS源收集内容,这里需要对RSS源构建一个接口
  • 准备数据:将文本文件解析成词条向量
  • 分析数据:检查词条确保解析的正确性
  • 训练算法:使用我们之前建立的trainNB0()函数
  • 测试算法:观察错误率,确保分类器可用.可以修改切分程序,以降低错误率,提高分类结果.
  • 使用算法:构建一个完整的程序,封装所有内容.给定两个RSS源,该程序会显示最常用的公共词.

下面将使用来自不同城市的广告训练一个分类器,然后观察分类器的效果。我们的目的并不是使用该分类器进行分类,而是通过观察单词和条件概率值来发现与特定城市相关的内容。