PYTHON机器学习实战——朴素贝叶斯

来源:互联网 发布:河北大学工商学院 知乎 编辑:程序博客网 时间:2024/06/06 02:07


       有时我们想知道给定一个样本时,它属于每个类别的概率是多少,即P(Ci|X), Ci表示类别,X表示测试样本,

有了概率后我们可以选择最大的概率的类别。

如果P(c1|x) > P(c2|x),那么属于类别c1。
如果P(c1|x) < P(c2|x),那么属于类别c2。

一般直接求P(Ci|X)比较难,但可以用经典贝叶斯公式转化:

p(C/X) = p(X/C)*p(C)/p(X)

而p(X|C)中,X是多个独立特征,即X=X1,X1,X3,... Xn时

p(X|C) = p(X1|C)*p(X2|C)*p(X3|C)*...*p(Xn|C)

所以:

p(C/X) = p(X/C)*p(C)/p(X)=p(X1|C)*p(X2|C)*p(X3|C)*...*p(Xn|C)*p(C)/p(X)

比较 P(c1|x) 与 P(c2|x) 那个较大时,只需要比较分母大小即可。

即比较:p(X1|C1)*p(X2|C1)*p(X3|C1)*...*p(Xn|C1)*p(C1)与

                p(X1|C2)*p(X2|C2)*p(X3|C2)*...*p(Xn|C2)*p(C2)的大小即可。

而当  太多很小的数相乘 时 会产生 下溢出问题,或者得到不正确的答案,

在代数中有ln(a*b) = ln(a)+ln(b),于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。

同时,采用自然对数进行处理不会有任何损失。

即 计较 log(p(X1|C1)) +...+  log(p(Xn|C1)) + log(p(C1)) 与

              log(p(X1|C2)) +...+  log(p(Xn|C2)) + log(p(C2))的大小 确定 X属于C1类还是C2类\

代码详解:

#-*- coding:utf-8 -*-#!/usr/bin/python'''朴素贝叶斯'''# 测试 侮辱性语句 import bayes as bs     bs.testingNB()# 测试 垃圾邮件   import bayes as bs     bs.spamTest()# 博客数据分析 测试  import feedparser   import bayes as bs# ny = feedparser.parse('http://newyork.craigslist.org/stp/index.ress')# sf = feedparser.parse('http://sfbay.craigslist.org/stp/index.ress')# bs.getTopWords(ny,sf)from numpy import *# import feedparser # rss数据分析器dist_lab =  {0: '非侮辱', 1: '侮辱'}dist_mail = {0: '非垃圾邮件', 1: '垃圾邮件'}# 自建立简单数据集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):    vocabSet = set([])                      # 创建空集合(不重复)    for document in dataSet:        vocabSet = vocabSet | set(document) # 两个集合的并集 set(返回不重复的词表)    return list(vocabSet)# 词集模型 词向量 ,出现很多次 也为1# 用字典中的单词 线性表示 输入的词汇 流# 词表转为向量 classVec 类别标签向量  词汇表 vocabList,    输入文档 inputSet def setOfWords2Vec(vocabList, inputSet):    returnVec = [0]*len(vocabList) # 类别标签向量 与  词表(单词字典)   等长    for word in inputSet:          # 遍历文档中的单词        if word in vocabList:      # 如果出现了 词汇表中的单词            returnVec[vocabList.index(word)] = 1      # 相应单词计数为1, 标记为1 出现很多次 也为1,表示使用过词典里的该单词        else: print "the word: %s is not in my Vocabulary!" % word    return returnVec# 训练 朴素贝叶斯分类器# trainMatrix 训练文档矩阵(已用字典线性表示)   trainCategory 训练文档类别标签向量 1 为侮辱性言论 , 0 非侮辱性言论# p(类别/词向量) = p(词向量/类别)* p(类别) / p(词向量)# p(ci/w)       = p(w/ci)       * p(ci)   / p(w)# 而   p(w/ci) = p(w1/ci)*p(w2/ci)*。。。*p(wn/ci) # 而   p(w1/ci)  为对应 文章中 所有同类别文档 w1单词出现的 概率def trainNB0(trainMatrix,trainCategory):    numTrainDocs = len(trainMatrix) # 文档个数    numWords = len(trainMatrix[0])  # 文档总字典单词数    pAbusive = sum(trainCategory)/float(numTrainDocs)   # 属于侮辱性文档的概率 p(ci=1)    # p(word/ci)  各类别(侮辱和非侮辱)中出现 各个单词的总次数 初始化为1    p0Num = ones(numWords); p1Num = ones(numWords)      # 避免出现乘以0的情况    p0Denom = 2.0; p1Denom = 2.0                        # change to 2.0    for i in range(numTrainDocs):   # 每个文档        if trainCategory[i] == 1:   # 对应类别  如果是侮辱类            p1Num += trainMatrix[i]          # 所有文档中 侮辱类类别 各个单词出现的次数 直接用词向量+            p1Denom += sum(trainMatrix[i])   # 侮辱类文档单词总数 用以计算 侮辱类文档中每个单词出现的概率        else:            p0Num += trainMatrix[i]          # p(x/c0) 所有文档中 非侮辱类 各个单词出现的次数            p0Denom += sum(trainMatrix[i])   # 非侮辱类文档单词总数    p1Vect = log(p1Num/p1Denom)              # change to log()   p(w/c1) 取 对数避免下溢出    p0Vect = log(p0Num/p0Denom)              # change to log()   p(w/c0)    return p0Vect,p1Vect,pAbusive # 返回[p(w1/c0),...,p(wn/c0)] [p(w1/c1),...,p(wn/c1)] p(c1)# 测试 朴树贝叶斯分类器def test_trainNB0():    pList,cVec = loadDataSet()          # 文档句子  和 对于标签    vocList    = createVocabList(pList) # 生成字典    trainMat   = []                     # 用字典线性表示的 文档    for pDoc in pList:                  # 字典   待测试文档        trainMat.append(setOfWords2Vec(vocList, pDoc))    #        线性表示后的矩阵(词向量)  与类别标签    p0_not_Vect,p1_yes_Vect,pAbusive=trainNB0(trainMat,cVec )# 得到 参数向量    return p0_not_Vect,p1_yes_Vect,pAbusive# 根据模型计算词向量属于每一类的概率,最大的为分类概率# vec2Classify 要分类的词向量# p(ci/w新)= p(w/ci)* p(ci)/ p(w) 正比  p(w/ci)* p(ci) # 正比log(p(w/ci)* p(ci) ) = log(p(w/ci)) + log(p(ci)) = # sum(log(p(w1/ci))+...+log(p(wn/ci))) + log(p(ci))def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):    p1 = sum(vec2Classify * p1Vec) + log(pClass1)       # 对数 + 即为乘    p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)    if p1 > p0:        return 1 # 属于1类 侮辱类    else:         return 0 # 属于0类# 词袋模型   统计单词出现的次数#  字典单词 线性 表示 句子 成 词向量def bagOfWords2VecMN(vocabList, inputSet):    returnVec = [0]*len(vocabList)    for word in inputSet:        if word in vocabList:            returnVec[vocabList.index(word)] += 1 # 统计出现的次数    return returnVec# 测试分类器def 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)) # 得到 朴树贝叶斯分类器参数    #测试句子包含的单词1    testEntry = ['love', 'my', 'dalmation']    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))    # 测试句子的 词向量    print testEntry,'分类为: ',dist_lab[classifyNB(thisDoc,p0V,p1V,pAb)]# 打印分类结果    #测试句子包含的单词2    testEntry = ['stupid', 'garbage']    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))    print testEntry,'分类为: ',dist_lab[classifyNB(thisDoc,p0V,p1V,pAb)]# 打印分类结果# 垃圾邮件过滤  英文句子 解析成 单词向量def textParse(bigString):     # 输入为长长的字符串, # 输出为单词列表向量    import re    listOfTokens = re.split(r'\W*', bigString)# 按除去单词、数字外的任意字符分割字符串    return [tok.lower() for tok in listOfTokens if len(tok) > 2] # 排除 单词个数少于2 的小词 变小些   # 垃圾邮件测试 def spamTest():    docList=[]; classList = []; fullText =[]# 文本词向量  标签  所有的词向量    for i in range(1,26):# 总共有25个测试文本        wordList = textParse(open('email/spam/%d.txt' % i).read())        docList.append(wordList)  # 垃圾文档向量        fullText.extend(wordList)        classList.append(1)       # 标签为1        wordList = textParse(open('email/ham/%d.txt' % i).read())        docList.append(wordList)  # 非垃圾文档向量        fullText.extend(wordList)        classList.append(0)       # 标签为0    vocabList = createVocabList(docList)          # 创建 单词字典    trainingSet = range(50); testSet=[]               # 50个训练样本   从训练样本随机抽取 10个 测试样本,并从训练样本中删除    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(bagOfWords2VecMN(vocabList, docList[docIndex]))#训练样本 词袋模型生成词向量        trainClasses.append(classList[docIndex])                       #训练样本 标签    p0V,p1V,pSpam = trainNB0(array(trainMat),array(trainClasses))      #得到模型参数    # 测试    errorCount = 0    for docIndex in testSet:      # 对每一个测试样本分类        wordVector = bagOfWords2VecMN(vocabList, docList[docIndex]) #测试样本 词袋模型生成词向量        if classifyNB(array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:            errorCount += 1            print "分类错误",docList[docIndex]    print '错误率为: ',float(errorCount)/len(testSet)    #return vocabList,fullText# 计算最常出现的词汇 的频率 以及 词汇 前30个def calcMostFreq(vocabList,fullText):    import operator    freqDict = {}    for token in vocabList:        freqDict[token]=fullText.count(token)    sortedFreq = sorted(freqDict.iteritems(), key=operator.itemgetter(1), reverse=True)     return sortedFreq[:30]       # def localWords(feed1,feed0):    import feedparser # RSS阅读器  下载 wget https://pypi.python.org/packages/source/f/feedparser/feedparser-5.1.3.tar.gz#md5=f2253de78085a1d5738f626fcc1d8f71   sudo python setup.py install    docList=[]; classList = []; fullText =[] # 文本词向量  标签  所有的词向量    minLen = min(len(feed1['entries']),len(feed0['entries'])) # 访问所有的条目  好像关键字没有!!!    for i in range(minLen):        wordList = textParse(feed1['entries'][i]['summary'])  # 对RSS源数据 分解        docList.append(wordList)        fullText.extend(wordList)        classList.append(1)       # NY is class 1 类别1        wordList = textParse(feed0['entries'][i]['summary'])        docList.append(wordList)        fullText.extend(wordList)        classList.append(0)       # 类别2    vocabList = createVocabList(docList)            # 字典    top30Words = calcMostFreq(vocabList,fullText)   # 字典单词中 在 所有词汇中出现次数最多的30个词汇    for pairW in top30Words:        if pairW[0] in vocabList: vocabList.remove(pairW[0]) # 去除出现次数最高的词汇    # 创建测试数据    trainingSet = range(2*minLen); testSet=[]                #create test set    for i in range(20):        randIndex = int(random.uniform(0,len(trainingSet)))print  randIndex, len(trainingSet)        testSet.append(trainingSet[randIndex])        del(trainingSet[randIndex])     # 创建训练数据 词向量    trainMat=[]; trainClasses = []    for docIndex in trainingSet:         trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))# 词向量        trainClasses.append(classList[docIndex])    p0V,p1V,pSpam = trainNB0(array(trainMat),array(trainClasses))      # 得到朴树贝叶斯分类器参数    errorCount = 0    for docIndex in testSet:        # 对每一个测试样本分类        wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])    # 测试样本 词袋模型生成词向量        if classifyNB(array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:#识别错误的样本            errorCount += 1    print '错误率: ',float(errorCount)/len(testSet)    return vocabList,p0V,p1V# def getTopWords(ny,sf):    import operator    vocabList,p0V,p1V=localWords(ny,sf)    topNY=[]; topSF=[]    for i in range(len(p0V)):        if p0V[i] > -6.0 : topSF.append((vocabList[i],p0V[i]))        if p1V[i] > -6.0 : topNY.append((vocabList[i],p1V[i]))    sortedSF = sorted(topSF, key=lambda pair: pair[1], reverse=True)    print "SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**"    for item in sortedSF:        print item[0]    sortedNY = sorted(topNY, key=lambda pair: pair[1], reverse=True)    print "NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**"    for item in sortedNY:        print item[0]



原创粉丝点击