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


       有时我们想知道给定一个样本时,它属于每个类别的概率是多少,即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]



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EwenWanW

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值