热线电话:13121318867

登录
2018-12-06 阅读量: 784
怎么定义复杂语法

还有一种语言建模方法,那就是利用语法规则(grammar) 来生成符合要求的句子。在小

学的时候,我们就已经知道了词的词类及其组合方式。例如,如果你有一个非常糟糕的英

语老师,你必定会认为句子都是由名词后面跟动词构成的。这样的话,如果给你一个由名

词和动词组成的列表,你就可以根据这种规则来造句了。

下面,我们将定义一个稍微复杂的语法:

grammar = {

"_S" : ["_NP _VP"],

"_NP" : ["_N",

"_A _NP _P _A _N"],

"_VP" : ["_V",

"_V _NP"],

"_N" : ["data science", "Python", "regression"],

"_A" : ["big", "linear", "logistic"],

"_P" : ["about", "near"],

"_V" : ["learns", "trains", "tests", "is"]

}

我们约定,以下划线开头的名称表示语法规则,它们需要进一步展开; 而其他名称是不需

要进一步处理的终端符号。

例如, "_S" 是“句子”规则,其产生一个 "_NP"(“名词短语”)规则,后面紧跟一个 "_VP"

(“动词短语”)规则。

动词短语规则可能会产生一个 "_V"(“动词”)规则,也可能会产生一个动词规则继之以名

词短语规则。

请注意, "_NP" 规则所生成的规则中也包括其自身。我们知道,语法是可以递归的,因此,

尽管这里这些语法非常有限,但是照样能够产生无穷多不同的句子。

那么,我们如何通过这些语法来生成句子呢? 我们不妨从一个包含句子规则的列表 ["_S"]

着手。然后,我们将不断展开每一项规则,即从该规则的产物中随机选择一个来代替它。

当我们的列表元素全部变成终端符号时,我们就可以停下来了。

举例来说,上述过程可能像下面这样:

['_S']

['_NP','_VP']

['_N','_VP']

['Python','_VP']

['Python','_V','_NP']

['Python','trains','_NP']

['Python','trains','_A','_NP','_P','_A','_N']

['Python','trains','logistic','_NP','_P','_A','_N']

['Python','trains','logistic','_N','_P','_A','_N']

['Python','trains','logistic','data science','_P','_A','_N']

['Python','trains','logistic','data science','about','_A', '_N']

['Python','trains','logistic','data science','about','logistic','_N']

['Python','trains','logistic','data science','about','logistic','Python']

我们如何实现它呢? 首先,我们需要创建一个简单的辅助函数来识别终端符号:

def is_terminal(token):

return token[0] != "_"

接下来,我们需要编写一个函数,将一个标记列表变成一个句子。首先,我们需要找到第

一个非终结符号标记。 如果我们找不到这种标记,那就意味着我们已经有一个完整的句

子,可以收工了。

如果我们真的找到了一个非终端符号, 那么就在其产物中随机选择一个。如果选中的是个

终端符号(即一个单词),那么直接用它替换相应的标记即可。除此之外,如果选中的是

一个由空格符分隔的非终端符标记, 那么则需要进行拆分,并将其拼接到当前标记中。总

之,我们的工作就是在一组新标记上不断重复这个过程。

上述过程可以通过下列代码实现:

def expand(grammar, tokens):

for i, token in enumerate(tokens):

# 跳过终端符号

if is_terminal(token): continue

# 如果这一步我们发现了一个非终端符号

# 需要随机选择一个替代者

replacement = random.choice(grammar[token])

if is_terminal(replacement):

tokens[i] = replacement

else:

tokens = tokens[:i] + replacement.split() + tokens[(i+1):]

# 现在展开新的符号列表

return expand(grammar, tokens)

# 如果到达这一步,就找出了所有的终端符号,可以收工了

return tokens

现在我们可以生成句子了:

def generate_sentence(grammar):

return expand(grammar, ["_S"])

只要我们不断改变语法——例如添加更多的单词、添加更多的规则、添加各种词类等——

就能得到足够多的网页来满足公司的需要。

0.0000
5
关注作者
收藏
评论(0)

发表评论

暂无数据
推荐帖子