目录
在tensorflow2.0 中,使用模块model.compile时,使用loss选择损失函数。
均方误差损失函数 mean_squared_error
均方误差函数,又称mse,最基本的损失函数表示法,通常情况下mse函数会整体乘上二分之一,方便简化求导出的函数。
loss = ‘mean_squared_error’
优点:1、二次函数仅具有全局最小值。由于没有局部最小值,所以我们永远不会陷入它。因此,可以保证梯度下降将收敛到全局最小值(如果它完全收敛)。2、便于梯度下降,误差大时下降快,误差小时下降慢,有利于函数收敛。
缺点:通过平方误差来惩罚模型犯的大错误。把一个比较大的数平方会使它变得更大,因而对异常值的健壮性低,当数据集存在许多异常值时,不建议使用它。
手写代码
下面图看出MAE非常受离群点的影响,从而导致拟合效果不太好。
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif']=['SimHei']
#⽤来正常显示中⽂标签
plt.rcParams['axes.unicode_minus']=False
#⽤来正常显示负号 #有中⽂出现的情况,需要u'内容'
x = np.linspace(0,20,40) #(40,)
m = x.shape[0]
y = x + [np.random.choice(4) for _ in range(40)] #(40,)
y[-5:] -= 8 #异常点
X = np.vstack((np.ones((m,)),x)) #(2,40)
W = np.zeros((1,2)) #(1,2) b,w
iter_num = 100 #迭代次数
L = [] #记录每次迭代前的损失值大小
w_list = [] #记录权重的改变
n = 0.001 #学习率
for i in range(iter_num):
y_pre = W.dot(X)
loss = 1/(2*m)*np.sum((y - y_pre)**2)
L.append(loss)
W = W + n * 1/m * (y - y_pre).dot(X.T)
w_list.append(W)
y1 = W[0,0] + W[0,1]*0
y2 = W[0,0] + W[0,1]*20
l = len(L)
x1 = np.linspace(1,l,l)
w0 = [i[0][0] for i in w_list]
w1 = [i[0][1] for i in w_list]
plt.figure(figsize=(10,10))
plt.subplot(2,2,1)
plt.scatter(x, y)
plt.plot([0,20],[y1,y2],'r')
plt.title("MAE")
plt.subplot(2,2,2)
plt.title("损失值变化图")
x1 = np.linspace(1,l,l)
plt.plot(x1,L,'-r')
plt.xlabel('迭代次数')
plt.ylabel('损失值')
plt.subplot(2,2,3)
plt.title('偏置变化图')
plt.xlabel('迭代次数')
plt.ylabel('偏置')
plt.plot(list(x1),w0,'-r')
plt.subplot(2,2,4)
plt.title('权重变化图')
plt.xlabel('迭代次数')
plt.ylabel('权重')
plt.plot(list(x1),w1,'-b')
plt.show()
平均绝对误差 mean_absolute_error
平均绝对误差,又称mae,每个训练样本的绝对误差是预测值和实际值之间的距离,预测值与符号无关。
优点:相比较均方误差,对异常值健壮性更高。
缺点:收敛速度比均方误差慢,因为当误差大或小时其都保持同等速度下降,而且在某一点处还不可导,计算机求导比较困难。
手写代码
下图可以看出MAE函数拟合效果更好,且最后达到的损失值降为2以内。但是从权重和偏置的优化效果可以看出,不如上面的MSE函数,迭代效果出现震荡情况。
x = np.linspace(0,20,40) #(40,)
m = x.shape[0]
y = x + [np.random.choice(4) for _ in range(40)] #(40,)
y[-5:] -= 8 #异常点
X = np.vstack((np.ones((m,)),x)) #(2,40)
W = np.zeros((1,2)) #(1,2) b,w
iter_num = 100 #迭代次数
L = [] #记录每次迭代前的损失值大小
w_list = [] #记录权重的改变
n = 0.01 #学习率
for i in range(iter_num):
y_pre = W.dot(X)
loss = 1/m*np.sum(np.abs(y-y_pre))
L.append(loss)
mask = (y-y_pre).copy()
mask[y-y_pre > 0] = 1
mask[mask <= 0] = -1
W = W + n * 1/m * mask.dot(X.T)
w_list.append(W)
y1 = W[0,0] + W[0,1]*0
y2 = W[0,0] + W[0,1]*20
Huber损失 huber_loss
Huber损失结合了MSE和MAE的最佳特性。对于较小的误差,它是二次的,否则是线性的(对于其梯度也是如此)。但是Huber损失需要确定超参数 δ 。
Huber Loss 是对二者的综合,包含了一个超参数 δ。δ 值的大小决定了 Huber Loss 对 MSE 和 MAE 的侧重性,当 |y−f(x)| ≤ δ 时,变为 MSE;当 |y−f(x)| > δ 时,则变成类似于 MAE。
x_ = np.linspace(-10,10,400)
y_ = x_.copy()
q = 1
y_ = np.where(np.abs(y_) <= q ,1/2*y_**2,y_)
y_ = np.where(np.abs(y_) > q,q*np.abs(y_)- 1/2*q**2,y_)
q = 0.1
y_1 = np.where(np.abs(y_) <= q ,1/2*y_**2,y_)
y_1 = np.where(np.abs(y_) > q,q*np.abs(y_)- 1/2*q**2,y_)
plt.plot(x_,y_,'-r',label = 'q = 1')
plt.plot(x_,y_1,'-b',label = 'q = 0.1')
plt.legend()
plt.show()
上图: Huber Loss 在 |y−f(x)| > δ 时,梯度一直近似为 δ,能够保证模型以一个较快的速度更新参数。当 |y−f(x)| ≤ δ 时,梯度逐渐减小,能够保证模型更精确地得到全局最优值。因此,Huber Loss 同时具备了前两种损失函数的优点。
手写代码
可以发现huber函数在一开始损失值函数下降的非常快,基本上迭代10次以内就下降到3以内,但之后的下降就会变得很慢。
x = np.linspace(0,20,40) #(40,)
m = x.shape[0]
y = x + [np.random.choice(4) for _ in range(40)] #(40,)
y[-5:] -= 8 #异常点
X = np.vstack((np.ones((m,)),x)) #(2,40)
W = np.zeros((1,2)) #(1,2) b,w
iter_num = 500 #迭代次数
L = [] #记录每次迭代前的损失值大小
w_list = [] #记录权重的改变
n = 0.01 #学习率
delta = 1
for i in range(iter_num):
y_pre = W.dot(X)
y_ = y-y_pre
y_ = np.where(np.abs(y_) <= delta ,1/2*y_**2,y_)
y_ = np.where(np.abs(y_) > delta, delta*np.abs(y_)- 1/2* delta**2,y_)
loss = 1/m*np.sum(y_)
L.append(loss)
mask = (y-y_pre).copy()
mask = np.where(np.abs(y-y_pre) > delta,y-y_pre,mask)
mask = np.where((-delta <y-y_pre) & (y-y_pre < 0) ,-delta,mask)
mask = np.where((0 <y-y_pre) & (y-y_pre < delta ),delta,mask)
W = W + n * 1/m * mask.dot(X.T)
w_list.append(W)
y1 = W[0,0] + W[0,1]*0
y2 = W[0,0] + W[0,1]*20
softmax损失函数-交叉熵
activation=tf.nn.softmax
loss = ‘sparse_categorical_crossentropy’ #用于数字的标签
loss = ‘categorical_crossentropy’ #用于独热编码标签,如(1,0,0)、(0,1,0)、(0,0,1)
独热编码 = tf.keras.utils.to_categorical(‘数字标签’)
tf.keras.losses.sparse_categorical_crossentropy(y_true, y_pred, from_logits=False, axis=-1)
from_logits=False, 指出进行交叉熵计算时,输入的y_pred是否是logits(没有经过softmax激活函数的fully connect的输出),如果在fully connect层之后经过了激活函数softmax的处理,那这个参数就可以设置为False
Softmax 一般用于实现多分类,使用函数对输出层每一个结果进行运算,得到一个0~1的浮点值,且所有的值之和为1。
函数定义:
下面结果简单的神经网络进行解释上面的公式。
上图中,x1、x2 和 x3 是输入层,它们分别通过两个线性回归模型来产生 y1 和 y2:
然后通过Softmax函数得到y1’和y2’ :
最后将得到的y1 、y2、y1’、y2’ 通过交叉熵得到损失值。
上式中,q 表示有 q 个分类,y^{(j)} 为第 j 个分类的概率。
原因:用交叉熵(cross entropy)来衡量两个概率分布的差异,交叉熵越小,这两个分布越接近
tf.losses.binary_crossentropy
对数损失适,用于二分类的损失,必须给定参数预测值和真实值,并会返回一个矩阵,即每一个位置上的损失。
H p ( q ) = − 1 N ∑ i = 1 n [ y i I n ( p ( y i ) ) + ( 1 − y i ) I n ( 1 − p ( y i ) ) ] H_{p}(q) = - \frac{ 1 }{ N } \sum_{i=1}^n [y_{i}In(p(y_{i})) + (1-y_{i})In(1-p(y_{i}))] Hp(q)=−N1i=1∑n[yiIn(p(yi))+(1−yi)In(1−p(yi))]
true = tf.Tensor([0. 0. 0. 0. 1. 0. 0. 0. 0. 0.], shape=(10,), dtype=float32)
pred = tf.Tensor([0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1], shape=(10,), dtype=float32)
tf.losses.binary_crossentropy(true, pred) # tf.Tensor(0.32508284, shape=(), dtype=float32)
math.log(0.1) + 9*math.log(0.9))*0.1 # 0.32508284