cs224d-lecture14 Tree-RNN and Constituency Parsing

主要内容:

  • 语言的语义解释
  • 如果将短语结构映射到向量空间中:利用语义的合成性
  • 对比 RNN 和 CNN
  • Recursive neural networks
  • Parsing a sentence with an RNN
  • 使用tree-rnn 进行分类: assignment3 情感分类

语言的语义解释--并不只是词向量

词向量只是词语级别的向量,人们可以用更大颗粒度的文本来表达自己的意思,而不仅仅是词袋中的某个单词。

比如: the country of my birth, the place where I was born

Question: how can we represent the meaning of longer phrases?

Answer: By mapping them into the same vector space.

How should we map phrases into a vector space?

利用语义的合成性: use principle of Compositionality

  • the meanings of its words
  • the rules that combine them

其实想想RNN也是将一个sentence或者是phrase压缩到一个向量中去。后面会介绍它们的区别。

通过同时学习句法树和复合性向量表示,就可以得到短语的向量表示了。

句法树:

句法树结构和向量表示Learn Structure and Representation:

问题是:我们真的需要学习这种树结构吗?

Do we really need to learn this structure?

从两个角度来说明这个问题,一是对比recursive 和 rnn, 而是从语言的本质来解释。

  1. Recursive vs. RNN

Richard mentioned that the recurrent models are really sort of capturing representations of whole prefixes and you're not getting any representations of smaller units than that.

两者都是递归神经网络,只不过前者在空间上递归,后者在时间上递归。中文有时会把后者翻译为“循环神经网络”,但这明显混淆了等级,令人误解。

它们各有各的优缺点,Recursive neural net需要分析器来得到句法树,而Recurrent neural net只能捕捉“前缀”“上文”无法捕捉更小的单位。

但人们还是更倾向于用后者,LSTM之类。因为训练Recursive neural net之前,你需要句法树;句法树是一个离散的决策结果,无法连续地影响损失函数,也就无法简单地利用反向传播训练Recursive neural net。另外,复杂的结构也导致Recursive neural net不易在GPU上优化。

  1. 语言本质是递归的吗? 在认知科学上虽然有些争议,因为一般一个句子是有长度限制的,人们几乎从不说300个词以上的句子。但是递归是描述语言的最佳方式,比如

[The man from [the company that you spoke with about [the project] yesterday]]

这里面一个名词短语套一个名词短语,一级级下去。从实用的角度讲

1、通过递归地描述句子(句法树),可以有效地消歧:

2、便于指代相消等任务。

3、便于利用语法树结构(基于短语的机器翻译)

从 RNNs 到 CNNs

RNN只会为满足语法的短语计算向量,而CNN为每个可能的短语计算向量。从语言学和认知科学的角度来讲,CNN并不合理。甚至recurrent neural network也比tree model和CNN更合理。

两者的关系可以这样想象,RNN将CNN捕捉的不是短语的部分删除了:

得到:

  • So the sort of picture is that for the CNN, you're sort of making a representation of every pair of words, every triple of words, every four words.

  • Where as the tree recursive neural network is saying well some of those representations don't correspond to a phrase and so we're gonna delete them out. So that for the convolultional neural network, you have a representation for every bigram. So you have a representation for there speak and trigram there speak slowly. Whereas for the recursive neural network, you only have representations for the sort of semantically meaningful phrases like people there and speaks slowly going together to give a representation for the whole sentence.

Recursive Neural Networks for Structure Prediction

  • 输入: 两个子节点的向量表示
  • 输出: 两个子节点合并后的新节点语义表示,以及新节点成立的分值

Recursive Neural Network Definition

可以同时得到句法树和向量表示的一种任务。通过socre来得到句法树。

顺便提一下assignment3:

在 assignment3 是这样的tree-RNN

\[h=relu([h^{(1)}_{left},h^{(1)}_{right}]W+b^{(1)})\] \[\hat y = softmax(h^{(1)}U+b^{(s)})\]

\(L\in R^{|V|\times d},W^{(1)}\in R^{2d\times d}, b^{(1)}\in R^{1\times d}, U\in R^{(d\times 5)}, b^{(s)}\in R^{1\times 5}\)

在assignment3中用tree-rnn进行情感分析,是已经通过句法分析得到了句法树的,所以只需要从根节点开始,递归找到子节点,并计算出对应的向量表示,并归一化softmax,然后与每个节点(包括叶节点)真实标签对比,计算得到损失值。然后用梯度下降优化得到模型参数。

那么这里的参数怎么理解?在传统的rnn中 \(W_{hh}\) 可以看做是隐藏状态转移矩阵,这里呢???关于 \(W_{hh}\) 的理解,可以看知乎 HMM和RNN是什么关系?功效上两者有冲突重叠?

Parsing a sentence with an RNN

greedily incrementally building up parse structure.

计算任意两个单词合并的得分(虽然下图是相邻两个,但我觉得那只是绘图方便;就算是我第一次写的玩具级别的依存句法分析器,也是任意两个单词之间计算):

然后贪心地选择得分最大的一对合并:

重复这一过程:计算任意两个节点,合并得分最大的一对

直到得到根节点:

模型中只有一个合成函数,使用同一个权值矩阵W处理NP、VP、PP……这明显是不合理的。

Max-Margin Framework-Details

损失函数使用最大间隔

再回顾一下多分类支持向量机损失 Multiclass Support Vector Machine Loss

我的cs231n笔记

对于单个节点,可以看做单个样本损失 \[L_i=\sum_{j\ne y_j}^N max(0, s_j-(s_{y_i}-\Delta))\] 其中 \(s_{y_j}\) 表示真实标签对应的值,非真实分类的得分不能超过 \(s_{y_j}-\Delta\),凡是超过的都会对 \(L_i\) 产生影响。比这个值就没事~

对于整个sentence:

\[J=\sum_imax(0, s(x_i,y_j)-max_{y\in A(x_i)}(s(x_i,y)+\Delta(y,y_i)))\]

  • \(\Delta\) 表示对所有非正确分类的惩罚
  • max 表示贪心搜索得到的syntactic tree的得分
  • 有时候也可用beam search

使用 tree-RNN 进行分类任务

这里的rnn指的是 递归recursive neural networks.空间结构上的递归,而以前学的RNN也是递归,不过是时间序列上的递归。

以assignment3中的情感分类任务为例,进行前向、反向传播推导。

由于前向传播时每个节点的信号来自所有子节点,所以梯度也来自所有子节点。并且前向传播时父节点的信号是利用子节点信号的拼接计算的,所以梯度需要针对子节点的信号计算:

这个问题其实在TensorFlow那一课已经讲过了,图计算:前向传播信号流入某节点,反向传播误差就得从某节点分流到所有源节点。树只是图的一个特例:

Richard Socher 的代码比如softmax之类的可真熟练~

1
2
3
4
5
6
7
8
9
10
11
12
13
def forwardProp(self, node):
# Recursive
...

# This is node's hidden activation
node.h = np.dot(self.W, np.hstack([node.left.h, node.right.h])) + self.b # [1,d]
# Relu
node.h[node.h<0] = 0

# Softmax
node.score = np.dot(self.Ws, node.h) + self.bs # [1, 5]
node.score -= np.max(node.probs)
node.probs = np.exp(node.score)/np.sum(np.exp(node.score))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def backProp(self.node, error=None):
# softmax`grad
deltas = node.probs
deltas[node.label] -= 1.0
self.dWs = np.outer(deltas, node.h) # Compute the outer product of two vectors. 训练时一个batch只有一个sentence,所有h是向量。
self.dbs += deltas
# 对隐藏状态求导
dh = np.dot(self.Ws.T, deltas)

# Add deltas from above
if error is not None:
dh += error

# f'(z) now:
# relu 反向传播
dh *= (node.h != 0)

# Updata word vector if leaf node:
# 如果是叶节点,h 就是 L,词向量.
if node.isLeaf:
self.dL[node.word] += deltas
return

# Recursively backProp
# 如果当前节点不是叶节点,那么需要更新权重W和b,同时将error
if not node.isLeaf:
self.dW += np.outer(deltas, np.hstack([node.left.h, node.right.h]))
self.db += deltas
# Error signal to children
dh = np.dot(self.W.T, dh) # 就是公式 h=relu([h^{(1)}_{left},h^{(1)}_{right}]W+b^{(1)})
# 递归计算左子节点,node.lef用来计算左子节点自身的损失, dh[:self.hiddenDim]用来计算父节点传递下来的损失
self.backProp(node.left, dh[:self.hiddenDim])
self.backProp(node.right, dh[self.hiddenDim:])

ok!完全弄懂了吧?!~

Syntactically-Untied RNN

Version3

Presentation

[Deep reinforcement learning for dialogue generation]

reference