最近派给我一个任务,说是要做情感分析的东西,最开始对此也是一点都不了解,经过一段时间的学习,对一个给出的算法做了一下简单的实现。
首先,扯一下概念性的东西:
人们对事物的情感都是有两面性的,如正面与反面、褒义与贬义等。一般认为,文本的情感倾向分配是一个两分类问题,就是把文本分成正面和负面两类,一般中文情感分类器的构建过程主要包括中文文本处理及表示、特征选择、分类器训练和分类器评测等,此博文主要侧重于分类器训练功能的实现。
要进行情感分类器的构建,前期工作也是不可少的。
实现确定大的两个方向:
A. 模型的训练与保存
B. 依据模型进行测试文本的分类
1、中文分词
简单理解为拿来一篇文章,其中包含的有介词、名词、动词、数量词等等,我们该如何将这些词从这篇文章中剥离出来,作为后续处理的语料呢?我们在这里用到了自然语言处理系统(NLP),这里有一个对外的小Demo可以对简单的文本进行处理http://61.135.193.166:8080/Demo2/,我们用到的就是对文本处理后返回的XML。
例如上面的XML,就是对一篇文本处理过之后返回的结果,我们可以直观的看到我们所需要的词就存在于type为group的<tok>标签中,那么下来就是一个对XML进行解析的过程了,解析的目标是获取其中所有的词。(代码实现在最后贴上)
2、情感词典
在人工对文本的情感倾向进行标注时,文本中往往少数带有情感倾向的词汇起决定性作用,如褒义词和贬义词。中文情感词汇非常复杂,情感词汇的词性很多,主要为形容词、名词、副词等,但仅仅考虑词性选择情感词并不科学,如"垃圾""棒槌"都带有负面情感,但大量的名词并不带情感色彩,如果选用势必会降低分类的性能。情感词是最好的表示文本情感的特征,此博文用到台湾大学情感词典作为判断情感词极性的依据。词典请戳 http://pan.baidu.com/s/1u12m0 有了情感词典,下一步的工作就是从获得的原始语料中提取中具有情感倾向的词。
3、特征权值计算
特征权值是指特征词在文本中的权重,也可称为词的向量,是分类器分类的重要依据。此博文采用词频作为权值进行情感分类,也就是计算出某个情感词在文本中出现的次数。
前期工作准备就绪,接下来就是进行文本情感分类器的构建了。
本文采用了朴素贝叶斯方法来构建情感分类器。朴素贝叶斯是一种基于概率的学习算法,它基于假设的先验概率,给定假设下观察不同特征的概率。
由上面的公式可以看出,只要统计doc(cj)和weight(wi, cj)就可以计算出先验和后验概率,因此对于训练模型的存储问题也就简化为了保存这两部分;而其中的doc(cj),对于训练样本而言有所要求——训练样本已经是分好类的(我们提前人工分好了一部分训练文本,其中已经有确定的感情色彩)。作为例子在此贴上训练样本 http://pan.baidu.com/s/1s0pPW 这些是抓取的博客信息已经通过NLP处理过后的XML,其中分为积极地、消极的以及不确定的三类文本。
算法思想已经给出,那么我们现在来梳理一下代码实现的思路。(我在这里使用Java语言进行实现)
首先是模型训练部分:
1. 读入情感词典,作为后面判断文本中情感词的先决条件;
2. 读入训练预料,筛选出其中的情感词,并统计每个情感词在训练预料中的出现次数作为其权值保存;
对于第一步:
我是用HashSet来进行情感词典的存储工作,因为其拥有较快的查找速率,他的作用仅仅是用于判断读取的训练预料中的词汇是否是情感词。
对于第二步:
首先是对训练预料的读取,根据给好的训练预料可以直接获取到相应情感倾向的文本数doc(cj);因为我们获得的是XML文本,所以不可避免的要有XML的解析工作。目的是提取出type为group中的文本信息,因为这才可以断定为一个词汇;
之后通过先前构建好的情感词HashSet对于得到的词汇进行判断,如果其属于情感词范畴,那么将其保存在一个Hashtable<String, Integer>中,同时保存的就是他的出现次数也就是权值。(当然也可以用)
最后就是保存模型咯,我们可以以多种多样的形式进行存储,我在这里为了方便起见使用了对象流,一次性将整个对象写入文件,在之后的对文本进行分类时直接读入整个对象就获得了先前训练好的模型了。
这样看来,训练模型的过程很简单哦,因为它本身就很简单...
下来看看对文本分类的部分
1. 读取获得的样本模型;
2. 获得测试语料中每个情感词的权值;
3. 将情感词的权值与之前训练得到的模型带入到公式中进行计算,依据得到的结果对测试语料进行分类存储;
对于第一步:
同样使用对象流进行读取,获得训练模型。
对于第二步:
类似于之前训练样本时统计出情感词的出现次数即可,最后得到的依旧是一个Hashable<String, Integer>;
对于第三步:
很简单的套公式即可得到,然后通过FileUtils包中的copyFile方法对测试语料进行分类存储。
分类部分更简单... -_-
参考文档贴上:http://pan.baidu.com/s/1EKVca
下面就贴上我写的代码吧..
训练模型:
- import java.io.*;
- import java.util.*;
- import org.apache.commons.io.FileUtils;
- import org.dom4j.Document;
- import org.dom4j.Element;
- import org.dom4j.io.SAXReader;
- public class Sentiment {
- static private HashSet<String> Negative, Positive; //两种情感词典
- static private Integer NegativeDoc, PositiveDoc, UnsureDoc; //属于两种情感的文本数 - 所构建模型需要保存下的值
- static private Hashtable<String, Integer> NegativeWeight, PositiveWeight, UnsureWeight; //两种情感中所有词与他的权值 - 所构建模型需要保存下的值
- public static void main(String[] args) throws Exception {
- // TODO 自动生成的方法存根
- Sentiment Sentiment = new Sentiment();
- Sentiment.Model( );
- Sentiment.Save_Model();
- }
- public void Model( ) throws Exception {
- this.Read_Sentiment_Dictionary();
- this.Sentiment_Doc_Weight("500trainblogxml/");
- }
- @SuppressWarnings("resource")
- public void Read_Sentiment_Dictionary() throws Exception {
- BufferedReader buf;
- String str;
- Negative = new HashSet<String>();
- buf = new BufferedReader( new InputStreamReader(new FileInputStream("Sentiment_Dictionary/ntusd-negative.txt"), "UTF-8") );
- while( (str = buf.readLine()) != null ) {
- Negative.add(str);
- }
- Positive = new HashSet<String>();
- buf = new BufferedReader( new InputStreamReader(new FileInputStream("Sentiment_Dictionary/ntusd-positive.txt"), "UTF-8") );
- while( (str = buf.readLine()) != null ) {
- Positive.add(str);
- }
- }
- public void Sentiment_Doc_Weight( String DirPath ) throws Exception {
- File NegativeDir = new File( DirPath + "negativeout" );
- String[] NegativeFiles = NegativeDir.list();
- NegativeDoc = NegativeFiles.length;
- ArrayList<String> NegativeCurrentList = new ArrayList<String>();
- for ( int i = 0; i < NegativeFiles.length; i ++ ) {