基于hadoop的分布式分词程序(庖丁分词)

编程语言 来源:gywtzh0889 12℃ 0评论

一、使用的分词包——庖丁分词器介绍

1.1、简介:

庖丁系统是个完全基于lucene中文分词系统,它就是重新建了一个analyzer,叫做PaodingAnalyzer,这个analyer的核心任务就是生成一个可以切词TokenStream。

1.2、优点:

这里之所以使用庖丁主要考虑到庖丁的分词效率比其他的分词器要高

1.3、缺点:

其分词有一个缺点

例如下面一段文字:

“发展社区老年活动场所和服务设施”
如果想搜索日本的和服相关资料,输入关键字“和服”的时候,上面的资料也会被搜索出来
搜索引擎是第一步搜索:
在浩瀚的信息中,快速集结最后可能是所想要的结果, 按照可能是最好的顺序展现出来。
人的眼睛是第二步搜索:
找寻最符合要求的结果,同时将机器无法轻易识别的少数“无效”结果过滤
“和服”问题,涉及了汉语语义的问题,几乎不可完全解决(可作为“特例”解决,或通过排序方法,将他排到相对靠后等价解决)。

但是这同时也是庖丁分词的一个有点,因为对于一般的分词而言要确定出到底是怎样的一种组合也只是选择概率较大的,将可能的组合全部给出给了我们选择的机会。

——摘自百度百科


这里我把我用的版本和词典文件共享到网盘欢迎下载:http://pan.baidu.com/s/1eQekI9k

二、分词程序单机测试:

2.1、导入相应的分词包、词典

需要的包:

字典:将整个dic文件拷贝到src目录下

2.2、java分词程序:(注意:这里并没有去除噪音和停用词,只是简单的测试庖丁分词的可用性)

[csharp]  view plain   copy
 
  1. public class TestPaoding {  
  2.     public static void main(String[] args) {  
  3.         String line = "亚洲:中华人民共和国";  
  4.         PaodingAnalyzer analyzer = new PaodingAnalyzer();  
  5.         StringReader sr = new StringReader(line);  
  6.         TokenStream ts = analyzer.tokenStream("", sr);  
  7.         try{  
  8.             while(ts.incrementToken()){  
  9.                 CharTermAttribute ta = ts.getAttribute(CharTermAttribute.class);  
  10.                 System.out.println(ta.toString());  
  11.             }  
  12.         }catch(Exception e){  
  13.               
  14.         }  
  15.     }  
  16. }  

2.3、单机程序测试结果:

 

三、单机程序mapreduce化:

3.1、开发环境:

伪分布式系统

hadoop1.1.2平台

开发环境eclipse

需要的包跟上面写的一样,这里就不赘余了

3.2、mapreduce程序:

mapreduce计算框架浅析:

这里因为只是进行简单的分词,没有必要多个mapper,也不需要reducer。

这里的inputformat没有加控制,如果文件很多时,就一定要对inputformat进行控制,控制分片的大小,这样才能保证mapreduce的计算效率最高

[java]  view plain   copy
 
  1. package test;  
  2.   
  3. import java.io.IOException;  
  4. import java.io.StringReader;  
  5.   
  6. import net.paoding.analysis.analyzer.PaodingAnalyzer;  
  7.   
  8. import org.apache.hadoop.conf.Configuration;  
  9. import org.apache.hadoop.conf.Configured;  
  10. import org.apache.hadoop.fs.Path;  
  11. import org.apache.hadoop.io.LongWritable;  
  12. import org.apache.hadoop.io.Text;  
  13. import org.apache.hadoop.mapreduce.Job;  
  14. import org.apache.hadoop.mapreduce.Mapper;  
  15. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;  
  16. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;  
  17. import org.apache.hadoop.util.Tool;  
  18. import org.apache.hadoop.util.ToolRunner;  
  19. import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;  
  20.   
  21. public class TestTOkenizer extends Configured implements Tool {  
  22.     public static class Map extends Mapper<LongWritable,Text,Text,Text>{  
  23.         public static void main(String[] args)throws Exception{  
  24.             int res = ToolRunner.run(new Configuration(), new TestTOkenizer(), args);  
  25.             System.exit(res);  
  26.         }  
  27.         //计数器,当在跑数据的时候出错,计数器就起作用了,能统计出多少数据是成功完成了的  
  28.         enum Counter  
  29.         {  
  30.             LINESKIP,  
  31.         }  
  32.         //Map程序  
  33.         public void map(LongWritable key,Text value,Context context) throws IOException, InterruptedException{  
  34.             //获取单行文本内容  
  35.             //1 这里是长江大学石油软件实验班,我是张三  
  36.             //这里为了模仿微博分词过程中前面可能都有一个ID号,所以在输入的要进行分词的String中也加入了序号  
  37.             String line = value.toString();  
  38.             try  
  39.             {  
  40.                 //根据空格进行字符串拆分  
  41.                 String[] lineSplit = line.split("[\\s]+");  
  42.                 //将第一个序号放在anum变量中  
  43.                 String anum = lineSplit[0];  
  44.                 //将文本内容放到bnum变量中  
  45.                 String bnum = lineSplit[1];  
  46.                 //初始化庖丁分词器  
  47.                 PaodingAnalyzer analyzer = new PaodingAnalyzer();  
  48.                 //根据文本内容构造StringReader  
  49.                 StringReader sr = new StringReader(bnum);  
  50.                 //用来整合分好的单个词  
  51.                 StringBuilder sb = new StringBuilder();  
  52.                 //去停用词后的文本  
  53.                 String resultString="";  
  54.                 //分词  
  55.                 org.apache.lucene.analysis.TokenStream ts = analyzer.tokenStream("", sr);  
  56.                 try{  
  57.                     while(ts.incrementToken()){//将单个分好的词组装成一个StringBuilder,方便后面的去噪和去停用词  
  58.                         CharTermAttribute ta = ts.getAttribute(CharTermAttribute.class);  
  59.                         sb.append(ta.toString()+"   ");  
  60.                     }  
  61.                     //构造去停用词对象  
  62.                     FileExcludeStopWord fileExcludeStopWord = new FileExcludeStopWord();  
  63.                     //去停用词,返回最终的分词结果  
  64.                     resultString = fileExcludeStopWord.fileExcludeStopWord(sb.toString());  
  65.                 }catch(Exception e){  
  66.                       
  67.                 }  
  68.                 //将结果输出到hdfs中  
  69.                 context.write(new Text(anum), new Text(resultString));  
  70.             }  
  71.             catch(java.lang.ArrayIndexOutOfBoundsException e){  
  72.                 context.getCounter(Counter.LINESKIP).increment(1);  
  73.                 return;  
  74.             }  
  75.         }  
  76.     }  
  77.     //配置、提交任务  
  78.     public int run(String[] args) throws Exception {  
  79.         Configuration conf = getConf();  
  80.         Job job = new Job(conf, "TestTOkenizer");//任务名  
  81.         job.setJarByClass(TestTOkenizer.class);  
  82.         FileInputFormat.addInputPath(job, new Path(args[0]));//配置输入路径  
  83.         FileOutputFormat.setOutputPath(job, new Path(args[1]));//配置输出路径  
  84.         job.setMapperClass(Map.class);//指定Map类的位置  
  85.         job.setOutputFormatClass(org.apache.hadoop.mapreduce.lib.output.TextOutputFormat.class);  
  86.         job.setOutputKeyClass(Text.class);  
  87.         job.setOutputValueClass(Text.class);  
  88.         job.waitForCompletion(true);  
  89.         //接下来的两句,是自己测试玩的,查看下job的状态  
  90.         System.out.println("job's name"+job.getJobName());  
  91.         System.out.println("job status"+(job.isSuccessful()?"yes":"no"));  
  92.         return job.isSuccessful()?0:1;  
  93.     }  
  94. }  

3.3、去停用词类(这个类写的还不是很完美,停用词表也还不够完善,微博数据中,有一些没包含在停用词表中)

 

[java]  view plain   copy
 
  1. package test;  
  2. import java.io.BufferedReader;  
  3. import java.io.FileInputStream;  
  4. import java.io.InputStreamReader;  
  5. import java.util.HashSet;  
  6. import java.util.Set;  
  7.   
  8. public class FileExcludeStopWord {  
  9.     public static final String stopWordFile = "/home/jonsen/workspace/testPaoding/src/test/stopword.txt";  
  10.     public String fileExcludeStopWord(String splitResultStr) {  
  11.         try {  
  12.             BufferedReader stopWordFileBR = new BufferedReader(  
  13.                     new InputStreamReader(new FileInputStream(stopWordFile)));  
  14.             // 用来存放停用词的集合  
  15.             Set stopWordSet = new HashSet();  
  16.             // 初始化停用词表  
  17.             String stopWord = null;  
  18.             while ((stopWord = stopWordFileBR.readLine()) != null) {  
  19.                 stopWordSet.add(stopWord);  
  20.             }  
  21.             if(stopWordFileBR!=null){  
  22.                 stopWordFileBR.close();  
  23.             }  
  24.             // 得到分词后的词汇数组,以便后续比较  
  25.             String[] resultArray = splitResultStr.split("[\\s]+");  
  26.             // 过滤停用词  
  27.             for (int i = 0; i < resultArray.length; i++) {  
  28.                 if (stopWordSet.contains(resultArray[i])) {  
  29.                     resultArray[i] = null;  
  30.                 }  
  31.             }  
  32.             // 把过滤后的字符串数组存入到一个字符串中  
  33.             StringBuilder finalStr = new StringBuilder();  
  34.             for (int j = 0; j < resultArray.length; j++) {  
  35.                 if (resultArray[j] != null) {  
  36.                     finalStr = finalStr.append(resultArray[j]).append(" ");  
  37.                 }  
  38.             }  
  39.             return finalStr.toString();  
  40.   
  41.         } catch (Exception e) {  
  42.             e.printStackTrace();  
  43.             return "";  
  44.         }  
  45.     }  
  46. }  

 

3.4、上传文件到hdfs中:(直接右键上传文件即可)

文本文件内容:

3.5、配置输入输出路径运行程序:

注意:如果输出路径已经存在,一定要预先删除,否则会报错:

3.6、分词结果:

四、总结

到这里整个基于分布式的分词程序就完成了,但是只能处理文件数量不多的文本,大文件处理是没有问题的,如果都是单个的小文件,就需要自定义inputformat了,自己控制分片大小,才能提高效率!从分词的结果来看,很显然很多停用词是没有去掉的,很多没有意义的词语,噪音较大,还有一个问题就是庖丁的分词器,给出的所有可能的组合,这个虽好但是还需要花精力去分辨哪些是无用的,一般情况下,最长的是可能的组合的可能性比较大!