利用mmSeg4j分词实现网页文本倾向性分析

利用mmSeg4j分词实现网页文本倾向性分析

最近一直在做网页情感倾向性分析的工作,找了一些论文,发现基于机器学习的算法在项目中不太合适,于是自己鼓捣了一套基于中文分词和正负面词库的分析算法。

原理很简单:

文章倾向性 = ∑(出现的正面词汇 * 权重) —∑(出现的负面词汇 * 权重)。

在这个基础上对于负面新闻再加上相关性判断。

在中文分词方面选择了mmSeg4j,没别的原因,就是之前一直用这个,相对来说性能非常不错,但有些词汇需要自己添加到他的words.dic文件中。mmSeg4j下载地址:http://code.google.com/p/mmseg4j/。

在正式编码之前规划了3个文本文件:

  1. neg_words: 配置负面词汇,每个词一行,格式为“太差-1”。“-”后面的数字作为负面词汇的权重。
  2. pos_words:配置的正面词汇,配置方式与负面词汇类似。
  3. rel_words: 相关词汇表,每行一个词即可,增加这个配置文件是为了识别出于特定内容相关的文本情感。如:仅关心近期与“万科”有关的分析。

在工程启动时将这三个文件加载到一个对象中(单例)代码如下:

 
  1. importjava.io.BufferedReader;
  2. importjava.io.FileNotFoundException;
  3. importjava.io.FileReader;
  4. importjava.io.IOException;
  5. importjava.util.ArrayList;
  6. importjava.util.HashMap;
  7. importjava.util.List;
  8. importjava.util.Map;
  9. importorg.springframework.stereotype.Component;
  10. importcom.yidatec.vis.psms.commons.PSMSConstants;
  11. /**
  12. *加载词汇
  13. *@authorWilliamXu
  14. */
  15. @Component
  16. publicclassTrendencyWordsLoader{
  17. privateMap<String,Integer>negWordMap;
  18. privateMap<String,Integer>posWordMap;
  19. privateList<String>refWordList;
  20. publicTrendencyWordsLoader(){
  21. loadWords();
  22. }
  23. privatevoidloadWords(){
  24. negWordMap=newHashMap<String,Integer>();
  25. posWordMap=newHashMap<String,Integer>();
  26. refWordList=newArrayList<String>();
  27. try{
  28. FileReaderfr=newFileReader(this.getClass().getClassLoader().getResource(PSMSConstants.NEG_WORDS_PATH).getFile());
  29. BufferedReaderbr=newBufferedReader(fr);
  30. Stringline=null;
  31. while((line=br.readLine())!=null){
  32. String[]words=line.split("-");
  33. negWordMap.put(words[0],Integer.parseInt(words[1]));
  34. }
  35. fr=newFileReader(this.getClass().getClassLoader().getResource(PSMSConstants.POS_WORDS_PATH).getFile());
  36. br=newBufferedReader(fr);
  37. line=null;
  38. while((line=br.readLine())!=null){
  39. String[]words=line.split("-");
  40. posWordMap.put(words[0],Integer.parseInt(words[1]));
  41. }
  42. fr=newFileReader(this.getClass().getClassLoader().getResource(PSMSConstants.REL_WORDS_PATH).getFile());
  43. br=newBufferedReader(fr);
  44. line=null;
  45. while((line=br.readLine())!=null){
  46. refWordList.add(line);
  47. }
  48. br.close();
  49. fr.close();
  50. }catch(FileNotFoundExceptione){
  51. e.printStackTrace();
  52. }catch(NumberFormatExceptione){
  53. e.printStackTrace();
  54. }catch(IOExceptione){
  55. e.printStackTrace();
  56. }
  57. }
  58. publicMap<String,Integer>getNegWordMap(){
  59. returnnegWordMap;
  60. }
  61. publicMap<String,Integer>getPosWordMap(){
  62. returnposWordMap;
  63. }
  64. publicList<String>getRefWordList(){
  65. returnrefWordList;
  66. }
  67. }

加载词汇表后,就可以使用mmSeg4j对网页文本进行分词,并进行分析了,代码如下:

 
  1. importjava.io.IOException;
  2. importjava.io.Reader;
  3. importjava.io.StringReader;
  4. importjava.util.ArrayList;
  5. importjava.util.HashMap;
  6. importjava.util.List;
  7. importjava.util.Map;
  8. importjava.util.Set;
  9. importorg.springframework.beans.factory.annotation.Autowired;
  10. importorg.springframework.stereotype.Component;
  11. importcom.chenlb.mmseg4j.ComplexSeg;
  12. importcom.chenlb.mmseg4j.Dictionary;
  13. importcom.chenlb.mmseg4j.MMSeg;
  14. importcom.chenlb.mmseg4j.Word;
  15. importcom.yidatec.vis.psms.entity.SolrQueryResult;
  16. @Component
  17. publicclassTrendencyAnalyser{
  18. @Autowired
  19. TrendencyWordsLoaderwordLoader;
  20. protectedstaticfinalDictionarydic=Dictionary.getInstance();
  21. protectedstaticfinalComplexSegseg=newComplexSeg(dic);
  22. /**
  23. *正序阈值
  24. */
  25. privatefinalintPS_THRESHOLD=50;
  26. /**
  27. *逆序阈值
  28. */
  29. privatefinalintNS_THRESHOLD=30;
  30. /**
  31. *整片文章分词Map
  32. */
  33. privateMap<String,List<Word>>segments=null;
  34. privateList<Word>negs=null;
  35. privateList<Word>poses=null;
  36. privateList<Word>rels=null;
  37. publicintanalyzeTrendency(Stringtitle,Stringcontent){
  38. try{
  39. booleanflag=isRelTitle(title);
  40. if(flag){
  41. inttitleTendency=getTitleTrendency();
  42. if(titleTendency<0){
  43. returnSolrQueryResult.NEGATIVE_NATURE;
  44. }elseif(titleTendency>0){
  45. returnSolrQueryResult.POSITIVE_NATURE;
  46. }
  47. }
  48. clearAll();
  49. initSegmentsMap(newStringReader(title+""+content));
  50. parseNegWordsMap();
  51. parsePosWordsMap();
  52. intresult=analyzeContentsTrendency();
  53. if(flag){//标题相关,仅判断文本倾向性
  54. if(result<0){
  55. returnSolrQueryResult.NEGATIVE_NATURE;
  56. }elseif(result==0){
  57. returnSolrQueryResult.NEUTRAL_NATURE;
  58. }else{
  59. returnSolrQueryResult.POSITIVE_NATURE;
  60. }
  61. }else{//标题无关,需要复杂的矩阵算法
  62. parseRelWordsMap();
  63. if(result<0){
  64. if(analyzeTrendencyByMatrix()){
  65. returnSolrQueryResult.NEGATIVE_NATURE;
  66. }else{
  67. returnSolrQueryResult.NEUTRAL_NATURE;
  68. }
  69. }elseif(result==0){
  70. returnSolrQueryResult.NEUTRAL_NATURE;
  71. }else{
  72. returnSolrQueryResult.POSITIVE_NATURE;
  73. }
  74. }
  75. }catch(IOExceptione){
  76. returnSolrQueryResult.NEUTRAL_NATURE;
  77. }
  78. }
  79. privatevoidclearAll(){
  80. if(segments!=null){
  81. segments.clear();
  82. }
  83. if(negs!=null){
  84. negs.clear();
  85. }
  86. if(poses!=null){
  87. poses.clear();
  88. }
  89. }
  90. /**
  91. *是否是倾向性相关标题
  92. *
  93. *@paramtitle
  94. *@return
  95. */
  96. privatebooleanisRelTitle(Stringtitle){
  97. try{
  98. initTitleSegmentsMap(newStringReader(title));
  99. List<String>relWords=wordLoader.getRefWordList();
  100. for(Stringword:relWords){
  101. if(segments.containsKey(word)){
  102. returntrue;
  103. }
  104. }
  105. }catch(IOExceptione){
  106. returnfalse;
  107. }
  108. returnfalse;
  109. }
  110. /**
  111. *获取标题倾向性
  112. *
  113. *@paramtitle
  114. *@return
  115. */
  116. privateintgetTitleTrendency(){
  117. parseNegWordsMap();
  118. parsePosWordsMap();
  119. returnanalyzeContentsTrendency();
  120. }
  121. /**
  122. *判断整篇文章的倾向性
  123. *
  124. *@paramtitle
  125. *@paramcontent
  126. *@return
  127. */
  128. privateintanalyzeContentsTrendency(){
  129. intnegScore=0;
  130. intposScore=0;
  131. if(negs!=null&&negs.size()>0){
  132. for(Wordword:negs){
  133. negScore+=wordLoader.getNegWordMap().get(word.getString());
  134. }
  135. }
  136. if(poses!=null&&poses.size()>0){
  137. for(Wordword:poses){
  138. posScore+=wordLoader.getPosWordMap().get(word.getString());
  139. }
  140. }
  141. returnposScore-negScore;
  142. }
  143. /**
  144. *交叉矩阵判断文本倾向性
  145. *
  146. *@return
  147. */
  148. privatebooleananalyzeTrendencyByMatrix(){
  149. if(rels==null||rels.size()==0){
  150. returnfalse;
  151. }
  152. if(negs==null||negs.size()==0){
  153. returnfalse;
  154. }
  155. for(inti=0;i<rels.size();i++){
  156. for(intj=0;j<negs.size();j++){
  157. WordrelWord=rels.get(i);
  158. WordnegWord=negs.get(j);
  159. if(relWord.getStartOffset()<negWord.getStartOffset()){
  160. if(negWord.getStartOffset()-relWord.getStartOffset()
  161. -relWord.getLength()<PS_THRESHOLD){
  162. returntrue;
  163. }
  164. }else{
  165. if(relWord.getStartOffset()-negWord.getStartOffset()
  166. -negWord.getLength()<NS_THRESHOLD){
  167. returntrue;
  168. }
  169. }
  170. }
  171. }
  172. returnfalse;
  173. }
  174. /**
  175. *先对标题进行分词
  176. *
  177. *@paramreader
  178. *@throwsIOException
  179. */
  180. privatevoidinitTitleSegmentsMap(Readerreader)throwsIOException{
  181. segments=newHashMap<String,List<Word>>();
  182. MMSegmmSeg=newMMSeg(reader,seg);
  183. Wordword=null;
  184. while((word=mmSeg.next())!=null){
  185. if(segments.containsKey(word.getString())){
  186. segments.get(word.getString()).add(word);
  187. }
  188. List<Word>words=newArrayList<Word>();
  189. words.add(word);
  190. segments.put(word.getString(),words);
  191. }
  192. }
  193. /**
  194. *对正文进行分词
  195. *
  196. *@paramreader
  197. *@throwsIOException
  198. */
  199. privatevoidinitSegmentsMap(Readerreader)throwsIOException{
  200. if(segments==null){
  201. segments=newHashMap<String,List<Word>>();
  202. }
  203. MMSegmmSeg=newMMSeg(reader,seg);
  204. Wordword=null;
  205. while((word=mmSeg.next())!=null){
  206. if(segments.containsKey(word.getString())){
  207. segments.get(word.getString()).add(word);
  208. }
  209. List<Word>words=newArrayList<Word>();
  210. words.add(word);
  211. segments.put(word.getString(),words);
  212. }
  213. }
  214. /**
  215. *解析负面词汇
  216. */
  217. privatevoidparseNegWordsMap(){
  218. Map<String,Integer>negMap=wordLoader.getNegWordMap();
  219. Set<String>negKeys=negMap.keySet();
  220. for(StringnegKey:negKeys){
  221. List<Word>negWords=segments.get(negKey);
  222. if(negWords!=null){
  223. if(negs==null){
  224. negs=newArrayList<Word>();
  225. }
  226. negs.addAll(negWords);
  227. }
  228. }
  229. }
  230. /**
  231. *解析正面词汇
  232. */
  233. privatevoidparsePosWordsMap(){
  234. Map<String,Integer>posMap=wordLoader.getPosWordMap();
  235. Set<String>posKeys=posMap.keySet();
  236. for(StringposKey:posKeys){
  237. List<Word>posWords=segments.get(posKey);
  238. if(posWords!=null){
  239. if(poses==null){
  240. poses=newArrayList<Word>();
  241. }
  242. poses.addAll(posWords);
  243. }
  244. }
  245. }
  246. /**
  247. *解析相关词汇
  248. */
  249. privatevoidparseRelWordsMap(){
  250. List<String>refWords=wordLoader.getRefWordList();
  251. for(Stringword:refWords){
  252. List<Word>relWords=segments.get(word);
  253. if(relWords!=null){
  254. if(rels==null){
  255. rels=newArrayList<Word>();
  256. }
  257. rels.addAll(relWords);
  258. }
  259. }
  260. }
  261. }
  262. //3个文件路径
  263. public class PSMSConstants { public static final String NEG_WORDS_PATH = "D:\\pdfReader\\153分钟学会R.txt"; public static final String POS_WORDS_PATH = "D:\\pdfReader\\R语言简明入门教程.txt"; public static final String REL_WORDS_PATH = "D:\\pdfReader\\R语言与统计分析.txt"; }
  264. //正倾向1 负倾向-1 相关词 0
  265. public class SolrQueryResult { public static final int NEGATIVE_NATURE = -1; public static final int NEUTRAL_NATURE = 0; public static final int POSITIVE_NATURE = 1; }

这里面用了一些策略:

  1. 先分析标题,如果标题中出现相关词汇,仅需判断正文倾向性即可。
  2. 如果标题中出现相关词汇,并且标题存在倾向,以标题倾向为准。
  3. 如果上述都不成立,则合并标题与正文,一起进行分词与情感词汇识别。
  4. 对于通篇识别为负面情感的文章需要进一步判断相关性。
  5. 采用距离矩阵的方式判断相关性。
  6. 需要设定正向最大距离阈值与反向最大距离阈值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值