神经网络的效果以及优化目标,都是通过损失函数来定义的。
交叉熵
交叉熵刻画了两个概率分布之间的距离,是分类问题中使用比较广泛的一种损失函数。
给定两个概率分布p和q,通过p和q表示的交叉熵为:
H ( p , q ) = − ∑ x p ( x ) ∗ l o g q ( x ) H(p,q)=-∑_xp(x)*logq(x) H(p,q)=−x∑p(x)∗logq(x)
交叉熵的函数并不是对称的,即H(p,q) != H(q, p)。它刻画的是通过概率分布q来表达概率分布p的困难程度。
使用交叉熵作为神经网络的损失函数时,p代表的是正确答案,q代表的是预测值。交叉熵刻画的是两个概率分布之间的距离,即交叉熵越小,两个概率分布越接近
Softmax回归
交叉熵刻画的是两个概率分布之间的距离,但是神经网络的输出不一定是一个概率分布。Softmax回归的作用,就是将神经网络的原始输出转换为一个概率分布。
Softmax回归本身可以作为一个学习算法来优化结果,但在teasorflow中,Softmax回归的参数被去掉了,它只作为一个额外的处理层,将神经网络的输出变成一个分布。
假设原始的输入为
y
1
,
y
2
,
⋅
⋅
⋅
,
y
n
y_1, y_2, ···,y_n
y1,y2,⋅⋅⋅,yn,则经过Softmax回归处理后的输出为:
S
o
f
t
m
a
x
(
y
)
i
=
y
i
′
=
e
y
i
∑
j
=
1
n
e
y
j
Softmax(y)_i = y_i' =\frac {e^{y_i}}{\sum_{j=1}^{n}e^{y_j}}
Softmax(y)i=yi′=∑j=1neyjeyi
原始神经网络的输出被作为一个置信度来产生一个新的输出,而新的输出完全满足概率分布的条件。
均方差(MSE mean square error)
均方差是分类问题和回归问题都常用的一种损失函数。定义如下:
M
S
E
(
y
,
y
′
)
=
∑
i
=
1
n
(
y
i
−
y
i
′
)
n
MSE(y, y') =\frac{ \sum_{i=1}^{n}(y_i - yi')}{n}
MSE(y,y′)=n∑i=1n(yi−yi′)
自定义的损失函数
Teasonflow中也支持自定义损失函数。例如以下就是一个当预测值大于真实值和预测值小于真实值时有不同损失函数的实例。
L
o
s
s
(
y
,
y
′
)
=
∑
i
=
1
n
f
(
y
i
,
y
i
′
)
;
f
(
x
,
y
)
=
{
b
(
y
−
x
)
,
x
<
=
y
a
(
x
−
y
)
,
x
>
y
Loss(y,y')=\sum_{i=1}^{n}f(y_i, y_i') ; f(x,y)=\{^{a(x-y), x > y}_{b(y-x),x<=y}
Loss(y,y′)=i=1∑nf(yi,yi′);f(x,y)={b(y−x),x<=ya(x−y),x>y
在Teasonflow中,可以使用以下代码来实现这个函数:
loss = tf.reduce_sum(tf.where(tf.greater(v1,v2), a*(v1-v2), b*(v2-v1)))
tf.where函数会以第一个参数的真假值作为判断,若为真,则使用第二个参数的值,否则使用第三个参数的值。
tf.greater会比较两个张量中每一个元素的大小,并返回比较结果
一个简单的神经网络
通过一个简单的神经网络来说明损失函数对训练结果的影响:
"""
function: 损失函数对模型训练结果的影响
source: Tensorflow实战Google深度学习框架 P79
"""
import tensorflow as tf
from numpy.random import RandomState
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)
print(w1)
# 定义预测多了或者预测少了的成本
loss_less = 10
loss_more = 1
# tf.select已修改为tf.where
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.5] 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时,运算结果为[[-0.40019357] [ 1.8274188 ]],当loss_less = 1,loss_more = 10时,运算结果为:[[-0.9383631] [ 1.0969642]]
因为在第一种情况中,预测少了,损失更大,所以预测结果偏向较大的值;在第二种情况中,预测多了,损失更大,所以预测结果偏向于较小的值。