学习《TensorFlow实战Google深度学习框架》(二)深层神经网络及损失函数

版权声明:本文由洛北辰南原创,转载请注明原创地址,谢谢。 https://blog.csdn.net/qq_18941713/article/details/88205357

第四章 深层神经网络

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

维基百科对深度学习的精确定义为“一类通过多层非线性变换对高复杂性数据建模算法的合集”。因为深层神经网络是实现“多层非线性变换”最常用的一种方法,所以在实际中基本上可以认为深度学习就是深层神经网络的代名词。
深度学习有两个非常重要的特性——多层非线性

4.1.1 线性模型的局限性

书上讲的我没太看懂…我觉得这里就一句话概括一下…
线性模型无法描述异或问题
即(1,0)= true,(1,1)= false,(0,1)= true,(0,0)= false
此类问题,无法用一条很好的进行分类
在这里插入图片描述在这里插入图片描述
如图,线性变换的局限性,无法很好的描述异或问题。

4.1.2 激活函数实现去线性化

我直接描述自己的理解…书上的太复杂。
在没有激活函数的网络中,数据输入神经元,不作处理,继续传播,数据的传播只经过乘法运算,整体都是线性的。
在加入激活函数后,数据输入神经元,经过非线性的激活函数处理,去除整个模型的线性化,让模型能力更强。
对于激活函数的提出,这里借鉴《python神经网络编程》书中的解释,在生物神经网络中,神经元需要一定能量的生物电刺激,才会进入激活状态,当能量大到一定程度,神经元的激活强度达到顶峰。根据这一特性,人工神经网络中,提出激活函数sigmoid来更好的模拟生物神经网络。
在这里插入图片描述
这里只是我个人理解,如果不对,请大佬们指出,谢谢。

4.1.3 多层网络解决异或运算

这里…书上直接实操证明,多层网络可以解决异或运算,没有从原理上说明,跳过。

4.2 损失函数定义

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

4.2.1 经典损失函数

分类问题和回归问题是监督学习的两大种类。

  • 分类问题希望解决的是将不同的样本分到实现定义好的类别中。

通过神经网络解决多分类问题最常用的方法是设置n个输出节点,其中n为类别的个数。对于每一个样例,神经网络可以得到的一个n维数组作为输出结果。数组中每一个维度对应一个类别。

在理想情况下,如果一个样本属于类别K,那么这个类别所对应的输出节点的输出值应该为1。那么如何判断一个输出向量和期望向量有多接近呢?交叉熵(cross entropy)是常用的评判方法之一。交叉熵刻画了两个概率分布之间的距离。

给定两个概率分布p和q,通过q来表示p的交叉熵为:
H(p,q)=
注意交叉熵刻画的是两个概率分布之间的距离,然而神经网络的输出却不一定是一个概率分布。概率分布刻画了不同事件发生的概率。
当事件总数有限的情况下,概率分布函数p(X=x)满足: 对于任意x p(X=x)∈[0,1]且∑p(X=x)=1

扫描二维码关注公众号,回复: 5707470 查看本文章

也就是说,任意时间发生的概率都在0和1之间,且总有某一个事件发生(概率和为1)。如果将分类问题中“一个样例属于某一个类别”看成一个概率事件,那么训练数据的正确答案就符合一个概率分布。

那么如何将前向传播的结果变成概率分布呢?Softmax回归是一个常用的方法。
在这里插入图片描述
其中,Vi 是分类器前级输出单元的输出。i 表示类别索引,总的类别个数为 C。
Si 表示的是当前元素的指数与所有元素指数和的比值。

Softmax 将多分类的输出数值转化为相对概率,更容易理解和比较。我们来看下面这个例子。

一个多分类问题,C = 4。线性分类器模型最后输出层包含了四个输出值,分别是:

	V = np.array([-2, 3, 0, -2])
	# V = [-2  3  0 -2]

经过Softmax处理后,数值转化为相对概率:

	S = np.exp(V) / np.sum(np.exp(V))
	# S = [0.00633705 0.94050111 0.04682479 0.00633705]

在实际应用中,往往会出现Vi值比较大的情况,这会使得指数的值特别大,所以一般会对Vi进行减去最大Vi的操作,对结果没有影响。

	# Vi减去最大Vi,对Softmax结果无影响
	V = np.array([-2, 3, 0, -2])
	# V = [-2  3  0 -2]
	V -= np.max(V)
	# V = [-5  0 -3 -5]
	S = np.exp(V) / np.sum(np.exp(V))
	# S = [0.00633705 0.94050111 0.04682479 0.00633705]

这个Softmax的输出概率值,我目前理解为图像分类最后的置信度。

这里书上描述了为什么交叉熵是H(p,q)而不是H(q,p)。
其实我没看懂,就去查了查资料。

  • 交叉熵 = 熵 + KL散度
  • 而熵是从分布 P 中随机抽选一个事件,传达这条信息所需的最优平均信息长度,是不变的。
  • 所以交叉熵的不对称性来自KL散度。
  • KL 散度:用分布 P 的最佳信息传递方式来传达分布 Q,比用分布 Q 自己的最佳信息传递方式来传达分布 Q,平均多耗费的信息长度为 KL 散度,表达为 D_p(Q) 或 D_KL(Q||P),KL 散度衡量了两个分布之间的差异

在这里插入图片描述
公式 D_P(Q) 里一共涉及了两个分布:

  • 要传达的信息来自哪个分布,答案是 Q
  • 信息传递的方式由哪个分布决定,答案是 P

交叉熵衡量的是,按照分布P的信息传递方式来传达分布Q的信息,所耗费的平均信息长度。
损失函数的意义是,按照真实值的分布来判断预测值的分布与其的差异,也就是说公式D_P(Q)中的P就是真实值的分布,Q是预测值的分布。

这里大概是我钻牛角尖了吧,单说交叉熵是描述差异,完全不明白为什么不能反过来…

TensorFlow中已经实现了交叉熵的计算

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

y_表示真实值,y表示预测值。

tf.clip_by_value函数可以将一个张量中的数值限制在一个范围之内,这样可以避免一些运算错误。

	v = tf.constant([1.0, 2.0, 3.0], [4.0, 5.0, 6.0])
	print(tf.clip_by_value(v, 2.5, 4.5).eval())
	# output
	[[2.5 2.5 3. ]
	 [4.  4.5 4.5]]

将小于2.5的值替换为2.5,将大于4.5的值替换为4.5,就可以保证不会出现类似log(0)之类的错误。

tf.log函数完成了对张量中所有元素一次求对数的功能

	v = tf.constant([1.0, 2.0, 3.0])
	print(tf.log(v).eval())
	# output
	[0.        0.6931472 1.0986123]

对张量使用 * 乘法,不同于tf.matmul的矩阵乘法,而是对位元素相乘类似卷积计算的过程。

	v1 = tf.constant([[1.0, 2.0], [3.0, 4.0]])
	v2 = tf.constant([[5.0, 6.0], [7.0, 8.0]])
	
	print((v1 * v2).eval())
	#output
	[[ 5. 12.]
	 [21. 32.]]
	 
	print(tf.matmul(v1, v2).eval())
	# output
	[[19. 22.]
	 [43. 50.]]

计算结果为n×m维的矩阵,n为一个batch中的样例总数,m为分类的类别数量。
按照交叉熵的公式在这里插入图片描述
应先将每一个样例的m个分类交叉熵相加,然后除以n得到平均交叉熵。
但由于分类总数是不变的,所以可以直接对整个矩阵做平均而并不改变计算结果的意义。
tf.reduce_mean就是用来求取整个矩阵平均值的

	v = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
	print(tf.reduce_mean(v).eval())
	# output 
	3.5

一般而言,交叉熵总是和Softmax同时使用,所以TensorFlow对这两个功能进行了封装。

	cross_entropy = tf.nn.softmax_cross_entropy_with_logits(
		labels=y_, logits=y)

y代表原始神经网络的输出结果,y_代表真实值(标准答案)。这样通过一行代码就可以得到使用了Softmax回归之后的交叉熵。

  • 与分类问题不同,回归问题要解决的是对具体数值的预测。比如房价预测、销量预测、电价预测都是回归问题。解决回归问题的神经网络一般只有一个输出节点,这个节点的输出值就是预测值。对于回归问题,最常用的损失函数是均方误差(MSE,mean squared error)。在这里插入图片描述
  • 均方误差是指参数估计值与参数真值之差平方的期望值
  • MSE可以评价数据的变化程度,MSE的值越小,说明预测模型描述实验数据具有更好的精确度
	mse = tf.reduce_mean(tf.square(y_ - y))

y代表神经网络输出的预测值,y_代表标准答案(真实值)。

4.2.2 自定义损失函数

在预测商品销量时,如果预测多了(预测值大于真实销量),损失的是生产成本;而预测少了(预测值小于真实销量),损失的是商品利润。一般商品的成本和利润都是相等的,所以如果使用MSE来做损失函数,效果不好。
例如:一个商店的商品,成本1元,利润10元,那么预测多一件,只亏损1元成本,而预测少一件,却亏损10元利润。这样的情况下,使用MSE是无法最大化商店利润的。
以下公式给出了一个当预测多于真实值和预测少于真实值时有不用损失系数的损失函数。
在这里插入图片描述
yi为第一个batch中第i个数据的正确答案,yi’为神经网络得到的预测值,a和b是常量。
比如在上面的销量预测问题中,a=10元(预测值低的代价),b=1元(预测值高的代价)。通过对这个自定义损失函数的优化,模型提供的预测值更有可能最大化收益。在Tensorflow中,可以通过一下代码来实现这个损失函数。

	loss = tf.reduce_sum(tf.where(tf.greater(v1, v2),
											(v1 - v2) * a, (v2 - v1) * b))
  • tf.greater的输入时两个张量,此函数会比较这两个输入张量中每一个元素的大小,并返回比较结果。当tf.greater的输入张量维度不一样时,TensorFlow会进行类似NumPy广播操作(broadcasting)的处理。
  • tf.where有三个参数,第一个为选择条件根据,当选择条件为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])
    
    print(tf.greater(v1, v2).eval())
	# output
	[False False  True  True]

    print(tf.where(tf.greater(v1, v2), v1, v2).eval())
    # output
    [4. 3. 3. 4.]

下面通过一个简单的神经网络程序来讲解损失函数对训练结果的影响。
程序包括两个输入节点和一个输出节点。

# -*- coding: utf-8 -*-
# @Time    : 2019/3/8 12:00
# @Author  : Chord

import tensorflow as tf;
from numpy.random import RandomState;

if __name__ == "__main__":
    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')

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

    # 定义预测多了和预测少了的成本
    loss_less = 10
    loss_more = 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)

    # 通过随机数生成一个模拟数据集
    rdm = RandomState(1)
    dataset_size = 128
    X = rdm.rand(dataset_size, 2)
    # 设置回归的正确值为两个输入的和加上一个随机量,这是为了加入不可预测的噪音
    # 否侧不同损失函数的意义就不大了,因为不同损失函数都会在能完全预测正确的时
    # 候最低。一般来说噪音为一个均值为0的小量。
    # 这里的噪音设置为 -0.05 ~ 0.05
    Y = [[x1 + x2 + rdm.rand()/10.0 - 0.05] for (x1, x2) in X]

    # 训练神经网络
    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) % dataset_size
            end = min(start + batch_size, dataset_size)
            sess.run(train_step,
                     feed_dict={x:X[start:end], y_: Y[start:end]})
            print(sess.run(w1))
   # loss_less=10, loss_more=1时最后一次的output
   [[1.019347 ]
    [1.0428089]]
   # loss_less=1, loss_more=10时最后一次的output
   [[0.9552581]
    [0.9813394]]

运行以上代码会得到w1,也就是说得到的预测函数是1.02x1 + 1.04x2,这要比x1 + x2大,因为在损失函数中指定预测少了的代价更大(loss_less > loss_more)。若交换loss_less和loss_more的值,则会得到w1。
从这个样例可以体会到,对于相同神经网络,不同的损失函数会对训练得到的模型产生重要影响。

这里由于是通过RandomState生成随机数据集,只要是相同的随机数种子,生成的序列就是一样的,所以怎么运行都会是相同的结果。

猜你喜欢

转载自blog.csdn.net/qq_18941713/article/details/88205357