维基百科对深度学习的精确定义为“一类通过多层非线性变换对高复杂性数据建模算法的合集”。因为深层神经网络是实现“多层非线性变换”最常用的一种方法,所以在实际中基本上可以认为深度学习就是深层神经网络的代名词。
深度学习有两个非常重要的特性——多层和非线性。
书上讲的我没太看懂…我觉得这里就一句话概括一下…
线性模型无法描述异或问题
即(1,0)= true,(1,1)= false,(0,1)= true,(0,0)= false
此类问题,无法用一条很好的进行分类
如图,线性变换的局限性,无法很好的描述异或问题。
我直接描述自己的理解…书上的太复杂。
在没有激活函数的网络中,数据输入神经元,不作处理,继续传播,数据的传播只经过乘法运算,整体都是线性的。
在加入激活函数后,数据输入神经元,经过非线性的激活函数处理,去除整个模型的线性化,让模型能力更强。
对于激活函数的提出,这里借鉴《python神经网络编程》书中的解释,在生物神经网络中,神经元需要一定能量的生物电刺激,才会进入激活状态,当能量大到一定程度,神经元的激活强度达到顶峰。根据这一特性,人工神经网络中,提出激活函数sigmoid来更好的模拟生物神经网络。
这里只是我个人理解,如果不对,请大佬们指出,谢谢。
这里…书上直接实操证明,多层网络可以解决异或运算,没有从原理上说明,跳过。
神经网络模型的效果以及优化的目标是通过损失函数来定义的。
分类问题和回归问题是监督学习的两大种类。
通过神经网络解决多分类问题最常用的方法是设置n个输出节点,其中n为类别的个数。对于每一个样例,神经网络可以得到的一个n维数组作为输出结果。数组中每一个维度对应一个类别。
在理想情况下,如果一个样本属于类别K,那么这个类别所对应的输出节点的输出值应该为1。那么如何判断一个输出向量和期望向量有多接近呢?交叉熵(cross entropy)是常用的评判方法之一。交叉熵刻画了两个概率分布之间的距离。
给定两个概率分布p和q,通过q来表示p的交叉熵为:
注意交叉熵刻画的是两个概率分布之间的距离,然而神经网络的输出却不一定是一个概率分布。概率分布刻画了不同事件发生的概率。
当事件总数有限的情况下,概率分布函数p(X=x)满足: 对于任意x p(X=x)∈[0,1]且∑p(X=x)=1
也就是说,任意时间发生的概率都在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)。
其实我没看懂,就去查了查资料。
交叉熵衡量的是,按照分布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 = tf.reduce_mean(tf.square(y_ - y))
y代表神经网络输出的预测值,y_代表标准答案(真实值)。
在预测商品销量时,如果预测多了(预测值大于真实销量),损失的是生产成本;而预测少了(预测值小于真实销量),损失的是商品利润。一般商品的成本和利润都是相等的,所以如果使用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))
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生成随机数据集,只要是相同的随机数种子,生成的序列就是一样的,所以怎么运行都会是相同的结果。