blog.Ring.idv.tw

Articles

Pairwise Document Similarity in Large Collections with MapReduce

Pairwise Document Similarity in Large Collections with MapReduce.這是一篇由UMD的一位博士生Tamer M. Elsayed和他的指導教授所共同發表在「ACL-08: HLT」的短篇論文,主要用「MapReduce」來處理大量文件相似度的計算,如果您對這篇論文有興趣的話,請參考上述論文連結,筆者不再詳述。

下述筆者撰寫的驗證程式需要用到「Cloud9 - A MapReduce Library for Hadoop」,「Cloud9」是由UMD所開發的,主要用來作為課程的教學工具和一些文字處理方面的研究,它採用Apache License,所以您可以直接用Subversion checkout下來使用,而下述範例主要用到「PairOfIntString」和「ArrayListWritable」。

Pairwise Document Similarity

在進行「Pairwise Similarity」的「Map」階段時,筆者純粹利用Regular Expression來處理~ 這並不是最佳的處理方式(我承認偷懶~),最佳的方式應該撰寫一些特定的「OutputFormat」和「Writable」來加以處理,整個效率才會大大的提高!(如:Cloud9所提供的Tuple)

由於此範例需要處理二次的MapReduce,所以筆者直接利用「job2.addDependingJob(job1);」將兩個Job產生相依性,也就是先執行job1完成之後JobControl才會去呼叫job2開始執行。

import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapred.*;
import org.apache.hadoop.mapred.jobcontrol.Job;
import org.apache.hadoop.mapred.jobcontrol.JobControl;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

import edu.umd.cloud9.io.ArrayListWritable;
import edu.umd.cloud9.io.PairOfIntString;

public class PairwiseDS extends Configured implements Tool
{

    public static class Map extends MapReduceBase implements
            Mapper<LongWritable, Text, Text, PairOfIntString>
    {
        private Text word = new Text();

        public void map(LongWritable key, Text value,
                OutputCollector<Text, PairOfIntString> output, Reporter reporter)
                throws IOException
        {
            FileSplit fileSplit = (FileSplit) reporter.getInputSplit();
            String fileName = fileSplit.getPath().getName();
            fileName = fileName.substring(0, fileName.length() - 4);

            String line = value.toString();
            StringTokenizer tokenizer = new StringTokenizer(line);
            while (tokenizer.hasMoreTokens())
            {
                word.set(tokenizer.nextToken());
                output.collect(word, new PairOfIntString(1, fileName));
            }
        }
    }

    public static class Reduce extends MapReduceBase implements
            Reducer<Text, PairOfIntString, Text, ArrayListWritable>
    {
        public void reduce(Text key, Iterator<PairOfIntString> values,
                OutputCollector<Text, ArrayListWritable> output,
                Reporter reporter) throws IOException
        {

            ArrayList<PairOfIntString> al = new ArrayList<PairOfIntString>();
            HashMap<String, Integer> map = new HashMap<String, Integer>();

            while (values.hasNext())
            {
                PairOfIntString psi = values.next();
                if (map.containsKey(psi.getRightElement()))
                {
                    Integer i = (Integer) map.get(psi.getRightElement());
                    map.put(psi.getRightElement(), i.intValue() + 1);
                } else
                {
                    map.put(psi.getRightElement(), psi.getLeftElement());
                }
            }
            Iterator i = map.entrySet().iterator();
            while (i.hasNext())
            {
                java.util.Map.Entry m = (java.util.Map.Entry) i.next();
                al.add(new PairOfIntString((Integer) m.getValue(), (String) m
                        .getKey()));
            }
            output.collect(key, new ArrayListWritable<PairOfIntString>(al));

        }
    }

    public static class Map2 extends MapReduceBase implements
            Mapper<LongWritable, Text, Text, IntWritable>
    {
        private Text word = new Text();

        public void map(LongWritable key, Text value,
                OutputCollector<Text, IntWritable> output, Reporter reporter)
                throws IOException
        {
            String line = value.toString().trim();

            ArrayList<String> keyList = new ArrayList<String>();
            ArrayList<Integer> valList = new ArrayList<Integer>();

            String p = "\\(([0-9]+), ([a-z0-9.]+)\\)";
            Pattern r = Pattern.compile(p);
            Matcher m = r.matcher(line);
            while (m.find())
            {
                String k = m.group(2);
                String v = m.group(1);
                keyList.add(k);
                valList.add(new Integer(v));
            }

            if (keyList.size() > 1)
            {
                String[] key_arr = keyList.toArray(new String[0]);
                Integer[] val_arr = valList.toArray(new Integer[0]);
                int klen = key_arr.length;
                for (int i = 0; i < klen; i++)
                {
                    for (int j = i + 1; j < klen; j++)
                    {
                        word.set(key_arr[i] + "," + key_arr[j]);
                        output.collect(word, new IntWritable(val_arr[i]
                                * val_arr[j]));
                    }
                }
            }

        }
    }

    public static class Reduce2 extends MapReduceBase implements
            Reducer<Text, IntWritable, Text, IntWritable>
    {
        public void reduce(Text key, Iterator<IntWritable> values,
                OutputCollector<Text, IntWritable> output, Reporter reporter)
                throws IOException
        {
            int sum = 0;
            while (values.hasNext())
            {
                sum += values.next().get();
            }
            output.collect(key, new IntWritable(sum));
        }
    }

    public int run(String[] args) throws Exception
    {
        // ===================== Indexing =====================
        JobConf conf = new JobConf(getConf(), PairwiseDS.class);
        conf.setJobName("Indexing");
        conf.setOutputKeyClass(Text.class);
        conf.setOutputValueClass(PairOfIntString.class);

        conf.setMapperClass(Map.class);
        conf.setReducerClass(Reduce.class);

        conf.setInputFormat(TextInputFormat.class);
        conf.setOutputFormat(TextOutputFormat.class);

        conf.setNumReduceTasks(1);
        FileInputFormat.setInputPaths(conf, new Path(args[0]));
        TextOutputFormat.setOutputPath(conf, new Path(args[1]));

        Job job1 = new Job(conf);
        // ===================== Pairwise Similarity =====================
        JobConf conf2 = new JobConf(getConf(), PairwiseDS.class);
        conf2.setJobName("Pairwise Similarity");
        conf2.setOutputKeyClass(Text.class);
        conf2.setOutputValueClass(IntWritable.class);

        conf2.setMapperClass(Map2.class);
        conf2.setReducerClass(Reduce2.class);

        conf2.setInputFormat(TextInputFormat.class);
        conf2.setOutputFormat(TextOutputFormat.class);
        conf2.setNumReduceTasks(1);

        FileInputFormat.setInputPaths(conf2, new Path(args[1] + "/p*"));
        TextOutputFormat.setOutputPath(conf2, new Path(args[2]));
        Job job2 = new Job(conf2);

        job2.addDependingJob(job1);
        JobControl controller = new JobControl("Pairwise Document Similarity");
        controller.addJob(job1);
        controller.addJob(job2);
        new Thread(controller).start();

        while (!controller.allFinished())
        {
            System.out.println("Jobs in waiting state: "+ controller.getWaitingJobs().size());
            System.out.println("Jobs in ready state: "+ controller.getReadyJobs().size());
            System.out.println("Jobs in running state: "+ controller.getRunningJobs().size());
            System.out.println("Jobs in success state: "+ controller.getSuccessfulJobs().size());
            System.out.println("Jobs in failed state: "+ controller.getFailedJobs().size());
            System.out.println();

            try
            {
                Thread.sleep(20000);
            } catch (Exception e)
            {
                e.printStackTrace();
            }
        }
        return 0;

    }

    public static void main(String[] args) throws Exception
    {
        int res = ToolRunner.run(new Configuration(), new PairwiseDS(), args);
        System.exit(res);
    }
}

after Indexing:

after Pairwise Similarity:

相關資源

Hadoop常用SDK系列四 JobControl

2009-01-05 22:30:04 | Comments (4)

PDFBox - 擷取PDF檔案中的純文字

PDFBox.是一個Open Source的Java PDF Library,可以利用它來協助處理PDF檔案的一些應用(用iText也是可行的,不過它好像不支援擷取純文字「iText in Action, pp. 576」),例如:擷取PDF檔案中的純文字、轉換PDF檔案到Image檔等等.. 諸如此類的應用。

而且「Lucene」就是用它來轉換PDF到純文字再進行索引的~

下述是擷取PDF檔案中的純文字:(FontBox-0.1.0-dev.jar、PDFBox-0.7.3.jar required!)

import java.io.IOException;
import org.pdfbox.pdmodel.PDDocument;
import org.pdfbox.util.PDFTextStripper;

public class PDFTextExtractor
{
    private PDDocument document;
    
    public String extractText(String file) throws IOException
    {
        document = PDDocument.load(file);       
        PDFTextStripper stripper = new PDFTextStripper();
        stripper.setStartPage(1);
        stripper.setEndPage(document.getNumberOfPages());
        return stripper.getText(document);
    }

    public static void main(String[] args)
    {
        PDFTextExtractor extractor = new PDFTextExtractor();
        try
        {
            String text = extractor.extractText("C:\\test.pdf");
            System.out.println(text);
        }catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}

相關資源

PDF Reference and Adobe Extensions to the PDF Specification - PDF規格書

Glyph & Cog: Text Extraction - 解釋為何擷取PDF中的純文字不是那麼容易

2009-01-03 18:17:22 | Comments (3)

Twitter - ChingShen

寫了Blog那麼久了... 居然今天才開始用「Twitter」,真是慚愧~~ 還是應該說潛水潛太深了~ XDDD

總之~ 我也要來碎碎唸了~ ^^a follow me: http://twitter.com/chingshen

P.S. 順便裝一下「twhirl」~ 好用的呢!! ^^

2009-01-03 15:18:54 | Comments (2)

邁向 - 2009 - 新年!!

(拿起我的小腳架... 在家自拍一下:p 其實這就是平常我做事的樣子~ 夠專注吧~XDDD)

用自家的「日曆」拿來當翅膀~ 因為要呈現「邁向」的感覺~ 並在翅膀的右半部色調採用「灰階」~ 用來加強表示從過去飛向未來(2009)的fu~ 就這樣~ ^^a

今年.. 嗯~ 很充實~~ 希望明年一樣充實就好~(<<這句話有玄機..XDD 夠忙了~ 保持這樣就好了~ XDDD)

另外格主算了一下~ 本網誌到今天的po文為止~ 平均每「二天」就有一篇po文~ 我的「宅度計」溫度應該算高了吧~ XDDD 不相信!!!! 請看下述程式(計算平均po文天數):

import java.util.Calendar;

public class Main
{
    public static void main(String[] args)
    {
        Calendar ec = Calendar.getInstance();
        ec.set(2009, Calendar.JANUARY,1);
        Calendar bc = Calendar.getInstance();
        bc.set(2007, Calendar.APRIL,30);          
        long t = ec.getTimeInMillis() - bc.getTimeInMillis();   
        long days = t/60/60/1000/24;
        System.out.println(days/306);
    }
}

而新的一年的第一個月的第一件事~ 嗯~ 我要和學弟實作一個延續我碩士成果的系統~ 這件事在我心中的Priority是排行第一的!! 先將這件事情做好~ 其它的事先行排除!!!!!!! 就這樣~ :p

2009-01-01 02:23:01 | Comments (4)

SwfVersion.com - 辨別你的Flash版本服務

SwfVersion.com.是一個由位於挪威的「Tarantell」公司所建立的服務~

如同它所註冊的網域名稱「SwfVersion.com」,它的服務就是提供您不論local或online的Flash動畫,都可以利用此服務來辨別這個動畫檔所發佈的「FlashPlayer版本」、「每秒播放的影格數(FPS)」和「ActionScript版本」。

我好奇的想知道這個服務的需求性和必要性~ 什麼時候我們需要取得這些資訊?(若客倌們有任何idea的話~ 麻煩回覆一下 ^^)

從另一個角度來看~ 的確,如同Google的作法~ 只會有愈來愈多的線上化服務~ 因為後端可以有一大片的「Cloud」當靠山。

相關消息

swfversion.com

Learn The Version Of A SWF File: SwfVersion

2008-12-30 20:05:56 | Add Comment

Next Posts~:::~Previous Posts
Copyright (C) Ching-Shen Chen. All rights reserved.

::: 搜尋 :::

::: 分類 :::

::: 最新文章 :::

::: 最新回應 :::

::: 訂閱 :::

Atom feed
Atom Comment