白话RNN系列(一)

RNN,循环神经网络,全称Recurrent Neural Network。

本文,从RNN的基本原理讲起,会探讨RNN的前向传播和反向传播,并通过一些浅显易懂的小例子,展示RNN这个东东的神奇之处,尽最大可能以通俗易懂的方式,让看到本文的童鞋都能够掌握RNN。

1:RNN的基本原理

即便是RNN,也依旧脱离不了神经网络的基本架构,换句话说,我们看RNN的时候,一定要记住一句,它不过是高级一些的神经网络而已,其深层也不过是神经网络而已,没什么可怕的。

我们先拿个神经网络来做例子:

è¿éåå¾çæè¿°

上图展示的是最基础的神经网络,即全连接神经网络,除了最左侧的输入层和最右侧的输出层,其他均为隐藏层,其之所以叫做全连接神经网络,从图中我们也可以看出,输入元素会与隐藏层的每一个节点产生连接,同时,隐层的每个节点又会与下一层的所有节点产生连接,因此,叫做全连接神经网络。

而我们神经网络最终的目的是什么?求取的实际上是一个权重矩阵。

举个例子来说,如上图,我们可以看到输入层有五个节点,可以假设一个输入有五个特征的向量,如X=(x1,x2,x3,x4,x5)。

而隐藏层只有四个节点,这时候,我们需要一个维度为5*4的权重矩阵W,将其转化为4个特征的向量,而这个权重矩阵,就是我们最终需要的东西。

有些童鞋会提到偏置,我们这里不予考虑,或者可以将其简化入权重矩阵内。

那是不是神经网络就是这一层层的权重矩阵相乘,最后得到一个我们需要维度的输出呢?比如说最终我们需要做一个softmax的多分类,比如常见的手写字体识别,是不是最后输出一个十个维度的的向量,交给softmax就可以了呢?

显然不是。

这种方式下,可以看出,就成了完全的线性变换,如输入层与第一隐层之间的权重矩阵为W1,第一隐层与第二隐层之间的权重矩阵为W2,循环往复,最终的输出可能为:

Y = W1 * W2 * ···Wn * X

前面的多个矩阵的乘积,与一个单独的矩阵没什么本质区别,换句话说,我们多个隐层的存在是没有意义的,我们完全可以用一个简单的线性变换来完成我们的工作。

讨论到此结束,大家都去琢磨线性变换就可以了。

但是,神经网络绝不是那么简单的,其在线性变换的同时,于隐藏层的神经元内,做了非线性变换,线性变换与非线性变换的结合,最终完成了很多不可思议的任务。

必须注意,直到这里,我们谈论的也只是全连接神经网络,而其妙处,则是在隐藏层神经元上,我们看下隐藏层神经元的结构:

这是简单的M-P神经元模型,图片来自于周志华老师的机器学习一书;这里,我们单独看隐藏层的一个节点,其他节点的处理雷同。

上面所说的输入层与第一隐层之间的权重矩阵,其维度为5*4,原因在于,我们输入的特征是5维的,而隐藏层神经元的个数为4层;换算到这里,就相当于n=5,我们用一个隐藏层节点来分析的话,会发现对于这个节点来说,我们输入向量实际上是左乘了一个5维的向量,得到了一个实数。

向量乘法很简单: (1 * 5) * (5 * 1),得到一个实数;前面的1 * 5 是输入层与第一个神经元之间的权重关系,那么,如果隐藏层神经元为4个的话,我们把四个权重向量堆叠成权重矩阵,自然而然得到了一个 4 * 5 的权重矩阵;而这个权重矩阵与输入向量乘积,得到的则是一个 4 * 1的向量,正好对应于隐藏层神经元,每个节点的输出。

我们在吴恩达老师的课程中会看到,其写的是权重矩阵的转置,即这里的4 * 5 是权重矩阵的转置,实际上的权重矩阵应该是5 * 4的矩阵。

但是,这对于问题的理解并无影响,只是写起来更加方便,我们可以轻松地通过输入层的特征数目 * 隐藏层神经元的数目,得到第一个权重矩阵的维度。

在代码中的实现形如(手写字体的部分代码):

x = tf.placeholder(tf.float32, [None, 784])
y = tf.placeholder(tf.float32, [None, 10])
keep_prob = tf.placeholder(tf.float32)
# 自己定义一个学习率
lr = tf.Variable(0.001, dtype=tf.float32)

# 设计第一层神经网络 : 784 * 2000
# 因为输入样本是784维向量,而输出时2000个神经元
W1 = tf.Variable(tf.truncated_normal([784, 500], stddev=0.1))
b1 = tf.Variable(tf.zeros([500]) + 0.1)
L1 = tf.nn.tanh(tf.matmul(x, W1) + b1)
L1_drop = tf.nn.dropout(L1, keep_prob)

手写字体识别的输入向量,是1 * 784维的,即具有784个特征的向量,而我们的权重矩阵,则是784 * 500维的,这种写法更好理解一些。

插了一些关于权重矩阵的闲话,我们继续看隐藏层神经元中隐藏的非线性变换。

常见的有sigmoid,tanh,relu等,我们通过矩阵与向量相乘得到的输入,在每个隐藏层节点内都会经过非线性变换,才能作为下一层神经元节点的输入。

神经网络就是这么简单,没什么神秘的地方,说起来加入了那么多东西,其实质也就是我们平日里见到的线性和非线性变换而已。

说了这么多,我们认真看下全连接神经网络到底存在什么缺点,才催生出后续的卷积神经网络和循环神经网络。

1:全连接,导致权重矩阵过大,同时特征会被割裂。

简单说,如果我们输入的是非常大的图片,比如1920 * 1920这种高清图片,光是输入特征的维度就高达百万级别,再加上隐藏层的的维度,比如说隐藏层设置128个节点,那第一个权重矩阵的维度就高达千万个参数,别忘了,我们还有偏置......

这个代价很高,如果全连接神经网络的层数再多一些,那计算代价,高的可怕,所以我们平常用到的全连接神经网络速度慢,就可以理解了。

特征割裂,是我对于不考虑特征之间相互关系的一个称呼,能看得出来,全连接建立在一个假设的基础上,即各个特征之间完全不存在相关关系,因此对于每个隐藏层节点而言,都有自己的权重,在一些情况下,这是合理的,但很遗憾,现在大多数数据都存在相关性,如果完全不考虑其相关关系,不仅权重矩阵过大,计算代价高,往往还不会得到理想的效果。

所以权值共享的概念被提了出来,在卷积神经网络和循环神经网络里,我们都可以看到其踪迹。

2:隐层内部的神经元互相之间,其实是没有任何连接的。

很简单,上面的图都可以看到这点,每个神经元节点的输出,只会不断向后传递,同一层神经元之间,不存在任何的相互关联。

乍一看,没啥问题;但随着我们处理的任务越来越多,尤其是NLP的高速发展,神经网络在NLP方面的缺陷就暴露出来,为什么?

每个人说一句话,都会掺杂进自己先前的背景知识,即记忆,一句话的前言和后语,通常都是有联系的,可惜的是,当前的神经网络解决这类问题效果不是很好,因为其不存在记忆。

在这种情况下,有人提出了循环神经网络,其实质在于,在隐藏层的神经元节点之间加入了联系,产生的效果就是,第一个神经元节点的输出,可以被第二个神经元节点获取到;同理,最后一个隐层神经元节点会接收到几乎上面所有神经元节点的输出(只不过最近的神经元肯定对其输入影响最大而已)。

通俗来说,就是RNN在分析每一句话的时候,会尽最大可能考虑前面用户输入的每个字,以期望达到一个比较好的效果,这是非常好的设想,也的确达到了不错的效果。

而这个RNN,就是我们接下来需要探讨的重点;其内部不仅有循环输入,同样也加入了权值共享,对于我们深入对其他神经网络的理解,很有帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值