RNN每个时间步都需要将 x 值的输入序列映射到输出值 o 的对应序列。其中 o 是未归一化的对数概率,并且在损失函数 L 内部计算 y^=softmax(x)。损失函数 L 用于衡量每个 o 与相应的训练目标 y 的距离。
RNN输入到隐含单元的连接由权重矩阵 U 参数化,隐含单元与隐含单元连接由权重矩阵 W 参数化,隐含单元到输出节点的连接由权重矩阵 V 参数化。从上面的图中也可以看出,与CNN类似,在RNN中同样有参数共享的思想(共享了参数W,U,V等)。这使得模型的泛化能力更强,因为模型不会依赖于特定位置上的输入x。例如考虑这两句话:“I went to Nepal in 2009’’ 和 “In 2009, I went to Nepal.” ,他们表达的意思完全相同,但是时间2009出现在不同的地方,RNN的参数共享机制可以更有效的提取到这个时间信息。参数共享也允许模型泛化到没有见过的序列长度,并且训练模型所需的训练样本远远少于不带参数共享的模型。
当训练循环网络根据过去的信息去预测未来的信息时,网络通常要使用隐含状态 h (t) 来表示时刻 t 之前的序列信息: h(t)=g(t)(x(t),x(t−1),x(t−2),…,x(2),x(1))=f(h(t−1),x(t);θ). 函数g(t)将过去的序列(x(t),x(t−1),x(t−2),…,x(2),x(1))作为输入来生成当前的隐含状态h(t)。实际上,我们可以递归调用函数f来完成隐含状态的计算。这么做主要有两个优点:
上面的图中没有指明**函数,假设使用tanh作为**函数,并且假设输出值是离散的,例如用于预测类别。一种表示离散变量的方式是:把输出 o 作为离散变量每种可能值的非标准化对数概率。然后,我们可以应用 softmax 函数获得标准化后概率的输出向量y^。
RNN从特定的初始状态 h (0) 开始前向传播。从 t = 1 到 t = τ 的每个时间步,我们应用以下更新方程: a(t)=b+Wh(t−1)+Ux(t)h(t)=tanh(a(t))o(t)=c+Vh(t)
y^(t)=softmax(o(t))=∑t=1τeo(t)eo(t)
其中的参数的偏置向量 b 和 c 连同权重矩阵 U、V 和 W,分别对应于输入到隐藏单元、 隐藏单元到输出和隐藏单元到隐藏单元的连接。这个循环网络将一个输入序列映射到相同长度的输出序列。与 x 序列配对的 y 的总损失就是所有时间步的损失之和,例如定义损失L为所有时间步的负对数似然函数: L({x(1),…,x(τ)},{y(1),…,y(τ)})=t∑L(t)=−t∑logPmodel(y(t)∣{x(1),…,x(τ)}) 了解正向传播的计算内容之后,就可以讨论通过时间反向传播算法来更新网络参数了。
通过时间反向传播(BPTT)
回顾之前的计算图:
对于每一个节点 N,我们需要先 N 后面的节点的梯度,然后递归地计算梯度 ∇NL。我们从最后一个节点开始递归: ∂L(t)∂L=1 观察正向传播的计算公式,不难发现 L(t)是关于y^(t)的函数,y^(t)是softmax的结果, 而softmax是关于 o(t)的函数。求L对非标准化对数概率 o(t)求偏导(相关求导过程可以参考Softmax求导),得到: (∇o(t)L)i=∂oi(t)∂L=∂L(t)∂L∂y^i(t)∂L(t)∂oi(t)∂y^i(t)={y^i(t)−1,yi(t)=labely^i(t)−0,yi(t)=label 从序列的末尾开始,反向进行计算。在最后的时间步 τ,只有 o (τ) 依赖于h (τ) ,不存在h (τ+1)依赖于h (τ) ,因此这个梯度很简单。由o(t)=c+Vh(t),可以知道: ∇h(τ)L=∂h(τ)∂o(τ)∇o(τ)L=VT∇o(τ)L 然后我们可以从时刻 t = τ − 1 到 t = 1 反向迭代,通过时间反向传播梯度。由下面的式子: a(t)=b+Wh(t−1)+Ux(t)h(t)=tanh(a(t))o(t)=c+Vh(t) 不难发现当t < τ时, o (t) 和 h (t+1) 都依赖于h (t) 。因此,它的梯度由两个部分组成: ∇h(t)L=(∂h(t)∂h(t+1))⊤(∇h(t+1)L)+(∂h(t)∂o(t))⊤(∇o(t)L)=W⊤(∇h(t+1)L)diag(1−(h(t+1))2)+V⊤(∇o(t)L) 其中: tanh′(x)=1−(tanh(x))2
1, 每个时间步都产生一个输出,但是只有当前时刻的输出 o 与下个时刻的隐藏单元之间有连接的RNN:
这样的RNN没有最开始介绍的 RNN 那样强大,上图中的RNN被训练为将特定输出值放入 o 中,并且 o 是允许传播到未来的唯一信息。此处没有从 h 前向传播的直接连接。之前的 h 仅通过产生的预测间接地连接到当前。o 通常缺乏过去的重要信息,除非它非常高维且内容丰富。这使得该图中的RNN不那么强大,但是它更容易训练,因为每个时间步可以与其他时间步分离训练,允许训练期间更多的并行化。
2, 隐藏单元之间存在循环连接,但读取整个序列后只在最后一个时间步产生单个输出的RNN:
这样的网络可以用于概括序列,并产生一个向量 o 用于表示整个输入序列,然后可以对 o 进行进一步的处理。例如在翻译任务中,先输入原始输入的句子,得到句子的向量表示 o ,然后对 o 展开进一步的翻译任务。
3,包含上下文的RNN:
此RNN包含从前一个输出到当前状态的连接。这些连接允许RNN在给定 x 的序列后,对相同长度的 y 序列上的任意分布建模。
4,双向RNN
在许多应用中,我们要输出的 y (t) 的预测可能依赖于整个输入序列。例如,在语音识别中,当前声音作为音素的正确解释可能取决于未来几个音素,甚至可能取决于未来的几个词,因为词与附近的词之间的存在语义依赖。双向RNN结构如下:
隐藏状态变量 h 在时间上向前传播信息(向右),而隐藏状态变量 g 在时间上向后传播信息(向左)。因此在每个点 t,输出单元 o (t) 可以受益于输入 h (t) 中关于过去的相关信息以及输入 g (t) 中关于未来的相关信息。
本文主要是利用图片的形式,详细地介绍了经典的RNN、RNN几个重要变体,以及Seq2Seq模型、Attention机制。希望这篇文章能够提供一个全新的视角,帮助初学者更好地入门。 Table of Contents 一、从单层网络谈起 二、N vs N(RNN经典结构) 三、N VS 1(类别判断) 四、1 VS N(生成模型) 五、N vs M(Seq2S…
You can always leverage the fact that nan != nan: >>> x = torch.tensor([1, 2, np.nan]) tensor([ 1., 2., nan.]) >>> x != x tensor([ 0, 0, 1], dtype=torch.uint8) Wi…
GPU上创建目标检测Pipeline管道 Creating an Object Detection Pipeline for GPUs 今年3月早些时候,展示了retinanet示例,这是一个开源示例,演示了如何加快gpu目标检测管道的训练和部署。在圣何塞举行的英伟达GPU技术会议上介绍了这个项目。这篇文章讨论了这项工作的动机,对体系结构的一个高级描述,以及…