机器学习实战——朴素贝叶斯中文情感分类模型

简介:

考研复试顺利结束,目前正忙于毕业设计——《网络文本数据的爬取与数据分析》,如题,概括的说包括两项任务,一个是爬虫与数据采集,另外一个是进行数据分析。目前爬虫工作全部完成,爬取的内容是豆瓣的电影评论(23000条),后文我会提供数据下载链接。而对于后者数据的分析,需要做的主要工作是对于评价文本进行情感的分类。也就是积极或者消极。对于其初步实现,初步计划是使用朴素贝叶斯、svm、cnn三种方法来实现。进过两天的调整,朴素贝叶斯情感分类模型初步实现。

朴素贝叶斯情感分类模型的实现思路

对于朴素贝叶斯这种有监督的机器学习算法来说,我们实现的思路主要包括:

  1. 数据标签标注
  2. 中文分词处理
  3. 去除停用词
  4. 词条向量化
  5. 统计特征数,尽可能的减少特征数,达到数据清洗的效果
  6. 编写算法,训练数据
  7. 调整参数,达到最优效果

1. 数据标签标注

这个操作处理需要根据自己的数据进行相应调整,以我的数据集为例,我爬取的数据集是豆瓣电影下的1 2 4 5分电影短评,这么做的原因有:

  1. 短评较长评来讲,根据情感倾向,灌水评论也更多一些,对于训练更有帮助。除此之外,分词后的特征维度更少,这样也有利于降低过多特征对于实验结果的影响。
  2. 1 2 4 5 以3为分界线,恰好分别置为消极、积极情感的评论,这样就可以通过写程序将1 2 分的评论打上0的标签,表示消极。另一方面,4 5 置为1,表示积极。

2.中文分词处理

这块借助工具,结巴(文档有更详细的使用方式)进行分词。分词效果如下:
豆瓣影评分词

3. 去除停用词

这里操作的实质就是查找,剔除。不在赘述。停用词地址

4.词条向量化

向量化的操作实质就是将分词后的句子,按照其单词在总词汇表中是否出现标记为0或1 [0:没出现|1:出现]
那么,一条分好词的句子就变成了[0,1,0,1,1…]的一维矩阵。所以在向量化之前我们要做的便是生成总词汇表,思路也很简单,无非就是把所有的词去重后,全部放在一个list中,那么此时词条向量化后的长度也就确定下来,即总词汇表的长度。

5.统计特征数

什么是特征数呢?其实在4中已经提到。就是总词汇表的长度,也就是总词汇表的单词数。那么这里我将借助Jupyter notebook、sklearn演示特征数,以及特征数将维的思路。

  1. 首先我们读入csv文件,看一下总共有多少条数据:
    在这里插入图片描述没错,总共23644条评价数据,那么特征数即去重后的词语有多少呢,MemoryError!当我使用sklearn进行特征数统计时,jupyter直接报错了。4G的内存,可想而知特征数实在太多了,在笔记本硬件条件有限的情况下只能选择降低维度。

  2. 降低维度,如果还是使用sktlearn来做的话,非常方便,如下图
    在这里插入图片描述可以看出,维度已经下降到11825,比评论总数都要少。这下我的电脑终于可以跑了。

  3. 降维思路:
    这里我采用的是非常常见的做法。就是根据词汇的出现频率来做判断,那么对于频占比超过0.8的词汇,我们认为它太平凡,将其删除。对于频率少于5的词汇,我们认为它太特殊,也将其删除。经过这样的处理,可以大大降低维度,从而提高准确率

小小总结:此处为了演示方便,我选择使用工具降维,不过对于初学者来说,我感觉还是需要自己手动编写程序来实现朴素贝叶斯和其中的降低维度处理。这样,可以加深我们对于机器学习基本算法的理解。并且这种算法网上很多,拿来自己看懂,敲几遍,总之多多折腾,收获满满。等我们对这些算法有了自己的理解后,使用工具时便更加得心应手。文末我会附上所有代码。

6.编写算法 ,训练数据

'''
对豆瓣影评进行情感分析,训练集共1000条数据
'''

import numpy as np
import mmap as mp

def loadFile(filename):
    """
    1.函数说明;加载数据集 包括训练集 测试集
    2.filename:文件路径
    3.return:文件返回词条 邮件的类别分类
    """
    # 分词列表 评论分类列表
    contentList = []
    classVec = []
    file = open(filename)
    # 读取文件中的所有行
    contents = file.readlines()
    
    for line in contents:
        content = line.strip('\n').split(' ')
        print(line)
        classVec.append(int(content[0]))
        del(content[0])
        while '' in content:
            content.remove('')
        contentList.append(content)
    file.close()
    # print(contentList)
    # print(classVec)
    print("已经将分词后的影评,转换成了词汇列表和对应的词向量")
    return contentList,classVec 

def createVocabList(dataSet):
    """
    1.函数说明: 生成词汇表 并去除重复的词汇
    2.dataset: 通过loadFile函数生成的列表型评论词汇数据
    3.返回不重复的评论词汇
    """
    # 使用set的数据结构 去除重复的词汇
    vocabList = set([])
    for doc in dataSet :
        vocabList = vocabList | set(doc) 
    # 以列表的形式返回 不重复词汇的集合
    return list(vocabList)


def Words_to_vec(vocabList,wordSet):
    """
    1.函数说明:根据vocabList词汇表 将每个评价分词后在进行向量化 即出现为1 不出现为0
    2.vocablit: 词汇表
    3.wordSet: 生成的词向量
    return:返回的词向量
    """
    
    returnVec = [0]*len(vocabList)

    for word in wordSet:
        if word in vocabList:
            # 如果在词汇表中的话 便将其所在位置赋为1
            returnVec[vocabList.index(word)] = 1
        else:
            pass
    return returnVec

def trainNB(trainMat,trainLabel):
    """
    1.函数说明:朴素贝叶斯训练函数
    2.trainMat:训练文档的词向量矩阵ss
    3.trainLable:训练数据的类别标签
    4.return:
        p0vec:消极情感的评论
        p1vec:积极情感的评论
        p_positive:积极评论的概率
        p_negative:消极评论的概率
    """
    # 训练集的数量
    numTraindocs = len(trainMat)
    # 单词数 ???? 不理解
    numWords = len(trainMat[0])
    print("numwords%s"%numWords)
    # 积极情感类评论数量及概率
    p_positive = sum(trainLabel) /float(numTraindocs)
    p_negative = 1-p_positive

    p_positiveNum = np.ones(numWords)
    p_negativeNum = np.ones(numWords)

    p_positiveDemo = 2.0
    p_negativeDemo = 2.0

    for i in range(numTraindocs):
        if trainLabel[i] == 1:
            # 如果是积极情感的话,则统计积极情感评论的个数
            p_positiveNum += trainMat[i]
            p_positiveDemo += sum(trainMat[i])
        else:
            # 统计消极情感的评论
            p_negativeNum += trainMat[i]
            p_negativeDemo += sum(trainMat[i])

    print(p_negativeNum)
    print(p_positiveNum)

    p1vec = np.log(p_positiveNum/p_positiveDemo)
    p0vec = np.log(p_negativeNum/p_negativeDemo)

    return p1vec,p0vec,p_positive


def classifyNB(vec2Classify,p0vec,p1vec,p_positive):
    """
    1.函数说明:分类 比较p0 和 p1的大小 并返回相应的预测类别
    2.vec2Classify:返回的词汇表对应的词向量
    3.p0vec 消极的条件概率
    4.p1vec 积极的条件概率
    5.p_positive 积极情感的类别概率
    return:
        1:表示积极情感
        0:表示消极情感
    """
    p_positive1 = sum(vec2Classify*p1vec) + np.log(p_positive)
    p_negative0 = sum(vec2Classify*p0vec) + np.log(1-p_positive)

    if p_positive1 > p_negative0:
        return 1
    else:
        return 0


def main():
    trainList,trainLable = loadFile('影评训练.txt')
    # print(trainList)
    vocabList =createVocabList(trainList)
    print(vocabList)
    trainMat = []
    cnt = 0
     
    for train in trainList:
        trainMat.append(Words_to_vec(vocabList,train))
        cnt+=1
        # print("当前正在处理%s组训练数据"%cnt)
  
    print("训练集数据处理完毕")
    p1vec,p0vec,p_positive = trainNB(np.array(trainMat,dtype='float16'),np.array(trainLable,dtype='float16'))
    print("生成训练集指标")
    print(p1vec)
    print(p0vec)
    print(p_positive)
    # 加载测试集数据进行测试
    # testList ,testLable = loadFile('影评测试.txt')
    testList ,testLable = loadFile('新测试.txt')
    resultMat = []
    nn = 0
    for test in testList:
        doc = np.array(Words_to_vec(vocabList,test))

        if classifyNB(doc,p0vec,p1vec,p_positive):
            print("积极")
            resultMat.append(1)
        else:
            resultMat.append(0)
            print("消极")
        nn+=1
        # print("正在处理第%s条数据"%nn)
    cc = 0
    for i  in range(len(testLable)):
        if testLable[i] == resultMat[i]:
            cc+=1
    print("准确率为:"+str(100*cc/float(len(testLable)))+"%")


if  __name__ == '__main__':
    main()

7. 调参优化

目前我正在学习相关知识,来做这个处理。

8. 实验结果

我使用了8000条左右的训练集,2000条左右的测试集,进行测试,得到了87.449%的准确率
在这里插入图片描述

总结:

  1. 要通过写代码来理解机器学习算法,多动手,多实践
  2. 要提前规划自己的代码逻辑,提高编写效率

鸣谢:

机器学习大佬Jack_cui

猜你喜欢

转载自blog.csdn.net/qq_36251958/article/details/88963654