2

我在尝试编写一个将单词的所有变形都视为同一个基本单词的搜索引擎时遇到了麻烦。

  1. 所以对于动词来说,这些都是同一个词根,be
    • 数字/人(例如 am; is; are
    • 过去时或将来时的时态/情绪(例如 是;是;将是
    • 过去分词(例如 has been ; had been
    • 现在分词和动名词(例如 is being ; was not be funny; 早点不如正确重要)
    • 虚拟语气(例如 可能是;对完成某事至关重要我希望这样)⁠⁠⁠⁠</p>

  2. 然后对于名词,单数形式和复数形式都应该算作同一个基本词[ ᴇᴅɪᴛᴏʀ's ɴᴏᴛᴇ:这通常被称为单词的引用形式]

例如,对于“<em>enable”,我不想将“<em>enables”和“<em>enabled”打印为单独的条目。这三个都应该算作同一个基本词,动词enable

我可以使用以下哈希来防止打印重复项:

unless ($seenmatches{ $headmatches[$l] }++)
  1. 有人可以解释一下吗?在下面的评论中解释。

  2. 这并不能阻止复数/过去继续。有没有办法做到这一点,或者一些完全不同的方法,可能涉及一个正则表达式和/或替换,然后是一个 unsub ?

我不能用替换来修改这个词,因为那样打印就不会正确打印出来。虽然我还没到阶段,但最终我想包括不规则过去时 [ ᴇᴅɪᴛᴏʀ's ɴᴏᴛᴇ: 和不规则名词] 以及

我不确定您还需要回答我的问题,所以请让我知道我无意中遗漏的任何内容,我会填写任何缺失的部分以帮助使其更清楚。

4

4 回答 4

5

典型搜索引擎的工作方式如下:

  • 输入字符串被标记化,在单词边界处被切碎 - 字符偏移开始/结束与每个标记相关联
  • 然后对每个令牌进行词干处理 - 我会使用Lingua::Stem(或者,更好的是Lingua::Stem::Snowball),它们是 Porter 词干分析器的略微更新版本
  • 每个标记及其原始字符偏移开始/结束都被保留和索引,通常与原始文本的副本一起,在它被标记之前。这基本上是一个将术语文本与其原始文档相关联的表(通常作为标识符)

现在,当查询到达时,它也被标记化并且每个标记都被提取,但是这次我们不关心位置。我们将每个标记与我们索引的标记进行比较,以定位发布(匹配文档标识符)。我们现在可以检索存储的开始/结束偏移量,以确定术语在原始文本中的位置。

因此,您确实丢失了索引的后缀(用于定位匹配文档),但您保留了这些文档的原始文本和偏移量,因此您可以在需要时进行查询突出显示和漂亮的显示内容。

词干提取绝对是这项工作的正确工具。主要技巧是确保您以相同的方式处理查询和文档。您可以修改原始文档,但实际上,您希望将其转换为类似书后索引的东西,而不是您使用正则表达式的字符串——如果您真的在做搜索引擎的事情,那就是。如果您愿意,可以查看 CPAN 上的优秀KinoSearch模块,或者查看它最初源自的 Apache Lucene 项目。

于 2011-05-31T19:06:06.350 回答
1

Text::English模块包含一个 Porter 词干分析器,这是将同一单词的不同形式视为相同以进行匹配的常用方法。

于 2011-05-31T17:33:45.910 回答
1

查看 verbTenseChanger.pl ( http://cogcomp.cs.illinois.edu/page/tools_view/1 ) 这是自述文件:

##codes for the various tenses are:
#0 - Base Form
#1 - Past Simple
#2 - Past Participle
#3 - 3rd Person Singular
#4 - Present Participle

##Example use:
##my $newTense = changeVerbForm("see",0,4);
##changes tense from base form to the present participle

我通过创建不同的形式来使用它(我猜它包括一个词干分析器):

my @changeverbforms = map changeVerbForm( $search_key, 0, $_ ), 1..4;
my @verbforms;
push (@verbforms, $changeverbforms[0]) unless ($changeverbforms[0] eq "");
push (@verbforms, $changeverbforms[1]) unless ($changeverbforms[1] eq "");
push (@verbforms, $changeverbforms[2]) unless ($changeverbforms[2] eq "");
push (@verbforms, $changeverbforms[3]) unless ($changeverbforms[3] eq "");

然后循环遍历@verbforms(围绕整个搜索引擎 perl 代码)和我拥有的所有地方$search_key,我也放了or $verbform. 有一些额外的事情需要解决,但这是一般的实现(尽管针对我的具体情况)

有关错误在线代码的一些调试,请参阅:https ://stackoverflow.com/questions/6459085/need-help-understanding-this-verb-tense-changing-code-please

于 2011-06-08T19:21:28.663 回答
0

我尝试了 Lingua::Stem、Lingua::Stem::Snowball 和 WordNet::stem,但它们都无法阻止最常见的单词。要获得这些简单的单词,您可以在之后运行这个简单的词干分析器,它使用 WordNet 的 .exc(异常?)文件:

1. Download and install WordNet.
2. export WNHOME='/usr/lib/wnres' (if that is the directory containing the dict directory; that's where Cygwin puts it. You'll need that to install Wordnet::QueryData.)
3. cat $WNHOME/dict/*.exc > wordnet.exc  (combine all the .exc files)
4. Make this perl file:

$ cat > stem.pl
use strict;
use warnings;

# Read in WordNet exception files
my $ExcFile = "wordnet.exc";
my %Stems;
open(my $FILE, "<$ExcFile") or die "Could not read $ExcFile: $!";
while (my $line = <$FILE>) {
        chomp($line);
        my ($word, $stem) = split(/\s+/, $line);
        $Stems{$word} = $stem;
}
close($FILE);

while (defined(my $in = <>)) {
        chomp($in); $in =~ s/\r$//;
        $in =~ s/^\s+//;
        $in =~ s/\s+$//;
        next if $in eq '';
        my @words = split(/\s+/, $in);
        foreach my $w (@words) {
                $w = $Stems{$w} if $Stems{$w};
        }
        print "@words\n";
}
<ctrl-D>

然后你可以用

perl stem.pl < foo.txt

You may want to run the other stemmers before rather than after this step, because if they're smart and use word context to stem (though I doubt they do), they'll need the full unstemmed line to work with, whereas stem.pl works word-by-word.

于 2015-05-31T15:33:24.753 回答