你如何解析一段文字到句子? (相当于Ruby)

你如何考虑段落或大量文本并将其分解为句子(最好使用Ruby),同时考虑到Mr.和Dr.以及USA这样的案例? (假设你只是将句子放入一个数组数组中)

更新:我想到的一个可能的解决方案是使用词性标注器(POST)和分类器来确定句子的结尾:

从琼斯先生那里获取数据时,他走出意大利避暑别墅的阳台,感受到了温暖的阳光。 他很高兴活着。

CLASSIFIER先生/ PERSON Jones / PERSON感觉/ O / O温暖/ O太阳/ O开/ O他/ O面/ O as / O他/ O步/ O出/ O到/ O / O阳台/ O / O他/ O夏天/ O家/ O in / O意大利/ LOCATION ./O他/ O是/ O快乐/ O到/ O是/ O活着/ O ./O

POST先生/ NNP Jones / NNP感觉/ VBD / DT温暖/ JJ太阳/ NN开/他/ PRP $ face / NN as / IN他/ PRP踩/ VBD输出/ RP上/ IN / DT阳台/ NN /他/ PRP $ summer / NN home / NN in / IN Italy./NNP He / PRP / VBD happy / JJ to / TO be / VB alive./IN

我们可以假设,由于意大利是一个地点,这段时间是句子的有效结束吗? 自从“先生”结束 没有其他词性,我们可以假设这不是一个有效的句末期吗? 这是我问题的最佳答案吗?

思考?

试着看看Stanford Parser周围的Ruby包装器 。 它有一个getSentencesFromString()函数。

为了说清楚,没有简单的解决方案。 正如谷歌快速搜索所显示的那样,这是NLP研究的主题。

但是,似乎有一些开源项目处理NLP支持句子检测,我发现了以下基于Java的工具集:

openNLP

附加注释:决定句子开始和结束位置的问题在自然语言处理中也称为句子边界消歧 (SBD)。

看看NLTK (自然语言工具包)中的Python句子分割器:

Punkt句子标记器

它基于以下论文:

Kiss,Tibor和Strunk,Jan(2006): 无监督多语言句子边界检测 。 计算语言学 32:485-525。

论文中的方法非常有趣。 它们将句子分裂的问题减少到确定单词与后续标点符号相关联的强度的问题。 缩写后的句点超载是大多数模糊句点的原因,因此如果您能够识别缩写,则可以高概率地识别句子边界。

我已经非正式地测试了这个工具,它似乎可以为各种(人类)语言提供良好的结果。

将它移植到Ruby将是非常重要的,但它可能会给你一些想法。

看起来这个rubygem可能会成功。

https://github.com/zencephalon/Tactful_Tokenizer

如果你真的关心正确的话,这是一个难题。 您会发现NLP解析器包可能提供此function。 如果你想要更快的东西,你需要最终用一个训练的令牌窗口的概率函数复制一些function(你可能想要一个换行计数作为一个标记,因为我可能会丢弃一段时间,如果它是段落的结尾)。

编辑:如果您可以使用Java,我建议使用Stanford解析器。 我没有推荐其他语言,但我很想知道其他什么是开源的。

不幸的是,我不是一个ruby的家伙,但也许perl中的一个例子会让你朝着正确的方向前进。 使用不匹配的外观作为结束标点符号然后在一个非后面的一些特殊情况后跟任意数量的空格,然后向前看大写字母。 我确信这不完美,但我希望它指出你正确的方向。 不知道你怎么知道美国是否真的在句子的末尾……

#!/usr/bin/perl $string = "Mr. Thompson is from the USA and is 75 years old. Dr. Bob is a dentist. This is a string that contains several sentances. For example this is one. Followed by another. Can it deal with a question? It sure can!"; my @sentances = split(/(?:(?<=\.|\!|\?)(? 

也许尝试将其拆分一段时间后跟一个空格后跟一个大写字母? 我不确定如何找到大写字母,但那将是我开始看的模式。

编辑: 用Ruby查找大写字母。

另一个编辑:

检查不是以大写字母开头的单词后面的句子结尾标点符号。

Manning博士的答案是最合适的,如果你正在考虑JAVA(以及Ruby也很难;))。 是这里-

有一个句子分割器:edu.stanford.nlp.process.DocumentPreprocessor。 尝试命令:java edu.stanford.nlp.process.DocumentPreprocessor /u/nlp/data/lexparser/textDocument.txt

oneTokenizedSentencePerLine.txt。 (这是通过(良好但启发式)FSM完成的,因此它很快;您没有运行概率解析器。)

但是有一点建议,如果我们修改命令java edu.stanford.nlp.process.DocumentPreprocessor /u/nlp/data/lexparser/textDocument.txt> oneTokenizedSentencePerLine.txt TO java edu.stanford.nlp.process.DocumentPreprocessor -file / u /nlp/data/lexparser/textDocument.txt> oneTokenizedSentencePerLine.txt 。 它将正常工作,因为您需要指定将哪种文件呈现为输入。 所以文件文件的文件,HTML的-html等。

我没试过,但如果英语是你唯一关心的语言,我建议给Lingua :: EN :: Readability一个表情。

Lingua :: EN :: Readability是一个Ruby模块,用于计算英文文本的统计信息。 它可以提供单词,句子和音节的计数。 它还可以计算几种可读性度量,例如雾指数和Flesch-Kincaid级别。 该软件包包括Lingua :: EN :: Sentence模块,它将英文文本分解为符合缩写的句子,以及Lingua :: EN :: Syllable,它可以猜出书面英文单词中的音节数。 如果发音字典可用,它可以查找字典中的音节数量以获得更高的准确性

你想要的位在sentence.rb中如下:

 module Lingua module EN # The module Lingua::EN::Sentence takes English text, and attempts to split it # up into sentences, respecting abbreviations. module Sentence EOS = "\001" # temporary end of sentence marker Titles = [ 'jr', 'mr', 'mrs', 'ms', 'dr', 'prof', 'sr', 'sen', 'rep', 'rev', 'gov', 'atty', 'supt', 'det', 'rev', 'col','gen', 'lt', 'cmdr', 'adm', 'capt', 'sgt', 'cpl', 'maj' ] Entities = [ 'dept', 'univ', 'uni', 'assn', 'bros', 'inc', 'ltd', 'co', 'corp', 'plc' ] Months = [ 'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec', 'sept' ] Days = [ 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun' ] Misc = [ 'vs', 'etc', 'no', 'esp', 'cf' ] Streets = [ 'ave', 'bld', 'blvd', 'cl', 'ct', 'cres', 'dr', 'rd', 'st' ] @@abbreviations = Titles + Entities + Months + Days + Streets + Misc # Split the passed text into individual sentences, trim these and return # as an array. A sentence is marked by one of the punctuation marks ".", "?" # or "!" followed by whitespace. Sequences of full stops (such as an # ellipsis marker "..." and stops after a known abbreviation are ignored. def Sentence.sentences(text) text = text.dup # initial split after punctuation - have to preserve trailing whitespace # for the ellipsis correction next # would be nicer to use look-behind and look-ahead assertions to skip # ellipsis marks, but Ruby doesn't support look-behind text.gsub!( /([\.?!](?:\"|\'|\)|\]|\})?)(\s+)/ ) { $1 << EOS << $2 } # correct ellipsis marks and rows of stops text.gsub!( /(\.\.\.*)#{EOS}/ ) { $1 } # correct abbreviations # TODO - precompile this regex? text.gsub!( /(#{@@abbreviations.join("|")})\.#{EOS}/i ) { $1 << '.' } # split on EOS marker, strip gets rid of trailing whitespace text.split(EOS).map { | sentence | sentence.strip } end # add a list of abbreviations to the list that's used to detect false # sentence ends. Return the current list of abbreviations in use. def Sentence.abbreviation(*abbreviations) @@abbreviations += abbreviations @@abbreviations end end end end 

同意接受的答案,使用斯坦福Core NLP是一个没有脑子的人。

然而,在2016年, 斯坦福分析师与斯坦福核心nlp的后期版本之间存在一些不兼容性 (我遇到了Stanford Core NLP v3.5的问题 )。

以下是我使用Ruby与Stanford Core NLP接口将文本解析成句子的方法

  1. 安装斯坦福CoreNLPgem – 它仍然保持和工作,它一直很难找到最近工作的NLPrubygem:

    gem install stanford-core-nlp

  2. 然后按照使用最新版本的Stanford CoreNLP自述文件的说明进行操作 :

使用最新版本的Stanford CoreNLP(2014年10月31日版本3.5.0)需要一些额外的手动步骤:

  • 从http://nlp.stanford.edu/下载Stanford CoreNLP 3.5.0版 。

  • 将提取的存档的内容放在stanford-core-nlp gem的/ bin /文件夹中(例如[…] / gems / stanford-core-nlp-0.x / bin /)或在配置的目录位置内通过设置StanfordCoreNLP.jar_path。

  • 从http://nlp.stanford.edu/下载完整的Stanford Tagger 3.5.0版 。
  • 在stanford-core-nlp gem的/ bin /文件夹中创建一个名为’taggers’的目录(例如[…] / gems / stanford-core-nlp-0.x / bin /)或者在由设置StanfordCoreNLP.jar_path。
  • 将提取的存档的内容放在标记器目录中。
  • 从https://github.com/louismullie/stanford-core-nlp下载bridge.jar文件 。
  • 将下载的bridger.jar文件放在stanford-core-nlp gem的/ bin /文件夹中(例如[…] / gems / stanford-core-nlp-0.x / bin / taggers /)或目录内通过设置StanfordCoreNLP.jar_path进行配置。

然后将ruby代码分割成句子:

 require "stanford-core-nlp" #I downloaded the StanfordCoreNLP to a custom path: StanfordCoreNLP.jar_path = "/home/josh/stanford-corenlp-full-2014-10-31/" StanfordCoreNLP.use :english StanfordCoreNLP.model_files = {} StanfordCoreNLP.default_jars = [ 'joda-time.jar', 'xom.jar', 'stanford-corenlp-3.5.0.jar', 'stanford-corenlp-3.5.0-models.jar', 'jollyday.jar', 'bridge.jar' ] pipeline = StanfordCoreNLP.load(:tokenize, :ssplit) text = 'Mr. Josh Weir is writing some code. ' + 'I am Josh Weir Sr. my son may be Josh Weir Jr. etc. etc.' text = StanfordCoreNLP::Annotation.new(text) pipeline.annotate(text) text.get(:sentences).each{|s| puts "sentence: " + s.to_s} #output: #sentence: Mr. Josh Weir is writing some code. #sentence: I am Josh Weir Sr. my son may be Josh Weir Jr. etc. etc. 

打破一段时间后,一个空格和一个大写的字母不会像“布朗先生”这样的标题。

这些时期使事情变得困难,但要处理的一个简单案例是感叹号和问号。 但是,有些情况会导致这种情况无效。 即Yahoo!的公司名称

显然, paragraph.split('.')不会削减它

#split将采用正则表达式作为答案,因此您可以尝试使用零宽度lookbehind来检查以大写字母开头的单词。 当然,这将拆分专有名词所以你可能不得不诉诸这样的正则表达式/(Mr\.|Mrs\.|U\.S\.A ...)除非你以编程方式构建正则表达式,否则这将是非常丑陋的。

我认为这并不总是可以解决,但你可以根据“。”(一个句号后跟空格)分开,并validation句点之前的单词不在像Mr,Dr等单词列表中。

但是,当然,你的列表可能会省略一些单词,在这种情况下你会得到不好的结果。

我不是一个Ruby人,而是一个分裂的RegEx

  ^(Mr|Mrs|Ms|Mme|Sta|Sr|Sra|Dr|U\.S\.A)[\.\!\?\"] [AZ] 

一旦你有了段落(在\ r \ n上分开),这将是我最好的选择。 这假设你的句子是正确的。

显然这是一个相当难看的RegEx。 怎么在句子之间强制两个空格

谢谢

我非常喜欢这个讨论,所以我对解析器很感兴趣。 我试了一下,然后写了一些关于如何使用Ruby和Rails的一切工作的笔记!

试图使用正则表达式是一场噩梦……

我的2美分