1.损失函数简介
1.1损失函数作用
损失函数绝对是深度学习中最重要的一部分,要想训练出好的模型,就必须要有个好的损失函数。
首先,得明确损失函数是干嘛用的!
通俗地说,损失函数就是告诉网络模型预测值与真实值的差距,描述模型预测的好坏,好让网络模型朝着我们期望的好的方向进行学习。
举个简单的例子,就像猜数字游戏,我们随便写下一个数字,让计算机来预测我们写的值,这时我们就需要一个损失函数来告诉计算机预测值大了还是小了,然后计算机再通过损失函数的反馈来调整模型,最后接近我们的真实值。
例如以下实验:
import numpy as np
y = int(input('请输入一个整数(0-100):'))
#预测次数
i = 1
#预测值上限
upmax = 100
预测值下限
lowmin = 0
while True:
#AI预测
pred = np.random.randint(lowmin,upmax)
print('我是AI,这是我第%s次预测这个数,这个数是:%s' % (i,pred))
#损失函数
loss = pred - y
print('我是损失函数,损失值是:%s' % loss)
if loss==0:
print('恭喜预测正确!')
break
elif loss < 0:
print('预测值小了,请重新预测!')
lowmin = pred
else:
print('预测值大了,请重新预测!')
upmax = pred
i += 1
print('---------')
损失函数很重要,不同的模型拥有不同的损失函数,接下来简单介绍几种损失函数。
1.2 损失函数的分类
因为深度学习模型需要解决的问题无非是分类问题和回归问题,对于解决不同问题的模型来说,所需要的损失函数自然不同。
因此我们可以将损失函数分为分类损失函数和回归损失函数。
2.回归损失函数
2.1平均绝对误差(MAE)与L1_Loss
MAE公式:
M A E = ∑ 1 n ∣ y i − p r e d i ∣ n MAE =\frac{\sum_1^n\left|y_i-pred_i\right|}{n} MAE=n∑1n∣yi−predi∣
L1_loss公式:
L
1
_
l
o
s
s
=
∑
1
n
∣
y
i
−
p
r
e
d
i
∣
L1\_loss =\sum_1^n\left|y_i-pred_i\right|
L1_loss=1∑n∣yi−predi∣
从公式可以看出,MAE就是L1_loss取平均值,他们没多大区别,效果是一样的,所以介绍L1_loss就可以了。
(1)L1_loss代码实现:
import numpy as np
import matplotlib.pyplot as plt
#定义L1_loss函数:
def L1_Loss_fuction(pred):
losses = []
for i in range(len(pred)):
losses.append(np.abs(y[i]-pred[i]))
return np.sum(losses)
#定义预测方法1:
def pred_fuction1(x):
a = x/2
b = 2*x
c = 500
return a,b,c
#定义预测方法2:
def pred_fuction2(x):
a = x/10
b = a*9+99
c = a+b
return a,b,c
#输入值x是[-1000,1000]的整数
x = np.linspace(-1000,1000,2001,dtype=int)
#真实值y是一个包含3个元素的向量
y = [1,2,9]
#损失函数
L1_losses1 = []
L1_losses2 = []
for i in range(2):
for j in x:
pred = []
#预测方法1的L1_loss
if i == 0:
pred1,pred2,pred3 = pred_fuction1(j)
pred = [pred1,pred2,pred3]
L1_losses1.append(L1_Loss_fuction(pred))
#预测方法2的L1_loss
else:
pred1,pred2,pred3 = pred_fuction2(j)
pred = [pred1,pred2,pred3]
L1_losses2.append(L1_Loss_fuction(pred))
plt.plot(x, L1_losses1, linewidth=1,c='r',label='pred_fuction1')
plt.plot(x, L1_losses2, linewidth=1,c='g',label='pred_fuction2')
plt.legend(loc = 'best')
plt.show()
(2)L1_loss解析
1.从L1_loss的公式可以看出,它是无法求导的,所以计算机在计算时,无法精确找到那个最小值,只能将损失降到最小值附近区间。
2.从L1_loss的图像来看,在折点左右两边是具有稳定的梯度的,所以在损失值计算方面快捷方便且稳定。
2.2 均值平方差(MSE)与L2_Loss
MSE公式:
M S E = ∑ 1 n ( y i − p r e d i ) 2 n MSE =\frac{\sum_1^n(y_i-pred_i)^2}{n} MSE=n∑1n(yi−predi)2
L2_loss公式:
L
2
_
l
o
s
s
=
∑
1
n
(
y
i
−
p
r
e
d
i
)
2
L2\_loss =\sum_1^n(y_i-pred_i)^2
L2_loss=1∑n(yi−predi)2
从公式可以看出,MSE就是L2_loss取平均值,他们没多大区别,效果是一样的,所以介绍L2_loss就可以了。
(1)L2_loss代码实现:
import numpy as np
import matplotlib.pyplot as plt
#定义L2_loss函数:
def L2_Loss_fuction(pred):
losses = []
for i in range(len(pred)):
losses.append((y[i]-pred[i])**2)
return np.sum(losses)
#定义预测方法1:
def pred_fuction1(x):
a = x/2
b = 2*x
c = 500
return a,b,c
#定义预测方法2:
def pred_fuction2(x):
a = x/10
b = a*9+99
c = a+b
return a,b,c
#输入值x,真实值y
x = np.linspace(-1000,1000,2001,dtype=int)
y = [1,2,9]
#损失函数
L2_losses1 = []
L2_losses2 = []
for i in range(2):
for j in x:
pred = []
#预测方法1的L1_loss
if i == 0:
pred1,pred2,pred3 = pred_fuction1(j)
pred = [pred1,pred2,pred3]
L2_losses1.append(L2_Loss_fuction(pred))
#预测方法2的L1_loss
else:
pred1,pred2,pred3 = pred_fuction2(j)
pred = [pred1,pred2,pred3]
L2_losses2.append(L2_Loss_fuction(pred))
plt.plot(x, L2_losses1, linewidth=1,c='r',label='L2_loss_1')
plt.plot(x, L2_losses2, linewidth=1,c='g',label='L2_loss_2')
plt.legend(loc = 'best')
plt.show()
(2)L2_loss解析
1.从公式上来看,L2_loss可以求导,所以可以精确地找到它的最小值。
2.从L2_loss的图像来看,请观察其纵坐标的值,然后再去看L1_loss纵坐标的值。两者的数据是一样的,唯一不同就是损失函数,L2_loss损失函数会将误差扩大会导致梯度爆炸!
举个简单的例子:
假如真实值y = [1,1,1,1],
预测值 pred = [3,8,6,20]
L
1
_
l
o
s
s
=
∑
[
2
,
7
,
5
,
19
]
L1\_loss = \sum[2,7,5,19]
L1_loss=∑[2,7,5,19]
L
2
_
l
o
s
s
=
∑
[
4
,
49
,
25
,
361
]
L2\_loss = \sum[4,49,25,361]
L2_loss=∑[4,49,25,361]
看到了吧,对于L1_loss来说,19对整体的影响不是很大;但对于L2_loss来说,
1
9
2
19^2
192 对于整体的影响很大,如果要降低
1
9
2
19^2
192势必要求其他值变化大,这样就会造成梯度爆炸!
2.3 Huber损失函数
(1)总结下L1和L2损失;
1.L1无法找到精确的最小值,但L2可以。
2.L1梯度下降稳定,异常点影响小,但L2对异常点很敏感,出现异常点容易造成梯度爆炸。
3.既然L1和L2都各有优缺点,那是否有函数能将他们的有点结合,同时又能摒弃他们的缺点呢?当然,那就是我们的Huber函数!
(2)Huber函数公式:
H
u
b
e
r
=
∑
1
n
{
1
2
(
y
i
−
p
r
e
d
i
)
2
,
∣
y
i
−
p
r
e
d
i
∣
⩽
δ
δ
∣
y
i
−
p
r
e
d
i
∣
−
1
2
δ
2
,
otherwise
Huber = \sum_1^n\begin{cases} \frac{1}{2}(y_i-pred_i)^2, & \text{$\left|y_i-pred_i\right|\leqslant\delta$} \\ \delta\left|y_i-pred_i\right|-\frac{1}{2}\delta^2, & \text{otherwise} \end{cases}
Huber=1∑n{21(yi−predi)2,δ∣yi−predi∣−21δ2,∣yi−predi∣⩽δotherwise
公式中的
δ
\delta
δ是需要自己调整的。
(3)代码实现:
import numpy as np
import matplotlib.pyplot as plt
#定义Huber函数:
def Huber_fuction(pred,delta=300):
losses = []
for i in range(len(pred)):
a = y[i]-pred[i]
a = np.abs(a)
if a<delta:
losses.append(0.5*(a**2))
else:
losses.append(delta*a-(delta**2)/2)
return np.sum(losses)
#定义预测方法1:
def pred_fuction1(x):
a = x/2
b = 2*x
c = 500
return a,b,c
#定义预测方法2:
def pred_fuction2(x):
a = x/10
b = a*9+99
c = a+b
return a,b,c
#输入值x,真实值y
x = np.linspace(-1000,1000,2001,dtype=int)
y = [1,2,9]
#损失函数
Huber1 = []
Huber2 = []
for i in range(2):
for j in x:
pred = []
#预测方法1的Huber损失
if i == 0:
pred1,pred2,pred3 = pred_fuction1(j)
pred = [pred1,pred2,pred3]
Huber1.append(Huber_fuction(pred))
#预测方法2的Huber损失
else:
pred1,pred2,pred3 = pred_fuction2(j)
pred = [pred1,pred2,pred3]
Huber2.append(Huber_fuction(pred))
plt.plot(x, Huber1, linewidth=1,c='r',label='Huber1')
plt.plot(x, Huber2, linewidth=1,c='g',label='Huber2')
plt.legend(loc = 'best')
plt.show()
公式看起来很复杂,但看图就很直观了,其实就是将L1和L2损失函数各自截取一部分,从哪里截取则由我们通过调整
δ
\delta
δ来控制。
上图是
δ
=
300
\delta=300
δ=300时的结果,下图则是
δ
=
30
\delta=30
δ=30时的结果:
(4)Huber的优缺点
1.Huber拥有L1和L2的所有优点,并且没有他们的缺点,是个非常不错的选择。
2.但Huber使用效果的好坏由
δ
\delta
δ决定,需要人为调整,比较麻烦。