毕业论文代码实现思路

基于循环神经网络的成分句法分析

Posted by WeiYang on 2018-02-26

一个寒假就写了个基本的代码,难受啊,整理一下思路吧,好久不看代码头都大了。

数据预处理


首先使用的是PTB数据集,原始的数据是长这样的:
(S (NP-SBJ (NNP Ms.) (NNP Haag) ) (VP (VBZ plays) (NP (NNP Elianti) )) (. .) )

因为不一定是二叉树,所以要先预处理成二叉树,这里全部借用了github上别人的代码来进行预处理,二叉化之后变成了这样:
(S (S*^. (NP-SBJ (NNP Ms.) (NNP Haag)) (VP (VBZ plays) (NP_NNP Elianti))) (. .))

然后将整个数据集中出现次数过小的单词替换为unk:
(S (S*^. (NP-SBJ (NNP Ms.) (NNP Haag)) (VP (VBZ plays) (NP_NNP < unk>))) (. .))

最后还需要将每个训练数据对应的句子单独提取出来,方便训练时用,比如上面的例子提取出来就是:
Ms. Haag plays Elianti .

文法规则提取


接着还是利用现成代码将数据集中出现的所有文法生成规则提取出来,保存到文件pcfg中。

训练


这部分大部分代码是自己写的,也有一部分是从CKY算法代码修改得到的。

首先将数据集中出现的所有单词和词向量数组下标一一映射,词性类别也做一个映射吧。

然后初始化神经网络的各个参数,要训练的权值矩阵一共有两个,\(W\)和\(d\),接下来就是训练了。

训练过程是这样的,采用了动态规划的思想,用三个维度\(i\),\(j\),\(A\)来表示这个句子从第\(i\)个位置到第\(j\)个位置且类别为\(A\)的信息。
用\(e(i,j,A)\)作为每个结点的向量表示,然后对他的儿子的所有情况进行遍历:
\[e(i,j,A) = \tanh (W \cdot [e(i,k,B),e(k,j,C)]) + type[A]\]
然后计算这个节点的分数:
\[s(i,j,A) = d \cdot e(i,j,A)\]
找出能使得分数最大的那个分割点和对应的类别,保存下来。

这样动态规划做好之后一棵树实际上就已经建好了,再回溯生成这棵树就好了。

但是这棵树很不准确的,刚开始就是随意生成的,所以要和标准树进行对比,计算出损失函数进行反向传播。

所以接下来用当前的权值矩阵计算出标准树的分数,然后对刚刚生成的结果和这个标准括号序列进行对比,我就直接粗暴统计出两个字符串有多少位置不同,记为\(cnt\)。

总的损失函数就是:
\[\left| {result - gold + cnt} \right|\]
然后进行反向传播就行了。

测试


在测试集上面直接照搬训练过程代码就行了,按照动态规划生成一棵树就行了。

然后用现成的代码和标准结果进行对比,得出F1值。

一些问题


总的来说,大体就是这样的了,但是还有很多问题没有解决。

  • 动态规划结合一个节点的两个子节点的时候,现在还只是直接连接的,准备将其改成LSTM的结点函数。
  • 改成LSTM的话就有左右结点的顺序问题,准备再加一个维度,0和1分别表示左右儿子的顺序。
  • 我在\(e(i,j,A) = \tanh (W \cdot [e(i,k,B),e(k,j,C)]) + type[A]\)加了一个\(type[A]\),其实原来没有这个的,但是不加会出现一个很大的问题,就是会出现A1->B,C和A2->B,C这两种情况,但是不加的话两种情况算出的分数是一样的,先入为主,后算的那种就永远不会考虑了。所以我强行加上了父节点类型向量,来区别这两种情况。但是具体怎么加还没有个说法,我只是随便试试。
  • 关于损失函数,论文里写的就是\(result - gold + cnt\),但是这样并不能保证非负,我就强行加上了一个绝对值。貌似也可以收敛了,到底应该怎么搞不清楚。