Preface
文本的分类和聚类是一个比较有意思的话题,我以前也写过一篇blog《基于K-Means的文本聚类算法》,加上最近读了几本数据挖掘和机器学习的书籍,因此很想写点东西来记录下学习的所得。
在本文的上半部分《基于朴素贝叶斯分类器的文本分类算法(上)》一文中简单介绍了贝叶斯学习的基本理论,这一篇将展示如何将该理论运用到中文文本分类中来,具体的文本分类原理就不再介绍了,在上半部分有,也可以参见代码的注释。
文本特征向量
文本特征向量可以描述为文本中的字/词构成的属性。例如给出文本:
Good good study,Day day up.
可以获得该文本的特征向量集:{ Good, good, study, Day, day , up.}
朴素贝叶斯模型是文本分类模型中的一种简单但性能优越的的分类模型。为了简化计算过程,假定各待分类文本特征变量是相互独立的,即“朴素贝叶斯模型的假设”。相互独立表明了所有特征变量之间的表述是没有关联的。如上例中,[good]和[study]这两个特征变量就是没有任何关联的。
在上例中,文本是英文,但由于中文本身是没有自然分割符(如空格之类符号),所以要获得中文文本的特征变量向量首先需要对文本进行中文分词
中文分词
这里采用极易中文分词组件,这个中文分词组件可以免费使用,提供Lucene接口,跨平台,性能可靠。
package com.vista; import java.io.IOException; import jeasy.analysis.MMAnalyzer; /** * 中文分词器 */ public class ChineseSpliter { /** * 对给定的文本进行中文分词 * @param text 给定的文本 * @param splitToken 用于分割的标记,如"|" * @return 分词完毕的文本 */ public static String split(String text,String splitToken) { String result = null; MMAnalyzer analyzer = new MMAnalyzer(); try { result = analyzer.segment(text, splitToken); } catch (IOException e) { e.printStackTrace(); } return result; } }
停用词处理
去掉文档中无意思的词语也是必须的一项工作,这里简单的定义了一些常见的停用词,并根据这些常用停用词在分词时进行判断。
package com.vista; /** * 停用词处理器 * @author phinecos * */ public class StopWordsHandler { private static String stopWordsList[] ={"的", "我们","要","自己","之","将","“","”",",","(",")","后","应","到","某","后","个","是","位","新","一","两","在","中","或","有","更","好",""};//常用停用词 public static boolean IsStopWord(String word) { for(int i=0;i<stopWordsList.length;++i) { if(word.equalsIgnoreCase(stopWordsList[i])) return true; } return false; } }
训练集管理器
我们的系统首先需要从训练样本集中得到假设的先验概率和给定假设下观察到不同数据的概率。
package com.vista; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; /** * 训练集管理器 */ public class TrainingDataManager { private String[] traningFileClassifications;//训练语料分类集合 private File traningTextDir;//训练语料存放目录 private static String defaultPath = "D:\\TrainningSet"; public TrainingDataManager() { traningTextDir = new File(defaultPath); if (!traningTextDir.isDirectory()) { throw new IllegalArgumentException("训练语料库搜索失败! [" +defaultPath + "]"); } this.traningFileClassifications = traningTextDir.list(); } /** * 返回训练文本类别,这个类别就是目录名 * @return 训练文本类别 */ public String[] getTraningClassifications() { return this.traningFileClassifications; } /** * 根据训练文本类别返回这个类别下的所有训练文本路径(full path) * @param classification 给定的分类 * @return 给定分类下所有文件的路径(full path) */ public String[] getFilesPath(String classification) { File classDir = new File(traningTextDir.getPath() +File.separator +classification); String[] ret = classDir.list(); for (int i = 0; i < ret.length; i++) { ret[i] = traningTextDir.getPath() +File.separator +classification +File.separator +ret[i]; } return ret; } /** * 返回给定路径的文本文件内容 * @param filePath 给定的文本文件路径 * @return 文本内容 * @throws java.io.FileNotFoundException * @throws java.io.IOException */ public static String getText(String filePath) throws FileNotFoundException,IOException { InputStreamReader isReader =new InputStreamReader(new FileInputStream(filePath),"GBK"); BufferedReader reader = new BufferedReader(isReader); String aline; StringBuilder sb = new StringBuilder(); while ((aline = reader.readLine()) != null) { sb.append(aline + " "); } isReader.close(); reader.close(); return sb.toString(); } /** * 返回训练文本集中所有的文本数目 * @return 训练文本集中所有的文本数目 */ public int getTrainingFileCount() { int ret = 0; for (int i = 0; i < traningFileClassifications.length; i++) { ret +=getTrainingFileCountOfClassification(traningFileClassifications[i]); } return ret; } /** * 返回训练文本集中在给定分类下的训练文本数目 * @param classification 给定的分类 * @return 训练文本集中在给定分类下的训练文本数目 */ public int getTrainingFileCountOfClassification(String classification) { File classDir = new File(traningTextDir.getPath() +File.separator +classification); return classDir.list().length; } /** * 返回给定分类中包含关键字/词的训练文本的数目 * @param classification 给定的分类 * @param key 给定的关键字/词 * @return 给定分类中包含关键字/词的训练文本的数目 */ public int getCountContainKeyOfClassification(String classification,String key) { int ret = 0; try { String[] filePath = getFilesPath(classification); for (int j = 0; j < filePath.length; j++) { String text = getText(filePath[j]); if (text.contains(key)) { ret++; } } } catch (FileNotFoundException ex) { Logger.getLogger(TrainingDataManager.class.getName()).log(Level.SEVERE, null,ex); } catch (IOException ex) { Logger.getLogger(TrainingDataManager.class.getName()).log(Level.SEVERE, null,ex); } return ret; } }
先验概率
先验概率是我们需要计算的两大概率值之一
package com.vista; /** * 先验概率计算 * <h3>先验概率计算</h3> * P(c<sub>j</sub>)=N(C=c<sub>j</sub>)<b>/</b>N <br> * 其中,N(C=c<sub>j</sub>)表示类别c<sub>j</sub>中的训练文本数量; * N表示训练文本集总数量。 */ public class PriorProbability { private static TrainingDataManager tdm =new TrainingDataManager(); /** * 先验概率 * @param c 给定的分类 * @return 给定条件下的先验概率 */ public static float calculatePc(String c) { float ret = 0F; float Nc = tdm.getTrainingFileCountOfClassification(c); float N = tdm.getTrainingFileCount(); ret = Nc / N; return ret; } }
分类条件概率
这是另一个影响因子,和先验概率一起来决定最终结果
package com.vista; /** * <b>类</b>条件概率计算 * * <h3>类条件概率</h3> * P(x<sub>j</sub>|c<sub>j</sub>)=( N(X=x<sub>i</sub>, C=c<sub>j * </sub>)+1 ) <b>/</b> ( N(C=c<sub>j</sub>)+M+V ) <br> * 其中,N(X=x<sub>i</sub>, C=c<sub>j</sub>)表示类别c<sub>j</sub>中包含属性x<sub> * i</sub>的训练文本数量;N(C=c<sub>j</sub>)表示类别c<sub>j</sub>中的训练文本数量;M值用于避免 * N(X=x<sub>i</sub>, C=c<sub>j</sub>)过小所引发的问题;V表示类别的总数。 * * <h3>条件概率</h3> * <b>定义</b> 设A, B是两个事件,且P(A)>0 称<br> * <tt>P(B∣A)=P(AB)/P(A)</tt><br> * 为在条件A下发生的条件事件B发生的条件概率。 */ public class ClassConditionalProbability { private static TrainingDataManager tdm = new TrainingDataManager(); private static final float M = 0F; /** * 计算类条件概率 * @param x 给定的文本属性 * @param c 给定的分类 * @return 给定条件下的类条件概率 */ public static float calculatePxc(String x, String c) { float ret = 0F; float Nxc = tdm.getCountContainKeyOfClassification(c, x); float Nc = tdm.getTrainingFileCountOfClassification(c); float V = tdm.getTraningClassifications().length; ret = (Nxc + 1) / (Nc + M + V); //为了避免出现0这样极端情况,进行加权处理 return ret; } }
分类结果
用来保存各个分类及其计算出的概率值,
package com.vista; /** * 分类结果 */ public class ClassifyResult { public double probility;//分类的概率 public String classification;//分类 public ClassifyResult() { this.probility = 0; this.classification = null; } }
朴素贝叶斯分类器
利用样本数据集计算先验概率和各个文本向量属性在分类中的条件概率,从而计算出各个概率值,最后对各个概率值进行排序,选出最大的概率值,即为所属的分类。
package com.vista; import com.vista.ChineseSpliter; import com.vista.ClassConditionalProbability; import com.vista.PriorProbability; import com.vista.TrainingDataManager; import com.vista.StopWordsHandler; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Vector; /** * 朴素贝叶斯分类器 */ public class BayesClassifier { private TrainingDataManager tdm;//训练集管理器 private String trainnigDataPath;//训练集路径 private static double zoomFactor = 10.0f; /** * 默认的构造器,初始化训练集 */ public BayesClassifier() { tdm =new TrainingDataManager(); } /** * 计算给定的文本属性向量X在给定的分类Cj中的类条件概率 * <code>ClassConditionalProbability</code>连乘值 * @param X 给定的文本属性向量 * @param Cj 给定的类别 * @return 分类条件概率连乘值,即<br> */ float calcProd(String[] X, String Cj) { float ret = 1.0F; // 类条件概率连乘 for (int i = 0; i <X.length; i++) { String Xi = X[i]; //因为结果过小,因此在连乘之前放大10倍,这对最终结果并无影响,因为我们只是比较概率大小而已 ret *=ClassConditionalProbability.calculatePxc(Xi, Cj)*zoomFactor; } // 再乘以先验概率 ret *= PriorProbability.calculatePc(Cj); return ret; } /** * 去掉停用词 * @param text 给定的文本 * @return 去停用词后结果 */ public String[] DropStopWords(String[] oldWords) { Vector<String> v1 = new Vector<String>(); for(int i=0;i<oldWords.length;++i) { if(StopWordsHandler.IsStopWord(oldWords[i])==false) {//不是停用词 v1.add(oldWords[i]); } } String[] newWords = new String[v1.size()]; v1.toArray(newWords); return newWords; } /** * 对给定的文本进行分类 * @param text 给定的文本 * @return 分类结果 */ @SuppressWarnings("unchecked") public String classify(String text) { String[] terms = null; terms= ChineseSpliter.split(text, " ").split(" ");//中文分词处理(分词后结果可能还包含有停用词) terms = DropStopWords(terms);//去掉停用词,以免影响分类 String[] Classes = tdm.getTraningClassifications();//分类 float probility = 0.0F; List<ClassifyResult> crs = new ArrayList<ClassifyResult>();//分类结果 for (int i = 0; i <Classes.length; i++) { String Ci = Classes[i];//第i个分类 probility = calcProd(terms, Ci);//计算给定的文本属性向量terms在给定的分类Ci中的分类条件概率 //保存分类结果 ClassifyResult cr = new ClassifyResult(); cr.classification = Ci;//分类 cr.probility = probility;//关键字在分类的条件概率 System.out.println("In process."); System.out.println(Ci + ":" + probility); crs.add(cr); } //对最后概率结果进行排序 java.util.Collections.sort(crs,new Comparator() { public int compare(final Object o1,final Object o2) { final ClassifyResult m1 = (ClassifyResult) o1; final ClassifyResult m2 = (ClassifyResult) o2; final double ret = m1.probility - m2.probility; if (ret < 0) { return 1; } else { return -1; } } }); //返回概率最大的分类 return crs.get(0).classification; } public static void main(String[] args) { String text = "微软公司提出以446亿美元的价格收购雅虎中国网2月1日报道 美联社消息,微软公司提出以446亿美元现金加股票的价格收购搜索网站雅虎公司。微软提出以每股31美元的价格收购雅虎。微软的收购报价较雅虎1月31日的收盘价19.18美元溢价62%。微软公司称雅虎公司的股东可以选择以现金或股票进行交易。微软和雅虎公司在2006年底和2007年初已在寻求双方合作。而近两年,雅虎一直处于困境:市场份额下滑、运营业绩不佳、股价大幅下跌。对于力图在互联网市场有所作为的微软来说,收购雅虎无疑是一条捷径,因为双方具有非常强的互补性。(小桥)"; BayesClassifier classifier = new BayesClassifier();//构造Bayes分类器 String result = classifier.classify(text);//进行分类 System.out.println("此项属于["+result+"]"); } }训练集与分类测试
作为测试,这里选用Sogou实验室的文本分类数据,我只使用了mini版本。迷你版本有10个类别 ,共计100篇文章,总大小244KB 使用的测试文本:
微软公司提出以446亿美元的价格收购雅虎
中国网2月1日报道 美联社消息,微软公司提出以446亿美元现金加股票的价格收购搜索网站雅虎公司。
微软提出以每股31美元的价格收购雅虎。微软的收购报价较雅虎1月31日的收盘价19.18美元溢价62%。微软公司称雅虎公司的股东可以选择以现金或股票进行交易。
微软和雅虎公司在2006年底和2007年初已在寻求双方合作。而近两年,雅虎一直处于困境:市场份额下滑、运营业绩不佳、股价大幅下跌。对于力图在互联网市场有所作为的微软来说,收购雅虎无疑是一条捷径,因为双方具有非常强的互补性。(小桥)
使用mini版本的测试结果:
In process.
IT:2.8119528E-5
In process.
体育:2.791735E-21
In process.
健康:3.3188528E-12
In process.
军事:2.532662E-19
In process.
招聘:2.3753596E-17
In process.
教育:4.2023427E-19
In process.
文化:6.0595915E-23
In process.
旅游:5.1286412E-17
In process.
汽车:4.085446E-8
In process.
财经:3.7337095E-10
此项属于[IT]
相关推荐
http://www.cnblogs.com/not-code/archive/2011/07/16/2108369.html 成功解决asmack不能收发文件的问题 http://www.eoeandroid.com/forum.php?mod=viewthread&tid=81207 asmack :...
SSO(Single Sign-On)单点登录是一种身份验证机制,允许用户在多个应用系统中只需登录一次,即可访问所有相互信任的应用系统,无需再次进行身份验证。本详细设计说明书的下篇将深入探讨SSO的核心原理、实现方式及...
http://www.360doc.com/userhome.aspx?userid=19107491&cid=3 jQuery基本选择器及用法 前端模块化开发(AMD和CDM规范) ...http://www.cnblogs.com/dwlsxj/p/4052871.html 浅析MSIL中间语言——PE文件结构篇
策略模式 http://www.cnblogs.com/java-my-life/archive/2012/05/10/2491891.html 模板方法模式 http://www.cnblogs.com/java-my-life/archive/2012/05/14/2495235.html 访问者模式 ...
https://www.cnblogs.com/interdrp/p/17650069.html 在此提交bug 有需要的自行下载 https://sms.reyo.cn/test/v2.zip 2023-08-24 已更新自适应版本下载,不再需要配置不同版本的ini文件了,请保持对应关系即可!!!...
开发文档-ASP.NET:http://www.cnblogs.com/xproer/archive/2012/02/17/2355469.html 升级日志:http://www.cnblogs.com/xproer/archive/2012/02/17/2355449.html 示例下载:...
这个项目可以考虑用在特定情况下加速加载无损格式的预览图,提升用户体验 该项目没有demo代码,demo代码可参考博客园的一位网友的文章:http://www.cnblogs.com/tntmonks/p/5143350.html 标签:无损图片
webApi请求插件PostMan,请参考博客:http://www.cnblogs.com/chenwolong/p/PostMan.html。webApi请求插件PostMan,请参考博客:http://www.cnblogs.com/chenwolong/p/PostMan.html。webApi请求插件PostMan,请参考博客...
,C# 简单的作业调度,亦可参考博客:http://www.cnblogs.com/chenwolong/p/Job.html,C# 简单的作业调度,亦可参考博客:http://www.cnblogs.com/chenwolong/p/Job.html,C# 简单的作业调度,亦可参考博客:...
存储过程版、EF框架版,详情请结合博客:http://www.cnblogs.com/chenwolong/p/BF.html 网址。 C#并发处理。存储过程版、EF框架版,详情请结合博客:http://www.cnblogs.com/chenwolong/p/BF.html 网址。 C#并发处理...
开发文档-ASP.NET(C#):http://www.cnblogs.com/xproer/archive/2010/12/04/1896552.html 开发文档-PHP:http://www.cnblogs.com/xproer/archive/2011/05/16/2047915.html 开发文档-JSP:...
可以看我以前的截图,做了个界面,请大家PP http://www.cnblogs.com/Tangf/archive/2006/03/16/351640.html 特色四:简单的加密方式也可以学习下。这个加密方式可以对移植性进行控制。 特色五:移植性强,许多东西...
与标题一致的描叙,你值得拥有,确实很值。...http://www.cnblogs.com/not-code/archive/2011/08/01/2124340.html http://android-zhang.iteye.com/blog/1836312 http://www.2cto.com/kf/201303/199088.html ...
INSERT INTO Users (id, name, email) VALUES (1, 'John Doe', 'john.doe@example.com'); ``` 4. **查询数据**: 执行SELECT语句获取数据: ```sql SELECT * FROM Users; ``` **源码分析** HSQLDB是用纯Java...
前几天写的django 简易博客开发记录,贴个链接吧 django 简易博客开发 1 安装、创建、配置、admin使用 ...django 简易博客开发 2 模板和数据查询 ...http://www.cnblogs.com/cacique/archive/2012/10/07/2713703.html
SmartPopupWindow ...Android7.0 PopupWindow的兼容问题,修复了问题1和3中描述的问题,但是由引入了新的问题吗,具体详情 博文地址:http://www.cnblogs.com/popfisher/p/6434757.html Github地址:...
CKEditor+CKFinder使用与配置:http://www.cnblogs.com/dmeiyang002/p/3808307.html 官网地址:http://ckeditor.com CKEditor下载地址:http://ckeditor.com/download CKFinder(免费版本)下载地址:...
bndong https://bndong.github.io/Cnblogs-Theme-SimpleMemory/v2/#/
http://www.cnblogs.com/xuqiang/archive/2010/11/02/1953419.html http://ynial.iteye.com/blog/741092 http://www.oschina.net/p/dhtmlxscheduler/news http://www.fanganwang.com/Product-detail-item-1447.html ...
1. **Struts2**:作为MVC(Model-View-Controller)框架,负责处理HTTP请求,调度业务逻辑,并将结果返回给视图层。它提供了一种灵活的机制来定义页面间的数据传递和控制流程,使得学员信息的增删改查操作更加简便。...