我想使用 Sequitur(特别是它在 Python scikit 包中非常好的实现)从大量字符串(“句子”)中推断出 CFG。 Sequitur 期望单个字符串作为条目。当然,我可以将字符串包含到 BEGIN 和 END 符号中,并将它们连接成一个长字符串,而不会丢失任何信息。但在这种情况下,Sequitur 找到的结构将跨越句子边界,这不是我想要的。
如何使 Sequitur 只查看句子内部,并且不生成包含 BEGIN 符号后跟 END 符号的投影的规则? (除了第一条规则,这将是所有句子的保护伞......)
示例:如果我有句子“男孩吃苹果”和“女孩睡觉”,并且我用词性标签替换单词(“a”表示冠词,“n”表示名词,“v”表示动词),我得到“anvan”和“anv”。如果我将它们合并,包括 B (BEGIN) 和 E (END) 符号,我会得到字符串“BanvanEBanvE”。当我将此字符串提供给 Sequitur 时,我得到
0 → 1 2 E 1 E
1 → B 2 v
2 → a n
其中第一条规则有 E 符号但没有 B 符号,第二条规则有 B 符号、中间符号(本质上是名词短语 ART+NOUN)和 v。
我想要的是
0 → B 1 E B 2 E
等等。这样语法的其余部分只有句子内部的规则。在我们简单的例子中,它是
0 → B 1 E B 2 E
1 → 3 v 3
2 → 3 v
3 → a n
有没有办法在不改变算法代码的情况下实现这一点?如果没有,是否有其他(实现的)算法可以准确地获得该结果?
我可能有一个使用 nltk 的解决方案。我尝试使用 sksequitur 但没有成功。您可以尝试将两者结合起来。 这是我所拥有的:
import nltk
Corpus=['B','a','n','v','a','n','E','B','a','n','v','E','B','a','n','E']
nbSentences=Corpus.count('B') # Counts the nb. if sentences (B for "BEGIN" and "E" for END)
print('Nb. of sentences: ',nbSentences)
C='C -> '+'T '*nbSentences # The corpus C is made of nbSentences "Tokens"
core_grammar= """
T -> BEGIN S END
S -> NP VP | NP
PP -> P NP
NP -> A N
VP -> V NP | V
A -> 'a'
N -> 'n'
V -> 'v'
BEGIN -> 'B'
END -> 'E'
"""
# Generate the grammar:
gramm_str=C+core_grammar
print('grammar string: \n',gramm_str)
# Parsing:
simple_grammar = nltk.CFG.fromstring(gramm_str)
parser = nltk.ChartParser(simple_grammar)
tree = parser.parse(Corpus)
#print(list(tree)[0]) # simple output
list(tree)[0].pretty_print() # for a pretty_print
#list(tree)[0].draw() # draw in w window
#list(tree)[0] # to draw tree in jupyter notebook
结果:
正如您所看到的,所有句子都被处理,每个句子都会生成自己的树(句子之间没有交叉) 现在,如果你有数百万个句子......这可能是一个问题。
最诚挚的问候, 史蒂芬
您可以使用 sksequitur.Mark 在不同字符串之间放置分隔符来完成此操作。
from sksequitur import Grammar, Parser, Mark
parser = Parser()
parser.feed('anvan')
parser.feed([Mark()])
parser.feed('anv')
print(Grammar(parser.tree))
>> 0 -> 1 2 | 1
>> 1 -> 2 v anv
>> 2 -> a n an