2

我有一组文章描述,我必须将文本分成句子。第一个实现使用 opennlp 工具 sentdetect 效果很好,但对于我的目的来说太慢了。有没有类似的东西执行得更快并且质量相似或稍差?

注意:我正在处理(大量)简短的编辑德语文本。

4

5 回答 5

7

是的,提及您正在使用德语会有所帮助:)

可以在GATE中找到带有缩写列表的基于正则表达式的句子检测器。它使用位于此处的三个文件。正则表达式非常简单:

//more than 2 new lines
(?:[\u00A0\u2007\u202F\p{javaWhitespace}&&[^\n\r]])*(\n\r|\r\n|\n|\r)(?:(?:[\u00A0\u2007\u202F\p{javaWhitespace}&&[^\n\r]])*\1)+

//between 1 and 3 full stops
\.{1,3}"?

//up to 4 ! or ? in sequence
(!|\?){1,4}"?

使用这 3 个文件的代码可以在这里找到。

我会用可以在网上找到的东西来增强正则表达式,比如这个

然后我会想到 GATE 列表中所有单词的德语翻译。如果这还不够,我会浏览其中一些缩写列表:12,然后自己创建列表。

编辑:

如果性能如此重要,我不会将整个 GATE 用于句子拆分器 - 切换到他们的文档、创建注释、然后将它们解析回来等需要时间和内存。

我认为对您来说最好的方法是从 RegexSentenceSplitter 类(上面的链接)中获取代码并根据您的上下文进行调整。

我认为代码太长,无法粘贴到这里。您应该看到 execute() 方法。一般来说,它会找到内部、外部和阻塞正则表达式的所有匹配项,然后迭代并仅使用那些不与任何阻塞重叠的内部和外部匹配项。

以下是您应该查看/重用的一些片段:

  • 如何解析文件

    // for each line
    if(patternString.length() > 0) patternString.append("|");
    patternString.append("(?:" + line + ")");
    
    //...
    return Pattern.compile(patternString.toString());
    
  • 在 execute 方法中,如何填充阻塞拆分:

    Matcher nonSplitMatcher = nonSplitsPattern.matcher(docText);
    //store all non split locations in a list of pairs
    List<int[]> nonSplits = new LinkedList<int[]>();
    while(nonSplitMatcher.find()){
       nonSplits.add(new int[]{nonSplitMatcher.start(), nonSplitMatcher.end()});
    }
    

还要检查否决方法,该方法“检查可能的匹配是否被非拆分匹配否决。如果可能的匹配与否决区域重叠,则可能的匹配被否决。”

希望这可以帮助。

于 2014-04-10T21:08:14.960 回答
2

Maybe String.split("\\. |\\? |! "); does it?

于 2014-04-07T07:27:16.910 回答
2

总的来说,我认为 OpenNLP 会比基于规则的分割器(如斯坦福分割器或实现正则表达式来解决任务)更好(在性能方面)。基于规则的分段器必然会错过一些例外情况。例如,德语句子“Ich wurde am 17.Dezember geboren”(我出生于 12 月 17 日)会被许多基于规则的分段器错误地分成 2 个句子,尤其是如果它们是内置的关于英语规则而不是德语。即使您的文本质量非常好,也会出现这样的句子,因为它们构成了语法正确的德语。因此,检查您要使用的分段器是以哪种语言模型为模型的非常重要。

PS:在 OpenNLP、BreakIterator 分段器和斯坦福分段器中,OpenNLP 最适合我。

于 2014-04-16T15:02:52.280 回答
1

值得一提的是,Java 标准 API 库提供了依赖于语言环境的功能来检测测试边界BreakIterator可用于确定句子边界。

于 2014-04-15T21:45:13.990 回答
1

还有一种解决方案。不知道与您的解决方案相比性能如何,但肯定是最全面的。您可以使用 ICU4J 库和 srx 文件。您可以在此处下载库http://site.icu-project.org/download/52#TOC-ICU4J-Download。像它的多语言魅力一样工作。

package srx;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

import net.sf.okapi.common.ISegmenter;
import net.sf.okapi.common.LocaleId;
import net.sf.okapi.common.Range;
import net.sf.okapi.lib.segmentation.LanguageMap;
import net.sf.okapi.lib.segmentation.Rule;
import net.sf.okapi.lib.segmentation.SRXDocument;

public class Main {

/**
 * @param args
 */
public static void main(String[] args) {

    if(args.length != 2) return;

    SRXDocument doc = new SRXDocument();

    String srxRulesFilePath = args[0];
    String text = args[1];
    doc.loadRules(srxRulesFilePath);
    LinkedHashMap<String, ArrayList<Rule>> rules =  doc.getAllLanguageRules();
    ArrayList<LanguageMap> languages = doc.getAllLanguagesMaps();
    ArrayList<Rule> plRules = doc.getLanguageRules(languages.get(0).getRuleName());     
    LocaleId locale = LocaleId.fromString("pl_PL");     
    ISegmenter segmenter = doc.compileLanguageRules(LocaleId.fromString("pl_PL"), null);


    segmenter.computeSegments(text);

    List<Range> ranges = segmenter.getRanges();

    System.out.println(ranges.size());
    for (Range range : ranges) {
        System.out.println(range.start);
        System.out.println(range.end);
    }
}

}
于 2014-04-16T13:31:18.267 回答