机器学习:朴素贝叶斯算法+中文情感分类+python

朴素贝叶斯中文情感分类

1、写在前面

朴素贝叶斯算法理论在很多博客上已经解释的很详细了,本文就不再叙述,本文注重于算法的应用以及编程实现,在读取前人的博客以及他们的项目应用,本人结合书本《机器学习算法原理与编程实践_郑捷》中的贝叶斯算法介绍,将其运用于中文情感分类中,书本中的代码运用了矢量编程,代码块简练易懂,这样也能提高对算法的理解。

本人对贝叶斯分类的理解,简单的概括就是:要想由什么特征属性来判定属于哪种分类,即求,可以由在这种分类中具有这些特征属性的概率求得,而可以通过训练集得到。算法针对中文文本分析,情感分类区别在于特征属性的选择,前者是将所有文本内容的分词去除重复后,得到词典,后者是将情感词作为特征属性,情感词由各大学教授,权威人士在各自研究领域内公布了各自的情感分词库,免费公开(真诚感谢)。中文文本分析的编程实现已在上面提到的书本内完成,因它采取的是Scikit-Learn模块的朴素贝叶斯分类模块,可以进Scikit-Learn官方网站下载朴素贝叶斯分类模块源码学习,也可以自己编写贝叶斯算法类,本人觉得自己编写贝叶斯算法类有利于对算法的理解,在理解后,再采取Scikit-Learn模块进行相应的项目开发。故本人将书本中编写的贝叶斯算法类运用在中文情感分类中,增加对算法的理解。

2、项目实践

本人实现的中文情感分类过程,分为两个部分:(1)训练集数据的准备过程,(2)文本情感分类;

 

 

训练集数据的准备过程分为:

(1)文本下载:文本下载的方式有很多,其中高效的方法就是从文本网站上下载各个文章,下载可采取网络爬虫方法,可采取python+scrapy框架的方法对文本网页进行文章自动爬取(链接:https://pan.baidu.com/s/1EJ2QLjw_bEUza5JG9jDSLQ 密码:czuo),共下载文本(497)然后对文本进行分类,语料包括:积极文本(150),消极文本(266),其他的因情感倾向不明显,就不加入训练集。

(2)中文分词,本人将(1)中的文本采取jieba模块进行中文分词(代码在后面贴上)

(3)下载情感词典,链接: http://pan.baidu.com/s/1u12m0 ,来自台湾大学教授免费公布(再次感谢)

将爬虫到的xml文本进行解析以及中文分词,得到语料结构为list[[文本1],[文本2],[文本3]...[文本n]],代码如下:

#xml文件解析
# -*- coding: utf-8 -*-
import sys
import os
import jieba
from lxml import etree

class GetCorpus(object):
    def savefile(self,path,content):
        fp=open(path,"w")
        jiebacontent=[]
        for word in content.split():
            if (word !='[' and word!=']' and word!="'" and word!="'"):
                jiebacontent.append(word)
        fp.write(str(jiebacontent))
        fp.close()

    def readfile(self,path):
        fp=open(path,"rb")
        content=fp.read()
        fp.close()
        return content

    #获取未分词语料
    #list xml文件路径,parse_list语料路径
    #xml文件内容
    def wordparse(self,html):
        page_source = etree.HTML(html)
        title = page_source.xpath("//tok[@type='group']/tok[@type='atom']/text()")
        words=[]
        for word in title:
            #str(word).replace()
            words.append(word)
        wordsString=[]
        string=""
        for word in words:
            string+=word
        string = string.replace("\n", "")
        string = string.replace("\t", "")
        wordsString.append(string)
        return wordsString
#中文文本解析
 
import sys
import os
import jieba
from lxml import etree
from xmlParse import *
import pickle
#dunp 写入
def writedumpobj(path,obj):
    file=open(path,"wb")
    pickle.dump(obj,file)
    file.close()

#dump 读取
def readdumpobj(path):
    file=open(path,"rb")
    bunch=pickle.load(file)
    file.close()
    return bunch

def _jieba():
    postingList=[]
    os.chdir('D:/linguistic-corpus/')
    xml_path=r"500trainblogxml/"
    #parse_path=r"train_corpus_small/"#parse xml语句解析目录
    parse_path = r"train_corpus_seg/"
    catelist=os.listdir(xml_path)#xml文件目录路径
    cr=GetCorpus()#导入xml文件解析的类
    classVec=[]
    i=0
    for mydir in catelist:
        i += 1
        class_path=xml_path+mydir+"/"# 分类子目录路径
        seg_dir=parse_path+mydir+"/"
        if not os.path.exists(seg_dir):
            os.makedirs(seg_dir)
        file_list=os.listdir(class_path)
        for file_path in file_list:
            if i==1:
                classVec.append(1)#建立类别标签
            if i==2:
                classVec.append(0)#建立类别标签
            fullname=class_path+file_path
            content=cr.readfile(fullname)
            contentpurse=cr.wordparse(content.decode("utf-8"))
            seg_list=jieba.cut(str(contentpurse))
            replase_seg_list=" ".join(seg_list).replace("[ '","").replace("' ]","")#删除一些无用字符串
            split_seg_list = replase_seg_list.split()
            postingList.append(split_seg_list)
    writedumpobj("D:\linguistic-corpus\postingList\postingList.dat",postingList)  # 写入分词,持久化保存
    writedumpobj("D:\linguistic-corpus\postingList\classVec.dat",classVec)#写入分类
    print(i)
    return postingList

if __name__=="__main__":
    postingList=_jieba()
 
 
 

训练集准备就绪,以下进行文本分类

import pickle
import numpy as np

def readdumpobj(path):
    file=open(path,"rb")
    bunch=pickle.load(file)
    file.close()
    return bunch

def outemotionword(path):
    emotionset=[]
    with open(path,"rb") as fp:
        for word in fp:
            if not word.isspace():
                word=word.decode("utf-8")
                emotionset.append(word.strip())
    return emotionset

def loadDataSet(path):#path是为了读入将情感词典
    postingList=readdumpobj("D:\linguistic-corpus\postingList\postingList.dat")
    classVec=readdumpobj("D:\linguistic-corpus\postingList\classVec.dat")
    emotionset=outemotionword(path)
    return postingList, classVec,emotionset

class NBayes(object):
    def __init__(self):
        self.vocabulary = [] # 词典,文本set表
        self.idf=0           # 词典的idf权值向量
        self.tf=0            # 训练集的权值矩阵
        self.tdm=0           # P(x|yi)
        self.Pcates = {}     # P(yi)--是个类别字典
        self.labels=[]       # 对应每个文本的分类,是个外部导入的列表[0,1,0,1,0,1]
        self.doclength = 0   # 训练集文本数,训练文本长度
        self.vocablen = 0    # 词典词长,self.vocabulary长度
        self.testset = 0     # 测试集
    #   加载训练集并生成词典,以及tf, idf值
    def train_set(self,trainset,classVec,emotionset):
            self.cate_prob(classVec)   # 计算每个分类在数据集中的概率:P(yi)
            self.doclength = len(trainset)
            tempset = set()
            [tempset.add(word) for word in emotionset ] # 生成词典
            self.vocabulary = list(tempset)
            self.vocablen = len(self.vocabulary)
            #self.calc_wordfreq(trainset)
            self.calc_tfidf(trainset)  # 生成tf-idf权值
            self.build_tdm()           # 按分类累计向量空间的每维值:P(x|yi)

    # 生成 tf-idf
    def calc_tfidf(self,trainset):
        self.idf = np.zeros([1,self.vocablen])
        self.tf = np.zeros([self.doclength,self.vocablen])
        for indx in range(self.doclength):
            for word in trainset[indx]:
                if word in self.vocabulary:
                    self.tf[indx,self.vocabulary.index(word)] +=1
            # 消除不同句长导致的偏差
            self.tf[indx] = self.tf[indx]/float(len(trainset[indx]))
            for signleword in set(trainset[indx]):
                if signleword in self.vocabulary:
                    self.idf[0,self.vocabulary.index(signleword)] +=1
        self.idf = np.log(float(self.doclength)/(self.idf+1))#防止该词语不在语料中,就会导致分母为零
        self.tf = np.multiply(self.tf,self.idf) # 矩阵与向量的点乘

   # 生成普通的词频向量
    def calc_wordfreq(self,trainset):
        self.idf = np.zeros([1,self.vocablen]) # 1*词典数
        self.tf = np.zeros([self.doclength,self.vocablen]) # 训练集文件数*词典数
        for indx in range(self.doclength):    # 遍历所有的文本
            for word in trainset[indx]:          # 遍历文本中的每个词
                if word in self.vocabulary:
                    self.tf[indx,self.vocabulary.index(word)] +=1  # 找到文本的词在字典中的位置+1
            for signleword in set(trainset[indx]):
                if signleword in self.vocabulary:
                    self.idf[0,self.vocabulary.index(signleword)] +=1

    # 计算每个分类在数据集中的概率:P(yi)
    def cate_prob(self,classVec):
        self.labels = classVec
        labeltemps = set(self.labels) # 获取全部分类
        for labeltemp in labeltemps:
            # 统计列表中重复的值:self.labels.count(labeltemp)
            self.Pcates[labeltemp] = float(self.labels.count(labeltemp))/float(len(self.labels))

    #按分类累计向量空间的每维值:P(x|yi)
    def build_tdm(self):
        self.tdm = np.zeros([len(self.Pcates),self.vocablen]) #类别行*词典列
        sumlist = np.zeros([len(self.Pcates),1])  # 统计每个分类的总值
        for indx in range(self.doclength):
                self.tdm[self.labels[indx]] += self.tf[indx]  # 将同一类别的词向量空间值加总
                sumlist[self.labels[indx]]= np.sum(self.tdm[self.labels[indx]])  # 统计每个分类的总值--是个标量
        self.tdm = self.tdm/sumlist   # P(x|yi)

    # 测试集映射到当前词典
    def map2vocab(self,testdata):
        self.testset = np.zeros([1,self.vocablen])
        #删除测试集中词不在训练集中
        for word in testdata:
            if word  in self.vocabulary:
                self.testset[0,self.vocabulary.index(word)] +=1

    # 输出分类类别
    def predict(self,testset):
        if np.shape(testset)[1] != self.vocablen:
            print ("输入错误")
            exit(0)
        predvalue = 0
        predclass = ""
        for tdm_vect,keyclass in zip(self.tdm,self.Pcates):
            # P(x|yi)P(yi)
            temp = np.sum(testset*tdm_vect*self.Pcates[keyclass])
            if temp > predvalue:
                predvalue = temp
                predclass = keyclass
        return predclass

if __name__=="__main__":
    postingList,classVec,emotionset=loadDataSet("D:\sentiment-word\emotionword.txt")
    testset=postingList[119]
    nb = NBayes()  # 类的实例化
    nb.train_set(postingList,classVec,emotionset)  # 训练数据集
    nb.map2vocab(testset)  # 随机选择一个测试句,这里2表示文本中的第三句话,不是脏话,应输出0。
    print(nb.predict(nb.testset))  # 输出分类结果0表示消极,1表示积极
    print("分类结束")


结合代码,文本情感分类这一部分是算法核心,具体解析过程如下:

重点:文本情感分类有两种加权方法,一种是词频加权,一种是TF-IDF策略,这里重点分析下tf-idf策略以及怎么通过训练集求出,即代码中 def calc_tfidf(self,trainset),def cate_prob(self,classVec)函数的解析

首先了解下如下几个数据结构:

 

a、idf矩阵:

 n为情感词典词的总数,元素表示情感词编号m在所有文本中出现的次数,注意:如果在一文本中情感词 m 出现了多次,也只算一次。接着将上面的idf数组转化为逆向文件频率。

self.idf = np.log(float(self.doclength)/(self.idf+1))#防止该词语不在语料中,就会导致分母为零。

 

 

b、tf矩阵:

 

其中,n表示情感词典词的总数,m表示所有文本个数,即创建了一个二维矩阵,矩阵元素表示在文本标号m中,出现标号为n的情感词的词频,接着再将tf消除不同句长导致的偏差

self.tf[indx] = self.tf[indx]/float(len(trainset[indx]))

即将矩阵元素除以标号为m文本的所有单词数

这样tf,idf数组向量求出后再相乘,就得到了TF-IDF权重,将权重再赋予tf矩阵

不得不说,这个策略对文本分类的重要性。

 

c、tdm矩阵

既然权重得到了,接下来就是求,即

这个概率,本人在捉摸多次后理解如下:

首先,建立tdm矩阵,矩阵行为分类数,在本项目中,行数就为2,列为情感词典个数,为11086,而将tf中属于同一类的行相加,即得到tdm矩阵。

其次,建立sunlist矩阵,行数为分类数,列数为1,其元素是一标量,将tdm的属于同一类的行数元素累加,比如具体点的就是将tdm中是积极类的行数累加,

那么tdm=tdm/sumlista就是,即tdm最终保存的是在一分类中具有这些特征属性的概率。

 

得到tdm矩阵后,就可以对测试文本进行分类了,首先,必须要将测试文本进行分词,本代码是输入的是直接分词好的。然后将测试集映射到当前词典,得到测试向量testset即情感词典,就是求在测试文本中,各个情感词出现的频数,然后将testset与tdm矩阵相乘,将结果最大的值对应的分类作为这个测试文本的分类。

 

 

3、写在后面

一起学习,真诚感谢!

 

  • 4
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
### 回答1: 你可以使用Python中的scikit-learn库来实现朴素贝叶斯分类器。以下是一个简单的示例代码: ```python from sklearn.naive_bayes import GaussianNB import numpy as np # 准备数据 X = np.array([[1, 2], [3, 4], [5, 6], [7, 8]]) y = np.array([0, 0, 1, 1]) # 创建分类器对象 clf = GaussianNB() # 训练分类器 clf.fit(X, y) # 预测新数据 print(clf.predict([[1, 1], [4, 5]])) ``` 在这个例子中,我们准备了一个包含4个样本的数据集X和对应的标签y。然后我们创建了一个GaussianNB对象作为分类器,用fit()方法对其进行训练,并用predict()方法对新数据进行分类预测。 ### 回答2: 朴素贝叶斯分类器是一种常用的机器学习算法,用于进行分类任务。它基于贝叶斯公式和特征之间的独立性假设,可用于文本分类、垃圾邮件过滤、情感分析等应用。 Python中有多个库可以实现朴素贝叶斯分类器,如scikit-learn。下面以该库为例,简要介绍如何使用朴素贝叶斯分类器进行分类。 首先,导入需要的库: ```python from sklearn.naive_bayes import MultinomialNB from sklearn.feature_extraction.text import CountVectorizer ``` 然后,准备训练数据和测试数据。训练数据是一组已经标记好的数据,每个数据包含一条文本和对应的类别。测试数据是用来评估分类器性能的数据。 接下来,对文本数据进行向量化处理。使用CountVectorizer将文本转换为特征向量,以便分类器能够处理。比如,将每个文本表示为词频向量。 ```python vectorizer = CountVectorizer() train_X = vectorizer.fit_transform(train_texts) test_X = vectorizer.transform(test_texts) ``` 然后,创建朴素贝叶斯分类器对象,并用训练数据拟合分类器模型。 ```python classifier = MultinomialNB() classifier.fit(train_X, train_labels) ``` 最后,使用训练好的分类器对测试数据进行分类预测。 ```python predictions = classifier.predict(test_X) ``` 以上就是使用Python中的朴素贝叶斯分类器进行分类的简要流程。通过调整特征提取方式、调整模型参数等,我们可以改进分类器的性能。同时,学习更多关于朴素贝叶斯分类器的理论和实践知识也有助于更好地使用该算法。 ### 回答3: 朴素贝叶斯分类器是一种基于概率论和贝叶斯定理的分类算法。它的名字中的“朴素”是因为它假设特征之间相互独立,即每个特征对分类结果的影响是相互独立的。这种假设简化了模型的计算过程,使朴素贝叶斯分类器能够高效地进行分类。 在Python中,可以使用scikit-learn库中的朴素贝叶斯分类器来实现。具体步骤如下: 1. 导入所需库: ```python from sklearn.datasets import load_iris from sklearn.naive_bayes import GaussianNB from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score ``` 2. 加载数据集: ```python iris = load_iris() X = iris.data # 特征数据 y = iris.target # 分类结果 ``` 3. 划分训练集和测试集: ```python X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42) ``` 4. 创建朴素贝叶斯分类器对象: ```python nb_classifier = GaussianNB() ``` 5. 拟合数据并进行预测: ```python nb_classifier.fit(X_train, y_train) y_pred = nb_classifier.predict(X_test) ``` 6. 计算分类准确率: ```python accuracy = accuracy_score(y_test, y_pred) ``` 朴素贝叶斯分类器在处理文本分类、垃圾邮件过滤等任务上表现良好。虽然它假设特征之间相互独立,但实际应用中还是能取得不错的效果。当特征之间的依赖性较强时,可以使用其他更复杂的分类算法来取代朴素贝叶斯分类器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值