源代码下载:NaviveBayesClassify.rar html
Prefacejava
文本的分类和聚类是一个比较有意思的话题,我之前也写过一篇blog《基于K-Means的文本聚类算法》,加上最近读了几本数据挖掘和机器学习的书籍,所以很想写点东西来记录下学习的所得。算法
在本文的上半部分《基于朴素贝叶斯分类器的文本分类算法(上)》一文中简单介绍了贝叶斯学习的基本理论,这一篇将展现如何将该理论运用到中文文本分类中来,具体的文本分类原理就再也不介绍了,在上半部分有,也能够参见代码的注释。app
文本特征向量机器学习
文本特征向量能够描述为文本中的字/词构成的属性。例如给出文本:ide
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]