语料库中的高效搜索

时间:2010-01-24 22:31:36

标签: algorithm search data-structures hadoop mapreduce

我有几百万字,我想在十亿字的语料库中搜索。什么是有效的方法。

我在考虑使用trie,但是有没有可用的trie开源实现?

谢谢

- 更新 -

让我添加一些关于具体要求的细节。

我们有一个系统,我们抓取新闻来源,并根据单词的频率得到流行的单词。可能有一百万字。

我们的数据看起来像这样。

Word1频率1 Word2频率2 (制表符分隔)

我们还从其他来源获得了最受欢迎的词汇(10亿),其中也包含上述格式的数据。

以下是我想要输出的内容。

  • 两个来源共有的词语
  • 单词仅出现在我们的来源中,但不出现在参考来源中。
  • 单词仅出现在参考源中,但不在我们的来源中。

我只能使用comm(bash命令)来获取上述信息。我不知道如何使用comm来比较一列而不是两列。

系统应该是可扩展的,我们希望每天都执行此操作并比较结果。我也想得到大概的比赛。

所以,我正在考虑编写一个地图减少工作。我打算写下地图并减少功能如下,但我几乎没有问题。

Map
For each word
output key = word and value = structure{ filename,frequency}
done

Reduce
For each key
Iterate through all the values and check if both file1 and file2 are contained.
If yes, then write it to appropriate file.
If only in file1, write it to file1only file
If only in file2, write it to file2only file.
Done.

我有两个问题。 在map reduce中,我可以输入一个包含我的两个文件的目录。我不知道如何获取我正在阅读文字的文件名。如何获得这些信息? 如何写入不同的输出文件,因为reduce阶段只会自动写入名为part-xxxxx的默认文件。如何写入不同的输出文件。

感谢您阅读本文。

9 个答案:

答案 0 :(得分:2)

使用MapReduce,你不应该尝试在单步或工作中做所有事情。看起来您应该将此问题分解为多个步骤。由于您正在生成存储在HDFS上的数据,并且您需要知道源代码,因此您应该采用以下格式:

  

{SOURCE},{WORD},{FREQUENCY}

请记住,您所说的是分布式文件系统,因此将输入称为file1和file2在技术上并不正确。您的参考数据和源数据都将分布在整个群集中,每个节点都位于每个节点上。

接下来,从伪代码示例开始,您需要创建一个将单词与源及其频率相关联的作业。您的映射器可以正常工作,但reduce需要将单词链接到源。您将需要创建自己的Writable对象,其中包含Map<来源,频率>这将作为后续过滤器作业可以使用的中间数据输出到HDFS上。

然后,您可以使用此步骤的输出作为3个不同MapReduce作业的输入。每个人都在寻找不同的来源组合。这些作业非常简单,因为映射器只会传递相同的数据,但是reducer会检查不同源组合的每个值。

因此,如果采用这种方法,则需要4个MapReduce作业。您不需要手动运行每一个,您可以拥有一个按顺序运行每个作业的作业。或者,由于最后3个作业将使用相同的输入数据,因此您可以在第一个作业完成后同时启动这三个作业。这可能取决于您的群集能够管理的数据量和中间数据量,以及每个作业所需的映射器/缩减器数量。

希望这个建议有所帮助。

答案 1 :(得分:1)

这看起来像是为Aho-Corasick字符串搜索算法设计的作业。我自己从来没有对它进行编码,但谷歌搜索应该会出现一些代码。

Rabin-Karp也可能有效,但我不知道多种模式在长度不同时它是如何工作的。注意:维基百科文章中的多模式伪代码似乎是错误的。但是应该给你一个起点。

答案 2 :(得分:1)

本着快速和肮脏的精神:

fgrep --mmap -f query-file corpus-file

答案 3 :(得分:0)

如果我在Java中这样做,我会使用HashMap。 Wikipedia表明在某些情况下,特里略好一点,但我不确定你会看到多少差异。

答案 4 :(得分:0)

文本搜索引擎中使用的数据结构称为inverted index。正如已经说过的那样,非常好的开源搜索引擎是Lucene

答案 5 :(得分:0)

我不确定它的性能,但Python的nltk旨在做这样的事情:标记大型文本语料库并允许你在它们之间进行比较。 “使用Python进行自然语言处理”这本书使用了这个工具包并有很多例子。它免费提供online

答案 6 :(得分:0)

编译为a.out的tokenizer.c可以对语料库进行标记,然后使用systemclose shell脚本实现高效性能

 ./a.out <
/live/memory/var/cache/man/whatis  | sort | awk {'print $1'} | uniq -c
| sort -rn > file.txt

答案 7 :(得分:0)

台式电脑可以做到这一点。较小的数据集将适合内存,这就是你所需要的。

在Python中:

# Load the words from the small file into one big hash set
small_set = set(line.split()[0] for line in open("small.txt", "r"))

# Open 3 output files.
f1 = open("common.txt", "w")
f2 = open("large_only.txt", "w")
f3 = open("small_only.txt", "w")

# Find all words in the large set that aren't in the small set.
for line in open("large.txt", "r"):
    word = line.split()[0]
    if word in small_set:
        f1.write(line)  # word is in both sets
        small_set.remove(word)
    else:
        f2.write(line)  # word is in large but not small

# Everything left over in small_set wasn't in the large_set.
for word in small_set:
    f3.write(word + "\n")

群集可以更快地完成。但你可以在家里试试。

答案 8 :(得分:0)

由于您可以使用comm,我认为您必须已对输入文件进行排序。

这是一个像comm这样的程序,它只查看第一列,但产生包含整行输入的输出。它仅在输入已排序时才有效!

这是一个完整的计划。您所要做的就是将它放在一个文本文件中,然后从命令行运行它。

#!/usr/bin/env python
#
# comm.py - Compare 2 sorted files line by line, based on the first column.
# Usage:   python compare.py FILE1 FILE2 OUTFILE1 OUTFILE2 OUTFILE12
# OUTFILE1 receives all entries that are only in FILE1, etc.

import sys

def compare(f1, f2, out1, out2, out12):
    def get(f):
        line = f.readline()
        if line == '':
            return None
        first, rest = line.rstrip('\n').split('\t', 1)
        return first, rest, line

    e1 = get(f1)
    e2 = get(f2)
    while e1 and e2:
        if e1[0] == e2[0]:   # common entry
            out12.write(e1[0] + "\t" + e1[1] + "\t" + e2[1] + "\n")
            e1 = get(f1)
            e2 = get(f2)
        elif e1[0] < e2[0]:  # e1 is not in f2
            out1.write(e1[2])
            e1 = get(f1)
        else:                # e2 is not in f1
            out2.write(e2[2])
            e2 = get(f2)
    if e1:
        buf = e1[2]
        while buf:
            out1.write(buf)
            buf = f1.read(8192)
    if e2:
        buf = e2[2]
        while buf:
            out2.write(buf)
            buf = f2.read(8192)

compare(open(sys.argv[1], "r"),
        open(sys.argv[2], "r"),
        open(sys.argv[3], "w"),
        open(sys.argv[4], "w"),
        open(sys.argv[5], "w"))