机器学习实战 (python3.7)-朴素贝叶斯

本文通过实战演示如何利用朴素贝叶斯算法进行文本分类,包括数据预处理、特征提取、模型训练及评估等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如果觉得本篇文章对您的学习起到帮助作用,请 点赞 + 关注 + 评论 ,留下您的足迹💪💪💪

本篇文章为我对机器学习实战-朴素贝叶斯的理解与我在学习时所做笔记,一是为了日后查找方便并加深对代码的理解,二是希望能帮助到使用这本书遇到困难的人。

代码可在python3.7跑通
因此代码相对原书做了一些修改,增加了可读性,同时也解决了一些问题。

代码及详细注释如下:

import re
import numpy as np

def textParse(bigString):
    '''
    # 接受大字符串,并解析成为字符串列表。去掉字符少于2个的字符串,并将所有字符串转化为小写
    :param bigString:
    :return:
    '''
    # 此处 r'\W+' 与书中不一致
    listOfTokens = re.split(r'\W+', bigString)
    return [tok.lower() for tok in listOfTokens if len(tok) > 2]

def createVocabList(dataset):
    '''
    # 创建一个列表,用来记录无重复词条
    :param dataset:
    :return:
    '''
    # 建立集合,用于过滤掉重复的词条
    vocabSet = set()
    for document in dataset:
        # 列表是不可以进行并操作的,集合可以
        vocabSet = vocabSet | set(document)
    # 转化为列表,但是此列表是没有顺序的
    # 加了sorted() 排序后,列表是有顺序的
    return sorted(list(vocabSet))

def setOfWordsVec(vocabList, inputSet):
    '''
    # 创建与词汇表等长向量,查看输入文档是否在词汇表中出现,出现了则在相应位置置1,否则保持0
    :param vocabList:词汇表
    :param inputSet:某个文档
    :return:
    '''
    # 创建同 vocabList 词汇表等长,元素全为0的列表
    returnVec = [0] * len(vocabList)
    for word in inputSet:
        if word in vocabList:
            # 词集模型,index() 函数用于从列表中找出某个值第一个匹配项的索引位置
            # returnVec[vocabList.index(word)] = 1
            # # 词袋模型,可以返回单词出现的次数
            returnVec[vocabList.index(word)] += 1
        else:
            print('单词 %s 不在我的词典里!' % word)
    return returnVec

def trainNB0(trainMatrix, trainCategory):
    '''
    # 朴素贝叶斯分类器训练函数

    :param trainMatrix: 训练文档矩阵,即setOfWordsVec返回的returnVec构成的 0,1 列表
                        多个0,1列表即可构成数据矩阵
    :param trainCategory: 训练类别标签向量,即loadDataset返回的标签向量classVec
    :return:
        p0Vect:侮辱类的条件概率组数
        p1Vect:非侮辱类的条件概率组数
        pAbusive:文档属于侮辱类的概率,为先验3概率
    '''
    # 计算文档数目
    numTrainDocs = len(trainMatrix)
    # 计算无重复词汇表的单词数目
    numWords = len(trainMatrix[0])
    # 先验概率,侮辱性文档所占概率
    pAbusive = sum(trainCategory) / float(numTrainDocs)

    # 改进,防止出现概率为0的情况
    p0Num = np.ones(numWords);p1Num = np.ones(numWords)
    # lamda=1, numWords=n*lamda,为了保证概率加和为1
    p0Denom = float(numWords); p1Denom = float(numWords)

    for i in range(numTrainDocs):
        if trainCategory[i] == 1:
            # 计算侮辱性文档内各个单词出现的次数,p1Num是一个向量
            p1Num += trainMatrix[i]
            # 侮辱性文档不同词条的个数
            p1Denom += sum(trainMatrix[i])
        else:
            # 计算非侮辱性文档内各个单词出现的次数
            p0Num += trainMatrix[i]
            # 非侮辱性文档不同词条的个数
            p0Denom += sum(trainMatrix[i])

    # 由于概率都是小于1的,多个数值相乘后会非常小,为了防止数值下溢出而得不到正确答案,使用log计算
    p1Vect = np.log(p1Num / p1Denom)
    p0Vect = np.log(p0Num / p0Denom)
    return p0Vect, p1Vect, pAbusive


def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    '''
    # 判断新邮件是否属于垃圾文件
    :param vec2Classify: 需要预测邮件的词条分布
    :param p0Vec: 非侮辱性各个词条出现的条件概率
    :param p1Vec: 侮辱性各个词条出现的条件概率
    :param pClass1: 侮辱性先验概率
    :return:
    '''
    # 连乘加log变成相加
    p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)
    p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
    if p1 > p0:
        return 1
    else:
        return 0


def spamText():
    docList = []; classList = []; fullText = []
    # 读取每一个垃圾文件,大字符串转化为字符列表
    for i in range(1, 26):
        with open('email//spam//%d.txt' % i, encoding="ISO-8859-1") as frSpam:
            wordList = textParse(frSpam.read())
            # 弄明白 append() 和 extend() 的区别就可明白
            docList.append(wordList)
            fullText.extend(wordList)
            # 垃圾邮件类别添加 1
            classList.append(1)

        with open('email//ham//%d.txt' % i, encoding="ISO-8859-1") as frHam:
            wordList = textParse(frHam.read())
            # 弄明白 append() 和 extend() 的区别就可明白
            docList.append(wordList)
            fullText.extend(wordList)
            # 非垃圾邮件类别添加 0
            classList.append(0)
    print(docList)
    # 生成无重复词条列表
    vocabList = createVocabList(docList)
    # 创建训练数据集索引列表
    trainingSet = list(range(50))
    # 创建测试数据集索引列表
    testSet = []
    for i in range(10):
        # 0-50 不包含50,随机取数,int()取整作索引
        randIndex = int(np.random.uniform(0, len(trainingSet)))
        # 随机选取50封邮件的其中10件,索引存放于 testSet 中
        testSet.append(trainingSet[randIndex])
        # 删除属于测试集的索引
        del(trainingSet[randIndex])

    trainMat = []; trainClasses = []
    for docIndex in trainingSet:
        trainMat.append(setOfWordsVec(vocabList, docList[docIndex]))
        trainClasses.append(classList[docIndex])
    p0V, p1V, pSpam = trainNB0(trainMat, trainClasses)
    errorCount = 0.0
    for docIndex in testSet:
        wordVector = setOfWordsVec(vocabList, docList[docIndex])
        if classifyNB(wordVector, p0V, p1V, pSpam) != classList[docIndex]:
            errorCount += 1
    errorRate = errorCount / len(testSet)
    print('错误率为:',errorRate)

if __name__ == '__main__':
    spamText()

希望文章内容可以帮助到你,快来动手敲代码吧!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值