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

主要功能:

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

官网信息:

snownlp github:https://github.com/isnowfy/snownlp

使用及源码分析:

snownlp类库的安装:

$ pip install snownlp

使用snownlp进行情感分析:

# -*- coding:utf-8 -*-
from snownlp import SnowNLP #创建snownlp对象,设置要测试的语句
s = SnowNLP(u'买来给家婆用来洗儿子的衣服的') print("",s.words)
#将句子分成单词
# ['买', '来', '给', '家婆', '用', '来', '洗', '儿子', '的', '衣服', '的'] s.tags
# 例如:[(u'这个', u'r'), (u'东西', u'n'),
# (u'真心', u'd'), (u'很', u'd'),
# (u'赞', u'Vg')] # 调用sentiments方法获取积极情感概率 positive的概率
print("",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_literals from . import normal
from . import seg
from . import tag
from . import sentiment
from .sim import bm25
from .summary import textrank
from .summary import words_merge class SnowNLP(object): def __init__(self, doc):
self.doc = doc
self.bm25 = bm25.BM25(doc) @property
def words(self):
return seg.seg(self.doc) @property
def sentences(self):
return normal.get_sentences(self.doc) @property
def han(self):
return normal.zh2hans(self.doc) @property
def pinyin(self):
return normal.get_pinyin(self.doc) @property
def sentiments(self):
return sentiment.classify(self.doc)#调用了sentiment的classify分类方法 @property
def tags(self):
words = self.words
tags = tag.tag(words)
return zip(words, tags) @property
def tf(self):
return self.bm25.f @property
def idf(self):
return self.bm25.idf def sim(self, doc):
return self.bm25.simall(doc) def summary(self, limit=5):
doc = []
sents = self.sentences
for 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 ret def keywords(self, limit=5, merge=False):
doc = []
sents = self.sentences
for 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_literals import os
import codecs from .. import normal
from .. import seg
from ..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-prob classifier = 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 classifier
classifier = 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 classifier
classifier = Sentiment()
#训练数据,传入积极、消极评论list
classifier.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_literals import sys
import gzip
import marshal
from math import log, exp from ..utils.frequency import AddOneProb class 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,遍历分词list
for 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, 0
for k in self.d:
now = 0
try:
for otherk in self.d:
now += exp(tmp[otherk]-tmp[k])
now = 1/now
except OverflowError:
now = 0
if now > prob:
ret, prob = k, now
return (ret, prob)
from . import good_turing

class BaseProb(object):

    def __init__(self):
self.d = {}
self.total = 0.0
self.none = 0 def exists(self, key):
return key in self.d def getsum(self):
return self.total def get(self, key):
if not self.exists(key):
return False, self.none
return True, self.d[key] def freq(self, key):
return float(self.get(key)[1])/self.total def samples(self):
return self.d.keys() class NormalProb(BaseProb): def add(self, key, value):
if not self.exists(key):
self.d[key] = 0
self.d[key] += value
self.total += value class AddOneProb(BaseProb): def __init__(self):
self.d = {}
self.total = 0.0
self.none = 1 #添加单词
def add(self, key, value):
#更新该类别下的单词总数
self.total += value
#如果单词未出现过
if not self.exists(key):
#将单词加入对应标签的数据字典中,value设为1
self.d[key] = 1
#更新总词数
self.total += 1
#如果单词出现过,对该单词的value值加1
self.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类库 源码注释及使用的更多相关文章

  1. 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 百篇博客分析OpenHarmony源码 | v13.02

    百篇博客系列篇.本篇为: v13.xx 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 51.c.h .o 几点说明 kernel_liteos_a_note | 中文注解鸿蒙内核 ...

  2. 情感分析snownlp包部分核心代码理解

    snownlps是用Python写的个中文情感分析的包,自带了中文正负情感的训练集,主要是评论的语料库.使用的是朴素贝叶斯原理来训练和预测数据.主要看了一下这个包的几个主要的核心代码,看的过程作了一些 ...

  3. drf的基本使用、APIView源码分析和CBV源码拓展

    cbv源码拓展 扩展,如果我在Book视图类中重写dispatch方法 -可以实现,在get,post方法执行之前或者之后执行代码,完成类似装饰器的效果 def dispatch(self, requ ...

  4. Spring Ioc源码分析系列--Ioc源码入口分析

    Spring Ioc源码分析系列--Ioc源码入口分析 本系列文章代码基于Spring Framework 5.2.x 前言 上一篇文章Spring Ioc源码分析系列--Ioc的基础知识准备介绍了I ...

  5. Android5.1.1 - APK签名校验分析和修改源码绕过签名校验

    Android5.1.1 - APK签名校验分析和修改源码绕过签名校验 作者:寻禹@阿里聚安全 APK签名校验分析 找到PackageParser类,该类在文件“frameworks/base/cor ...

  6. JPEG概述和头分析(C源码)

    原创文章,转载请注明:JPEG概述和头分析(C源码)  By Lucio.Yang 部分内容来自:w285868925,JPEG压缩标准 1.JPEG概述 JPEG是一个压缩标准,又可分为标准 JPE ...

  7. 基于双向BiLstm神经网络的中文分词详解及源码

    基于双向BiLstm神经网络的中文分词详解及源码 基于双向BiLstm神经网络的中文分词详解及源码 1 标注序列 2 训练网络 3 Viterbi算法求解最优路径 4 keras代码讲解 最后 源代码 ...

  8. 中文自然语言处理工具HanLP源码包的下载使用记录

    中文自然语言处理工具HanLP源码包的下载使用记录 这篇文章主要分享的是hanlp自然语言处理源码的下载,数据集的下载,以及将让源代码中的demo能够跑通.Hanlp安装包的下载以及安装其实之前就已经 ...

  9. 配置Eclipse可以查看JDK类库源码

    一.配置方法 配置Eclipse可以查看JDK类库源码 Window->Preferences->Java->Installed JREs 若没有JRE,需要自己添加进来,有的话,点 ...

随机推荐

  1. Java自学-集合框架 List接口

    ArrayList与List接口 步骤 1 : ArrayList和List ArrayList实现了接口List 常见的写法会把引用声明为接口List类型 注意:是java.util.List,而不 ...

  2. csp201809-2 买菜

    问题描述 小H和小W来到了一条街上,两人分开买菜,他们买菜的过程可以描述为,去店里买一些菜然后去旁边的一个广场把菜装上车,两人都要买n种菜,所以也都要装n次车.具体的,对于小H来说有n个不相交的时间段 ...

  3. 中国爬虫违法违规案例汇总github项目介绍

    中国爬虫违法违规案例汇总github项目介绍 GitHub - 本项目用来整理所有中国大陆爬虫开发者涉诉与违规相关的新闻.资料与法律法规.致力于帮助在中国大陆工作的爬虫行业从业者了解我国相关法律,避免 ...

  4. CSS3制作文字背景图

    文字带上渐变色,或者说让文字透出图片.这些效果 CSS 属性也可以完成. 方法一.利用CSS3属性mix-blend-mode:lighten;实现 使用 mix-blend-mode 能够轻易实现, ...

  5. 那些年我们走过的坑,对Fortify的漏洞进行总结

    1.修复方案,过滤引起Log Forging漏洞的敏感字符的公共方法 /** * Log Forging漏洞校验 * @param logs * @return */ public static St ...

  6. 读《TCP/IP详解》:TCP

    TCP(Transmission Control Protocol,传输控制协议),位于传输层,提供一种面向连接.可靠的字节流服务. 字节流服务(Byte Stream Service)是指,为了方便 ...

  7. Shel脚本-初步入门之《04》

    Shel脚本-初步入门-04 脚本语言的种类 4.脚本语言的种类 4.1 Shell 脚本语言的种类 Shell 脚本语言是弱类型语言(无须定义变量的类型即可使用),在 Unix/Linux 中主要有 ...

  8. 201871010105-曹玉中《面向对象程序设计(java)》第十六周学习总结

    201871010105-曹玉中<面向对象程序设计(java)>第十六周学习总结 项目 内容 这个作业属于哪个过程 https://www.cnblogs.com/nwnu-daizh/ ...

  9. 201871010132--张潇潇--《面向对象程序设计(java)》第十二周学习总结

    博文正文开头格式:(2分) 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.co ...

  10. mybatis多对多关联查询

    多对多关系 一个学生可以选多门课程,而一门课程可以由多个学生选择,这就是一个典型的多对多关联关系.所谓多对多关系,其实是由两个互反的一对多关系组成.即多对多关系都会通过一个中间表来建立,例如选课表.学 ...