基于hadoop搜索引擎实践——生成倒排表文件(三)

来源:互联网 发布:新破天一剑淘宝 编辑:程序博客网 时间:2024/05/20 09:21
1.源文件过滤
    在对源文件进行功能性处理之前,有必要对生成的源文件进行一次预分析和过滤。
    (1)去重,过滤掉爬取过程中重复的帖子,保持帖子的唯一性。
    (2)过滤不符合要求的帖子,比如获取的信息不能正常转为json格式的数据。内容全部为空的数据等。
    这部分过滤处理相对简单,在map阶段,把帖子的url作为key,map中的value仍为value,组成<key,value>传输到reduce中。在reduce接段,同一个key,只输出一次。代码如下:
    
public static class RemoveRepeatMapper extends Mapper<LongWritable, Text, Text, Text>{        private static Gson gson=new Gson();        @Override        protected void map(LongWritable key, Text value, Context context)                throws IOException, InterruptedException {            BBS bbs=gson.fromJson(value.toString(), BBS.class);            context.write(new Text(bbs.getUrl()), value);        }    }       public static class RemoveRepeatReducer extends Reducer<Text, Text, NullWritable, Text>{        @Override        protected void reduce(Text key, Iterable<Text> values,                Context context)                throws IOException, InterruptedException {            while(values.iterator().hasNext()){                context.write(NullWritable.get(), values.iterator().next());                break;            }        }    }

2.生成倒排索引文件
    倒排文件使用文档中所含有的关键词作为索引,把文档作为索引目标的一种结构(类似于有些纸质书籍的索引附录中,用关键词做索引,书的页面是索引目标)。这部分工作中是整个系统的重点,大致工作过程有如下3步。
    <1>对已过滤的源文件中的每条记录进行切分,并将每条记录中的title和content转化为一组词的集合。
    <2>为了在后期能够对用户查询的结果进行排序并显示摘要,在对帖子分词过程中,还需要计算出每个索引词对该帖子的相关度(Rank),以及在该帖子中出现的位置(Position).
    <3>在完成上述的分析和准备工作之后,设计MapReduce算法,把帖子和索引词之间的映射转化为索引词到帖子的映射,并在此过程中计算并统计索引词相关帖子的Rank和Position,由此生成的文件成为倒排表。
    (1)分词
    获取的帖子内容可能是中文和英文,在英文中是通过空格分隔,而在中文中的词汇大多数由两个或者两个以上的汉字组成,并且语句是连续书写的。在对中文文本进行自动分析前,需要将整句切割成小的词汇但愿,即中文分词(或中文切词),被系统采用开源分词软件IKAnalyzer.经过分词处理,一片帖子切分出来的有效的词语数量大约在及时到几百不等。
    (2)索引词的Rank和position
    计算并统计索引词相对于帖子的相关度(Rank)的目的是能够排序显示用户查询到结果帖子;记录索引词在该帖子中的位置(Posistion)用于显示查询结果时,能够生成该词在帖子中的部分摘要。
    关于Rank的计算有Google著名的PageRank算法,该算法的主要思路是越链接的次数越多的网页,其重要程度和知名程度越高,相应的Rank值也就越高。而本系统是一条条帖子,这些帖子基本都是独立的,且帖子与帖子之间几乎不存在网页上的链接关系。因此本系统并没有采用PageRank算法,而是针对自身的特点采用改进的TF-IDF算法用于计算Rank值。
    TF-IDF(Term Frequency-Inverse Document Frequency)算法的大致思想有亮点。第一,如果一个词在某个文档中出现的频率越高,那么这个词语与这边文档的相关度(rank)也就越高;第二,如果一个词在越多的文档中出现,则该词用于区分文档相关性的作用也就越小。TF-IDF算法的公式描述如下

式中,式(1)中的表示词t在文档n中出现的次数,表示文档n中词的总个数;式(2)中的|D|表示库中文档的总数目;表示包含词t的文档的数据;式总的是指词t与文档n的相关度

    本系统改进的部分是将词出现在文档的区域(标题或者正文)加入计算Rank的考虑范畴,改进后的算法思想描述如下:

    (1)令词i与帖子j的相关度表示为R,这个值越大表示越相关,初始为0

    (2)统计词i在帖子j的标题中出现的次数,每次出现一次,则R的值增加5

    (3)统计词i在帖子j的正文中出现的次数,每次出现一次,则R的值加1

    (4)在所有帖子中统计出包含词i的帖子的数据,记为num

    (5)最后计算出R的值等于R/num

相对于Rank的计算,记录Position则要容易得多,下面对此进行简单的介绍。

    IKAnalzer分词软件在切分出每个词时,同时会输出这个词在帖子中出现的起始和结束为止(相对于帖子首部的偏移量)。因此,Position信息只要用两个整数表示即可,本系统用(start,end)来表示。

    由于在MapReduce的计算过程中,需要在Map和Reduce阶段之间传递Rank和Position,因此本系统在将Rank和Position封装成类之后,继承了Hadoop提供的IO类中的Writable类,重新实现了一个Writable类RecordWritable ,用于封装并序列化传输Rank和Position的信息。类RecordWritable 的核心实现代码如下:

public class RecordWritable implements WritableComparable<RecordWritable> {    private LongWritable DID = new LongWritable();    private FloatWritable rank = new FloatWritable();    private Text positions = new Text();    public RecordWritable() {    }    public RecordWritable(LongWritable DID, FloatWritable rank, Text positions) {        set(DID, rank, positions);    }    public void set(LongWritable DID, FloatWritable rank, Text positions) {        this.DID.set(Long.valueOf(DID.toString()).longValue());        this.rank.set(Float.valueOf(rank.toString()).floatValue());        this.positions.set(positions.toString());    }    public void set(long DID, float rank, String positions) {        this.DID.set(DID);        this.rank.set(rank);        this.positions.set(positions);    }    public void set(long DID, RankPosition rankPosition) {        this.DID.set(DID);        this.rank.set(rankPosition.getRank());        this.positions.set(rankPosition.getPositions());    }    public void setDID(long DID) {        this.DID.set(DID);    }    public void setRank(float rank) {        this.rank.set(rank);    }    public void setPositions(Text positions) {        this.positions.set(positions);    }    @Override    public void readFields(DataInput in) throws IOException {        // TODO Auto-generated method stub        this.DID.readFields(in);        this.rank.readFields(in);        this.positions.readFields(in);    }    @Override    public void write(DataOutput out) throws IOException {        // TODO Auto-generated method stub        this.DID.write(out);        this.rank.write(out);        this.positions.write(out);    }    public LongWritable getDID() {        return DID;    }    public FloatWritable getRank() {        return rank;    }    public Text getPositions() {        return positions;    }    @Override    public boolean equals(Object obj) {        if (obj instanceof RecordWritable) {            RecordWritable tmp = (RecordWritable) obj;            return this.DID.equals(tmp.DID) && this.rank.equals(tmp.rank)                    && this.positions.equals(tmp.positions);        }        return false;    }    @Override    public String toString() {        return this.DID.toString() + ":" + this.rank.toString() + ":"                + this.positions.toString();    }    @Override    public int compareTo(RecordWritable tmp) {        return this.DID.compareTo(tmp.DID);    }}

    具体实现代码可以查看:
    离线处理程序:http://download.csdn.net/detail/long1657/8059593
    在线处理程序:http://download.csdn.net/detail/long1657/8059567
参考文献:
1.刘鹏,hadoop实战,电子工业出版社,2011.9
0 0