这个其实是我数据挖掘 的大作业 :
实验原理
主要是利用朴素贝叶斯对文本进行分类,用TF-IDF 权重进行优化。
实验流程:
1 首先下载搜狗语料库
采用其中的五个类别,分别是,1 – 汽车2 –财经3 — 教育 IT4 — 健康5 — 体育 。其中每个分类的训练文档为1000篇,测试文档为131篇。
2中文分词
利用中科院的分词软件,对所有的文档进行分词。比如 第一个类别下的 10.txt 分词后的结果存储在1_re_10.txt 中 。 分词结果(只列出部分):
/x /x 本报/rz 记者/n 陈雪/nr 频/ag 实习/v 记者/n 唐翔/nr 发自/v 上海/ns
/x /x 一家/n 刚刚/d 成立/vi 两年/m 的/ude1 网络/n 支付/vn 公司/n ,/wd 它/rr 的/ude1 目标/n 是/vshi 成为/v 市值/n 100亿/m 美元/q 的/ude1 上市/vn 公司/n 。/wj
他的结果 后面还可以跟着词性,后面我们我们建立词典的时候,只用名词。
然后所有的训练文档和 测试 的文档放在同一个目录下,我的程序是 读一个ls .txt的文件来决定处理哪些文件。 我采用每个分类1000-1999 作为训练文档,10-140 作为测试文档。
对于第一个分类来说,1_re_10 到1_re_140 都是测试文档 , 1_re_1000 到1_re_1999是训练文档。
3 建立词典
对所有的文档进行词频统计,并只记录下高于某个times 的词,然后再利用stopword.txt ,去掉一些停用词。这样最后得到一个 dict.txt的文件。dict.txt 的格式是 :词 空格 词频,词典一共有33845 个名词。
其中可以通过生成dict.txt ,可以手工判断一些词作为新的停用词放到到 stopword中去,再次运行程序 ,下次生成新的dict.txt 就可以去掉了 。
4 利用朴素贝叶斯算法对文本进行分类
bayes 算法 : 假设 d 为任意文档,文档 d 用其含有的特征词来表示,即 d=(t
1,t2,……,tk),k 为文档的特征词数,tj表示文档 d 中的第 j 个特征词,由此得到下式:
; 其中, 表示分类器预测特征值在类别 cj的文档中发生的概率。
根据贝叶斯定理:
由于 P ( d )对所有类来说都是常数 , 同时有C++ 浮点运算的原因,对于 就不用乘法了 正比于 的和 。
表示分类cj特征词ti 出现的概率 ,那么就是等于 特征词ti出现的频率除以cj分类的总的词数 。
详见tf-idf-nb.cpp :bayes()用来训练训练文本,int getType(string filename ) 返回文件的类别。 下面主要介绍下主代码区。
但是 使用词频的方法正确率只有73%
5 利用TF-IDF 进行优化
之前使用的朴素贝叶斯,使用的是词频权重,这个对文本预处理的依赖性很强。然后我在网上搜索改进方法,使用TF-IDF 作为新的权重计算方法。
N 表示文档总数,N(tk)表示包含词 tk的文档数。IDF 的值越大,说明包含该特征词的文档越少,那么这个特征词分布得就相对集中,则这个特征词有可能包含更多的类别信息;相反,如果 IDF 的值比较小,则说明这个特征词在文档集中分布得相对均匀,对文档的分类没有多大贡献,该词不适合作为分类的特征。
相应在代码中的修改如下表:
while(input >> word) { string rc = word.substr(0,word.find("/")) ; //cout << rc << endl; for(int i = 0 ; i < N ; i ++) { iter = bMap[i].find(rc); if( iter != bMap[i].end() && dictMap.find(rc) != dictMap.end() ) { if( weightMap.find(rc) != weightMap.end() ) result[i] += (iter->second * (log( documentN / (weightMap.find(rc)->second) ) ) ) ; else cout << "can not find " << rc << "in weightMap" << endl; } } } |
实验结果为:
这里我用数字代表分类,一共五个类别,5 5 表示第5个类别的文档,然后用改进后的算法计算之后的的类别。下面简单的统计了每个分类的正确率,和整体的正确率。