基于Mahout的中文聚类平台搭建小记
最近的项目中要一个分布式计算环境下的中文聚类功能模块,由于分布式平台是用的开源Hadoop,所以就对其衍生出的数据挖掘框架Mahout做了一下聚类实现的调研。
下面就开始这个历程吧。。。在这里说明一下,Hadoop用版本号的是0.20.203,Mahout用verion的是0.5。在这里要强调下版本号是因为最新版的Hadoop中MapReduce与老版本的Mahout有可能出现不兼容的问题,在这里要统一一下,Hadoop选取的是稳定的版本。
将Hadoop与Mahout源码下载完成后还要下载Maven,因为我们要对Mahout进行编译,Maven会对编译好的jar包进行管理,方便我们以后Mahout程序的开发。
完成下载后对几个tar包进行解压,在我的系统中把maven与hadoop放在了/usr/program目录下,而将Maven放在了eclipse工程目录/usr/workspace目录下。
完成解压后要对几个环境变量进行配置为了方便,我直接将环境变量写在了/etc/profile中以便于开机加载:
Maven : M2_HOME=/usr/programs/apache-maven-3.0.4
PATH=$M2_HOME/bin:$PATH
export M2_HOME
export PATH
Hadoop: HADOOP_HOME=/usr/programs/hadoop-0.20.203.0
PATH=$HADOOP_HOME/bin:$PATH
HADOOP_CONF_DIR=$HADOOP_HOME/conf
export HADOOP_HOME
export HADOOP_CONF_DIR
export PATH
MAHOUT: MAHOUT_HOME=/usr/workspace/mahout-distribution-0.5
PATH=$MAHOUT_HOME/bin:$PATH
export MAHOUT_HOME
export PATH
(以上红色部分是一定要配置的,因为Mahout的Script对它有依赖,否则程序运行会出现异常)
配置完上述环境变量后就要对Hadoop进行配置并启动了,这里为了方便单机上的程序模拟,采用了伪分布模式的配置分别按官网上给出的配置文件的示例进行修改就行了,值得一提的是hadoop.tmp.dir一定要在core-site.xml中指明,否则会出现DataNode进程启动不起的情况。配置完成后,先格式化namenode (hadoop namenode -format),再执行start-all.sh脚本就可以启动伪分布式了,记得jps命令检查一下是不是有五个进程哦。。。
Hadoop配置完成后就可以用mahout做简单的数据挖掘算法模拟了,大家可以参考一下《mahout in action》一书,在我的csdn资源中共享给大家。为了在eclipse中进行开发,我们可以用Maven将Mahout与其所依赖的包编译到本地的库中。打开mahout的根目录,执行 mvn install -Dmaven.test.skip=true命令,这样就会跳过耗时的单元测试对mahout进行编译。编译完成后会在本地的库中看到mahout的包,目录默认会放在~/.m2/repository中。最新版本的eclipse中默认带了maven插件,所以我也没有太费力气。
另外要在eclipse中加入Hadoop插件。这个在网上有很多的教程,在这里强调的一点是一定要用同一版本Haoop中带的插件,此插件可以在Hadoop根目录中contrib/eclipse-plugin中找到,这时的插件还是不能用,因为在我加入的过程中出现各种异常,在网上找了很多解决办法,是因为插件中少一些依赖的包,我试过的最可行的办法就是把依赖的包中的class文件加入到eclipse插件的class目录中,在此过程中大家一定要注意目录的顺序哦(修改后的插件)。。。加入插件后我们可以打开eclipse在window>show view中看到我们可爱的小象了,单击后会出现Map/Reduce Location配置了,新建中配置参数一定要和Hadoop配置文件中一至,这样我们可以方便对DFS进行管理。
至此开发环境搭建完毕,说的有些粗,有问题可以再交流,下面就说一下中文聚类的实现方法,因为现在只是对功能进行调研,没有编写一个系统的聚类程序,聚类程序的其它功能会在以后陆续实现。本文中只采用了mahout提供的script进行聚类执行。
说说中文分词吧,中文聚类分词最重要,mahout中没有为我们提供一个很好的中分分词组件,但它所留的接口可以很方便对多语言的扩展。在Mahout中所有分词的实现都要继承lucene Analyzer父类。在实验中我选择了MMseg4j。这里就将把它加入Mahout的过程分享给大家。
首先看一下一个Analyzer实例是怎么在程序中进行传递的。在聚类中,mahout将聚类文本集所生成的序列文件进行分词处理:
DocumentProcessor.tokenizeDocuments(new Path(inputDir), analyzer.getClass().asSubclass(Analyzer.class), tokenizedPath, conf);// 从调用分词对象的函数中可以看出该对象要继承于Analyzer
接着看一下tokenizedDocments函数内部
public static void tokenizeDocuments(Path input, Class<? extends Analyzer> analyzerClass, Path output, Configuration baseConf) throws IOException, InterruptedException, ClassNotFoundException { Configuration conf = new Configuration(baseConf); // this conf parameter needs to be set enable serialisation of conf values conf.set("io.serializations", "org.apache.hadoop.io.serializer.JavaSerialization," + "org.apache.hadoop.io.serializer.WritableSerialization"); conf.set(ANALYZER_CLASS, analyzerClass.getName());//将class名字记录到了Configuration对象中作为分词Job参数的一部分 Job job = new Job(conf); job.setJobName("DocumentProcessor::DocumentTokenizer: input-folder: " + input); job.setJarByClass(DocumentProcessor.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(StringTuple.class); FileInputFormat.setInputPaths(job, input); FileOutputFormat.setOutputPath(job, output); //SequenceFileTokenizerMapper这个Map任务会进行分词操作 job.setMapperClass(SequenceFileTokenizerMapper.class); job.setInputFormatClass(SequenceFileInputFormat.class); job.setNumReduceTasks(0); job.setOutputFormatClass(SequenceFileOutputFormat.class); HadoopUtil.delete(conf, output); boolean succeeded = job.waitForCompletion(true); if (!succeeded) throw new IllegalStateException("Job failed!"); }
当分词作业提交到hadoop中后会生成Map任务,而SequenceFileTokenizerMapper会用我们自己定义的分词组件进行分词:
public class SequenceFileTokenizerMapper extends Mapper<Text, Text, Text, StringTuple> { private Analyzer analyzer; @Override protected void map(Text key, Text value, Context context) throws IOException, InterruptedException { //调用分词函数对value的值进行处理 TokenStream stream = analyzer.reusableTokenStream(key.toString(), new StringReader(value.toString())); CharTermAttribute termAtt = stream.addAttribute(CharTermAttribute.class); StringTuple document = new StringTuple(); stream.reset(); while (stream.incrementToken()) { if (termAtt.length() > 0) { document.add(new String(termAtt.buffer(), 0, termAtt.length())); } } context.write(key, document); } @Override protected void setup(Context context) throws IOException, InterruptedException { super.setup(context); //map任务开始时会从context中取出configuration对象,解析出分词组件的名称并生成一个对象 analyzer = ClassUtils.instantiateAs(context.getConfiguration().get(DocumentProcessor.ANALYZER_CLASS, DefaultAnalyzer.class.getName()), Analyzer.class); } }
看到这里后我们知道所要做的就是实现一个中文分词类,这个类是Analyzer的子类,由于选择了mmseg4j还要强调一下版本问题,我用的是mmseg4j-1.8.5,lucene用的是lucene-3.1.0,这样就能保证版本一致不会出现编译问题。以下是自己实现的一个ChineseAnalyzer类,它继承于Analyzer抽象类。此过程容易出现的问题是我直接包装mmseg4j提供的ComplexAnalyzer时会出现问题,提示异常,后来才发现ComplexAnalyzer是MMSegAnalyzer的子类,所以一定要实现一个直接继承于Analyzer的类才行,这个类加在了org.apache.mahout.vectorizer包中:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; import java.util.HashSet; import java.util.Set; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.StopFilter; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.util.Version; import com.chenlb.mmseg4j.ComplexSeg; import com.chenlb.mmseg4j.Dictionary; import com.chenlb.mmseg4j.MaxWordSeg; import com.chenlb.mmseg4j.Seg; import com.chenlb.mmseg4j.analysis.MMSegTokenizer; public class ChineseAnalyzer extends Analyzer{ protected Dictionary dic; private Set<String> stopWords = new HashSet<String>(); public ChineseAnalyzer() { this.dic = Dictionary.getInstance(); System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"); System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%setup dic"); System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"); InputStream chinstream = this.getClass().getResourceAsStream("/resources/chstopwords"); InputStream enginstream = this.getClass().getResourceAsStream("/resources/engstopwords"); try{ BufferedReader br = new BufferedReader(new InputStreamReader(enginstream, "utf-8")); String str = ""; while((str = br.readLine()) != null) { this.stopWords.add(str); } br.close(); br = new BufferedReader(new InputStreamReader(chinstream, "utf-8")); str = ""; while((str = br.readLine()) != null) { this.stopWords.add(str); } br.close(); }catch(IOException e) { e.printStackTrace(); } } private Seg newSeg() { return new ComplexSeg(this.dic); } @Override public TokenStream tokenStream(String fieldName, Reader reader) { // TODO Auto-generated method stub return new StopFilter(Version.LUCENE_31, new MMSegTokenizer(newSeg(), reader), this.stopWords); } public static void main(String[] args) throws IOException { ChineseAnalyzer ca = new ChineseAnalyzer(); TokenStream ts = ca.tokenStream("", new StringReader("网络巨头开始使用新的的的技术取代GFS和MapReduce")); CharTermAttribute termAtt = (CharTermAttribute) ts.getAttribute(CharTermAttribute.class); while(ts.incrementToken()) { System.out.println(new String(termAtt.buffer(), 0, termAtt.length())); } } }
在修改后将进入mahout core目录下将这部分代码进行编译后就可以在mahout script中调用了。上面的代码中涉及了几个点:加入停用词过滤,在以后的jar包中加入停用词表资源。当然对于文本的预处理这还不够,以后还会进一步的完善。。。
加入中文分词后选择一个合适的距离度量算法就可以用Kemans算法进行聚类了。
首先要对聚类文本进行处理,生成序列文件的格式,这可以写一个简单的SequenceFile写程序就可以完成了,键可以用文本的名称,值就是文本内容,写完后用
hadoop fs -put将序列文件上传到HDFS中指定目录下。提一下,文本集我是用sogou的,分为教育,军事,文化,政治等几个大类,文本集要进行转码utf8才能在linux系统中识别(iconv命令)。
生成的序列文件放在hdfs://localhost:9000/inputDir目录下,先将序列文件进行分词,将文档集向量化
mahout seq2sparse -i /inputDir -o newCluster -ow -a org.apache.mahout.vectorizer.ChineseAnalyzer -chunk 200 -wt tfidf -s 12 -md 3 -x 50 -seq
然后用canopy算法产生聚类中心
mahout canopy -i newCluster/tfidf-vectors -o centroids -dm org.apache.mahout.common.distance.CosineDistanceMeasure -t1 1500 -t2 2000
再运行kmeans算法产生聚类
mahout kmeans -i newCluster/tfidf-vectors -o clusters -dm org.apache.mahout.common.distance.CosineDistanceMeasure -c centroids/clusters-0 -cd 0.05 -ow -x 10 -k 20 -cl
致此经过几轮迭代算法收敛后用cluterdump工具查看聚类的结果
mahout clusterdump -dt sequencefile -d newCluster/dictionary.file-0 -s clusters/clusters-6 -b 25 -n 25 > output.txt
下面来查看一下结果output.txt中的内容
Running on hadoop, using HADOOP_HOME=/usr/programs/hadoop-0.20.203.0 HADOOP_CONF_DIR=/usr/programs/hadoop-0.20.203.0/conf :VL-2438{n=101 c=[0:0.097, Top Terms: 百分之 => 2.6854870602636054 美元 => 2.5802979233241317 亿 => 2.190108374793931 美国 => 2.129856071849861 下降 => 1.4628594039690377 年 => 1.4071295332200457 报告 => 1.299177877973802 日本 => 1.14190408498934 说 => 1.1112972580560363 华盛顿 => 1.072338850191324 4 => 1.0693445288308776 东京 => 1.0398799877355593 经济 => 0.9625353553507587 增加 => 0.9456107333154962 减少 => 0.882782001306515 :VL-2530{n=105 c=[0:0.062, Top Terms: 环境 => 3.7452682813008624 环境保护 => 3.615621248881022 环保 => 3.509159110841297 污染 => 3.4470987910316104 版 => 2.394707189287458 治理 => 2.383350186120896 期 => 2.359658995128813 正 => 2.2489379405975343 号 => 2.2439347562335787 文 => 2.2041911919911703 我国 => 2.148748207092285 作 => 2.048719987415132 防治 => 2.0349795296078637 运输 => 1.96319610504877 城市 => 1.935810965583438 保护 => 1.8974911190214612 :VL-2000{n=199 c=[0:0.631, Top Terms: 光华 => 4.584050397777078 日月 => 4.580641706984247 站 => 3.8814985907856543 e => 3.6572028188849215 发信人 => 2.8854713344094742 信区 => 2.80311750407195 发信 => 2.7912866410298562 fdu => 2.753490167646552 精华区 => 2.694643120070798 阅读 => 2.688430153544824 文章 => 2.6243694715164416 s => 2.542555816209496 标 => 2.4046570943228565 来源 => 2.3951629765668705 题 => 2.3717574570047195 :VL-2088{n=236 c=[0:0.859, Top Terms: 增长 => 2.6450742887238325 经济 => 2.6387660210415467 投资 => 2.437029139470246 年 => 2.3836404428643694 铁路 => 2.2971328274678378 交通 => 1.9418794260186665 公路 => 1.7924644199468323 发展 => 1.7917982653035955 企业 => 1.7642068044613983 运输 => 1.7030168791948739 站 => 1.6618157625198364 建设 => 1.5652386758287073 车辆 => 1.5498237165354065 部门 => 1.5418542906389399 :VL-2210{n=155 c=[0:1.515, Top Terms: 队 => 5.680033379216348 比赛 => 4.218737829885175 赛 => 3.0428727488363942 冠军 => 2.595917377164287 选手 => 2.496562954687303 分 => 2.4675952511449015 女子 => 2.4515093864933135 决赛 => 2.1590703595069147 胜 => 2.0928856019050843 男子 => 1.9445782815256427 1 => 1.9203212445782076 成绩 => 1.854680178242345 全国 => 1.6489737726026965 夺得 => 1.6239659740078833 3 => 1.6153583957302955 获 => 1.6151204878284084 :VL-2224{n=190 c=[0:0.403, Top Terms: 亚运会 => 5.040011654402081 比赛 => 3.359154502969039 金牌 => 2.913712719867104 选手 => 2.790788728312442 亚运 => 2.753038880699559 运动员 => 2.445935071142096 公斤 => 2.223523087250559 参加 => 2.0012358176080802 冠军 => 1.9969590965070223 健美 => 1.9324685849641499 中国 => 1.9145694958536248 队 => 1.883869430893346 女子 => 1.851896872018513 北京 => 1.8502511375828794 成绩 => 1.8033482099834242 男子 => 1.771682515897249 项目 => 1.7123018917284514 :VL-2306{n=154 c=[0:0.127, Top Terms: 演出 => 5.523823855759262 文艺 => 5.344725150566597 创作 => 4.550048354384187 艺术 => 4.278349899626398 文化 => 3.922486484824837 作品 => 3.809978807127321 举办 => 3.329316136124846 奖 => 3.3251358843469 市 => 2.887640445263355 音乐 => 2.824335123037363 年 => 2.6464415958949496 活动 => 2.642726690738232 舞蹈 => 2.624005871933776 观众 => 2.545687149097393 节目 => 2.4749666994268242 :VL-2311{n=102 c=[0:0.345, Top Terms: 药 => 5.1949627726685765 药物 => 4.31462350546145 治疗 => 4.112211638805913 病人 => 3.7196427046083937 health => 3.407629849863987 日月 => 2.594159659217386 光华 => 2.5888587096158195 站 => 2.5373335959864596 医院 => 2.466794953626745 服用 => 2.427780501982745 用药 => 2.3385075868344773 患者 => 2.189731233260211 疗效 => 2.1139510379118076 信 => 1.80543553127962 踟蹰 => 1.8029094677345425 转 => 1.8001968299641329 :VL-2343{n=33 c=[0:0.607, Top Terms: 南斯拉夫 => 5.631288615140048 南 => 3.5591550595832593 贝尔格莱德 => 3.450586463465835 联邦 => 3.1204751188104805 奇 => 2.422588232791785 维 => 2.275059295423103 艾滋病 => 2.135796171246153 捷克和斯洛伐克 => 1.4586484504468513 主席团 => 1.4190092086791992 公斤 => 1.3131130680893406 斯 => 1.301095182245428 世界 => 1.3008784380826084 锦标赛 => 1.2950631054964932 举重 => 1.2376490506258877 捷 => 1.2306104573336514 约 => 1.1500976157910896 抓举 => 1.1480039827751392 :VL-2449{n=169 c=[0:0.058, Top Terms: 南非 => 2.4718717823367147 德 => 2.0576189551833113 斯 => 2.0487567997543064 政府 => 1.8804266071883884 拉 => 1.7377890806931715 总统 => 1.6661139437432826 克 => 1.6181880538985574 登山队 => 1.4799560146218926 说 => 1.3736776515576967 曼 => 1.3692306117898614 队员 => 1.252501611878886 尼 => 1.2135252077903973 勒 => 1.1693784076081226 谈判 => 1.1128501186709432 珠峰 => 1.0986239952448558 和平 => 1.063394697460197 登山 => 1.0609329917727137 米 => 1.0526550916525035 :VL-2453{n=139 c=[0:0.117, Top Terms: 企业 => 4.361233001132663 百分之 => 2.8054099511757173 产品 => 2.674592695647864 生产 => 2.524535011044509 厂 => 1.8252047874944672 市 => 1.8030069234559862 万元 => 1.7715195065779652 记者 => 1.7418461429129402 职工 => 1.6803903682626409 万 => 1.5448395691329626 市场 => 1.5137106600425225 亿元 => 1.5080944273969252 商品 => 1.4282069789419929 年 => 1.360160093513324 资金 => 1.3422993584502516 工作 => 1.3264408111572266 全省 => 1.3161836013519506 :VL-2557{n=113 c=[0:0.415, Top Terms: 艺术 => 5.423138409589244 一个 => 4.151512909779506 经济 => 3.825582626646599 中国 => 3.7311821785648314 社会 => 3.5666315450077564 发展 => 3.356670286803119 一种 => 3.2347239920523316 作品 => 2.956042812988821 创作 => 2.898671289460849 文化 => 2.6856223292055383 历史 => 2.6831590053254524 生活 => 2.6319994209087 年 => 2.628407708311503 精神 => 2.5926266687106243 改革 => 2.446033749960165 时代 => 2.4163317933546757 影响 => 2.4050021213767803 市场 => 2.3764065725613484 :VL-2562{n=190 c=[0:0.171, Top Terms: 教育 => 7.981883796892668 学校 => 7.515391943329259 学生 => 7.197870181736193 教师 => 6.3607380616037466 教学 => 6.0520290951979785 校 => 3.320398147482621 培养 => 3.243756129867152 办学 => 3.2140094832370156 学习 => 2.852121488671554 发展 => 2.8008099468130814 提高 => 2.5831266930228787 社会 => 2.5024513420305756 建设 => 2.4907332934831317 素质 => 2.4616611430519506 工作 => 2.379560159382067 改革 => 2.3148778877760234 小学 => 2.2885150382393284 :VL-2607{n=200 c=[0:0.132, Top Terms: 苏联 => 2.2601522755622865 巴勒斯坦 => 2.089320592880249 以色列 => 2.0216490983963014 德 => 1.952136721611023 说 => 1.9029983282089233 德国 => 1.8865484476089478 阿拉伯 => 1.816526529788971 统一 => 1.8078111124038696 会议 => 1.731282593011856 欧洲 => 1.6291786336898804 外长 => 1.5735696864128113 北约 => 1.5375810956954956 总统 => 1.3768584966659545 美国 => 1.3483693027496337 会谈 => 1.2561198592185974 首脑会议 => 1.2024687767028808 条约 => 1.1398370552062989 西德 => 1.1342585563659668 :VL-2710{n=102 c=[0:0.604, Top Terms: 作战 => 4.957377648821064 美军 => 4.773399016436408 导弹 => 4.392558345607683 武器 => 4.18774580488018 飞机 => 4.021874432470284 2003 => 3.787949333003923 攻击 => 3.67272798687804 系统 => 3.6558577804004444 目标 => 3.5046984728644874 发射 => 3.4662113470189713 美国 => 3.1025989569869696 装备 => 3.0344099530986712 型 => 2.937773134194168 战争 => 2.9321658377553903 能力 => 2.905854017126794 伊拉克 => 2.842049196654675 飞行 => 2.800501814075545 :VL-2728{n=28 c=[0:0.704, Top Terms: 济南市 => 11.213080559458051 冠 => 10.98021251814706 拟定 => 10.877351232937404 地点 => 9.18290649141584 电话 => 9.089401892253331 合作 => 8.241872412817818 2003 => 7.943563427243914 单位 => 7.340387276240757 万元 => 7.221275857516697 方式 => 6.639718634741647 权 => 6.53313558442252 时间 => 6.43909410067967 报价 => 6.3524631432124545 广场 => 6.273530312946865 名 => 6.234395495482853 文化局 => 5.748960937772479 演出 => 5.590298243931362 :VL-2776{n=83 c=[0:2.684, Top Terms: 队 => 5.78382094222379 中国队 => 5.230112075805664 比赛 => 4.189694381621947 胜 => 3.159529191901885 0 => 2.6837377433317253 单打 => 2.4401888043047433 冠军 => 2.257155427013535 15 => 2.2092650252652457 3 => 2.2085073741085557 2 => 2.2018697721412384 古巴 => 2.1815123443143913 两 => 2.113119294844478 盘 => 2.081621089613581 中国 => 2.0202517681811227 双打 => 2.003711602774011 陈 => 1.9716711159212044 比分 => 1.9259789535798222 :VL-2788{n=74 c=[0:0.368, Top Terms: 军 => 2.739843020568023 部队 => 2.1953695400341138 工作 => 1.924035897126069 干部 => 1.694316393620259 战士 => 1.6576575975160341 县 => 1.6169729103913177 群众 => 1.513501930881191 越 => 1.495468958004101 年 => 1.4518073346163776 领导 => 1.3766053818367623 兵 => 1.3431222954311886 部 => 1.3383580188493471 队 => 1.3104517330994476 4 => 1.3000589995770842 李 => 1.2864641176687706 同志 => 1.2766218249862258 :VL-2801{n=171 c=[0:1.994, Top Terms: 软件 => 5.707896224239416 程序 => 4.562264286286649 用户 => 4.210792218035425 系统 => 4.111444738176134 数据 => 3.5135713571693463 网络 => 3.258378377434803 一个 => 3.216092968544765 站 => 3.1905950747038188 日月 => 3.1363124666158217 公司 => 3.1346129980700757 光华 => 3.1343329412895335 电脑 => 3.1174657693383288 计算机 => 3.113009349644533 技术 => 2.9617012578841537 开发 => 2.88567674787421

浙公网安备 33010602011771号