Tensorflow 实战 Google 深度学习框架 | 学习笔记(二)

Shoot on the moon and if you miss you will still be among the stars.

Caicloud Github :tensorflow-tutorialhttps://github.com/caicloud/tensorflow-tutorial

原 tutorial 使用的 Tensorflow 最新版本是 1.4.0 ,本人使用的 1.5.0 版本,所以部分代码会略有不同,该笔记仅为个人学习,理解使用。如有错误,还望批评指教。—-ZJ

4 深层神经网络

4.1 深度学习与深层神经网络

wiki 深度学习定义:一类通过多层非线性变换对高复杂性数据建模算法的合集。

深度学习,两个重要特性:多层 and 非线性。

  • 线性模型的局限性,线性模型能够解决的问题是有限的(解决线性可分的问题)。

    • 3.4.2 小节中,输出为所有输入的加权和,导致整个神经网络是一个线性模型。
    • (采用线性函数)任意层的全连接神经网络(FC)和单层神经网络模型的表达能力没有任何区别。
    • 线性模型可以解决线性可分的问题,如通过直线(或高维空间的平面)划分。
    • 现实世界中,绝大多数问题是无法线性分割的,所以出现 Relu 这样非线性的激活函数,来解决高复杂性的问题(深度学习的目的就是解决这样的问题)
  • 激活函数实现去线性化

    • 如果每个神经元的输出通过一个非线性函数,那么整个神经网络就不再是线性的了。
    • 添加偏置项(bias),偏置项 是神经网络中非常有用的一种结构。
    • 每个节点(神经元)的输出在 加权和的基础上,还做了一个非线性变换。
    • 常用非线性激活函数(tf.nn.relu, tf.sigmoid, tf.tanh)
  • 多层网络解决异或运算

    • 在神经网络的发展史,有个很重要的问题,异或问题
    • 1958 感知机模型,将输入进行加权和,没有隐藏层,然后经过激活函数最后得到输出。不能解决异或问题。
    • 异或问题(直观理解):两个输入的符号相同时(同正,同负)输出为 0 ,否则(一正一负)输出为 1.
    • 加入隐藏层后,异或问题得到很好的解决
    • 深层网络有组合特征提取的功能,这个特性对于解决不易提取特征向量的问题(如图片识别,语音识别等)有很大的帮助。

4.2 损失函数的定义

神经网络模型的效果以及优化的目标是通过损失函数(loss function)来定义的。

监督学习的两大种类:分类问题 and 回归问题。

  • 经典损失函数

  • 自定义损失函数

如何判断一个输出向量和期望的向量有多接近?——交叉熵(cross entropy)是最常用的评判方法之一。

交叉熵 (cross entropy):

给定两个概率分布 p,q

H(p,q)=xp(x)logq(x) H ( p , q ) = − ∑ x p ( x ) l o g q ( x )

注意:交叉熵刻画的是两个概率之间的分布,但是神经网络的输出不一定是一个概率分布。

  • 概率分布刻画了不同事件发生的概率,当事件总数有限的情况下,概率分布函数 p(X=x) p ( X = x ) 满足:

x  p(X=x)[0,1]xp(X=x)=1 ∀ x     p ( X = x ) ∈ [ 0 , 1 ] 且 ∑ x p ( X = x ) = 1

  • 任意事件发生的概率都在 0 和 1 之间
  • 且总有某一个事件发生(概率的和为 1)

如何将神经网络前向传播的结果变成概率分布?

  • softmax 回归是一个常用的方法

这里写图片描述

假设原始的神经网络输出为 y1,y2,,yn y 1 , y 2 , … , y n ,那么经过 softmax 处理之后的输出为:

softmax(y)i=yi=eyinj=1eyj s o f t m a x ( y ) i = y i ′ = e y i ∑ j = 1 n e y j

  • 原始神经网络的输出被用作置信度来生成新的输出,新的输出满足概率分布的所有要求
  • 新的输出可以理解为经过神经网络的推导,一个样本为不同类别的概率分别有多大
  • 因此神经网络的输出变为了一个概率分布,从而可以通过交叉熵来计算预测概率分布和真实答案的概率分布之间的距离。
  • 交叉熵函数不是对称的 ( H(p.q)H(q,p) H ( p . q ) ≠ H ( q , p ) ),刻画的是通过概率 q 来表达概率分布 p 的困难程度
  • 在神经网络中,p 代表正确标签,q 代表预测值
import tensorflow as tf

tf.__version__
'1.5.0'
'''
Tensorflow 实现交叉熵

'''

cross_entropy = -tf.reduce_mean(y_* tf.log(tf.clip_by_value(y, 1e-10, 1.0)))

reduce_mean 可以理解为 1n 1 n ∑ ,n 代表一个 batch 中样例的数量

其中:y_代表正确结果, y 代表预测值

上面一行代码,包含了 4 种不同的 Tensorflow 运算。

  • tf.clip_by_value函数可以将在一个张量中的数值限制在一个范围之内,可以避免一些运算错误,(如 log0 是无效的)

    • 小于 2.5 的数值替换为 2.5 ,大于 4.5 的数值 替换为 4.5
    • 通过 tf.clip_by_value 函数可以保证在进行 log 运算时,不会出现 log0 这样的错误
import tensorflow as tf

v = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])

with tf.Session() as sess:
    print(tf.clip_by_value(v, 2.5, 4.5).eval())
[[2.5 2.5 3. ]
 [4.  4.5 4.5]]
  • 第二个运算是 tf.log 函数,完成对张量中所有函数一次求对数的功能
v = tf.constant([1.0, 2.0, 3.0])

with tf.Session() as sess:

    print(tf.log(v).eval())
[0.        0.6931472 1.0986123]
  • 第三个是乘法运算,在实现交叉熵的代码中直接将两个矩阵通过 *操作相乘,这个操作不是矩阵乘法,而是元素之间直接相乘 (element-wise)
  • 矩阵乘法需要使用 tf.matmul 函数完成。下面示例给出两者区别:
v1 = tf.constant([[1.0, 2.0], [3.0, 4.0]])
v2 = tf.constant([[5.0, 6.0], [7.0, 8.0]])

with tf.Session() as sess:
    print('逐元乘积:\n',(v1*v2).eval())
    print('矩阵乘法:\n', tf.matmul(v1, v2).eval())
逐元乘积:
 [[ 5. 12.]
 [21. 32.]]
矩阵乘法:
 [[19. 22.]
 [43. 50.]]
  • v1 * v2 的结果是每个位置上对应元素的乘积,如结果中(1,1)元素值 5 是 1×5 = 5
  • tf.matmul 函数完成矩阵乘法 ,如 (1,1)元素值 19 是 1× 5 + 2× 7 = 19

  • 上面三个运算完成了对每一个样例中每个类别交叉熵 p(x)logq(x)的计算

  • 这三步计算分别得到的结果是一个 n × m的二维矩阵,n 为一个 batch 中样例的数量, m 为分类的类别数量。
  • 根据该交叉熵的公式,应该将每行中的 m 个结果相加得到所有样例的交叉熵
  • 然后再对这 n 行取平均得到一个 batch 的平均交叉熵
  • 因为分类问题的类别数量是不变的,所以可以直接对整个矩阵做平均而不改变计算结果的意义
  • 简单展示 tf.reduce_mean 函数的使用方法
v = tf.constant([[1.0,2.0,3.0],[4.0,5.0,6.0]])

with tf.Session() as sess:
    print(tf.reduce_mean(v).eval())
3.5
  • 因为 交叉熵一般会和 softmax 回归一起使用,所以 Tensorflow 对这两个功能进行了统一封装 ,并提供 tf.nn.softmax_cross_entopy_with_logits 函数
  • 可以通过下面代码来实现使用了 softmax 回归之后的交叉熵损失函数
# y 是预测输出值,y_ 是标准答案
cross_entopy = tf.nn.softmax_cross_entropy_with_logits(y,y_)
  • 回归问题解决的是对具体数值的预测,如房价预测,销量预测等。
  • 解决回归问题的神经网络一般只有一个输出节点,输出值为预测值。
  • 回归问题最常用的是损失函数是均方误差(MES, mean squared error)

MSE(y,y)=ni=1(yiy)2n M S E ( y , y ′ ) = ∑ i = 1 n ( y i − y ′ ) 2 n

  • yi y i 为一个 batch 中第 i 个数据的正确答案, y y ′ 为神经网络给出的预测值
  • Tensorflow 实现均方误差损失函数
mse = tf.reduce_mean(tf.square(y_ - y))

其中:y_代表标准答案, y 代表神经网络输出值

4.2.2 自定义损失函数

  • Tensorflow 支持任意优化的自定义损失函数
  • 下面公式给出一个 当预测多于真实值和预测少于真实值时有不同系数的损失函数

Loss(y,y)=i=1nf(yi,yi),  f(x,y)={a(xy)b(yx)x>yxy L o s s ( y , y ′ ) = ∑ i = 1 n f ( y i , y i ′ ) ,     f ( x , y ) = { a ( x − y ) x > y b ( y − x ) x ≤ y

  • yi y i 为一个 batch 中第 i 个数据的正确答案, y y ′ 为神经网络给出的预测值 a,b 是常量,如 a = 10 , b = 1,TF 实现:
loss = tf.reduce_sum(tf.where(tf.greater(v1,v2),(v1 - v2) * a, (v2- v1) *b ))
  • 用到了 tf.greatertf.where 来实现选择操作
  • tf.greater 的输入是两个张量,此函数会比较这两个输入张量中每一个元素的大小,并返回比较结果
  • tf.greater 的输入张量维度不一样是,TF 会进行类似 Numpy 广播(broadcasting) 的处理。
  • tf.where 有三个参数,1.选择条件根据,当选择条件 为 True 时,tf.where 函数会选择第二个参数中的值,否则使用第三个参数中的值。
  • 注意:tf.where 函数判断和选择都是在元素级别进行,下面展示用法:
v1 = tf.constant([1.0, 2.0, 3.0, 4.0])
v2 = tf.constant([4.0, 3.0, 2.0, 1.0])

sess = tf.InteractiveSession()

print(tf.greater(v1, v2).eval())

print(tf.where(tf.greater(v1,v2),v1,v2).eval())

sess.close()
[False False  True  True]
[4. 3. 3. 4.]

下面通过一个简单的神经网络程序来讲解损失函数对模型训练结果的影响。

  • 两个输入节点,一个输出节点,没有隐藏层
import tensorflow as tf
from numpy.random import RandomState

'''
1. 定义神经网络的相关参数和变量。

'''

batch_size = 8

# 输入两个节点
x = tf.placeholder(tf.float32, shape=(None, 2), name='x-input')

# 回归问题一般只有一个输出节点
y_ = tf.placeholder(tf.float32, shape=(None, 1), name='y-input')

# 定义一个单层神经网络的前向传播过程,这里是简单加权和 stddev=1 标准差
w1 = tf.Variable(tf.random_normal([2, 1], stddev=1, seed=1))
y = tf.matmul(x, w1)


'''
2. 设置自定义的损失函数。

'''
# 定义预测多了和预测少了的 成本
loss_less = 10 # 预测少了 会损失 10块
loss_more = 1  # 预测多了 只损失 1 块
loss = tf.reduce_sum(tf.where(tf.greater(y,y_),
                             (y - y_) * loss_more,
                             (y_ - y) * loss_less))

train_step = tf.train.AdamOptimizer(0.001).minimize(loss)

'''
3. 生成模拟数据集。
'''

# 通过随机数生成一个数据模拟集 128 个样本,每个样本两个特征
rdm = RandomState(1)

X = rdm.rand(128, 2)

print(X.shape)
# print(X)
'''
设置回归的正确值为两个输入的和加上一个随机量,之所以要加上一个随机量是为了
加入不可预测的噪音,否则不同损失函数的意义就不大了,因为不同损失函数都会在能
完全预测正确的时候最低,一般来说噪音是一个均值为 0 的小量,所以这里的
噪音设置为 -0.05 ~ 0.05 的随机数

'''
# Y 正确值 是一个 list (x1, x2) in X 一个样本有两个特征,128 行 2 列

Y = [[x1 + x2 + (rdm.rand()/10.0-0.05)]  for (x1, x2) in X]

'''
4.训练模型。

'''        

with tf.Session() as sess:
    init_op = tf.global_variables_initializer()
    sess.run(init_op)
    STEPS = 5000
    for i in range(STEPS):
        start = (i*batch_size) % 128
        end = (i*batch_size) % 128 + batch_size
        sess.run(train_step, feed_dict={x: X[start:end], y_: Y[start:end]})
        if i % 1000 == 0:
            print("After %d training step(s), w1 is: " % (i))
            print(sess.run(w1), "\n") 
    print("Final w1 is: \n", sess.run(w1)) 
(128, 2)
After 0 training step(s), w1 is: 
[[-0.81031823]
 [ 1.4855988 ]] 

After 1000 training step(s), w1 is: 
[[0.01247112]
 [2.1385448 ]] 

After 2000 training step(s), w1 is: 
[[0.45567414]
 [2.1706066 ]] 

After 3000 training step(s), w1 is: 
[[0.69968724]
 [1.8465308 ]] 

After 4000 training step(s), w1 is: 
[[0.89886665]
 [1.2973602 ]] 

Final w1 is: 
 [[1.019347 ]
 [1.0428089]]
'''
5. 重新定义损失函数,使得预测多了的损失大,于是模型应该偏向少的方向预测。

'''
loss_less = 1
loss_more = 10 # 预测多了的损失大

loss = tf.reduce_sum(tf.where(tf.greater(y, y_),(y - y_)* loss_more, (y_ - y) * loss_less))
train_step = tf.train.AdamOptimizer(0.001).minimize(loss)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    STEPS = 5000
    for i in range(STEPS):
        start = (i*batch_size) % 128
        end = (i*batch_size) % 128 + batch_size
        sess.run(train_step, feed_dict={x:X[start:end], y_:Y[start:end]})
        if i % 1000 == 0:
            print("After %d training step(s), w1 is: " % (i))
            print(sess.run(w1), "\n") 

        print("Final w1 is: \n", sess.run(w1))
After 0 training step(s), w1 is: 
[[-0.8123182]
 [ 1.4835987]] 

Final w1 is: 
 [[-0.8123182]
 [ 1.4835987]]
Final w1 is: 
 [[-0.81303465]
 [ 1.4826417 ]]
Final w1 is: 
 [[-0.81253535]
 [ 1.4817047 ]]

 ......

 Final w1 is: 
 [[0.95506585]
 [0.98148215]]
 Final w1 is: 
 [[0.9552581]
 [0.9813394]]
'''
6. 定义损失函数为MSE。
'''
loss = tf.losses.mean_squared_error(y,y_)
train_step = tf.train.AdamOptimizer(0.001).minimize(loss)

with tf.Session() as sess:
    init_op = tf.global_variables_initializer()
    sess.run(init_op)
    STEPS = 5000
    for i in range(STEPS):
        start = (i*batch_size) % 128
        end = (i*batch_size) % 128 + batch_size
        sess.run(train_step, feed_dict={x: X[start:end], y_: Y[start:end]})
        if i % 1000 == 0:
            print("After %d training step(s), w1 is: " % (i))
            print(sess.run(w1), "\n") 

        print("Final w1 is: \n", sess.run(w1))

After 0 training step(s), w1 is:
[[-0.81031823]
[ 1.4855988 ]]

Final w1 is:
[[-0.81031823]
[ 1.4855988 ]]

……

Final w1 is:
[[0.9741856]
[1.0243824]]
Final w1 is:
[[0.9742431]
[1.024378 ]]
Final w1 is:
[[0.9742944]
[1.0243853]]
Final w1 is:
[[0.97434247]
[1.0243782 ]]
Final w1 is:
[[0.9743756]
[1.0243336]]

  • 第一种得到的 w1= [[1.019347 ],[1.0428089]],也就是得到的预测函数 y=1.02x1+1.04x2 y = 1.02 x 1 + 1.04 x 2 x1+x2 x 1 + x 2 大,因为在损失函数中,规定的是 预测少了损失更大,所以为了降低损失,预测值会更大些
  • 第二种,将 loss_more 和 loss_less 调整后,w1 = [[0.9552581],[0.9813394]] ,预测多了损失更大,所以,少预测一些会降低损失
  • 第三种,使用 MSE 均方误差作为损失函数 [[0.9743756],[1.0243336]] ,离标准答案更近
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值