失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 中文情感分析——snownlp类库 源码注释及使用

中文情感分析——snownlp类库 源码注释及使用

时间:2020-04-17 16:53:53

相关推荐

中文情感分析——snownlp类库 源码注释及使用

最近发现了snownlp这个库,这个类库是专门针对中文文本进行文本挖掘的。

主要功能:

中文分词(Character-Based Generative Model)词性标注(TnT3-gram 隐马)情感分析(现在训练数据主要是买卖东西时的评价,所以对其他的一些可能效果不是很好,待解决)文本分类(Naive Bayes)转换成拼音(Trie树实现的最大匹配)繁体转简体(Trie树实现的最大匹配)提取文本关键词(TextRank算法)提取文本摘要(TextRank算法)tf,idfTokenization(分割成句子)文本相似(BM25)支持python3(感谢erning)

官网信息:

snownlp github:/isnowfy/snownlp

使用及源码分析:

snownlp类库的安装:

$ pip install snownlp

使用snownlp进行情感分析:

# -*- coding:utf-8 -*-from snownlp import SnowNLP#创建snownlp对象,设置要测试的语句s = SnowNLP(u'买来给家婆用来洗儿子的衣服的')print("1",s.words) #将句子分成单词# ['买', '来', '给', '家婆', '用', '来', '洗', '儿子', '的', '衣服', '的']s.tags # 例如:[(u'这个', u'r'), (u'东西', u'n'),# (u'真心', u'd'), (u'很', u'd'),# (u'赞', u'Vg')]# 调用sentiments方法获取积极情感概率 positive的概率print("2",s.sentiments)s.pinyin # 将汉字语句转换为Pinyin语句# 例如:[u'zhe', u'ge', u'dong', u'xi',# u'zhen', u'xin', u'hen', u'zan']#————————————————————————————————————————————————————————————————————————————————————————————————————————s = SnowNLP(u'「繁體字」「繁體中文」的叫法在臺灣亦很常見。')s.han #将繁体字转换为简体字# u'「繁体字」「繁体中文」的叫法# 在台湾亦很常见。'#————————————————————————————————————————————————————————————————————————————————————————————————————————text = u'''自然语言处理是计算机科学领域与人工智能领域中的一个重要方向。它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法。自然语言处理是一门融语言学、计算机科学、数学于一体的科学。因此,这一领域的研究将涉及自然语言,即人们日常使用的语言,所以它与语言学的研究有着密切的联系,但又有重要的区别。自然语言处理并不是一般地研究自然语言,而在于研制能有效地实现自然语言通信的计算机系统,特别是其中的软件系统。因而它是计算机科学的一部分。'''s = SnowNLP(text)s.keywords(3) # [u'语言', u'自然', u'计算机']s.summary(3) # [u'因而它是计算机科学的一部分',# u'自然语言处理是一门融语言学、计算机科学、#数学于一体的科学',# u'自然语言处理是计算机科学领域与人工智能#领域中的一个重要方向']s.sentences#分成句子#————————————————————————————————————————————————————————————————————————————————————————————————————————s = SnowNLP([[u'这篇', u'文章'],[u'那篇', u'论文'],[u'这个']])print(s.tf)#TF意思是词频(Term Frequency)print(s.idf) #IDF意思是逆文本频率指数(Inverse Document Frequency) s.sim([u'文章'])# [0.3756070762985226, 0, 0]

实现过程:

1.首先从SnowNLP入手,看一下sentiments方法,在sentiments方法中,调用了sentiment下的分类方法。

# -*- coding: utf-8 -*-from __future__ import unicode_literalsfrom . import normalfrom . import segfrom . import tagfrom . import sentimentfrom .sim import bm25from .summary import textrankfrom .summary import words_mergeclass SnowNLP(object):def __init__(self, doc):self.doc = docself.bm25 = bm25.BM25(doc)@propertydef words(self):return seg.seg(self.doc)@propertydef sentences(self):return normal.get_sentences(self.doc)@propertydef han(self):return normal.zh2hans(self.doc)@propertydef pinyin(self):return normal.get_pinyin(self.doc)@propertydef sentiments(self):return sentiment.classify(self.doc)#调用了sentiment的classify分类方法 @propertydef tags(self):words = self.wordstags = tag.tag(words)return zip(words, tags)@propertydef tf(self):return self.bm25.f@propertydef idf(self):return self.bm25.idfdef sim(self, doc):return self.bm25.simall(doc)def summary(self, limit=5):doc = []sents = self.sentencesfor sent in sents:words = seg.seg(sent)words = normal.filter_stop(words)doc.append(words)rank = textrank.TextRank(doc)rank.solve()ret = []for index in rank.top_index(limit):ret.append(sents[index])return retdef keywords(self, limit=5, merge=False):doc = []sents = self.sentencesfor sent in sents:words = seg.seg(sent)words = normal.filter_stop(words)doc.append(words)rank = textrank.KeywordTextRank(doc)rank.solve()ret = []for w in rank.top_index(limit):ret.append(w)if merge:wm = words_merge.SimpleMerge(self.doc, ret)return wm.merge()return ret

2.sentiment文件夹下的__init__文件

sentiment中创建了Sentiment对象

首先调用load方法加载训练好的数据字典,然后调用classify方法,在classify方法中实际调用的是Bayes对象中的classify方法。

# -*- coding: utf-8 -*-from __future__ import unicode_literalsimport osimport codecsfrom .. import normalfrom .. import segfrom ..classification.bayes import Bayes#数据文件路径data_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),'sentiment.marshal')class Sentiment(object):def __init__(self):#创建Bayes对象self.classifier = Bayes()#保存训练好的字典数据def save(self, fname, iszip=True):self.classifier.save(fname, iszip)#加载字典数据def load(self, fname=data_path, iszip=True):self.classifier.load(fname, iszip)#对文档分词def handle(self, doc):words = seg.seg(doc)words = normal.filter_stop(words)return words# 训练数据集def train(self, neg_docs, pos_docs):data = []#读取消极评论list,同时为每条评论加上neg标签,也放入到一个list中for sent in neg_docs:data.append([self.handle(sent), 'neg'])#读取积极评论list,为每条评论加上pos标签for sent in pos_docs:data.append([self.handle(sent), 'pos'])#调用分类器的训练数据集方法,对模型进行训练 self.classifier.train(data)#分类def classify(self, sent):#调用贝叶斯分类器的分类方法,获取分类标签和概率ret, prob = self.classifier.classify(self.handle(sent))#如果分类标签是pos直接返回概率值if ret == 'pos':return prob#如果返回的是neg,由于显示的是积极概率值,因此用1减去消极概率值return 1-probclassifier = Sentiment()classifier.load()#训练数据def train(neg_file, pos_file):#打开消极数据文件neg = codecs.open(neg_file, 'r', 'utf-8').readlines()pos = codecs.open(pos_file, 'r', 'utf-8').readlines()neg_docs = []pos_docs = []#遍历每一条消极评论,放入到list中for line in neg:neg_docs.append(line.rstrip("\r\n"))#遍历每一条积极评论,放入到list中for line in pos:pos_docs.append(line.rstrip("\r\n"))global classifierclassifier = Sentiment()#训练数据,传入积极、消极评论list classifier.train(neg_docs, pos_docs)#保存数据字典def save(fname, iszip=True):classifier.save(fname, iszip)#加载数据字典def load(fname, iszip=True):classifier.load(fname, iszip)#对语句进行分类def classify(sent):return classifier.classify(sent)

sentiment中包含了训练数据集的方法,看一下是如何训练数据集的:

在sentiment文件夹下,包含了以下文件:

neg.txt和pos.txt是已经分类好的评论数据,neg.txt中都是消极评论,pos中是积极评论

sentiment.marshal和sentiment.marshal.3中存放的是序列化后的数据字典,这个也稍后再说

(1)在train()方法中,首先读取消极和积极评论txt文件,然后获取每一条评论,放入到list集合中,格式大致如下

[ '还没有收到书!!!还没有收到书 ' , ' 小熊宝宝我觉得孩子不喜欢,能换别的吗 ' , ......]

#训练数据def train(neg_file, pos_file):#打开消极数据文件neg = codecs.open(neg_file, 'r', 'utf-8').readlines()pos = codecs.open(pos_file, 'r', 'utf-8').readlines()neg_docs = []pos_docs = []#遍历每一条消极评论,放入到list中for line in neg:neg_docs.append(line.rstrip("\r\n"))#遍历每一条积极评论,放入到list中for line in pos:pos_docs.append(line.rstrip("\r\n"))global classifierclassifier = Sentiment()#训练数据,传入积极、消极评论listclassifier.train(neg_docs, pos_docs)

然后调用了Sentiment对象中的train()方法:

在train方法中,遍历了传入的积极、消极评论list,为每条评论进行分词,并为加上了分类标签,此时的数据格式如下:

评论分词后的数据格式:['收到','没有'...]

加上标签后的数据格式(以消极评论为例):[ [['收到','没有' ...],'neg'] , [['小熊','宝宝' ...],‘neg’] ........]]

可以看到每一条评论都是一个list,其中又包含了评论分词后的list和评论的分类标签

# 训练数据集def train(self, neg_docs, pos_docs):data = []#读取消极评论list,对每条评论分词,并加上neg标签,也放入到一个list中for sent in neg_docs:data.append([self.handle(sent), 'neg'])#读取积极评论list,为每条评论分词,加上pos标签for sent in pos_docs:data.append([self.handle(sent), 'pos'])#调用分类器的训练数据集方法,对模型进行训练self.classifier.train(data)

经过了此步骤,已经对数据处理完毕,接下来就可以对数据进行训练

3.classification下的bayes.py

# -*- coding: utf-8 -*-from __future__ import unicode_literalsimport sysimport gzipimport marshalfrom math import log, expfrom ..utils.frequency import AddOneProbclass Bayes(object):def __init__(self):#标签数据对象self.d = {}#所有分类的词数之和self.total = 0#保存字典数据def save(self, fname, iszip=True):#创建对象,用来存储训练结果d = {}#添加total,也就是积极消极评论分词总词数d['total'] = self.total#d为分类标签,存储每个标签的数据对象d['d'] = {}for k, v in self.d.items():#k为分类标签,v为标签对应的所有分词数据,是一个AddOneProb对象d['d'][k] = v.__dict__#这里判断python版本if sys.version_info[0] == 3:fname = fname + '.3'#这里可有两种方法可以选择进行存储if not iszip:##将序列化后的二进制数据直接写入文件marshal.dump(d, open(fname, 'wb'))else:#首先获取序列化后的二进制数据,然后写入文件f = gzip.open(fname, 'wb')f.write(marshal.dumps(d))f.close()#加载数据字典def load(self, fname, iszip=True):#判断版本if sys.version_info[0] == 3:fname = fname + '.3'#判断打开文件方式if not iszip:d = marshal.load(open(fname, 'rb'))else:try:f = gzip.open(fname, 'rb')d = marshal.loads(f.read())except IOError:f = open(fname, 'rb')d = marshal.loads(f.read())f.close()#从文件中读取数据,为total和d对象赋值self.total = d['total']self.d = {}for k, v in d['d'].items():self.d[k] = AddOneProb()self.d[k].__dict__ = v#训练数据集def train(self, data):#遍历数据集for d in data:#d[1]标签-->分类类别c = d[1]#判断数据字典中是否有当前的标签if c not in self.d:#如果没有该标签,加入标签,值是一个AddOneProb对象self.d[c] = AddOneProb()#d[0]是评论的分词list,遍历分词listfor word in d[0]:#调用AddOneProb中的add方法,添加单词self.d[c].add(word, 1)#计算总词数self.total = sum(map(lambda x: self.d[x].getsum(), self.d.keys()))#贝叶斯分类def classify(self, x):tmp = {}#遍历每个分类标签for k in self.d:#获取每个分类标签下的总词数和所有标签总词数,求对数差相当于log(某标签下的总词数/所有标签总词数)tmp[k] = log(self.d[k].getsum()) - log(self.total)for word in x:#获取每个单词出现的频率,log[(某标签下的总词数/所有标签总词数)*单词出现频率]tmp[k] += log(self.d[k].freq(word))#计算概率,由于直接得到的概率值比较小,这里应该使用了一种方法来转换,原理还不是很明白ret, prob = 0, 0for k in self.d:now = 0try:for otherk in self.d:now += exp(tmp[otherk]-tmp[k])now = 1/nowexcept OverflowError:now = 0if now > prob:ret, prob = k, nowreturn (ret, prob)

from . import good_turingclass BaseProb(object):def __init__(self):self.d = {}self.total = 0.0self.none = 0def exists(self, key):return key in self.ddef getsum(self):return self.totaldef get(self, key):if not self.exists(key):return False, self.nonereturn True, self.d[key]def freq(self, key):return float(self.get(key)[1])/self.totaldef samples(self):return self.d.keys()class NormalProb(BaseProb):def add(self, key, value):if not self.exists(key):self.d[key] = 0self.d[key] += valueself.total += valueclass AddOneProb(BaseProb):def __init__(self):self.d = {}self.total = 0.0self.none = 1#添加单词def add(self, key, value):#更新该类别下的单词总数self.total += value#如果单词未出现过if not self.exists(key):#将单词加入对应标签的数据字典中,value设为1self.d[key] = 1#更新总词数self.total += 1#如果单词出现过,对该单词的value值加1self.d[key] += value

在bayes对象中,有两个属性d和total,d是一个数据字典,total存储所有分类的总词数,经过train方法训练数据集后,d中存储的是每个分类标签的数据key为分类标签,value是一个AddOneProb对象。

def __init__(self):self.d = {}self.total = 0.0

在AddOneProb对象中,同样存在d和total属性,这里的total存储的是每个分类各自的单词总数,d中存储的是所有出现过的单词,单词作为key,单词出现的次数作为value.

为了下次计算概率时,不用重新训练,可以将训练得到的数据序列化到文件中,下次直接加载文件,将文件反序列为对象,从对象中获取数据即可(save和load方法)。

4.得到训练数据后,使用朴素贝叶斯分类进行分类

该方法可自行查阅。

如果觉得《中文情感分析——snownlp类库 源码注释及使用》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。