语料库java
本文语料库特指文本分类语料库,对应IDataSet接口。而文本分类语料库包含两个概念:文档和类目。一个文档只属于一个类目,一个类目可能含有多个文档。好比搜狗文本分类语料库迷你版.zip,下载前请先阅读搜狗实验室数据使用许可协议。算法
用Map描述数组
这种关系能够用Java的Map<String, String[]>来描述,其key表明类目,value表明该类目下的全部文档。用户能够利用本身的文本读取模块构造一个Map<String, String[]>形式的中间语料库,而后利用IDataSet#add(java.util.Map<java.lang.String,java.lang.String[]>)接口将其加入到训练语料库中。安全
用文件夹描述服务器
这种树形结构也很适合用文件夹描述,即:数据结构
/**性能
* 加载数据集测试
*编码
* @param folderPath 分类语料的根目录.目录必须知足以下结构:<br>.net
* 根目录<br>
* ├── 分类A<br>
* │ └── 1.txt<br>
* │ └── 2.txt<br>
* │ └── 3.txt<br>
* ├── 分类B<br>
* │ └── 1.txt<br>
* │ └── ...<br>
* └── ...<br>
* 文件不必定须要用数字命名,也不须要以txt做为后缀名,但必定须要是文本文件.
* @param charsetName 文件编码
* @return
* @throws IllegalArgumentException
* @throws IOException
*/
IDataSet load(String folderPath, String charsetName) throws IllegalArgumentException, IOException;
例如:
每一个分类里面都是一些文本文档。任何知足此格式的语料库均可以直接加载。
数据集实现
考虑到大规模训练的时候,文本数量达到千万级,没法所有加载到内存中,因此本系统实现了基于文件系统的FileDataSet。同时,在服务器资源许可的状况下,可使用基于内存的MemoryDataSet,提升加载速度。二者的继承关系以下:
训练
训练指的是,利用给定训练集寻找一个能描述这种语言现象的模型的过程。开发者只需调用train接口便可,但在实现中,有许多细节。
分词
目前,本系统中的分词器接口一共有两种实现:
但文本分类是否必定须要分词?答案是否认的。 咱们能够顺序选取文中相邻的两个字,做为一个“词”(术语叫bigram)。这两个字在数量不少的时候能够反映文章的主题(参考清华大学2016年的一篇论文《Zhipeng Guo, Yu Zhao, Yabin Zheng, Xiance Si, Zhiyuan Liu, Maosong Sun. THUCTC: An Efficient Chinese Text Classifier. 2016》)。这在代码中对应BigramTokenizer. 固然,也能够采用传统的分词器,如HanLPTokenizer。 另外,用户也能够经过实现ITokenizer来实现本身的分词器,并经过IDataSet#setTokenizer来使其生效。
特征提取
特征提取指的是从全部词中,选取最有助于分类决策的词语。理想状态下全部词语都有助于分类决策,但现实状况是,若是将全部词语都归入计算,则训练速度将很是慢,内存开销很是大且最终模型的体积很是大。
本系统采起的是卡方检测,经过卡方检测去掉卡方值低于一个阈值的特征,而且限定最终特征数不超过100万。
调参
对于贝叶斯模型,没有超参数须要调节。
训练
本系统实现的训练算法是朴素贝叶斯法,无需用户关心内部细节。另有一个子项目实现了支持向量机文本分类器,可供参考。因为依赖了第三方库,因此没有集成在本项目中。相关性能指标以下表所示:
模型
训练以后,咱们就获得了一个模型,能够经过IClassifier#getModel获取到模型的引用。该接口返回一个AbstractModel对象,该对象实现了Serializable接口,能够序列化到任何地方以供部署。 反序列化后的模型能够经过以下方式加载并构造分类器:
NaiveBayesModel model = (NaiveBayesModel) IOUtil.readObjectFrom(MODEL_PATH);
NaiveBayesClassifier naiveBayesClassifier = new NaiveBayesClassifier(model);
分类
经过加载模型,咱们能够获得一个分类器,利用该分类器,咱们就能够进行文本分类了。
IClassifier classifier = new NaiveBayesClassifier(model);
目前分类器接口中与文本分类有关的接口有以下三种:
/**
* 预测分类
*
* @param text 文本
* @return 全部分类对应的分值(或几率, 须要enableProbability)
* @throws IllegalArgumentException 参数错误
* @throws IllegalStateException 未训练模型
*/
Map<String, Double> predict(String text) throws IllegalArgumentException, IllegalStateException;
/**
* 预测分类
* @param document
* @return
*/
Map<String, Double> predict(Document document) throws IllegalArgumentException, IllegalStateException;
/**
* 预测分类
* @param document
* @return
* @throws IllegalArgumentException
* @throws IllegalStateException
*/
double[] categorize(Document document) throws IllegalArgumentException, IllegalStateException;
/**
* 预测最可能的分类
* @param document
* @return
* @throws IllegalArgumentException
* @throws IllegalStateException
*/
int label(Document document) throws IllegalArgumentException, IllegalStateException;
/**
* 预测最可能的分类
* @param text 文本
* @return 最可能的分类
* @throws IllegalArgumentException
* @throws IllegalStateException
*/
String classify(String text) throws IllegalArgumentException, IllegalStateException;
/**
* 预测最可能的分类
* @param document 一个结构化的文档(注意!这是一个底层数据结构,请谨慎操做)
* @return 最可能的分类
* @throws IllegalArgumentException
* @throws IllegalStateException
*/
String classify(Document document) throws IllegalArgumentException, IllegalStateException;
classify方法直接返回最可能的类别的String形式,而predict方法返回全部类别的得分(是一个Map形式,键是类目,值是分数或几率),categorize方法返回全部类目的得分(是一个double数组,分类得分按照分类名称的字典序排列),label方法返回最可能类目的字典序。
线程安全性
相似于HanLP的设计,以效率至上,本系统内部实现没有使用任何线程锁,但任何预测接口都是线程安全的(被设计为不储存中间结果,将全部中间结果放入参数栈中)。
情感分析
能够利用文本分类在情感极性语料上训练的模型作浅层情感分析。目前公开的情感分析语料库有:中文情感挖掘语料-ChnSentiCorp,语料发布者为谭松波。
接口与文本分类彻底一致,请参考com.hankcs.demo.DemoSentimentAnalysis。
性能指标
通常来说,受到语料库质量的约束(部分语料库的分类标注模糊或有重叠),咱们评测一个分类器时,必须严谨地注明在哪一个语料库以何种比例分割数据集下获得这样的测试结果。
版本库中有一个在搜狗语料库上的测试com.hankcs.demo.DemoTextClassificationFMeasure,含有完整的参数,请自行运行评估。