句法分析-基于CKY的PCFG(概率上下文无法语法)

跟着其他博客走的步骤,最后做的不一定对,随便写写,表喷-_-

虽然也没完全弄懂。。

大体步骤是已知A->AB或A->b的概率,对输入的字符串,建立多棵树,最大概率的树即为该字符串的句法树。

首先根据https://blog.csdn.net/bbbeoy/article/details/79649690 

里已经推导出的各个产生式的概率

(新注:根据后来的不断实践,图表中的P和IN是同一个)

建立一个二维字典(和ll1和lr1同理)

引用博客给出的算法:

以下用pi表示Π

算法框架:

输入 长度为n的语句单词个数 和 PCFG文法(N:非终结字符集,大埃普西隆:终结字符集 R:产生式规则 S:开始符号)

首先 初始化 对于所有的i取值为1-n 如果存在X->xi(第i个单词)

那么 pi(i,i,X)就为该产生式对应的概率,其他为0,pi(i,j,X)表示单词序列下标i到j的单词段,以X为根的子树的概率,初始化也就是把X->单词的所有概率写入

采用n*n二维数组嵌套一个字典结构,初始化所有值为空字典{}、

执行算法:(根据实际储存对原算法进行修改)

第一层遍历L取值为0到n-2:

第二层遍历i取值为0到n-L-2:

令j为i+L

第三层遍历对于所有的非终结字符X:

初始化最大值为0,取最大值时的参数为-1

第四层遍历s取值为i到j-1:

如果 (X->YZ)的概率乘pi(i,s,Y)乘pi(s+1,j,Z)大于之前的最大值,则更新最大值和最大值参数为当前值

第四层遍历完:把最大值写入pi(i,j,X) 参数写入bp(i,j,X)

[特别像Floyd算法]

输入句子:the man saw the dog with the telescope

实现代码:

# -*- coding: utf-8 -*-
"""
Created on Thu Jan  3 19:58:23 2019

@author: 71405
"""
"""
对于字符串中的字符xi和任意非终结字符N
初始化
pi(i,i,N)=N->xi的概率 如果不满足产生式则为0

开始
for l=1:n-1
   for i=1:n-l
   另 j=i+l
    对于所有终结符X计算:
    pi(i,j,X)=max(q(X->YZ)*pi(i,s,Y)*pi(s+1,j,Z))  满足X->YZ且 s取值为i到j-1
    和bp(i,j,X)=argmax(q(X->YZ)*pi(i,s,Y)*pi(s+1,j,Z))
"""
from collections import defaultdict
import re
def addtwodimdict(thedict, key_a, key_b, val): 
    if key_a in thedict:
        thedict[key_a].update({key_b: val})
    else:
        thedict.update({key_a:{key_b: val}})


T=['saw','dog','man','telescope','the','with','in']
I=['SS','VP','NP','PP','VT','VI','NN','DT','IN','PX']
p=dict()
addtwodimdict(p, 'SS', ('NP','VP'), 1.0)
addtwodimdict(p, 'VP', 'VI', 0.4)
addtwodimdict(p, 'VP', ('VT','NP'), 0.4)
addtwodimdict(p, 'VP', ('VP','PP'), 0.2)
addtwodimdict(p, 'NP', ('DT','NN'), 0.3)
addtwodimdict(p, 'NP', ('NP','PP'), 0.7)
addtwodimdict(p, 'PP', ('IN','NP'), 1.0)
addtwodimdict(p, 'VI', 'sleep', 1.0)
addtwodimdict(p, 'VT', 'saw', 1.0)
addtwodimdict(p, 'NN', 'man', 0.7)
addtwodimdict(p, 'NN', 'dog', 0.2)
addtwodimdict(p, 'NN', 'telescope', 0.1)
addtwodimdict(p, 'DT', 'the', 1.0)
addtwodimdict(p, 'IN', 'with', 0.5)
addtwodimdict(p, 'IN', 'in', 0.5)

sen="the man saw the dog with the telescope"
ss=re.split(r'[\s]',sen)


R1=open('gram3.txt').readlines()# 非终->非终
R2=open('gram4.txt').readlines()#非终->终
i=0
while i<len(R1): #去掉'->'符号
    R1[i]=R1[i][0:2]+R1[i][4:len(R1[i])-1]
    i+=1
i=0
while i<len(R2): #去掉'->'符号
    R2[i]=R2[i][0:2]+R2[i][4:len(R2[i])-1]
    i+=1


"""
pi设置为二维数组内嵌一元字典
bp同理
"""
N=len(ss)
#初始化
pi=[ [{} for i in range(N)] for i in range(N)]
bp=[ [{} for i in range(N)] for i in range(N)]
for i in range(N):
    for x in I:
        if x+ss[i] in R2:
            pi[i][i][x]=p[x][ss[i]]
        else:
            pi[i][i][x]=0
l=0
while l<=N-2:
    i=0
    while i<=N-l-2:   
        j=i+l+1
        for X in I:
            s=i
            maxi=0
            args=-1
            while s<=j-1:
                for TAB in R1:
                    if X==TAB[0:2] and len(TAB)==6:
                        Y=TAB[2:4]
                        Z=TAB[4:]
                        maxp=p[X][(Y,Z)]*pi[i][s][Y]*pi[s+1][j][Z]

                        if maxp>maxi:
                            maxi=maxp
                            args=s
                s+=1
            pi[i][j][X]=maxi
            bp[i][j][X]=args
        i+=1
    l+=1


print('以第一个字符a开始第二个b为结束的段落以X为根的句法树的概率:')
print("a b X   P")
count=0
q=[ [0 for i in range(3)] for i in range(64)]
root=[]
i=0
while i<N:
    j=0
    while j<N:
        if len(pi[i][j])!=0:
            for k in pi[i][j]:
                if pi[i][j][k]!=0 :
                    print(str(i)+' '+str(j)+' '+k+' '+str(pi[i][j][k]))
                    q[count][0]=i
                    q[count][1]=j
                    q[count][2]=pi[i][j][k]
                    count+=1
                    root.append(k)
                
        j+=1
    i+=1
    """
def dp(start,end):
    #返回以start为开始end为结束的字符串的最大p
    maxi=0
    i=0
    argc=-1
    while i<count:
        if q[i][0]==start:
            if q[i][1]==end:
                maxq=q[i][2]
            else:
                maxq=q[i][2]*dp(q[i][1]+1,end)
            if maxq>maxi:
                maxi=maxq
        i+=1
    return maxi

dp(0,7)
"""

把pi打印了一下:


以第一个字符a开始第二个b为结束的段落以X为根的句法树的概率:
a b X   P
0 0 DT 1.0
0 1 NP 0.21
0 4 SS 0.00504
0 7 SS 5.2919999999999995e-05
1 1 NN 0.7
2 2 VT 1.0
2 4 VP 0.024
2 7 VP 0.000252
3 3 DT 1.0
3 4 NP 0.06
3 7 NP 0.0006299999999999999
4 4 NN 0.2
5 5 IN 0.5
5 7 PP 0.015
6 6 DT 1.0
6 7 NP 0.03
7 7 NN 0.1

 

由第五行得最大概率树的最大概率为5.29*10的负5次方

然后在根据pi和bp进行从下往上建树即可

由于时间关系先不程序实现列树了

有缘补上(一定)

【注:1.

本次编程出现一个错误 :

i=0

j=0

while i<n

     while j<n

发现第一个循环只执行i=0一次,原来才发现j=0应该放在while i 内(连续出现两次)

2.读取的gram3和gram4分别为图一左表和右表的产生式(为了方便录入NN等字符)

3.由于刚开始对图一P和NN的不可区分性,导致最后运行结果出现了0-4以SS为根的结果,后来思索了一天也没想出来怎么继续做(以为得出来的都是子树然后……) 刚开始用动态规划做(代码在最后注释,留下就当复习算法了),后来想想不太对,没有考虑规则R的问题………………………………直到最后手动建表发现IN没有其他非终结符能产生!然后查了另外一个图(未附)得出结论:P为IN 于是把gramm3和p表中的P更换为IN  跑出了正确的结果 (太尼玛坑了)

 

 

 

 

博主设置当前文章不允许评论。

没有更多推荐了,返回首页