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;
/**
* 先验概率计算
* 先验概率计算
* P(cj)=N(C=cj)/N
* 其中,N(C=cj)表示类别cj中的训练文本数量;
* 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;
/**
* 类条件概率计算
*
* 类条件概率
* P(xj|cj)=( N(X=xi, C=cj
* )+1 ) / ( N(C=cj)+M+V )
* 其中,N(X=xi, C=cj)表示类别cj中包含属性x
* i的训练文本数量;N(C=cj)表示类别cj中的训练文本数量;M值用于避免
* N(X=xi, C=cj)过小所引发的问题;V表示类别的总数。
*
* 条件概率
* 定义 设A, B是两个事件,且P(A)>0 称
* P(B∣A)=P(AB)/P(A)
* 为在条件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中的类条件概率
* ClassConditionalProbability
连乘值
*
@param
X 给定的文本属性向量
*
@param
Cj 给定的类别
*
@return
分类条件概率连乘值,即
*/
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]
文章图片
作者:洞庭散人
【文本分类算法之--贝叶斯分类算法的实现Java版本】出处:http://phinecos.cnblogs.com/
本博客遵从
Creative Commons Attribution 3.0 License,若用于非商业目的,您可以自由转载,但请保留原作者信息和文章链接URL。