MAHOUT文本向量相似度计算

6 篇文章 0 订阅
5 篇文章 0 订阅

相似度计算算法

mahout源包中包含了common,hadoop,lucent及mahout核心算法相关的classes,其中,对于mahout中常用的推荐,聚类及分类中的相似度计算,mahout中提供了若干种向量相似度计算的方法,如下图所示
这里写图片描述

关于每种相似度的计算原理这里不作详细介绍说明,可以参考以下博客:
http://www.cnblogs.com/dlts26/archive/2012/06/20/2555772.html
主要的有欧几里德,皮尔森及cos等算法的说明;下面,我将以现有的数据做实际应用的说明

相似度计算案例

  • 准备工作
    这里最好使用最新版mahout.0-11-0版本,旧版本在使用及类的接口上与新版本存在不兼容现象,很容易就报错,此外,我们使用的数据仅保存在一个文本文件中,且每行代表一条记录(这种存储格式是痛苦折磨的根源所在),如下所示:

阳江市江城区全区XXX兴安小区(XXXX城斜对面)
阳江市海陵区全区闸XX镇XX山庄X幢XX工商分局(XX大酒店对面)
天津市东丽区全区XX区XX路XX宿舍X号楼X门XXX
天津市塘沽区全境红光家园XXX

该数据集存在两个问题,一是分行保存,mahout中的seqdirectory命令默认每个文档存储进一个key-value对中;二是中文分词,mahout中虽然提供sequencefile转向量的seq2sparse命令,同时提供中文分词类(org.apache.lucene.analysis.cn.ChineseAnalyzer),但是实际中仍然会存在很多问题;如果从开始就解决这两个问题,该相似度计算可能很容易就实现,但是。。。

  • 实际计算过程
    首先,从hive中导出待分析的数据至hdfs下(可直接供mahout使用,避免存到本地的资源浪费),这里面存在的问题是中文乱码问题,使用hive中的insert命令保存到hdfs中的可能是已压缩数据文件(deflate格式),所以直接使用mahout中的seqdirectory命令后,产生的文件均是乱码,失败。解决的方案是使用linux中的管道模式将数据保存成文本文件,或者,将hive中输出文件格式的设置修改为: set hive.exec.compress.output=false,保证输出结果为非压缩数据,此外,在这步导出数据的同时,我们可以把中文分词的工作一并交给hive完成;

    这里,重写UDF接口实现hive的自定义的中文切词函数,这里可以直接使用类包IK-Analyzer,注意添加jar包时保证相关类一并打包上传,才能保证自定义函数可用,如下:

    public String evaluate(String str) {
        String items="";
        Analyzer analyzer = new IKAnalyzer(true);
        TokenStream ts = null;
        try {
            ts = analyzer.tokenStream("myfield", new StringReader(str));
            CharTermAttribute term = ts.addAttribute(CharTermAttribute.class);              
            ts.reset(); 
            while (ts.incrementToken()) {
                items=items+term.toString() + " ";
            }
            ts.end();       
        } catch (IOException e) {
            e.printStackTrace();
        } 
        return items;     
    }

,至此,我们默认所需要的数据已经经过分词且以文本形式保存到hdfs环境下了。

  • 相似度计算
    如果每行记录以当个文档形式保存,那么问题不大,你可以使用seqdirectory和seq2sparse命令实现以下工作,但是,当你仅用一个文档保存下所有的数据时,这些就需要重新做工作了。
    首先,你可以使用seqdirectory命令将该文本数据转化成hadoop平台下的序列文件保存,使用seqdumper命令查看时,你会发现,显示的数据只有一个key-value键值对,显然,这不符合转化向量的前提,否则你转化后的向量不会存在,样式如下所示:
    这里写图片描述

所以,到达这一步后,我们需要对value中的值进行行拆分,这里,mahout中提供了一个可供修改用的类org.apache.mahout.text.TextParagraphSplittingJob,但是,查看源码会发现有两个问题,一是该来默认使用(’\n\n’)来分段落,不适合行记录(除非保存原始数据的时候就多输出一个\n),二是拆开成无数的key-value对时,你会发现所有的key值一样,这样后面使用seq2sparse时仍会出现问题,不会产生向量集,故下面的工作是,先利用TextParagraphSplittingJob来完成行记录拆分同时另外赋值key的名称(其实后面可以利用主键来标识,所以还是源数据的格式定义问题,mahout中的rowid命令也可实现对序列文件key的重新定义,但有格式限制),修改后的源码如下:

 public static class SplitMap extends Mapper<Text,Text,Text,Text> {
    @Override
    protected void map(Text key, Text text, Context context) throws IOException, InterruptedException {
      Text outText = new Text();
      Text keys = new Text();
      int loc = 0;
      while (loc >= 0 && loc < text.getLength()) {
        int nextLoc = text.find("\n", loc + 1);
        if (nextLoc > 0) {
          outText.set(text.getBytes(), loc, nextLoc - loc);
          keys.set(loc+"_"+nextLoc);
          context.write(keys, outText);
        }
        loc = nextLoc;
      }
    }
  }

需要注意的是,尽量保持一个版本类使用,不同的版本的使用极容易产生错误提示: Can not create a Path from a null string,同时传参的格式也需要注意,如下所示运行命令:

hadoop jar splitlines.jar org.apache.mahout.text.TextParagraphSplittingJob -Dmapred.input.dir=input/addr_seq -Dmapred.output.dir=input/addr_seqs

这些工作做完后,得到我们最终需要的序列文件,然后使用seq2sparse命令生成向量文件,注意参数-a就是指明切词类的,如下:
mahout seq2sparse -i input/addr_seqs/ -ow -o input/addr_vec -nv -a org.apache.lucene.analysis.core.WhitespaceAnalyzer

最终产生的数据文件如下所示,而tfidf-vectors即是我们需要的用于相似度计算的数据集:
这里写图片描述

最后,就是利用产生的tfidf-vectors数据集进行相似度计算,其中org.apache.mahout.cf.taste.hadoop.similarity.item.ItemSimilarityJob类一般用于用户编号关系的计算,且不需要向量格式数据源;我们利用org.apache.mahout.math.hadoop.similarity.cooccurrence.RowSimilarityJob类完成相似度计算工作,可以利用-s参数制定需要的相似度计算算法,最后的最后,再次强调,运行的所有命令尽量保持一个版本完成,我就是因为数据格式的不对加上两个版本的mahout的使用才导致这么多曲折。

求助:如何将一个文本矩阵向量转化为mahout的向量对象?(聚类源码应该有这方面的提示)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值