机器学习算法中关于朴素贝叶斯代码详细注释

K-近邻算法和决策树算法容易理解,但从朴素贝叶斯开始,机器学习书中关于算法来历及证明几乎没有,很多都涉及到统计概论的知识,推荐李航老师的《统计学习方法》,清华大学出版社,里面有关于机器学习的很多算法原理,建议基础薄弱的同学把整本书看完,也不是很厚,200多页。

```
'''基于概率论的分类方法:朴素贝叶斯
   朴素的概念:整个形式化过程只做最原始最简单的假设
   优点:在数据较少的情况下仍然有效,可以处理多类别的问题
   缺点:对于输入数据的准备方式较为敏感
   适用数据类型:标称型数据
'''
'''
贝叶斯决策理论的核心思想:选择高概率对应的类别
'''
import operator
from numpy import *
import re
#词表到向量的转换函数

def loadDataSet():                            #创建实验样本
    postingList=[['my','dog','has','flea','problems'],\
                 ['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([])                          #创建空的集合,而且创建空集合必须用set()
    for document in dataSet:
        vocabSet=vocabSet|set(document)       #创建两个集合的并集,其中没有重复元素,这样生成的集合内的元素是无序的
    return list(vocabSet)                     #返回的是被列表化的集合

def setOfWords2Vec(vocabList,inputSet):       #传入一个集合和被检测列表
    returnVec=[0]*len(vocabList)              #初始化向量列表全为0,其中个数为单词(集合)列表元素数(len)
    for word in inputSet:                     #遍历被检测列表
        if word in vocabList:                 #检测被检测的列表中是否有给定样本中的词汇
            returnVec[vocabList.index(word)]=1#将该词出现的第一个位置的特征值标记为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])              #得到第一个文本的长度
    pAbusive=sum(trainCategory)/float(numTrainDocs) #计算了侮辱性言论总的比例
    p0Num=ones(numWords)                     #传入一个参数,初始化一个长度为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)                     #p1Vect的意义是:计算了每个单词在被判断为侮辱性言论中出现的概率
    p0Vect=log(p0Num/p0Denom)                     #基本同上(用对数的形式是为了避免结果下溢)
    return p0Vect,p1Vect,pAbusive                 #返回的是概率

'''
计算出来的p0Vect和p1Vect中对应的比例是该位置单词在对应类别标签中的出现比例,而比例最高的是该类别最具有代表性的单词
实际上是在算条件概率:p(A|B)=P(AB)/P(B)
'''

##listOPosts,listClasses=loadDataSet()
##myVocabList=createVocabList(listOPosts)
##trainMat=[]
##for postinDoc in listOPosts:
##    trainMat.append(setOfWords2Vec(myVocabList,postinDoc))
##print(trainMat)
##p1d,p0d,p1,p0=trainNB0(trainMat,listClasses)
##print(p1d)
##print(p0d)
##print(p1)
##print(p0)
##print(p1/p1d)

#以下两个函数是朴素贝叶斯分类函数
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
    p1=sum(vec2Classify*p1Vec)+log(pClass1) #实际上是在计算靠近值:单词出现在集合中的0/1情况*概率再全部求和+类别对数概率
    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.append(setOfWords2Vec(myVocabList,postinDoc))#得到所有文本中单词在集合中出现的情况
    p0V,p1V,pAb=trainNB0(array(trainMat),array(listClasses))
    testEntry=['love','my','dalmation']
    thisDoc=array(setOfWords2Vec(myVocabList,testEntry))#标记testEntry文本中单词在结合中的出现
    print(testEntry,'classified as:',classifyNB(thisDoc,p0V,p1V,pAb))#p0V、p1V、pAb是训练好的的分类器传入thisDoc进行测试
    testEntry=['stupid','garbage']
    thisDoc=array(setOfWords2Vec(myVocabList,testEntry))
    print(testEntry,'classified as:',classifyNB(thisDoc,p0V,p1V,pAb))


#朴素贝叶斯词袋模型
def bagOfWords2VecMN(vocabList,inputSet):
    returnVec=[0]*len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)]+=1#每个单词在集合中的出现次数
    return returnVec

##mySent='This book is the best book on Python or M.L. I have ever laid eyes upon.'
##mySent.split()
##regEx=re.compile('\\W')   #书上有*号,但是结果不对,应该是版本问题
##listOfTokens=regEx.split(mySent)
##a=[tok.lower() for tok in listOfTokens if len(tok)>0]
##emailText=open('email/ham/6.txt').read()
##listOfTokens=regEx.split(emailText)

#文件解析及完整的垃圾邮件测试函数
def textParse(bigString):
    #import re
    listOfTokens=re.split(r'\W',bigString)            #接受一个大字符串并将其解析为字符串列表(这里原文是:listOfTokens=re.split(r'\W*',bigString) )
    return [tok.lower() for  tok in listOfTokens if len(tok)>0]   #所有字符转换为小写

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)               #classList末尾添加1
        wordList=textParse(open('email/ham/%d.txt'% i).read())#这里在根目录ham中第23个文本中找到SciFinance?,把?替换成空格即可
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)
    vocabList=createVocabList(docList)    #创建词汇的集合(无重复项)
    trainingSet=list(range(50))           #一共有50个文本
    testSet=[]                            #初始化样本列表
    for i in range(10):
        randIndex=int(random.uniform(0,len(trainingSet)))#在[0,len(trainingSet))生成一个随机数
        testSet.append(trainingSet[randIndex])           #把抽取的每个样本附加在testSet的末尾作为其中一个元素
        del(trainingSet[randIndex])                      #添加完以后删除这个元素,后面的元素顶上来填空位置
    trainMat=[]                                          #初始化词汇出现情况(每个元素代表一个文本中词汇在集合中的出现情况)
    trainClasses=[]                                      #初始化0/1分类情况
    for docIndex in trainingSet:                         #此时的trainingSet已经是range(40)了
        trainMat.append(setOfWords2Vec(vocabList,docList[docIndex]))#循环结束得到全部文本中前40个中词汇在集合中出现的情况
        trainClasses.append(classList[docIndex])                    #循环结束得到的是(前40个)classList列表中第docList个文档的0/1归类情况
    p0V,p1V,pSpam=trainNB0(array(trainMat),array(trainClasses))     #得到每个词的出现次数与其所在句所有词0/1之和的比例以及训练样本中侮辱性言论文本比例
    errorCount=0                                                    #判断错误数量初始化
    for docIndex in testSet:                                        #遍历测试样本下标
        wordVector=setOfWords2Vec(vocabList,docList[docIndex])      #得到测试样本的单词集合(无重复项)
        if classifyNB(array(wordVector),p0V,p1V,pSpam)!=classList[docIndex]:#判断机器分类与实际分类是否相同
            errorCount+=1                                           #不同则判断错误数量加一
            print('classification error ',docList[docIndex])
    print('the error rate is: ',float(errorCount)/len(testSet))     #输出判断错误率
'''
1.留存交叉验证:随机选择数据的一部分作为训练集,剩余部分作为测试集的过程
2.UnicodeDecodeError: 'gbk' codec can't decode byte 0xae in position 199: illegal multibyte sequence该报错是由于存在未能编码的字符
3.python2与python3中range()的区别:python2中的range()返回一个列表,而python3中返回一个迭代值就是range(0,10)(如果输入的是range(10))
'''

#RSS源分类器及高频词去除函数
def calcMostFreq(vocabList,fullText):        #参数意义:单词集合(无重复)、文本所有单词构成的列表(有重复)
    freDict={}                               #声明计数字典
    for token in vocabList:                  #遍历集合中的每一个不重复元素
        freqDict[token]=fullText.count(token)#a.count(sub)函数是对a中sub个数的统计字典的
    sortedFreq=sorted(freqDict.items(),key=operator.itemgetter(1),reverse=True)#对字典迭代并按第1个元素排序(从零开始),顺序为倒序(从大到小)
    return sortedFreq[:30]                   #取出现频率最高的30个单词(0-29)
```

你可能感兴趣的:(机器学习算法中关于朴素贝叶斯代码详细注释)