基于概率论的分类方法:朴素贝叶斯

朴素:整个形式化过程制作最原始,最简单的假设。一个假设是特征的独立性,另一个是每个特征同等重要
优点:在数据较少的情况下仍然有效,可以处理多类别问题
缺点:对于输入数据的准备方式较为敏感
适用数据类型:标称型数据
贝叶斯理论的核心思想:选择高概率对应的类别
贝叶斯准则:P(h | D) = P(h) * P(D | h) / P(D)
独立性:一个特征出现的可能性和其他单词没有任何关系
如果每个特征需要N个样本,那么10个特征需要N**10个样本
如果特征之间相互独立,样本数最多可以减少到10*N个(最多是因为有些样本可能缺少某个特征)

常见的朴素贝叶斯的应用是文档分类,我们观察文档中出现的词,并把每个词的出现或者不出现作为一个特征,这样得到的特征数目就会跟词汇表中的一样多

流程:
1.创建数据集(包括数据特征集和对应的类别标签)
2.获取词汇表(所有语句中不重复的单词组成的列表)
3.计算每个句子中的单词在词汇表中出现的数量,组成列表
4.根据3计算出每类句子中每个单词在词汇表中出现的概率
5.构建分类器:
(1)获取输入语句对应的词汇表(每个单词出现在词汇表的个数)
(2)用4中计算出来的概率列表乘以输入语句的词汇列表,得到对应类别的概率(并加上样本集中该类别的句子占总句子的概率)
(3)对这几个类别的概率进行比较,最大的就判断为是该类别

  • 词表到向量的转换函数
#加载数据
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', 'setak', '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 ducument in dataSet:
        vocabSet = vocabSet | set(ducument) #合并两个集合
    return list(vocabSet) #返回所有单词的列表

#输入总文档对应的词汇表和单个文档,返回值:将单个文档中的单词转换为0和1,0表示未出现在词汇表,1表示出现,这种单个文档的词汇表在本文的注释中用‘单个词汇表’ 表示
def setOfWords2Vec(vocabList, inputSet): # vocabList为所有单词的列表,即词汇表 inputSet为一个单词的列表
    returnVec = [0] * len(vocabList) #创建一个和词汇表等长的向量,其元素都设为0
    for word in inputSet:
        if word in vocabList: #遍历文档中的所有单词,如果出现了词汇表中的单词,则将输出的文档变量对应值设为1
            returnVec[vocabList.index(word)] = 1 
        else:
            print("the word: %s is not in my Vocabulary!" % word)
    return returnVec 

测试

listOPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)
myVocabList

结果

['food',
  ...
 'setak',
 'has',
 'please']

测试

setOfWords2Vec(myVocabList, listOPosts[3])

结果

[0,1,...0,1,0,0,0,0]
  • 贝叶斯分类器训练函数
import numpy as np
from math import log

#trainMatrix为文档矩阵,矩阵中每个列表的长度都是词汇表的长度,列表元素为0或1,代表对应文档中的单词是否出现 
#trainCategory为每篇文档类别标签所构成的列表
def trainNBO(trainMatrix, trainCategory): 
    numTrainDocs = len(trainMatrix) #文档的数量
    numWords = len(trainMatrix[0]) #文档表的长度
    pAbusive = sum(trainCategory) / float(numTrainDocs) #侮辱文档的总数除以总文档数,即侮辱文档的占比
    # 计算文档属于哪个类别时,需进行多个概率的成绩( p(w0|1)p(w1|1)p(w2|1)),
    # 为防止某个概率为0导致最后乘积也为0,将所有词的出现次数初始化为1,分母(文档中侮辱性单词的数量)初始化为2(如果为1,相当于少了一组数据)
    #p0Num = np.zeros(numWords) #非侮辱性文档 对应的词汇表
    p0Num = np.ones(numWords)
    #p1Num = np.zeros(numWords) #侮辱性文档 对应的词汇表
    p1Num = np.ones(numWords)
    #p0Denom = 0.0 #非侮辱性文档中的侮辱性单词的数量
    p0Denom = 2.0
    #p1Denom = 0.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 #平均每篇非侮辱性文档所含有的侮辱性单词的比例,即该文档属于非侮辱性文档的概率
    #为了防止下溢出(多个较小的值相乘得到更小的值),因此对结果进行log()运算
    p1Vect = [log(float(p1)) for p1 in p1Vect]
    p0Vect = [log(float(p0)) for p0 in p0Vect]
    return p0Vect,p1Vect, pAbusive

测试

listOPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts) #获取文档中所有单词的列表
trainMat = []
for postinDoc in listOPosts:
    trainMat.append(setOfWords2Vec(myVocabList, postinDoc)) #trainMat中的每个元素都是一个列表,即每个文档对应的词汇表,如[0,0,1,1,0,1]

p0V,p1V,pAb = trainNBO(trainMat, listClasses)
pAb  

结果 0.5
测试p0V
结果
[-3.258096538021482,
-3.258096538021482,
-2.5649493574615367,
-2.5649493574615367,
......
-2.5649493574615367,
-2.5649493574615367]

  • 分类器:判断文档是否是侮辱性文档
#分类器,输入文档对应的词汇表的矩阵,文档是非侮辱性文档的概率,文档是侮辱性文档的概率,所有文档中侮辱性文档的占比
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): 
    #文档是侮辱性文档的概率乘以词汇表的矩阵,得到的结果相加,再加上所有文档中侮辱文档的占比就是此文档是侮辱性文档的概率
    p1 = sum(vec2Classify * p1Vec) + log(pClass1) 
    #同理,计算此文档是非侮辱性文档的概率,有趣的是 p1-p0 就会发现之前的某些初始化(只要设置的合理)不会对结果产生影响
    p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
    if p1 > p0:
        return 1
    else:
        return 0
  • 朴素贝叶斯分类函数
def testingNB():
    listOPosts, listClasses = loadDataSet() #获取文档矩阵和类别的列表
    myVocabList = createVocabList(listOPosts) #返回文档的所有单词
    trainMat = []
    for postinDoc in listOPosts: #trainMat是多个列表组成的矩阵,每个列表都是文档对应的词汇表,如[0,1,1,0,1]
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
    #print(type(trainMat))   <class 'list'>
    #print(type(np.array(trainMat)))  <class 'numpy.ndarray'>
    p0V, p1V, pAb = trainNBO(np.array(trainMat), np.array(listClasses))#获得的三个分别为文档是非侮辱性的文档的概率、是侮辱性文档的概率和侮辱性文档的占比
    testEntry = ['love', 'my', 'dalmation']
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
    print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))
    testEntry = ['stupid', 'garbage']
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
    print(testEntry, 'classified as :', classifyNB(thisDoc, p0V, p1V, pAb))

测试testingNB()
结果
['love', 'my', 'dalmation'] classified as: 0
['stupid', 'garbage'] classified as : 1

  • 文档的词袋模型:在setOfWords2Vec()中,一个单词无论出现多少次都为1,为适应磁带模型,需要对其修改
def bagOfWord2VecMN(vocbList, inputSet):
    returnVec = [0] * len(vocbList)
    for word in inputSet:
        if word in vocbList:
            returnVec[vocbList.index(word)] += 1
    return returnVec
  • 文本解析
def textParse(bigString):
    import re
    listOfTokens = re.split(r'\W*', bigString)
    return [tok.lower() for tok in listOfTokens if len(tok) > 2]

测试

bigString = 'This book is the best book on Python or ML . I have ever laid eyes upon.
textParse(bigString)

结果
['this',
'book',
'the',
'best',
'book',
'python',
'have',
'ever',
'laid',
'eyes',
'upon']

  • 对贝叶斯垃圾邮件分类器进行自动化处理
def spamTest():
    docList = [] #spam与ham文件夹下的单词组成的矩阵
    classList = [] #与spam与ham文件夹中的文件对应的矩阵,只有两个元素,1代表垃圾邮件,0代表正常邮件
    fullText = [] #两个文件夹下的所有的单词组成的列表
    for i in range(1,26): # 获取spam与ham下的文件,并把它们解析为词列表
        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 = list(range(50)) #构建一个一维列表[1,2,3....50]  样本集
    testSet = [] # 初始化测试集
    for i in range(10): #将测试集的长度设为10
        # 随机从样本中拿出10个作为测试集
        randIndex = int(np.random.uniform(0, len(trainingSet)))  #先获取0到训练集的长度之间的随机数字,再对这个数字向下取整
        testSet.append(trainingSet[randIndex]) #向测试集中添加样本集的下标
        del(trainingSet[randIndex]) #从列表中删除上边下标对应的元素,防止测试集重复选择
    trainMat = [] #训练集‘单个词汇表’组成的列表
    trainClasses = [] #训练集对应的样本类别标签
    for docIndex in trainingSet: #根据样本集剩下的下标,将剩下的作为训练集,
        trainMat.append(setOfWords2Vec(vocabList, docList[docIndex])) #获取每个训练集对应的‘单个词汇表’
        trainClasses.append(classList[docIndex]) #获取训练集对应的类别标签
    p0V, p1V, pSpam = trainNBO(np.array(trainMat), np.array(trainClasses))
    errorCount = 0
    for docIndex in testSet: #同上,遍历测试集
        wordVector = setOfWords2Vec(vocabList, docList[docIndex]) #获取测试集对应的‘单个词汇表’
        # 调用分类器,分类器结果是1或0 正好与classList中的元素对应,判断是否相等
        if classifyNB(np.array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
            errorCount += 1
    print("the error rate is : ", float(errorCount/len(testSet)))
  • RSS源分类器及高频词去除函数
#遍历词汇表中的每个单词,并统计它在文中出现的次数,然后根据出现次数从高到底对字典进行排序,
#输入不重复的单词列表 和 列表中所有的单词,  返回排序最高的30个单词
def calcMostFreq(vocabList, fullText):
    import operator #这个模块主要提供矩阵的一些操作
    freqDict = {}
    for token in vocabList:
        freqDict[token] = fullText.count(token)
    sortedFreq = sorted(freqDict.items(), key=operator.itemgetter(1), reverse=True)
    return sortedFreq[:30]

#输入两个数据集,一个作为正常文档,一个作为非正常文档
def localWords(feed1, feed0):
    import feedparser
    docList = [] #文档中的单词集单词的集合组成的列表
    classList = [] # 类别标签组成的列表
    fullText = [] #文档中所有的文字组成的列表
    minLen = min(len(feed1['entries']), len(feed0['entries'])) # 以数据集中'entries'的属性值作为数据集,获取两个数据集中的最小长度
    for i in range(minLen): #遍历,获取词汇杂表和文档中的单词列表还有文档中的单词列表对应的词汇分析表
        wordList = textParse(feed1['entries'][i]['summary']) #获取文本
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(1)
        wordList = textParse(feed0['entries'][i]['summary'])
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)
    vocabList = createVocabList(docList) #讲单词杂表转换成不重复单词的列表
    top30Words = calcMostFreq(vocabList, fullText) #获取文档中数量最多的30个单词
    for pairW in top30Words: #在词汇表中删除这30个单词
        if pairW[0] in vocabList: #pariW为列表,元素为单词和数量
            vocabList.remove(pairW[0])
    trainingSet = list(range(2 * minLen)) #设置训练集 长度为minLen的两倍
    testSet = []
    for i in range(20): #随机选20个数据作为测试集,剩下的为训练集
        randIndex = int(np.random.uniform(0, len(trainingSet)))
        testSet.append(trainingSet[randIndex])
        del(trainingSet[randIndex])
    trainMat = []
    trainClasses = []
    for docIndex in trainingSet:
        trainMat.append(bagOfWord2VecMN(vocabList, docList[docIndex])) #词袋模型:列表中的元素对应单词出现的次数
        trainClasses.append(classList[docIndex])
    p0V, p1V, pSpam = trainNBO(np.array(trainMat), np.array(trainClasses))
    errorCount = 0
    for docIndex in testSet:
        wordVector = bagOfWord2VecMN(vocabList, docList[docIndex])
        #通过训练集得到的三个参数与测试集的数据传入分类函数,看结果是否和测试集的类别标签一致
        if classifyNB(np.array(wordVector), p0V,p1V,pSpam) != classList[docIndex]:
            errorCount += 1
    print('the error rate is: ', float(errorCount/len(testSet)))
    return vocabList, p0V, p1V

测试 import feedparser
ny= feedparser.parse('http://newyork.craigslist.org/stp/index.rss')
sf = feedparser.parse('http://sfbay.craigslist.org/stp/index.rss')
vocabList, pSF, pNY=localWords(ny, sf)

输出 the error rate is: 0.35

  • 输出显示最具表征性的词汇
def getTopWords(ny, sf):
    import operator
    vocabList, p0V, p1V = localWords(ny, sf)
    topNY = []
    topSF = []
    for i in range(len(p0V)):
        if p0V[i] > -5.0 :
            topSF.append((vocabList[i],p0V[i]))
        if p1V[i] > -5.0 :
            topNY.append((vocabList[i],p1V[i]))
    sortedSF = sorted(topSF, key = lambda pair:pair[1], reverse=True) #pair表示列表中的元素? 例如(0, 'A')?
    print("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")
    for item in sortedNY:
        print(item[0])

测试getTopWords(ny, sf)
结果
`the error rate is: 0.55
SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF
know
body
live
girl
……
whether
lots
please

NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY
being
about
chat
kik
woman
active
……
when`

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值