《Machine Learning in action》- (笔记)之naive Bayes(2_实战篇)

《Machine Learning in action》,机器学习实战(笔记)之naive Bayes

使用工具:

- Python3.7 
- pycharm
- anaconda
- jupyter notebook

Example-1: classifying apam email with naive Bayes 用朴素贝叶斯过滤垃圾邮件

大概的步骤

  • 获取数据- 提供文本文件
  • 处理数据- 将数据处理成我们所需要的词汇向量,并确保其正确性
  • 训练算法- 使用我们之前建立的trainNB0()函数
  • 测试算法- 使用classifyNB()函数,并且创建一个新的函数来检测其错误率
  • 使用算法- 构建一个完整的程序,对一组文档进行分类,并且将错分的文档输出

1.获取数据:

  • 这里的数据包括函数,都已经上传在我的GitHub上面,可以全部下载
  • [数据集]https://github.com/Tupeng2019/Machine_jichu/tree/master/Machine-Learning/Machine-Learning_note/naive Bayes
  • 里面有两个文件夹,ham,spam,spam就是垃圾文件

2. 处理数据

  • 利用正则表达式,将邮件,划分为一个一个的单词,
  • 先是显示一下我饿对数据的处理结果:
import re

# bigString就是指一个大的字符串,就是相当于邮件当中的英语文章
# 该函数的作用就是将字符串转化为字符列表
def textParse(bigString):
    # 了利用正则表达式
    # 将特殊符号作为切分标志进行字符串切分,即非字母、非数字
    # 在Python3中,只用W就好了
    listOfTokens = re.split(r'\W', bigString)
    # 只是将字符长大大于2的,全部变成小写,除了单个字符的,如I
    return [tok.lower() for tok in listOfTokens if len(tok) > 2]


# 函数createVocabList()将创建所有文档中所有唯一单词的列表
def createVocabList(dataSet):
    # 创建一个空的不重复的列表
    vocabSet = set([])
    for document in dataSet:
        # Create the union of two sets创建两个列表的联合,就是取并集
        vocabSet = vocabSet | set(document)
    return list(vocabSet)

if __name__ == '__main__':
    docList = []; classList = []
    # 遍历ham,spam中的25个TXT文件
    for i in range(1, 26):
        #读取每一个垃圾邮件,并将字符串装换为字符串列表
        # r 既是表示的只读形式,读取文件
        wordList = textParse(open('email//spam/{0}.txt'.format(i),'r').read())
        # 并将其存放在docList找那个
        docList.append(wordList)
        # 标记每一个垃圾邮件,1就表示垃圾邮件,有一个就增加1
        classList.append(1)
        #读取每一个非垃圾邮件,并将字符串装换为字符串列表
        wordList = textParse(open('email/ham/{0}.txt'.format(i), 'r').read())
        docList.append(wordList)
        # 标记非垃圾邮件,0就是表示非垃圾邮件
        classList.append(0)
    # 调用createVocabList(),创建不重复词汇表
    vocabList = createVocabList(docList)

    print(vocabList)
  • 显示结果:
    《Machine Learning in action》- (笔记)之naive Bayes(2_实战篇)_第1张图片

3.下面就是训练和测试数据

import numpy as np
import re
import random


# bigString就是指一个大的字符串,就是相当于邮件当中的英语文章
# 该函数的作用就是将字符串转化为字符列表
def textParse(bigString):
    # 了利用正则表达式
    # 将特殊符号作为切分标志进行字符串切分,即非字母、非数字
    # 在书中用的是W*,估计是Python3和2的区别吧,在这里使用的W
    listOfTokens = re.split(r'\W', bigString)
    # 只是将字符长大大于2的,全部变成小写,除了单个字符的,如I
    return [tok.lower() for tok in listOfTokens if len(tok) > 2]


# 函数createVocabList()将创建所有文档中所有唯一单词的列表
def createVocabList(dataSet):
    # 创建一个空的不重复的列表
    vocabSet = set([])
    for document in dataSet:
        # Create the union of two sets创建两个列表的联合,就是取并集
        vocabSet = vocabSet | set(document)
    return list(vocabSet)

# 遍历词汇列表,输出数字向量(1代表存在在词汇列表中,0表示不在vocaSet
def setOfWords2Vec(vocabList, inputSet):
    # 创建于vocabList维度一样的列表,只是全部都是0
    returnVec = [0] * len(vocabList)
    # 遍历每一个词汇
    for word in inputSet:
        # 如果词条存在于词汇表当中,就变为1
        if word in vocabList:
            returnVec[vocabList.index(word)] = 1
        else: print("the word: {0}is not in my Vocabulary!".format(word))
    # 返回一个文档向量
    return returnVec

# 朴素贝叶斯分类器-训练函数
def trainNB0(trainMatrix,trainCategory):
    # trainMatrix 训练文档矩阵,就是扇面函数返回的returnVec构成的矩阵
    # 计算训练的文档数目
    numTrainDocs = len(trainMatrix)
    # 计算每一篇文档的词条数目
    numWords = len(trainMatrix[0])
    # 所有文档属中,属于垃圾类的概率
    pAbusive = sum(trainCategory)/float(numTrainDocs)
    # 创建numpy.zero数组,表示词条出现的次数,并且初始化为0
    # numerator 分子,denominator分母,都初始化为0
    p0Num = np.zeros(numWords); p1Num = np.zeros(numWords)
    p0Denom = 0.0; p1Denom = 0.0
    for i in range(numTrainDocs):
        #统计属于垃圾类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)···
        #这里统计的都是数字不是具体的数据
        if trainCategory[i] == 1:
            # 向量加法
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])
        else:#统计属于非垃圾类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)···
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    # 元素的划分
    # p1Vect就是属于垃圾类的条件概率数组
    p1Vect = p1Num/p1Denom
    # 属于非垃圾类的条件概率数组
    p0Vect = p0Num/p0Denom
    # #返回属于垃圾类的条件概率数组,属于非垃圾类的条件概率数组,文档属于垃圾类的概率
    return p0Vect,p1Vect,pAbusive

def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    # 对应元素相乘。logA * B = logA + logB,所以这里加上log(pClass1)
    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 spamTest():
    docList = []; classList = []; fullText = []
    # 遍历25个TXT文件
    for i in range(1, 26):                        #遍历25个txt文件
        # 读取每一个垃圾邮件,并字符串转换成字符串列表
        wordList = textParse(open('email//spam/%d.txt' % i).read())
        docList.append(wordList)
        fullText.append(wordList)
        # 标记垃圾邮件,1表示垃圾文件
        classList.append(1)
        # 读取每个非垃圾邮件,并字符串转换成字符串列表
        wordList = textParse(open('email//ham/%d.txt' % i, 'r').read())
        docList.append(wordList)
        fullText.append(wordList)
        # 标记废垃圾邮件0表示非垃圾邮件
        classList.append(0)
    # 创建不重复的词汇表,
    vocabList = createVocabList(docList)
    # 创建储存训练集的索引值的列表,和测试集的索引值的列表
    trainingSet = list(range(50)); testSet = []
    '''
    我们将我们的数据集分一部分作为我们的训练集,一部分作为我们的测试集
    在这里,选择40WieU训练集,10个为测试集
    这里的数据集都是没有什么规律,所以就是随机选就行
    '''
    for i in range(10):
        # 随机选取索引值0-39
        randIndex = int(random.uniform(0, len(trainingSet)))
        # 添加测试集的索引值
        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 = trainNB0(np.array(trainMat), np.array(trainClasses))
    # 这就是计算错误分类的的数量
    errorCount = 0
    # 现在开始遍历测试集
    for docIndex in testSet:
        # 测试的词汇模型
        wordVector = setOfWords2Vec(vocabList, docList[docIndex])
        # 如果分类错误,
        if classifyNB(np.array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
            # 分类错误加+1
            errorCount += 1
            print("分类错误的测试集:",docList[docIndex])
    print('错误率:%.2f%%' % (float(errorCount) / len(testSet) * 100))

if __name__ =='__main__':
    spamTest()
  • 结果显示
    《Machine Learning in action》- (笔记)之naive Bayes(2_实战篇)_第2张图片
  • 函数spamTest()会输出在10封随机选择的电子邮件上的分类错误概率。既然这些电子邮件是随机选择的,所以每次的输出结果可能有些差别。如果发现错误的话,函数会输出错误的文档的此表,这样就可以了解到底是哪篇文档发生了错误。如果想要更好地估计错误率,那么就应该将上述过程重复多次,比如说10次,然后求平均值。相比之下,将垃圾邮件误判为正常邮件要比将正常邮件归为垃圾邮件好。

Example-2: 朴素贝叶斯过- 新浪新闻分类(sklearn)

1.中文语句的切分

  • 考虑一个问题,英文的语句可以通过非字母和非数字进行切分,但是汉语句子呢?就比如我打的这一堆字,该如何进行切分呢?我们自己写个规则?

  • 幸运地是,这部分的工作不需要我们自己做了,可以直接使用第三方分词组件,即jieba,没错就是"结巴"。

  • jieba已经兼容Python2和Python3,使用如下指令直接安装即可

    • conda install jieba好像是不行的
    • pip install jieba
      Python 中文分词组件的使用的方法
  • 官方文档:https://www.oschina.net/p/jieba

  • 数据集分裂如下:

  • 这是我的数据集的下载地址https://github.com/Tupeng2019/Machine_jichu/tree/master/Machine-Learning/Machine-Learning_note/naive%20Bayes

  • 其中sample是数据集,ClassList,txt数据集的分类
    《Machine Learning in action》- (笔记)之naive Bayes(2_实战篇)_第3张图片

下面就是切分中文字

  • 代码如下:
import os
import jieba

def TextProcessing(folder_path):
    # 查看folder_path下的文件
    folder_list = os.listdir(folder_path)
    # 建立储存训练集的列表
    data_list = []
    class_list = []

    #遍历每个子文件夹
    for folder in folder_list:
        # 根据子文件夹,生成新的路径
        new_folder_path = os.path.join(folder_path, folder)
        # 根据子文件夹,生成新的路径
        files = os.listdir(new_folder_path)
        j = 1
        #遍历每个txt文件
        for file in files:
            # 每类txt样本数最多100个
            if j > 100:
                break
            # 打开txt文件
            with open(os.path.join(new_folder_path, file), 'r', encoding = 'utf-8') as f:
                raw = f.read()
            # 精简模式,返回一个可迭代的generator
            word_cut = jieba.cut(raw, cut_all = False)
            # generator转换为list
            word_list = list(word_cut)
            data_list.append(word_list)
            class_list.append(folder)
            j += 1
        print(data_list)
        print(class_list)
if __name__ == '__main__':
    #文本预处理
    # 训练集存放地址
    folder_path = './Sample'
    TextProcessing(folder_path)

其结果:
《Machine Learning in action》- (笔记)之naive Bayes(2_实战篇)_第4张图片

2. 特征的选择

  • 我们将所有文本分成训练集和测试集,并对训练集中的所有单词进行词的出现次数(词频)统计,并按降序排序。也就是将出现次数多的词语在前,出现次数少的词语在后进行排序
import os
import random
import jieba

"""
函数说明:中文文本处理

Parameters:
    folder_path - 文本存放的路径
    test_size - 测试集占比,默认占所有数据集的百分之20
Returns:
    all_words_list - 按词频降序排序的训练集列表
    train_data_list - 训练集列表
    test_data_list - 测试集列表
    train_class_list - 训练集标签列表
    test_class_list - 测试集标签列表
    
"""
# 该函数就是对中文文本的处理
def TextProcessing(folder_path, test_size = 0.2):
    # 查看folder_path下的路径的文件
    folder_list = os.listdir(folder_path)
    # 创建空列表放数据集数据
    data_list = []
    # 创建空列表放数据集类型
    class_list = []

    #遍历每个子文件夹
    for folder in folder_list:
        # 根据子文件夹,生成新的路径
        new_folder_path = os.path.join(folder_path, folder)
        # 存放子文件夹下的TXT文件的列表
        files = os.listdir(new_folder_path)
        # 定义j为每一类TXT样本的个数,并初始化为1
        j = 1
        #遍历每个txt文件
        for file in files:

            #每一类样本的个数最多为100个
            if j > 100:
                break
            # 打开txt文件
            with open(os.path.join(new_folder_path, file), 'r', encoding = 'utf-8') as f:
                raw = f.read()
            #  精简模式,返回一个可迭代的generator
            word_cut = jieba.cut(raw, cut_all = False)
            # 将generator装换为list
            word_list = list(word_cut)

            # 添加数据集数据
            data_list.append(word_list)
            # 向列表中添加数据集类型
            class_list.append(folder)
            j += 1
    # #zip压缩合并,将数据与标签对应压缩
    data_class_list = list(zip(data_list, class_list))
    # 将data_class_list乱序
    random.shuffle(data_class_list)
    # 训练集和测试集切分的索引值
    index = int(len(data_class_list) * test_size) + 1
    # 训练集
    train_list = data_class_list[index:]
    # 测试集
    test_list = data_class_list[:index]
    # 训练集解压缩
    train_data_list, train_class_list = zip(*train_list)
    # 测试集解压缩
    test_data_list, test_class_list = zip(*test_list)
    # 统计训练集词频
    all_words_dict = {}
    for word_list in train_data_list:
        for word in word_list:
            if word in all_words_dict.keys():
                all_words_dict[word] += 1
            else:
                all_words_dict[word] = 1

    #根据键的值倒序排序
    all_words_tuple_list = sorted(all_words_dict.items(), key = lambda f:f[1], reverse = True)
    # 解压缩
    all_words_list, all_words_nums = zip(*all_words_tuple_list)
    # 将all_words_list转换成列表
    all_words_list = list(all_words_list)
    return all_words_list, train_data_list, test_data_list, train_class_list, test_class_list

if __name__ == '__main__':
    #文本预处理
    # 训练集的地址
    folder_path = './Sample'
    all_words_list, train_data_list, test_data_list, train_class_list, test_class_list = TextProcessing(folder_path, test_size=0.2)
    print(all_words_list)
  • all_words_list就是将所有训练集的切分结果通过词频降序排列构成的单词合集。观察一下打印结果,不难发现,这里包含了很多标点符号,很显然,这些标点符号是不能作为新闻分类的特征的,所以我们应该有一个处理掉这些重复太多的的词
    《Machine Learning in action》- (笔记)之naive Bayes(2_实战篇)_第5张图片

  • 一个简单的规则可以这样制定:首先去掉高频词,至于去掉多少个高频词,我们可以通过观察去掉高频词个数和最终检测准确率的关系来确定。除此之外,去除数字,不把数字作为分类特征。同时,去除一些特定的词语,比如:”的”,”一”,”在”,”不”,”当然”,”怎么”这类的对新闻分类无影响的介词、代词、连词。怎么去除这些词呢?可以使用已经整理好的stopwords_cn.txt文本。下载地址:https://github.com/Tupeng2019/Machine-learning/tree/m-learning/Machine/Machine-Learning/Machine-Learning_note/2_naive Bayes/实战- 新浪新闻分类sklearn

  • 所以我们可以根据上面的文档,将这些高频单词去除,不作为分类的特征。我们先去除前100个高频词汇,然后编写代码如下:

import os
import random
import jieba

"""
函数说明:中文文本处理

Parameters:
    folder_path - 文本存放的路径
    test_size - 测试集占比,默认占所有数据集的百分之20
Returns:
    all_words_list - 按词频降序排序的训练集列表
    train_data_list - 训练集列表
    test_data_list - 测试集列表
    train_class_list - 训练集标签列表
    test_class_list - 测试集标签列表

"""
# 该函数就是对中文文本的处理
def TextProcessing(folder_path, test_size = 0.2):
    # 查看folder_path下的路径的文件
    folder_list = os.listdir(folder_path)
    # 创建空列表放数据集数据
    data_list = []
    # 创建空列表放数据集类型
    class_list = []

    #遍历每个子文件夹
    for folder in folder_list:
        # 根据子文件夹,生成新的路径
        new_folder_path = os.path.join(folder_path, folder)
        # 存放子文件夹下的TXT文件的列表
        files = os.listdir(new_folder_path)
        # 定义j为每一类TXT样本的个数,并初始化为1
        j = 1
        #遍历每个txt文件
        for file in files:

            #每一类样本的个数最多为100个
            if j > 100:
                break
            # 打开txt文件
            with open(os.path.join(new_folder_path, file), 'r', encoding = 'utf-8') as f:
                raw = f.read()
            #  精简模式,返回一个可迭代的generator
            word_cut = jieba.cut(raw, cut_all = False)
            # 将generator装换为list
            word_list = list(word_cut)

            # 添加数据集数据
            data_list.append(word_list)
            # 向列表中添加数据集类型
            class_list.append(folder)
            j += 1
    # #zip压缩合并,将数据与标签对应压缩
    data_class_list = list(zip(data_list, class_list))
    # 将data_class_list乱序
    random.shuffle(data_class_list)
    # 训练集和测试集切分的索引值
    index = int(len(data_class_list) * test_size) + 1
    # 训练集
    train_list = data_class_list[index:]
    # 测试集
    test_list = data_class_list[:index]
    # 训练集解压缩
    train_data_list, train_class_list = zip(*train_list)
    # 测试集解压缩
    test_data_list, test_class_list = zip(*test_list)
    # 统计训练集词频
    all_words_dict = {}
    for word_list in train_data_list:
        for word in word_list:
            if word in all_words_dict.keys():
                all_words_dict[word] += 1
            else:
                all_words_dict[word] = 1

    #根据键的值倒序排序
    all_words_tuple_list = sorted(all_words_dict.items(), key = lambda f:f[1], reverse = True)
    # 解压缩
    all_words_list, all_words_nums = zip(*all_words_tuple_list)
    # 将all_words_list转换成列表
    all_words_list = list(all_words_list)
    return all_words_list, train_data_list, test_data_list, train_class_list, test_class_list

# 读取文件中的内容,并且去重
def MakeWordsSet(words_file):
    # 创建set集合
    words_set = set()
    # 打开文件夹
    with open(words_file, 'r', encoding = 'utf-8') as f:
        # 以行为单位进行一行一行的读取
        for line in f.readlines():
            # 去掉回车,就是读取为一行
            word = line.strip()
            # 如果有文本,就添加到words_set中去
            if len(word) > 0:
                words_set.add(word)
    return words_set

'''
# 文本的特征提取:
# all_words_list就是训练集所有的文本
# deleteN 删除词频最高的单词
stopwords 指定的结束语

'''
def words_dict(all_words_list, deleteN, stopwords_set = set()):
    # 特征列表
    feature_words = []
    n = 1
    for t in range(deleteN, len(all_words_list), 1):
        # feature_words 的维度为1000
        if n > 1000:
            break
        #如果这个词不是数字,并且不是指定的结束语,并且单词长度大于1小于5,那么这个词就可以作为特征词
        if not all_words_list[t].isdigit() and all_words_list[t] not in stopwords_set and 1 < len(all_words_list[t]) < 5:
            feature_words.append(all_words_list[t])
        n += 1
    return feature_words

if __name__ == '__main__':
    #文本预处理
    # 训练集存放地址
    folder_path = './Sample'
    all_words_list, train_data_list, test_data_list, train_class_list, test_class_list = TextProcessing(folder_path, test_size=0.2)

    #生成stopwords_set
    stopwords_file = './stopwords_cn.txt'
    stopwords_set = MakeWordsSet(stopwords_file)

    feature_words = words_dict(all_words_list, 100, stopwords_set)
    print(feature_words)

现在我们的feature_words 就是所最终处理的结果既是用于新闻分类的特征,然后要做的工作就是将其向量化,最后就是训练我们的朴素贝叶斯模型,其过程和前面的一样,这里就是不说了

3.利用sklearn构建朴素贝叶斯模型

  • 下面就是官网的文档:

    • https://scikitlearn.org/dev/modules/generated/sklearn.naive_bayes.MultinomialNB.html
  • 具体的详情就是看看文档

  • 对于新闻分类,属于多分类问题。我们可以使用MultinamialNB()完成我们的新闻分类问题。另外两个函数的使用暂且不再进行扩展,可以自行学习。MultinomialNB假设特征的先验概率为多项式分布,即如下式:
    《Machine Learning in action》- (笔记)之naive Bayes(2_实战篇)_第6张图片
    其中,P(Xj = Xjl | Y = Ck)是第k个类别的第j维特征的第l个取值条件概率。mk是训练集中输出为第k类的样本个数。λ为一个大于0的常数,尝尝取值为1,即拉普拉斯平滑,也可以取其他值

  • 下面就是来看看MultinomialNB函数,是用于多项式模型的朴素贝叶斯分分类器
    在这里插入图片描述

  • 参数说明如下:

    • alpha:浮点型可选参数,默认为1.0,其实就是添加拉普拉斯平滑,即为上述 公式中的λ ,如果这个参数设置为0,就是不添加平滑;
    • fit_prior:布尔型可选参数,默认为True。布尔参数fit_prior表示是否要考虑先验概率,如果是false,则所有的样本类别输出都有相同的类别先验概率。否则可以自己用第三个参数class_prior输入先验概率,或者不输入第三个参数class_prior让MultinomialNB自己从训练集样本来计算先验概率,此时的先验概率为P(Y=Ck)=mk/m。其中m为训练集样本总数量,mk为输出为第k类别的训练集样本数。
    • class_prior:可选参数,默认为None。

此外还有一些方法可供我们使用:

《Machine Learning in action》- (笔记)之naive Bayes(2_实战篇)_第7张图片

  • MultinomialNB一个重要的功能是有partial_fit方法,这个方法的一般用在如果训练集数据量非常大,一次不能全部载入内存的时候。这时我们可以把训练集分成若干等分,重复调用partial_fit来一步步的学习训练集,非常方便。GaussianNB和BernoulliNB也有类似的功能。 在使用MultinomialNB的fit方法或者partial_fit方法拟合数据后,我们可以进行预测。此时预测有三种方法,包括predict,predict_log_proba和predict_proba。predict方法就是我们最常用的预测方法,直接给出测试集的预测类别输出。predict_proba则不同,它会给出测试集样本在各个类别上预测的概率。容易理解,predict_proba预测出的各个类别概率里的最大值对应的类别,也就是predict方法得到类别。predict_log_proba和predict_proba类似,它会给出测试集样本在各个类别上预测的概率的一个对数转化。转化后predict_log_proba预测出的各个类别对数概率里的最大值对应的类别,也就是predict方法得到类别。具体细节不再讲解,可参照官网手册。

  • 通过观察可以去不同的deleteN个高频词个数与最终的分类结果的准确率的关系,来确定deleteN的值:

from sklearn.naive_bayes import MultinomialNB
import matplotlib.pyplot as plt
import os
import random
import jieba

"""
函数说明:中文文本处理

Parameters:
    folder_path - 文本存放的路径
    test_size - 测试集占比,默认占所有数据集的百分之20
Returns:
    all_words_list - 按词频降序排序的训练集列表
    train_data_list - 训练集列表
    test_data_list - 测试集列表
    train_class_list - 训练集标签列表
    test_class_list - 测试集标签列表

"""
# 该函数就是对中文文本的处理
def TextProcessing(folder_path, test_size = 0.2):
    # 查看folder_path下的路径的文件
    folder_list = os.listdir(folder_path)
    # 创建空列表放数据集数据
    data_list = []
    # 创建空列表放数据集类型
    class_list = []

    #遍历每个子文件夹
    for folder in folder_list:
        # 根据子文件夹,生成新的路径
        new_folder_path = os.path.join(folder_path, folder)
        # 存放子文件夹下的TXT文件的列表
        files = os.listdir(new_folder_path)
        # 定义j为每一类TXT样本的个数,并初始化为1
        j = 1
        #遍历每个txt文件
        for file in files:

            #每一类样本的个数最多为100个
            if j > 100:
                break
            # 打开txt文件
            with open(os.path.join(new_folder_path, file), 'r', encoding = 'utf-8') as f:
                raw = f.read()
            #  精简模式,返回一个可迭代的generator
            word_cut = jieba.cut(raw, cut_all = False)
            # 将generator装换为list
            word_list = list(word_cut)

            # 添加数据集数据
            data_list.append(word_list)
            # 向列表中添加数据集类型
            class_list.append(folder)
            j += 1
    # #zip压缩合并,将数据与标签对应压缩
    data_class_list = list(zip(data_list, class_list))
    # 将data_class_list乱序
    random.shuffle(data_class_list)
    # 训练集和测试集切分的索引值
    index = int(len(data_class_list) * test_size) + 1
    # 训练集
    train_list = data_class_list[index:]
    # 测试集
    test_list = data_class_list[:index]
    # 训练集解压缩
    train_data_list, train_class_list = zip(*train_list)
    # 测试集解压缩
    test_data_list, test_class_list = zip(*test_list)
    # 统计训练集词频
    all_words_dict = {}
    for word_list in train_data_list:
        for word in word_list:
            if word in all_words_dict.keys():
                all_words_dict[word] += 1
            else:
                all_words_dict[word] = 1

    #根据键的值倒序排序
    all_words_tuple_list = sorted(all_words_dict.items(), key = lambda f:f[1], reverse = True)
    # 解压缩
    all_words_list, all_words_nums = zip(*all_words_tuple_list)
    # 将all_words_list转换成列表
    all_words_list = list(all_words_list)
    return all_words_list, train_data_list, test_data_list, train_class_list, test_class_list

# 读取文件中的内容,并且去重
def MakeWordsSet(words_file):
    # 创建set集合
    words_set = set()
    # 打开文件夹
    with open(words_file, 'r', encoding = 'utf-8') as f:
        # 以行为单位进行一行一行的读取
        for line in f.readlines():
            # 去掉回车,就是读取为一行
            word = line.strip()
            # 如果有文本,就添加到words_set中去
            if len(word) > 0:
                words_set.add(word)
    return words_set

'''
根据feature_words 将文本向量化
train_data_list 训练集
test_data_list 测试集
feature_words 特征集
-》返回参数
trian_feature_list 训练集向量化列表
test_feature_list 测试集向量化列表
'''
# 向量化:
def TextFeatures(train_data_list, test_data_list, feature_words):
    #
    def text_features(text, feature_words):
        text_words = set(text)
        # 出现在特征集中,则置1
        features = [1 if word in text_words else 0 for word in feature_words]
        return features
    train_feature_list = [text_features(text, feature_words) for text in train_data_list]
    test_feature_list = [text_features(text, feature_words) for text in test_data_list]
    return train_feature_list, test_feature_list


'''
# 文本的特征提取:
# all_words_list就是训练集所有的文本
# deleteN 删除词频最高的单词
stopwords 指定的结束语

'''
def words_dict(all_words_list, deleteN, stopwords_set = set()):
    # 特征列表
    feature_words = []
    n = 1
    for t in range(deleteN, len(all_words_list), 1):
        # feature_words 的维度为1000
        if n > 1000:
            break
        #如果这个词不是数字,并且不是指定的结束语,并且单词长度大于1小于5,那么这个词就可以作为特征词
        if not all_words_list[t].isdigit() and all_words_list[t] not in stopwords_set and 1 < len(all_words_list[t]) < 5:
            feature_words.append(all_words_list[t])
        n += 1
    return feature_words

'''
构建新闻分类器TextClassifier
    train_feature_list - 训练集向量化的特征文本
    test_feature_list - 测试集向量化的特征文本
    train_class_list - 训练集分类标签
    test_class_list - 测试集分类标签
'''
def TextClassifier(train_feature_list, test_feature_list, train_class_list, test_class_list):
    classifier = MultinomialNB().fit(train_feature_list, train_class_list)
    test_accuracy = classifier.score(test_feature_list, test_class_list)
    return test_accuracy

if __name__ == '__main__':
    #文本预处理
    folder_path = './Sample'                #训练集存放地址
    all_words_list, train_data_list, test_data_list, train_class_list, test_class_list = TextProcessing(folder_path, test_size=0.2)

    #生成stopwords_set
    stopwords_file = './stopwords_cn.txt'
    stopwords_set = MakeWordsSet(stopwords_file)

    # 建立test_accuracy_list 空列表
    test_accuracy_list = []
    # 建立一个列表是从1000以内,并且是间隔为20
    # 0 20 40 60 ... 980
    deleteNs = range(0, 1000, 20)
    for deleteN in deleteNs:
        feature_words = words_dict(all_words_list, deleteN, stopwords_set)
        train_feature_list, test_feature_list = TextFeatures(train_data_list, test_data_list, feature_words)
        test_accuracy = TextClassifier(train_feature_list, test_feature_list, train_class_list, test_class_list)
        test_accuracy_list.append(test_accuracy)

    plt.figure()
    plt.plot(deleteNs, test_accuracy_list)
    plt.title('Relationship of deleteNs and test_accuracy')
    plt.xlabel('deleteNs')
    plt.ylabel('test_accuracy')
    plt.show()
  • 通过结果来观察,我们所需要的deleteN的值,最好的取值是
    《Machine Learning in action》- (笔记)之naive Bayes(2_实战篇)_第8张图片
  • 我们绘制出了deleteNs和test_accuracy的关系,这样我们就可以大致确定去掉前多少的高频词汇了。每次运行程序,绘制的图形可能不尽相同,我们可以通过多次测试,来决定这个deleteN的取值,然后确定这个参数,这样就可以顺利构建出用于新闻分类的朴素贝叶斯分类器了。
  • 我这里测试了多次,感觉500的时候就不错了,所以这里的deleteN就是去500
    修改我们的主函数代码,就好了
if __name__ == '__main__':
    #文本预处理
    # 训练集存放地址
    folder_path = './Sample'
    all_words_list, train_data_list, test_data_list, train_class_list, test_class_list = TextProcessing(folder_path, test_size=0.2)

    # 生成stopwords_set
    stopwords_file = './stopwords_cn.txt'
    stopwords_set = MakeWordsSet(stopwords_file)

    test_accuracy_list = []
    feature_words = words_dict(all_words_list, 500, stopwords_set)
    train_feature_list, test_feature_list = TextFeatures(train_data_list, test_data_list, feature_words)
    test_accuracy = TextClassifier(train_feature_list, test_feature_list, train_class_list, test_class_list)
    test_accuracy_list.append(test_accuracy)
    ave = lambda c: sum(c) / len(c)
    print(ave(test_accuracy_list))

  • 看出来的结果
    《Machine Learning in action》- (笔记)之naive Bayes(2_实战篇)_第9张图片

所有的代码都可以在我的GitHub上面下载:

  • https://github.com/Tupeng2019/Machine-learning/tree/m-learning/Machine/Machine-Learning/Machine-Learning_note/2_naive Bayes

你可能感兴趣的:(Machine,Learning,人工智能,机器学习,朴素贝叶斯)