大量经典论文的算法均采用 TF 1.x 实现, 为了阅读方便, 同时加深对实现细节的理解, 需要 TF 1.x 的知识
【TensorFlow1.X】系列学习文章目录
实现神经网络的搭建,下一步需要学习神经网络优化原理和实现细节,本篇博文将详解损失函数作用。
入门4优化器的原理、
入门5学习率的影响
入门6反向传播的推导。
损失函数在深度学习中起着重要的作用,它用于衡量模型预测结果与实际标签之间的差异,指导模型的训练过程,是评估模型性能和优化模型参数的关键组成部分。损失函数具有以下几个主要作用:
深度学习常见的损失函数包括:
选择合适的损失函数取决于任务类型、数据特征和模型要解决的问题。不同的损失函数对模型的训练和优化过程产生不同的影响,因此需要仔细选择和调整以达到最佳结果。
均方误差mse:n 个样本的预测值 y y y与真实值 y _ y\_ y_之差的平方和,再求平均值。
M S E ( y _ , y ) = ∑ i = 1 n ( y − y _ ) 2 n MSE({\rm{y\_}},y) = \frac{{\sum\nolimits_{i =1 }^n {{{(y - y\_)}^2}} }}{n} MSE(y_,y)=n∑i=1n(y−y_)2
在 Tensorflow1.X中用 loss_mse = tf.reduce_mean(tf.square(y_ - y))
举个小案例:两个神经网络模型解决二分类问题中,已知标准答案为 y _ = ( 1 , 0 ) {\rm{y\_ = (1, 0)}} y_=(1,0),第一个神经网络模型预测结果为 y 1 = ( 0.7 , 0.5 ) {\rm{y_1= (0.7, 0.5)}} y1=(0.7,0.5),第二个神经网络模型预测结果为 y 2 = ( 0.8 , 0.1 ) {\rm{y_2= (0.8, 0.1)}} y2=(0.8,0.1),判断哪个神经网络模型预测的结果更接近标准答案。
根据均方误差的计算公式得:
M S E _ 1 ( ( 1 , 0 ) , ( 0 . 7 , 0 . 5 ) ) = ( 0 . 7 − 1 ) 2 + ( 0 . 5 − 0 ) 2 = 0 . 34 {\rm{MSE\_1((1,0),(0}}{\rm{.7, 0}}{\rm{.5)) = (0}}{\rm{.7 - 1}}{{\rm{)}}^2} + {{\rm{(0}}{\rm{.5 - 0)}}^2} = {\rm{0}}{\rm{.34}} MSE_1((1,0),(0.7,0.5))=(0.7−1)2+(0.5−0)2=0.34
M S E _ 2 ( ( 1 , 0 ) , ( 0 . 8 , 0 . 1 ) ) = ( 0 . 8 − 1 ) 2 + ( 0 . 1 − 0 ) 2 = 0 . 05 {\rm{MSE\_2((1,0),(0}}{\rm{.8, 0}}{\rm{.1)) = (0}}{\rm{.8 - 1}}{{\rm{)}}^2} + {{\rm{(0}}{\rm{.1 - 0)}}^2} ={\rm{0}}{\rm{.05}} MSE_2((1,0),(0.8,0.1))=(0.8−1)2+(0.1−0)2=0.05
由于0.34>0.05,所以预测值 y 2 y_2 y2与真实值 y _ y\_ y_更接近, y 2 y_2 y2预测更准确。
对于多分类问题,MSE并不是一个常用的评估指标
在上一篇博文【TensorFlow1.X入门三】中的线性回归和非线性回归中都用到了此方法。
交叉熵 Cross Entropy:n 个样本的预测值 y y y与真实值 y _ y\_ y_的概率分布之间的距离,交叉熵越大,两个概率分布距离越远,两个概率分布越相异;交叉熵越小,两个概率分布距离越近,两个概率分布越相似。
H ( y _ , y ) = − ∑ i = 1 n y _ ∗ log y H({\rm{y\_}},y) = - \sum\nolimits_{i = 1}^n {y\_} *\log y H(y_,y)=−∑i=1ny_∗logy
在 Tensorflow1.X中用loss_ce = -tf.reduce_mean(y_* tf.log(tf.clip_by_value(y, 1e-12, 1.0)))
同一个小案例,根据交叉熵的计算公式得:
H _ 1 ( ( 1 , 0 ) , ( 0.7 , 0.5 ) ) = − ( 1 ∗ l o g 0.7 + 0 ∗ l o g 0.3 ) ≈ 0.36 H\_1\left( {\left( {1,0} \right),\left( {0.7,0.5} \right)} \right){\rm{ }} = {\rm{ }} - \left( {1*log0.7{\rm{ }} + {\rm{ }}0*log0.3} \right) \approx {\rm{ }}0.36 H_1((1,0),(0.7,0.5))=−(1∗log0.7+0∗log0.3)≈0.36
H _ 2 ( ( 1 , 0 ) , ( 0.8 , 0.1 ) ) = − ( 1 ∗ l o g 0.8 + 0 ∗ l o g 0.1 ) ≈ 0.22 H\_2\left( {\left( {1,0} \right),\left( {0.8,0.1} \right)} \right){\rm{ }} = {\rm{ }} - \left( {1*log0.8{\rm{ }} + {\rm{ }}0*log0.1} \right) \approx {\rm{ }}0.22 H_2((1,0),(0.8,0.1))=−(1∗log0.8+0∗log0.1)≈0.22
由于0.36>0.22,所以预测值 y 2 y_2 y2与真实值 y _ y\_ y_更接近, y 2 y_2 y2预测更准确。
这里 n n n分类的输出概率并没有什么联系,而且通常会将每个概率值都压缩在min和max之间:小于min的值等于min,大于max的值等于max。tf.clip_by_value(y, 1e-12, 1.0)即在0到1之间,这样的话上面的H_1的值就被压缩成是 H _ 1 ( ( 1 , 0 ) , ( 0.7 , 0.3 ) ) ≈ 0.36 H\_1\left( {\left( {1,0} \right),\left( {0.7,0.3} \right)} \right){\rm{ }}\approx {\rm{ }}0.36 H_1((1,0),(0.7,0.3))≈0.36,所以 H _ 1 H\_1 H_1公式博主并没有写错。
在深度学习中,一般让模型的输出经过softmax函数,以获得输出分类的概率分布,再与真实值对比,求出交叉熵,得到损失函数。softmax函数要求 n n n分类的输出满足以下概率分布要求的函数: p ( X = x i ) ∈ [ 0 , 1 ] p(X = {x_i}) \in [0,1] p(X=xi)∈[0,1]且 ∑ i n p ( X = x i ) = 1 \sum\nolimits_i^n {p(X = {x_i}) = 1} ∑inp(X=xi)=1,softmax函数具体表示为: p ( X = x i ) = s o f t m a x ( x i ) = e x i ∑ i = 1 n e x i p(X = {x_i}) = {\rm{softmax}}({x_i}) = \frac{{{e^{{x_i}}}}}{{\sum\nolimits_{i = 1}^n {{e^{{x_i}}}} }} p(X=xi)=softmax(xi)=∑i=1nexiexi。
在 Tensorflow1.X中结合softmax和Cross Entropy后用loss_ce = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1)))
同一个小案例,根据改良版本的交叉熵的计算公式得:
H _ 1 ( ( 1 , 0 ) , s o f t m a x ( 0.7 , 0.5 ) ) ≈ H _ 1 ( ( 1 , 0 ) , ( 0.57 , 0.43 ) ) = 0.56 H\_1\left( {\left( {1,0} \right),softmax\left( {0.7,0.5} \right)} \right) \approx H\_1\left( {\left( {1,0} \right),\left( {0.57,0.43} \right)} \right){\rm{ }} = {\rm{ }}0.56 H_1((1,0),softmax(0.7,0.5))≈H_1((1,0),(0.57,0.43))=0.56
H _ 2 ( ( 1 , 0 ) , s o f t m a x ( 0.8 , 0.1 ) ) ≈ H _ 1 ( ( 1 , 0 ) , ( 0.79 , 0.21 ) ) = 0.24 H\_2\left( {\left( {1,0} \right),softmax\left( {0.8,0.1} \right)} \right) \approx H\_1\left( {\left( {1,0} \right),\left( {0.79,0.21} \right)} \right){\rm{ }} = {\rm{ }}0.24 H_2((1,0),softmax(0.8,0.1))≈H_1((1,0),(0.79,0.21))=0.24
由于0.56>0.24,所以预测值 y 2 y_2 y2与真实值 y _ y\_ y_更接近, y 2 y_2 y2预测更准确。
在上一篇博文【TensorFlow1.X入门三】中的逻辑回归中都用到了此方法。
根据问题的实际情况,定制合理的损失函数。
举个例子:对于预测酸奶日销量问题,如果预测销量大于实际销量则会损失成本;如果预测销量小于实际销量则会损失利润。在实际生活中,往往制造一盒酸奶的成本和销售一盒酸奶的利润是不等价的。因此,需要使用符合该问题的自定义损失函数: l o s s = ∑ i = 0 n f ( y _ , y ) loss = \sum\nolimits_{i = 0}^n {f(y\_,y)} loss=∑i=0nf(y_,y)
自定义损失函数制作为分段函数: f ( y _ , y ) = { p r o f i t × ( y _ − y ) , y < y _ c o s t × ( y _ − y ) , y > = y _ f(y\_,y) = \left\{ {\begin{array}{cc} {profit \times (y\_ - y),y < y\_}\\ {cost \times (y\_ - y),y > = y\_} \end{array}} \right. f(y_,y)={profit×(y_−y),y<y_cost×(y_−y),y>=y_
若预测结果 y 小于标准答案 y_,损失函数为利润乘以预测结果 y 与标准答案 y_之差;
若预测结果 y 大于标准答案 y_,损失函数为成本乘以预测结果 y 与标准答案 y_之差。
在 Tensorflow1.X中用loss = tf.reduce_sum(tf.where(tf.greater(y,y_),COST(y-y_),PROFIT(y_-y)))
自定义损失函数的完整的代码:
#coding:utf-8
#酸奶成本1元, 酸奶利润9元
#预测少了损失大,故不要预测少,故生成的模型会多预测一些
#0导入模块,生成数据集
import tensorflow as tf
import numpy as np
BATCH_SIZE = 8
SEED = 23455
COST = 8
PROFIT = 2
rdm = np.random.RandomState(SEED)
X = rdm.rand(32,2)
# 假设市场的真实利润Y=3*COST+2*PROFIT
Y = [[3*x1+2*x2+(rdm.rand()/10.0-0.05)] for (x1, x2) in X]
# 定义神经网络的输入、参数和输出,定义前向传播过程。
x = tf.placeholder(tf.float32, shape=(None, 2))
y_ = tf.placeholder(tf.float32, shape=(None, 1))
# 希望训练好的网络权重接近w=(a,b)接近(3,2)
w = tf.Variable(tf.random_normal([2, 1], stddev=1, seed=1))
y = tf.matmul(x, w)
#定义损失函数及反向传播方法。
loss = tf.reduce_sum(tf.where(tf.greater(y, y_), (y - y_)*COST, (y_ - y)*PROFIT))
train_step = tf.train.GradientDescentOptimizer(0.001).minimize(loss)
#生成会话,训练STEPS轮。
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
sess.run(init_op)
STEPS = 3000
for i in range(STEPS):
start = (i*BATCH_SIZE) % 32
end = (i*BATCH_SIZE) % 32 + BATCH_SIZE
sess.run(train_step, feed_dict={x: X[start:end], y_: Y[start:end]})
if i % 1000 == 0:
print ("After %d training steps, w1 is: " % (i))
print (sess.run(w), "\n")
print ("Final w1 is: \n", sess.run(w))
只需要修改loss部分
#coding:utf-8
#酸奶成本1元, 酸奶利润9元
#预测少了损失大,故不要预测少,故生成的模型会多预测一些
#0导入模块,生成数据集
import tensorflow as tf
import numpy as np
BATCH_SIZE = 8
SEED = 23455
COST = 8
PROFIT = 2
rdm = np.random.RandomState(SEED)
X = rdm.rand(32,2)
# 假设市场的真实利润Y=3*COST+2*PROFIT
Y = [[3*x1+2*x2+(rdm.rand()/10.0-0.05)] for (x1, x2) in X]
# 定义神经网络的输入、参数和输出,定义前向传播过程。
x = tf.placeholder(tf.float32, shape=(None, 2))
y_ = tf.placeholder(tf.float32, shape=(None, 1))
# 希望训练好的网络权重接近w=(a,b)接近(3,2)
w = tf.Variable(tf.random_normal([2, 1], stddev=1, seed=1))
y = tf.matmul(x, w)
#定义损失函数及反向传播方法。
loss = tf.reduce_mean(tf.square(y_ - y))
train_step = tf.train.GradientDescentOptimizer(0.001).minimize(loss)
#生成会话,训练STEPS轮。
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
sess.run(init_op)
STEPS = 3000
for i in range(STEPS):
start = (i*BATCH_SIZE) % 32
end = (i*BATCH_SIZE) % 32 + BATCH_SIZE
sess.run(train_step, feed_dict={x: X[start:end], y_: Y[start:end]})
if i % 1000 == 0:
print ("After %d training steps, w1 is: " % (i))
print (sess.run(w), "\n")
print ("Final w1 is: \n", sess.run(w))
由执行结果可知,无论怎么修改COST和PROFIT的值,采用自定义损失函数预测的权重(2.98,1.98)都比采用均方误差预测的权重(1.88,2.83)更接近真实值(3,2),更符合实际需求。
损失函数在机器学习和深度学习中起着非常重要的作用。它是用来衡量模型预测结果与真实标签之间的差异或误差的函数。通过最小化损失函数,我们可以训练模型的参数以使其能够更好地拟合训练数据,并在新的未见数据上做出准确的预测。博文对深度学习常见的损失函数进行的讲解,并根据问题的实际情况,举例分析讲解了如何定制合理的损失函数。