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

经过一周的读书、视频以及网站教程,终于弄清楚了朴素贝叶斯算法的原理,这里粘贴机器学习实战上相关的代码以记录我的学习成果。附代码及数据集。

手写数据集用于测试算法:

import numpy

'''Python进行文本分类'''
'''将所有文档出现过的单词找出来构成词汇表,然后每个文档生成文档向量来表示文档中具体出现了哪些词,以此将文档向量化'''
def loadDataSet():
    """创建一个样本,共包含6个文档"""
    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:  # dataset是一个列表,而列表里面的元素也是列表类型
        # print(document) # 此处可看出元素document是列表类型
        vocabSet = vocabSet | set(document)  # 列表document转换为集合,且合并两个集合所有元素
    return list(vocabSet)    # 最终返回一个包含所有词汇但不重复的列表

根据词汇表,将数据集文档转换为文档向量(数字化):

# 基于词集模型
def setOfWords2Vec(vocabList,inputSet):
    """判断词汇列表中的单词是否在输入文档中出现,返回文档向量"""
    returnVec1 = [0]*len(vocabList)  # 创建一个与词汇列表等长的0向量
    # returnVec2 = [0]*len(vocabList)  # 创建一个与词汇列表等长的0向量
    ''' 书上的代码  '''
    for word in inputSet:        # 遍历文档的单词,判断在词汇表哪一个位置出现,向量对应位置置1
        if word in vocabList:
            returnVec1[vocabList.index(word)] = 1
        else: print("the word :%s is not in my Vocabulary!" % word)  # 文档中单词未被词汇表收录
    ''' ----------------错误理解,,,--------------------------------------------- '''
    ''' for word in vocabList:       # 遍历列表的词汇,判断在文档中是否出现
        if word in inputSet:
            returnVec2[vocabList.index(word)] = 1
        else: print("the word :%s is not in my document!" % word)'''
    return returnVec1
# 基于词袋模型
def bagOfWords2VecMN(vocabList,inputSet):
    """判断单词在文档是否出现,且累计出现次数,返回文档向量"""
    returnVec = [0]*len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1
    return returnVec

训练分类器,这里的算法原理是贝叶斯公式:

在这里插入图片描述

'''分类器训练函数'''
def trainNB0(trainMatrix,trainCategory):
    """输入矩阵是文档向量组成的矩阵以及类别标签"""
    numTrainDocs = len(trainMatrix)
    numWords = len(trainMatrix[0])  # 文档向量长度(词汇表长度)
    pAbusive = sum(trainCategory)/float(numTrainDocs)  # 有侮辱性词汇出现的文档所占文档总数的比例  P(C) 任意文档属于侮辱性文档的概率
    '''拉普拉斯平滑,避免概率值为0'''
    p0Num = numpy.ones(numWords); p1Num = numpy.ones(numWords)  # 创建指定大小的数组,数组元素为0,数组类型为ndarray,多维数组
    p0Denom = 2.0;p1Denom = 2.0
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:  # 如果文档i是侮辱性言论(包含侮辱性词汇)(由loadDataSet中的类别标签决定)
            p1Num += trainMatrix[i]  # 把侮辱性文档向量叠加,最终得出词汇表每个单词在侮辱性文档中出现次数  Ni
            p1Denom += sum(trainMatrix[i])  # 因为文档中每个单词必然会出现在词汇表中,文档向量求和即为文档单词数,此处计算所有侮辱性文档的总单词数  N
        else:                           # 同理,计算每个单词在非侮辱性文档中出现次数以及文档总单词数
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    '''求对数,避免下溢出'''
    p1Vect = numpy.log(p1Num/p1Denom)          # P(Fi/C) = Ni/N   计算每个单词再侮辱性文档中出现的概率
    p0Vect = numpy.log(p0Num/p0Denom)                         # 计算每个单词再非侮辱性文档中出现的概率
    return  p0Vect,p1Vect,pAbusive

分母相同,只需要比较分子的大小:

'''分类函数'''
'''算法:通过比较贝叶斯公式分子的大小即可 '''
def classifyNB(vec2Clasify,p0Vec,p1Vec,pClass1):
    """输入为要分类的文档、训练概率数组、类别概率"""
    p1 = sum(vec2Clasify*p1Vec) + numpy.log(pClass1)    # 文档vec2Clasify是侮辱性文档的概率,因为上面求得是对数,所以这里求积转为求和
    p0 = sum(vec2Clasify*p0Vec) + numpy.log(1.0 - pClass1)
    if p1 > p0:
        return 1
    else:
        return 0

def testingNB():
    """测试以上bayes函数"""
    listOPosts,listClasses = loadDataSet()
    myVocabList = createVocabList(listOPosts)
    trainMat=[]
    for postingDoc in listOPosts:
        trainMat.append(setOfWords2Vec(myVocabList,postingDoc))
    p0V,p1V,pAb = trainNB0(numpy.array(trainMat),numpy.array(listClasses))
    testEntry = ['love','my','dalmation','abcd']        # abcd没有收录到词汇表的情况
    thisDoc = numpy.array(setOfWords2Vec(myVocabList,testEntry))
    print(testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb))
    testEntry = ['stupid','garbage']
    thisDoc = numpy.array(setOfWords2Vec(myVocabList,testEntry))
    print(testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb))

垃圾邮件分类测试:

'''垃圾邮件分类测试函数'''
def textParse(bigString):
    """文本解析器"""
    import re
    regEx = re.compile('\\W+')   # 编译正则表达式,生成一个正则表达式(pattern)对象    \W :匹配任何非单词字符  + :??
    listOfTokens = regEx.split(bigString)  # re.split() 将字符串分割后返回列表
    # listOfTokens = re.split(r'\W+',bigString)             # r'\W'等价于 \\W
    return [tok.lower() for tok in listOfTokens if len(tok) > 2]    # 不要少于2位的字符串

def spamTest():
    """测试过程自动"""
    docList=[]   # 文档列表,存放所有文档,元素为文档
    classList=[]  # 类别标签,1表示垃圾邮件,0表示非垃圾邮件
    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)        # 类别标签,先读入垃圾邮件,前25位置1
        wordList = textParse(open('email/ham/%d.txt' % i).read())  # 非垃圾邮件25个
        docList.append(wordList)   # 以列表形式保存所有文档
        fullText.extend(wordList)
        classList.append(0)        # 类别标签,后读非垃圾邮件,后25位置0
    vocabList = createVocabList(docList)  # 利用所有测试文档构建词汇表
    trainingSet = list(range(50));testSet=[]  # del range返回range对象,不返回数组对象,故不支持迭代删除
    '''随机抽取10个文档,作为分类测试集,其余40个文档作为训练集'''
    for i in range(10):
        randIndex = int(numpy.random.uniform(0,len(trainingSet)))  # 生成随机数
        testSet.append(trainingSet[randIndex])      # 随机抽取10个文档
        del(trainingSet[randIndex])  # 一次随机抽出一个文档,删除目录索引(列表trainingSet的元素),让已经抽过的文档不再重复抽
    trainMat=[];trainClasses=[]
    '''用训练集训练分类器'''
    for docIndex in trainingSet:
        trainMat.append(setOfWords2Vec(vocabList,docList[docIndex]))  # 构建所有训练文档向量
        trainClasses.append(classList[docIndex])
    p0V,p1V,pSpam = trainNB0(numpy.array(trainMat),numpy.array(trainClasses))  # 分类器训练,得到概率
    errorCount = 0
    '''对测试集分类并验证'''
    for docIndex in testSet:
        wordVector = setOfWords2Vec(vocabList,docList[docIndex])    # 文档向量
        if classifyNB(numpy.array(wordVector),p0V,p1V,pSpam)!=classList[docIndex]:  # 将分类结果与类别标签对比,累计错误数
            errorCount +=1
            print('classification error: ',docList[docIndex])  # 显示出错误文档
    print('the error rate is: ',float(errorCount)/len(testSet))

只需要Python3环境下执行spamTest()函数就会自动随机构建分类器并测试,我得到的错误率最大为0.2,一般为0或0.1。
借鉴的视频链接:https://www.bilibili.com/video/av57027507
Python教程网址:https://www.runoob.com/python3
邮件分类测试数据集:https://pan.baidu.com/s/1y7p2ZPqfdlDGWRicLD5Pnw

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值