思考:如何刻画不同神经网络模型的效果并且进行目标优化呢?
损失函数(loss function)
经典损失函数
监督学习的两大类:
- 分类问题
- 回归问题
分类问题
解决的问题: 将不同的样本分到事先定义好的类别中。
如何用神经网络解决分类问题
方法: 设置 n 个输出节点,其中 n 为类别的个数。对于每一个样例,神经网络可以得到一个 n 维数组作为输出结果。数组中的每一个维度(即每一个输出节点)对应一个类别。
理想情况下,如果一个样本属于类别 k,则这个类别所对应的输出节点的输出值应该为 1,而其他的节点输出都为 0。
如何判断一个输出向量和期望的向量接近程度
损失函数1:交叉熵
定义: 刻画两个概率分布之间的距离。
给定两个概率分布 p 和 q,通过 q 来表示 p 的交叉熵为:
H
(
p
,
q
)
=
−
∑
x
p
(
x
)
l
o
g
q
(
x
)
H(p,q)=-\sum_{x}p(x)logq(x)
H(p,q)=−x∑p(x)logq(x)
注意:
- 交叉熵刻画的是两个概率分布之间的距离,但是神经网络的输出不一定是一个概率分布。
- 交叉熵值越小,两个概率分布越接近。
- 从上述公式可以看到交叉熵函数不是对称的( H ( p , q ) ≠ H ( q , p ) H(p,q) \ne H(q,p) H(p,q)̸=H(q,p)),它刻画的是通过概率分布 q 来表达概率分布 p 的困难程度。
- 当交叉熵作为神经网络的损失函数时,p 代表正确答案,q 代表预测值。
概率分布刻画了不同事件发生的概率。当事件总数是有限的情况下,概率分布函数
p
(
X
=
x
)
p(X=x)
p(X=x)满足:
∀
x
p
(
X
=
x
)
∈
[
0
,
1
]
且
∑
x
p
(
X
=
x
)
=
1
\forall x \qquad p(X=x) \in [0,1]且 \sum_{x}p(X=x)=1
∀xp(X=x)∈[0,1]且x∑p(X=x)=1
即,任意事件发生的概率都在 0 和 1 之间,且总有某一个事件发生(概率的和为 1)。
如何将前向传播得到的结果变成概率分布
Softmax 回归
在 TensorFlow 中,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
原始神经网络的输出被用作置信度来生成新的输出,而新的输出满足概率分布的所有要求。
具体样例
给出两个样例来直观说明通过交叉熵可以判断预测答案和真实答案之间的距离。
三分类问题
假设有一个三分类问题,某个样例的正确答案是 (1,0,0)。某模型经过 Softmax 回归之后的预测答案是 (0.5,0.4,0.1),那么这个预测和正确答案之间的交叉熵为:
H
(
(
1
,
0
,
0
)
,
(
0.5
,
0.4
,
0.1
)
)
=
−
(
1
×
l
o
g
0.5
+
0
×
l
o
g
0.4
+
0
×
l
o
g
0.1
)
≈
0.3
H((1,0,0),(0.5,0.4,0.1))=-(1\times log0.5+0 \times log0.4 + 0 \times log0.1) \approx 0.3
H((1,0,0),(0.5,0.4,0.1))=−(1×log0.5+0×log0.4+0×log0.1)≈0.3
如果另一个模型的预测是 (0.8,0.1,0.1),那么这个预测值和真实值之间的交叉熵是:
H
(
(
1
,
0
,
0
)
,
(
0.8
,
0.1
,
0.1
)
)
=
−
(
1
×
l
o
g
0.0.8
+
0
×
l
o
g
0.1
+
0
×
l
o
g
0.1
)
≈
0.1
H((1,0,0),(0.8,0.1,0.1))=-(1\times log0.0.8+0 \times log0.1 + 0 \times log0.1) \approx 0.1
H((1,0,0),(0.8,0.1,0.1))=−(1×log0.0.8+0×log0.1+0×log0.1)≈0.1
直观上可以看出第二个预测答案优于第一个。
# 通过 TensorFlow 实现交叉熵
cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clp_by_value(y, 1e-10, 1.0)))
# y_ 代表正确结果
# y 代表预测值
tf.clip_by_value 函数
可以将一个张量中的数值限制在一个范围之内,可以避免一些运算错误(比如 log0 是无效的)
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())
# 输出 [[2.5 2.5 3.][4. 4.5 4.5]]
# 小于 2.5 的数被换成了 2.5,大于 4.5 的数被换成了 4.5。
# 因此可以通过这个函数保证在进行 log 运算时,不会出现 log0 这样的错误或者大于 1 的概率。
tf.log 函数
v = tf.constant([1.0, 2.0, 3.0])
print(tf.log(v).eval())
# 输出[0. 0.69314718 1.09861231]
tf.matmul 函数
矩阵乘法运算。
v1 = tf.constant([[1.0, 2.0], [3.0, 4.0]])
v1 = tf.constant([[5.0, 6.0], [7.0, 8.0]])
print((v1 * v2).eval())
# 输出 [[5. 12.] [21. 32.]]
print(tf.matmul(v1, v2).eval())
# 输出 [[19. 22.] [43. 50.]]
tf.reduce_mean 函数
对整个矩阵做平均。
v = tf.constant([[1.0, 2.0, 3.0],[4.0, 5.0, 6.0]])
print(tf.reduce_mean(v).eval())
# 输出 3.5
tf.nn.softmax_cross_entropy_with_logits 函数
交叉熵一般会与 softmax 回归一起使用,这个函数是对这两个功能进行了统一封装。
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(y, y_)
# y 代表原始神经网络的输出结果
# y_ 给出标准答案
回归问题
解决的问题: 对具体数值的预测。需要预测的不是一个事先定义好的类别,而是一个任意实数。
解决回归问题的神经网络一般只有一个输出节点,这个节点的输出值就是预测值。
损失函数2:均方误差
均方误差(MSE: mean squared error)的定义:
M
S
E
(
y
,
y
′
)
=
∑
i
=
1
n
(
y
i
−
y
i
′
)
2
n
MSE(y,y')=\frac{\sum^n_{i=1}(y_i-y'_i)^2}{n}
MSE(y,y′)=n∑i=1n(yi−yi′)2
其中:
y
i
y_i
yi 为一个 batch 中第
i
i
i 个数据的正确答案;
y
i
′
y'_i
yi′ 为神经网络给出的预测值。
# 通过 TensorFlow 实现均方误差
mse = tf.reduce_mean(tf.square(y_ - y))
y
i
y_i
yi为一个 batch 中第
i
i
i 个数据的正确答案;
y
i
′
y'_i
yi′为神经网络得到的预测值;
a 和 b 是常量。
比如在上面介绍的销量预测问题中,a 就等于 10(实际值大余预测值的代价),而 b 等于 1(实际值小余预测值的代价)。
通过对这个自定义损失函数的优化,模型提供的预测值更有可能最大化收益。
# TensorFlow 中实现自定义损失函数
loss = tf.reduce_sum(tf.select(tf.greater(v1, v2),(v1 - v2) * a, (v2 - v1) * b))
tf.greater 函数
params:输入是两个张量。
return:比较两个输入张量中每一个元素的大小,返回比较结果。
当 tf.greater 的输入张量维度不一样时,TensorFlow 会进行类似 NumPy 广播操作(broadcasting)的处理。
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())
# 输出 [False False True True]
tf.select 函数
params:三个参数
- params1:选择条件根据,当选择条件为 True 时,tf.select 函数会选择第二个参数中的值,否则使用第三个参数中的值
v1 = tf.constant([1.0, 2.0, 3.0, 4.0])
v2 = tf.constant([4.0, 3.0, 2.0, 1.0])
print(tf.select(tf.greater(v1,v2), v1, v2).eval()
# 输出 [4. 3. 3. 4. ]
自定义损失函数
通过自定义损失函数的方法,可以将神经网络优化的结果更加接近实际问题的需求。
具体样例——预测商品销量问题
预测商品销量时:
- 如果预测值比真实销量大,则商家损失的是生产商品的成本;
- 如果预测值比真实销量小,则商家损失的是商品的利润。
例如:如果一个商品的成本是 1 元,但是利润是 10 元,name 少预测一个就少挣 10 元;多预测一个才少挣 1 元。
一般商品的成本和商品的利润不会严格相等,所以使用均方误差损失函数不能够很好地最大化销售利润。为了最大化预期利润,需要将损失函数和利润直接联系。
注意损失函数定义的是损失,所以要将利润最大化,定义的损失函数应该刻画成本或代价。
下面公式给出一个当预测多余真实值和预测少于真实值时有不同损失系数的损失函数:
L
o
s
s
(
y
,
y
′
)
=
∑
i
=
1
n
f
(
y
i
,
y
i
′
)
,
f
(
x
,
y
)
=
{
a
(
x
−
y
)
b
(
y
−
x
)
Loss(y, y')=\sum_{i=1}^nf(y_i, y'_i), \quad f(x, y)=\left\{ \begin{aligned} a(x - y) \\ b(y - x) \end{aligned} \right.
Loss(y,y′)=i=1∑nf(yi,yi′),f(x,y)={a(x−y)b(y−x)