全球名校课程作业分享系列--斯坦福CS231n之RNN与计算机看图说话

Posted 寒小阳

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了全球名校课程作业分享系列--斯坦福CS231n之RNN与计算机看图说话相关的知识,希望对你有一定的参考价值。

课程作业原地址:CS231n Assignment 3
作业及整理:@张礼俊 && @Molly && @寒小阳
时间:2018年2月。
出处:http://blog.csdn.net/han_xiaoyang/article/details/79316411

1. 问题背景

在问题1里,我们要训练一个循环神经网络(Recurrent neural networks)来生成一个图片的文字注释(captions)。问题2中,用以长短时记忆单元(Long-short term memory,LSTM)为基础的循环神经网络来完成同样的任务。
我们将用到的数据集是微软的COCO数据集,该数据集是测试为图片加文字注释算法的标准数据集。在该数据集中有大概80000张训练图片和40000张测试图片,每张图片在Amazon Mechanical Turk上征集了五个志愿者来手动写文字说明。
我们可以运行问题1的前几个单元来对我们将要应用的数据有个直观的概念,要注意一点的是之后的处理中我们不会再碰到原始图像,该问题已经为我们提取了图像特征。

2. 循环神经网络

问题1要求我们实现一个循环神经网络。这一部分讲述了问题1的前半部分代码。
循环神经网络是一类处理序列数据的神经网络。在本题用到的循环神经网络的架构如下图所示:

1)单步前向传播(RNN step forward)

我们要实现的第一段代码是单步前向传播。对于每一层神经网络,或每一个时刻,我们输入一个隐藏状态 h t − 1 h_t-1 ht1(上一层神经网络的输出),一个外部输入 x t x_t xt;之后得到下一个隐藏状态 h t h_t ht,以及该时刻的输出 y t y_t yt。对应的数学表达式为
KaTeX parse error: No such environment: equation at position 8: \\begin̲e̲q̲u̲a̲t̲i̲o̲n̲̲ \\beginaligned…
b b b为偏差值。
作业中,我们要在rnn_layers.py文件下实现如下函数:

def rnn_step_forward(x, prev_h, Wx, Wh, b):
    """
    输入:
    - x: 外部输入数据, 维度 (N, D).
    - prev_h: 上一个时刻的隐藏状态, 维度 (N, H)
    - Wx: x对应的权值矩阵, 维度 (D, H)
    - Wh: 隐藏状态h对应的权值矩阵, 维度 (H, H)
    - b: 偏差值 shape (H,)

    输出:
    - next_h: 下一个时态的隐藏状态, 维度 (N, H)
    - cache: 计算梯度反向传播时需要用到的变量.
    """
    temp1 = np.dot(x,Wx)
    temp2 = np.dot(prev_h,Wh)
    cache=(x,prev_h,Wx,Wh,temp1+temp2+b)
    next_h = np.tanh(temp1+temp2+b)
    return next_h, cache

单层的前向传播并不难,实现的时候只要熟练使用numpy底下的矩阵运算即可。一个要注意的点是这里的激活函数是作用于向量中的每一个元素(element wise)。

2)梯度单步反向传播(RNN step backward)

可以说几乎所有训练神经网络的算法都是基于梯度下降(gradient descent)的,所以如何获得每个节点,以及相应的参数对应的梯度至关重要。正如之前的作业中训练神经网络时做的,求梯度反向传播其实只是在不断的应用求导的链式法则。假设最终的损失函数为 E E E,在进行反向传播时,我们得到了上一层传来的梯度 d E d h t \\fracdEdh_t dhtdE,我们需要计算 d E d h t − 1 \\fracdEdh_t-1 dht1dE, d E d x t \\fracdEdx_t dxtdE, d E d W x h \\fracdEdW_xh dWxhdE, d E d W h h \\fracdEdW_hh dWhhdE d E d b \\fracdEdb dbdE。还记得前向传播的公式为
$$
\\beginequation*

\\beginaligned
    h &=f_w(h_t-1,x_t)\\\\
    &=\\tanh(W_hhh_t-1+W_xhx_t+b)
\\endaligned

\\endequation*
KaTeX parse error: Can't use function '$' in math mode at position 4: 假设$̲a=W_hhh_t-1…
\\beginequation*

\\beginaligned
    \\fracdEdh_t-1&=\\fracdEdh_t\\fracdh_tdh_t-1  \\\\
    &=\\fracdEdh_t\\fracdh_tda\\fracdadh_t-1
\\endaligned

\\endequation*
$$

d h t d a \\fracdh_tda dadht就是在对激活函数求导。 d a d h t − 1 \\fracdadh_t-1 dht1da就是权值矩阵 W h h W_hh Whh
要注意一点的是,为了更易于阐明概念,上式做了一些简化, d E d h t \\fracdEdh_t dhtdE d E d h t \\fracdEdh_t dhtdE都是向量,他们的相乘是逐项(element wise)相乘。之后与 d a d h t − 1 \\fracdadh_t-1 dht1da相乘,则是一般的向量乘矩阵,编程时注意对应的维度就好。
我们可以用相似的思路获得 d E d x t \\fracdEdx_t dxtdE, d E d W x h \\fracdEdW_xh dWxhdE, d E d W h h \\fracdEdW_hh dWhhdE d E d b \\fracdEdb dbdE的计算方式。
作业中,我们要实现如下函数:

def rnn_step_backward(dnext_h, cache):
    """
    输入:
    - dnext_h: 下一层传来的梯度
    - cache: 前向传播存下来的值

    输出
    - dx: 输入x的梯度, 维度(N, D)
    - dprev_h: 上一层隐藏状态的梯度, 维度(N, H)
    - dWx: 权值矩阵Wxh的梯度, 维度(D, H)
    - dWh: 权值矩阵Whh的梯度, 维度(H, H)
    - db: 偏差值b的梯度,维度(H,)
    """
    x=cache[0]
    h=cache[1]
    Wx=cache[2]
    Wh=cache[3]
    # cache[4]对应着公式中的a
    cacheD=cache[4]
    N,H=h.shape
    # 计算激活函数的导数
    temp = np.ones((N,H))-np.square(np.tanh(cacheD))
    delta = np.multiply(temp,dnext_h)
    # 计算x的梯度
    tempx = np.dot(Wx,delta.T)
    dx=tempx.T
    # h的提督
    temph = np.dot(Wh,delta.T)
    dprev_h=temph.T
    # Wxh的梯度
    dWx = np.dot(x.T,delta)
    # Whh
    dWh = np.dot(h.T,delta)
    # b的梯度
    tempb = np.sum(delta,axis=0)
    db=tempb.T
    return dx, dprev_h, dWx, dWh, db

3)前向传播(RNN forward)

我们完成了单步前向传播之后,对于整个循环神经网络的前向传播过程就是单步前向传播的循环,而且在本题中,每一层神经网络都共享了参数 W x h W_xh Wxh W h h W_hh Whh b b b。对应的代码如下:

def rnn_forward(x, h0, Wx, Wh, b):
    """

    输入:
    - x: 整个序列的输入数据, 维度 (N, T, D).
    - h0: 初始隐藏层, 维度 (N, H)
    - Wx: 权值矩阵Wxh, 维度 (D, H)
    - Wh: 权值矩阵Whh, 维度 (H, H)
    - b: 偏差值,维度 (H,)

    输出:
    - h: 整个序列的隐藏状态, 维度 (N, T, H).
    - cache: 反向传播时需要的变量
    """
    N,T,D=x.shape
    N,H=h0.shape
    prev_h=h0
    # 之前公式中对应的a
    h1=np.empty([N,T,H])
    # 隐藏状态h的序列
    h2=np.empty([N,T,H])
    # 滞后h一个时间点
    h3=np.empty([N,T,H])
    for i in range(0, T):
        #单步前向传播
        temp_h, cache_temp = rnn_step_forward(x[:,i,:], prev_h, Wx, Wh, b)
        #记录下需要的变量
        h3[:,i,:]=prev_h
        prev_h=temp_h
        h2[:,i,:]=temp_h
        h1[:,i,:]=cache_temp[4]
    cache=(x,h3,Wx,Wh,h1)
    return h2, cache

4)梯度反向传播(RNN backward)

和前向传播一样,我们已经有了单步反向传播,编程时反向传播就是单步反向传播的循环。另外因为每一层神经网络都共享了参数 W x h W_xh Wxh W h h W_hh Whh b b b,最终的 d E d W x h \\fracdEdW_xh dWxhdE是每一层计算得到的 d E d W x h \\fracdEdW_xh dWxh以上是关于全球名校课程作业分享系列--斯坦福CS231n之RNN与计算机看图说话的主要内容,如果未能解决你的问题,请参考以下文章

全球名校课程作业分享系列(10)--斯坦福CS231n之Network visualization

全球名校课程作业分享系列(10)--斯坦福CS231n之Network visualization

全球名校课程作业分享系列--斯坦福计算机视觉与深度学习CS231n之KNN

全球名校课程作业分享系列--斯坦福计算机视觉与深度学习CS231n之tensorflow实践

全球名校课程作业分享系列--斯坦福CS231n之RNN与计算机看图说话

全球名校课程作业分享系列--斯坦福计算机视觉与深度学习CS231n之SVM图像分类