从无到有RNN

这段时间看了一些关于循环神经网络的资料,也了解了一些框架中对RNN的封装。觉得还是要在非深度学习框架下用最直接的方式过一遍RNN的例子才能更好地理解RNN和其中用到的一系列算法(bptt,sgd,adam等)这篇博客主要结合一个简单的例子理解RNN内部的结构以及bptt算法的过程。还有个小心思,最近CNN相当的火爆,我不信RNN在序列问题上干不过CNN?(开玩笑的。。。其实想有些创新,就要了解原理)

参考资料

http://www.wildml.com/2015/09/recurrent-neural-networks-tutorial-part-1-introduction-to-rnns/
http://cs231n.github.io/neural-networks-case-study/
https://gist.github.com/karpathy/d4dee566867f8291f086
http://cs231n.github.io/optimization-2/#staged
https://www.youtube.com/watch?v=cO0a0QYmFm8&feature=youtu.be&list=PLlJy-eBtNFt6EuMxFYRiNRS07MCWN5UIA&t=836
http://blog.csdn.net/linmingan/article/details/50958304
http://www.wildml.com/2015/10/recurrent-neural-networks-tutorial-part-3-backpropagation-through-time-and-vanishing-gradients/
http://www.wildml.com/2015/10/recurrent-neural-network-tutorial-part-4-implementing-a-grulstm-rnn-with-python-and-theano/

循环神经网络

循环神经网络的思想是利用序列信息。在传统神经网络中,输入之间相互独立。但是对于很多任务,这不是一个好的想法。如果要预测一个句子中下一个出现的单词,我们希望知道哪些词语已经出现过。循环神经网络被叫做“循环”,是因为网络对序列中的每个元素采取同样地操作,这些操作要用到前一步计算的结果。另一种对于循环神经网络的理解是网络有个“记忆单元”,可以捕捉到目前为止的信息。理论上循环神经网络可以发挥任意长序列的信息,但是在实践中,网络往往只能得到较短序列的信息。

RNN展开图

从图中可以看到循环神经网络展开后的状态。例如,如果一个句子包含5个单词,那这个循环神经网络展开后就会有5层,每层的输入对应一个单词。图中,Xt代表着t时刻的输入。在自然语言处理相关任务中输入往往是单词的词向量表示。

St是t时刻隐藏层的状态。他是网络的记忆单元。他的计算需要当前时刻的输入以及前一时刻隐藏层的状态,可表示为St = F(U*Xt + W*St-1)。F函数通常为tanh或者ReLU。St-1在第一时刻通常被初始化为0。想想也是这个道理,对于一个句子,开始的时候我们一无所知,记忆为0,随着一个个单词的读入,我们逐渐掌握很多信息。

Ot是t时刻的输出。通常有 Ot = softmax(V*St)。其实,并不是每个时刻都需要有输出,需要看具体任务。比如,情感分析只要输出最后一个Ot。机器翻译就需要有不同时刻的输出。

我们需要注意的是:
1)St作为网络t时刻的记忆单元,捕捉之前时刻中的信息。Ot的计算只是依靠t时刻的记忆。
2)U,V,W作为网络的参数,需要经过训练得到。并且,U,V,W在时间维度上共享权值。对!没错图中t-1时刻,t时刻,t+1时刻。。。的UVW是同样一组参数。所以,网络的计算过程中,只有每一步的输入是不同的。这样,减少了需要训练的参数的个数。
3) 这篇文章Gradients for an RNN介绍了RNN中梯度的推导过程,我看到过的最值得推荐的资料!!

训练循环神经网络

训练循环神经网络和训练普通的神经网络相似,但是需要用到bptt算法。因为循环神经网络的参数是随时间维度共享的,所以每次输出的梯度不仅需要当前的步骤还需要之前的步骤。例如为了求4时刻的梯度,我们需要计算之前3个时刻的梯度。下面语言模型的例子中展示了bptt算法的过程,再详细介绍。

一个用numpy写的rnn语言模型例子

1)这个例子的功能是在语料上训练,预测下一个出现的字符。
2)通过这个例子可以了解bptt算法实现过程。

代码:

#coding:utf-8
import numpy as np

data = open('input.txt','r').read()
#得到data中的所有字符
chars = list(set(data))
data_size, vocab_size = len(data), len(chars)
print('data has %d characters, %d unique.' % (data_size, vocab_size))
#字典,字符和数字一一对应,输入时用数字代表字符
char_to_ix = {ch:i for i,ch in enumerate(chars)}
ix_to_char = {i:ch for i,ch in enumerate(chars)}

#超参数
#隐层大小
hidden_size = 128
#句子长度
seq_length  = 25
#学习率
learning_rate = 1e-1

#模型参数
#输入到隐层矩阵,形状为(hidden_size, vocab_size)为了方便计算,下同
Wxh = np.random.randn(hidden_size, vocab_size)*0.01
#隐层到隐层矩阵
Whh = np.random.randn(hidden_size, hidden_size)*0.01
#隐层到输出矩阵
Why = np.random.randn(vocab_size, hidden_size)*0.01
#隐层及输出层偏置
bh = np.zeros((hidden_size, 1))
by = np.zeros((vocab_size, 1))

#最重要部分,损失函数,包括前向传播,后向计算误差,bptt算法
def lossFun(inputs, targets, hprev):

    xs, hs, ys, ps = {}, {}, {}, {}
    hs[-1] = np.copy(hprev)
    loss = 0
    #前向计算预测值
    for t in range(len(inputs)):
        xs[t] = np.zeros((vocab_size, 1))
        xs[t][inputs[t]] = 1
        '''
        公式
        h[t] = tanh(Wxh * x[t] + Whh * h[t-1] + bh)
        y[t] = Why * h[t] + by
        p[t] = sigmoid(y[t])
        loss = -targets*log(p)
        '''
        hs[t] = np.tanh(np.dot(Wxh, xs[t]) + np.dot(Whh, hs[t-1]) + bh)
        ys[t] = np.dot(Why, hs[t]) + by
        ps[t] = np.exp(ys[t]) / np.sum(np.exp(ys[t]))
        loss += -np.log(ps[t][targets[t],0])

    #后向进行梯度下降
    #d开头变量用于存储各自变量的梯度
    dWxh, dWhh, dWhy = np.zeros_like(Wxh), np.zeros_like(Whh), np.zeros_like(Why)
    dbh, dby = np.zeros_like(bh), np.zeros_like(by)
    dhnext = np.zeros_like(hs[0])
    for t in reversed(range(len(inputs))):
        dy = np.copy(ps[t])
        dy[targets[t]] -= 1

        dWhy += np.dot(dy, hs[t].T)
        dby += dy

        dh = np.dot(Why.T, dy) + dhnext
        dhraw = (1 - hs[t]*hs[t])*dh

        dbh += dhraw
        dWxh += np.dot(dhraw, xs[t].T)
        dWhh += np.dot(dhraw, hs[t-1].T)
        dhnext = np.dot(Whh.T, dhraw)
    #防止梯度爆炸,控制梯度在-5到5之间
    for dparam in [dWxh, dWhh, dWhy, dbh, dby]:
        np.clip(dparam, -5, 5, out=dparam)
    return loss, dWxh, dWhh, dWhy, dbh, dby, hs[len(inputs)-1]

#用于测试
#根据当前输入字符,产生后面的200个字符
def sample(h, seed_ix, n):
    x = np.zeros((vocab_size), 1))
    x[seed_ix] = 1
    ixes = []
    for t in xrange(n):
        h = np.tanh(np.dot(Wxh, x) + np.dot(Whh, h) + bh)
        y = np.dot(Why, h) + by
        p = np.exp(y) / np.sum(np.exp(y))
        ix = np.random.choice(range(vocab_size), p=p.ravel())
        x = np.zeros((vocab_size, 1))
        x[ix] = 1
        ixes.append(ix)
    return ixes

n, p = 0, 0
#用于Adagrad算法的变量
mWxh, mWhh, mWhy = np.zeros_like(Wxh), np.zeros_like(Whh), np.zeros_like(Why)
mbh, mby = np.zeros_like(bh), np.zeros_like(by)

smooth_loss = -np.log(1.0/vocab_size)*seq_length

while True:
    if p+seq_length+1 >= len(data) or n == 0:
        hprev = np.zeros((hidden_size,1))
        p = 0
    inputs = [char_to_ix[ch] for ch in data[p:p+seq_length]]
    targets = [char_to_ix[ch] for ch in data[p+1:p+seq_length+1]]

    if n%100 == 0:
        sample_ix = sample(hprev, inputs[0], 200)
        txt = ''.join(ix_to_char[ix] for ix in sample_ix)
        print('----\n %s \n----' % (txt, ))

    loss, dWxh, dWhh, dWhy, dbh, dby, hprev = lossFun(inputs, targets, hprev)
    smooth_loss = smooth_loss * 0.999 + loss * 0.001
    if n%100 == 0:
        print('iter %d, loss: %f'%(n,smooth_loss))
    #Adagrad算法,随机梯度下降法的改进
    for param, dparam, mem in zip([Wxh, Whh, Why, bh, by], [dWxh, dWhh, dWhy, dbh, dby], [mWxh, mWhh, mWhy, mbh, mby]):
        mem += dparam * dparam
        param += -learning_rate * dparam / np.sqrt(mem + 1e-8)

    p += seq_length
    n += 1

实验结果

这里写图片描述
这里写图片描述

例子中bptt算法公式过程

这里写图片描述
整个公式一气呵成!

总结

这篇文章简单介绍了循环神经网络的基本结构和算法。通过一个小例子,看懂了RNN中的很多细节,然后这只是基础的部分。我们都知道原始的RNN梯度消失问题比较严重,他的变种lstm,gru能够稍微减轻这个问题。还有很多要研究的问题。最重要的应当是RNN的应用问题。后面博主会继续关注~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值