TensorFlow实现全连接神经网络的前向传播过程

一.前向传播算法

以tensorflow游乐场为例子。在神经网络中,有若干个层。比如上图中就有一个输入层,一个隐藏层和一个输出层。那么在神经网络中,我们是如何通过计算得到最后的结果呢?先来看下面一张图:



一个最简单的神经元结构输出就是所有输入的加权和。也就是说如果要计算上图神经元的取值,我们可以这么来做:x1 * w1 + x2 * w2 + x3 * w3(+1在这里暂不考虑)。
下面再来看一个三层的全连接网络:

!

根据上面的结论我们来类推上面这个三层全连接神经网络的计算过程:
首先来看a11是怎么得到的。因为这里讨论的是全连接神经网络,所以a11会与输入的每一个神经元相连。(这里我们同样不考虑偏置项+1)每一条连线上都有一个权重,这里我们用W(1,1)代表输入层第一个神经元和隐藏层第一个神经元之间的权重,其它的以此类推。所以我们很容易地得出a11 = x1 * W(1,1) + x2 * W(2,1) + x3 * W(3,1) 。
a12和a13也可以用类似的方法计算到。
类似地,输出层中节点的取值就是第一层的加权和:b21 = a11 * a(2,1) + a12 * a(2,2) + a13 * a(3,2)。这就是一个最简单的神经网络前向传播算法。
前向传播算法可以表示为矩阵乘法,如下:
将输入层的三个神经元组织成1x3的矩阵x=[x1,x2,x3]。第一个隐藏层的权重W(1)组织成一个3*3的矩阵:

  W(1) = [ 
                W(1,1) W(1,2) W(1,3)
                W(2,1) W(2,2) W(2,3)
                W(3,1) W(3,2) W(3,3)
              ]

这样通过矩阵乘法可以得到隐藏层三个节点所组成的向量的取值:



其中f为激活函数,b为偏置项。在这里暂时不做介绍。以上计算在tensorflow中我们可以这样表示:

a = tf.matmul(x,w1)
y = tf.matmul(a,w2)

y就是上述神经网络前向传播之后的结果。tf.matmul实现了矩阵乘法的功能。在这里我们暂时忽略偏置项和激活函数,在后面我们会详细介绍他们。在这里w1,w2是神经网络的参数,那么我们是怎么获得的呢?来看下面代码。

w1 = tf.Variable(tf.random_normal([3,3],stddev=2))
w2 = tf.Variable(tf.random_normal([1,3],stddev=2))

这段代码调用了TensorFlow的变量声明函数tf.Variable,并给出了初始值。tf.random_normal([3,3],stddev=2)会产生一个3*3的矩阵,矩阵元素的均值为0,标准差为2的随机数。tf.random_normal函数可以通过参数mean来指定平均值,在没有指定时默认为0。通过满足正态分布的随机数来初始化神经网络的参数是一个常用方法。
在神经网络中,偏置项(bias)通常会使用常数来设置初始值。代码如下

biases = tf.Variable(tf.zeros([3]))

以上代码会生成一个初始值全部为0,长度为3的变量。
下面我们通过一段TensorFlow代码来实现上述神经网络的前向传播过程:

#下面是一个简单的神经网络例子
import tensorflow as tf
#定义神经网络参数(权重)
#在这里是3个输入变量,一个3节点隐藏层,一个输出层 。这里还通过seed参数设置了随机数种子。
w1 = tf.Variable(tf.random_normal([3,3],stddev=2,seed=1))
w2 = tf.Variable(tf.random_normal([3,1],stddev=2,seed=1))

#模拟输入向量,这里暂时定义为一个常量
x = tf.constant([0.7,0.9,1.1])

#通过矩阵相乘得到前向传播结果
a = tf.matmul(x,w1)
y = tf.matmul(a,w2)

#通过定义一个tf.Session来运行上述过程
#在运行前我们需要初始化所有变量
sess = tf.Session()
#tf.global_variables_initializer可以拿到所有定义的变量
init_op = tf.global_variables_initializer()
sess.run(init_op)
#打印传播结果
print(sess.run(y))
sess.close()

以上就是一个最简单的TensorFlow前向传播的过程。

二.激活函数

2.1线性模型的局限性(可以简单理解为直线无法表示曲线上的点)

之前在TensorFlow游乐场中已经介绍过线性变换不能解决非线性分类问题的例子,下面我们来介绍具体在数学上的原理。刚才我们介绍的前向传播算法中,抛开激活函数,那么我们模型的输出仅仅为输入的加权和。假设一个模型的输出z和输入x(i)满足以下关系,那么这个模型就是一个线性模型。



其中W、b为模型的参数。当输入x只有一个的时候(这里指输入层只有一个节点),x和z行成了二维坐标系上的一条直线。当模型有n个输入的时候,x和y形成了n+1维空间中的一个平面。而一个线性模型通过输入得到输出的函数被称之为一个线性变换。线性模型的最大特点就是任意线性模型的组合还是一个线性的。下面我们通过一个矩阵乘法的例子来说明多层神经网络依旧是一个线性变换。在之前的前向传播中,前向传播的公式可以表示为:
a(1) = x * W1 , y=a(1) * W2
其中x为输入,W为参数。整理一下以上公式可以得到整个模型的输出为:
y = (x * W1) * W2
根据矩阵的乘法结合律有
y = x * (W1 * W2)
W1 * W2实际上可以表示为一个新的参数W_new
W_new = W1 * W2 =
[W1(1,1) W1(1,2) W1(1,3)
W1(2,1) W1(2,2) W1(2,3)] * [W2(1,1)
             W2(2,1)
             W2(3,1)]

= [W1(1,1) * W2(1,1) + W1(1,2) * W2(2,1) + W1(1,3) * W2(3,1)
 W1(2,1) * W2(2,2) + W1(2,3) * W2(2,1) + W1(1,3) * W2(3,1)]

= [W1_new
 W2_new]
这样输入和输出的关系就可以表示为:
y = xW_new = [x1 x2] * [W1_new
           W2_new]
=[W1_new * x1 + W2_new * x2]
所以综上面的推倒可以看出,虽然这个模型有两层(不算输入层),但它和单层神经网络的表达能力没有区别,依然还是一个线性变换。线性变换只能解决线性分类问题,如果我们想要解决非线性问题,这里就需要激活函数出场了。

2.2常见激活函数

下面我们展示几个常用的激活函数:

sigmod函数

sigmod的数学公式是:

在sigmod函数中我们可以看到,其输出是在(0,1)这个开区间内,这点很有意思,可以联想到概率,但是严格意义上讲,不要当成概率。sigmod函数曾经是比较流行的,它可以想象成一个神经元的放电率,在中间斜率比较大的地方是神经元的敏感区,在两边斜率很平缓的地方是神经元的抑制区。
当然,流行也是曾经流行,这说明函数本身是有一定的缺陷的。

  1. 当输入稍微远离了坐标原点,函数的梯度就变得很小了,几乎为零。在神经网络反向传播的过程中,我们都是通过微分的链式法则来计算各个权重w的微分的。当反向传播经过了sigmod函数,这个链条上的微分就很小很小了,况且还可能经过很多个sigmod函数,最后会导致权重w对损失函数几乎没影响,这样不利于权重的优化,这个问题叫做梯度饱和,也可以叫梯度弥散。
  2. 函数输出不是以0为中心的,这样会使权重更新效率降低。对于这个缺陷,在斯坦福的课程里面有详细的解释。
  3. sigmod函数要进行指数运算,这个对于计算机来说是比较慢的。
tanh函数

tanh的数学公式是:

tanh是双曲正切函数,tanh函数和sigmod函数的曲线是比较相近的,咱们来比较一下看看。首先相同的是,这两个函数在输入很大或是很小的时候,输出都几乎平滑,梯度很小,不利于权重更新;不同的是输出区间,tanh的输出区间是在(-1,1)之间,而且整个函数是以0为中心的,这个特点比sigmod的好。

一般二分类问题中,隐藏层用tanh函数,输出层用sigmod函数。不过这些也都不是一成不变的,具体使用什么激活函数,还是要根据具体的问题来具体分析。
可以看到这些激活函数的图像都不是一条直线,所以通过这些函数,每一个节点都不是线性变换。

我们回顾一下之前前向传播时候所展示的数学公式:
在神经网络中,我们会把每一个神经元的输出都作为了一个激活函数的输入,通过激活函数处理后再给到神经元。因为激活函数都是非线性的,所以这里可以很好地解决线性模型的局限性。具体在TensorFlow代码中的写法如下:
a = tf.nn.tanh(tf.matmul(x,w1) + biases1)
y = tf.nn.tanh(tf.matmul(a,w2) + biases2)

三.偏置项

下面我们来介绍偏置项在神经网络中的作用。首先我们先用最简单的一输出一输入神经元来说明:

这里我们使用上面介绍过的sigmod函数来作为激活函数,可以看到,这时候可以改变函数图像的只是权重w0的取值。
举一个最直观的例子,如果此时我们想在输入为2的时候让输出为0,不管w0怎么变换我们都没有办法实现input为2,output为0。这里我们就需要用到偏置项。
当我们加入偏置项后,网络结构如下:

下图是加入偏置项后,函数的图像。简单来说,偏置项就是可以让原函数实现左移和右移操作,来覆盖原来覆盖不到的点。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342