4.损失函数
对于倒运的数据科学家来说,要解决一个给定的机器学习问题,数据科学家必须找到构建函数方法,这个函数的最小值能编码解决手头问题的方案。幸运的是机器学习文献里有很多损失函数的历史可以进行这种编码。实践上的机器学习归根于理解不同类型的损失函数并知道哪个损失函数可以用来解决哪个问题。换句话说,损失函数是数据科学项目转化为数学的机制。所有的机器学习以及大部分的人工智能,归根于创建合适的损失函数以解决手头的问题。我们将带你了解一下一些最常见的损失函数。我们将满足一定数学特征的有意义的损失函数标记为 ℒ。首先,ℒ必须有数据点x和标签y。我们记这样的损失函数为ℒ(x, y)。 用上一章学习的语言, x 和y 都是张量,ℒ是从成对的张量到标量的函数。损失函数的型式中如何的呢? 我们常见的假定是损失函数是可加的。假如xi, yi 是来自实例i 的数据且总共有 N 个实例,则损失函数为
(实际上对于每一个点 ℒi都一样。这种可加分解有许多好处。 第一个是通过加的可微性因素,所以计算总的损失的梯度简化为:
这种学习技巧意味着只要更小的函数ℒi是可微的,总的损失函数就是可微的。所以设计损失函数就是设计更小的函数 ℒi(xi, yi)。
在我们深入设计 ℒi之前,解释一下分类和回归问题有好处。
分类和回归
机器学习算法可以分为有监督促学习和无监督学习。监督学习问题是有数据点x 和标签
y 的,而非监督学习问题只有数据点x而没有标签 y的。通常非监督学习更难并且没有很好的定义(理解“数据点x”的意思是什么?)。现在我们不深入非监督学习的损失函数,因为实际上大部分的非监督学习损失函数是重用监督学习损失函数。
监督机器学习可以分为分类和回归问题。分类问题是你要设计机器学习系统来分配离散的标签,假如0/1 (或更一般的0, ⋯, n) 到给定的数据点。回归问题是你要设计一种机器学习系统来将真实值附着到给定的数据点。从高层次看,这些问题明显不同。数学上离散对象和连续对象的处理是不同的。但是机器学习的一部分技巧是用连续可微的损失函数来编码分类和回归问题。
如我们前面提到的,许多机器学习问题就是将现实的复杂的系统转换为可微函数的简单艺术。
下一节我们介绍能将分类和回归任务转换到合适的损失函数的有用的数学函数。
L2 损失
L2 损失 (读作 ell-two 损失)常用于回归问题。 L2 损失 (有的地方也称为 L2-norm ) 提供了向量的大小的测量方法:
这里, 假定a 是长度为N.的向量。 L2- norm用来定义两个向量的距离:
L2 作为距离测量的思想对于解决回归问题很有用。假如 x 是数据点的集合,y 是相关标签。令f 是编码我们的机器学习模型的可微函数。为了让 f能预测y, 我们创建 L2 损失函数
作为快速标记 ,实际上不直接用L2 损失,而是使用它的二次方
为了避免处理梯度里的1/ x 形式。我们反复的使用L2 损失的二次方。
L2损失的失效模式
L2 尖锐的惩罚对真实值的大的偏离,但是对于真实值的良好匹配没有太大用。我们可以从数学上理解这种差异。通过研究原点附近的x2和 x 的行为 (图 3-14).
图3-14. 比较原点附近的平方函数和相等函数
注意,对于小的x,x2 快速的减少到 0。结果,小的偏离没有通过L2 损失严重的惩罚。在低维回归里,这不是问题,但是在高维回归里,L2是很差的损失函数,因为有许多小的偏离使得回归输出很差。
概率分布
在介绍分类问题的损失函数之前,快速的介绍一下概率分布是有好处的。什么是概率分布,为什么机器学习需要关注概率分布?
概率是个较深的主题,我们只深入到足于理解的程度。在高的层次上,概率分布提供了将离散选择转换为连续问题的数学技巧。 例如,如果你要设计机器学习系统来预测硬币是面朝上还是底朝上。看起来面朝上或底朝上不能编码为连续函数,不是可微的函数。你如何用微积分或TensorFlow来解决离散的选择?进入概率分布。不是硬选择,让分类器预测面朝上和底朝上的概率。例如分类器学习到面朝上的概率为 0.75而底朝上的概率为 0.25。注意概率不同于连续! 然后通过离散事件的概率而不是事件本身,你就可以回避微积分不处理离散事件的问题了。
概率分布 p 简单的来说就是手头的离散事件发生概率的列表。本例中, p = (0.75, 0.25)。注意,可选的,你可以将p: 0, 1 ℝ看作来自实数的两个元素的函数。这种理解很有用。可以给真实的事件分配概率分布。我们在后面讨论。
交叉熵损失
交叉熵损失是测量两种概率分布的距离的数学方法:
这里p和q是两种概率分布。记 p(x)为事件x的概率p。 这种定义值得认真讨论。像L2-norm, H 提供了距离的标记。注意,本例中p = q,
这个量是 p的熵写作 H(p)。它量度分布的无序性,当所有的事件相同时熵为最大。H(p) 总是小于等于 H(p, q)。事实上,q离p,“越远”,交叉熵越大。我们不深入这个语句的准确意义。但是直观上交叉熵是距离测量是值得记忆的。
记住,不像L2 norm, H 是对称的! 即, H( p, q) ≠ H( q, p)。
回到离散事件,假如p = y, 1−y是有两种结果的离散事件的真实的数据分布,q = ypred, 1 − ypred是机器学习的预测。则交叉熵为
这种形式的损失广泛应用于训练分类器的机器学习系统。经验上,最小化 H(p, q) 能构建良好地重复提供训练标签的分类器。
绘制损失不同的函数
这个python脚本说明用于回归和分类的不同的损失函数。我们从加载必要的库并重置计算图开始。
#List3-12
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.python.framework import ops
ops.reset_default_graph()
数值的预测
为了研究损失函数,我们从数值的损失函数开始。为此,我们必须创建目标值附近的预测值序列。这个练习里我们考虑目标值为零。
# 多个预测的X值
x_vals = tf.linspace(-1., 1., 500)
# 创建目标零
target = tf.constant(0.)
L2 损失函数
L2 loss是最常用的回归损失函数。这里我们看一下如何用tensorflow创建它并评估它以供后面绘图。
# L2 loss
# L = (pred - actual)^2
l2_y_vals = tf.square(target - x_vals)
l2_y_out = l2_y_vals
L1 损失函数
一个可选的损失函数是L1 loss。这与L2非常相似,不同的是我们计算差的“绝对值”而不是平方。
# L1 loss
# L = abs(pred - actual)
l1_y_vals = tf.abs(target - x_vals)
l1_y_out = l1_y_vals
Pseudo-Huber损失函数
(predicted - target)变大时,psuedo-huber loss函数是L1 loss的平滑近似。当预测值接近目标值时pseudo-huber loss的行为与L2 loss相似。
# L = delta^2 * (sqrt(1 + ((pred - actual)/delta)^2) - 1)
# Pseudo-Huber with delta = 0.25
delta1 = tf.constant(0.25)
phuber1_y_vals = tf.multiply(tf.square(delta1), tf.sqrt(1. + tf.square((target - x_vals)/delta1)) - 1.)
phuber1_y_out = phuber1_y_vals
# Pseudo-Huber with delta = 5
delta2 = tf.constant(5.)
phuber2_y_vals = tf.multiply(tf.square(delta2), tf.sqrt(1. + tf.square((target - x_vals)/delta2)) - 1.)
phuber2_y_out = phuber2_y_vals
绘制回归的损失函数
这里我们用Matplotlib来绘制L1, L2,和Pseudo-Huber Losses。
x_array = x_vals
plt.plot(x_array, l2_y_out, 'b-', label='L2 Loss')
plt.plot(x_array, l1_y_out, 'r--', label='L1 Loss')
plt.plot(x_array, phuber1_y_out, 'k-.', label='P-Huber Loss (0.25)')
plt.plot(x_array, phuber2_y_out, 'g:', label='P-Huber Loss (5.0)')
plt.ylim(-0.2, 0.4)
plt.legend(loc='lower right', prop={'size': 11})
plt.grid()
plt.show()
图3-15
分类的预测
我们现在考虑分类的损失函数。这里,预测值将在目标值1附近。
# Various predicted X values
x_vals = tf.linspace(-3., 5., 500)
# Target of 1.0
target = tf.constant(1.)
targets = tf.fill([500,], 1.)
Hinge损失函数
hinge loss用于分类预测。这里它是max(0, 1-(pred*actual))
。
# Hinge loss
# 用于预测二项 (-1, 1)分类
# L = max(0, 1 - (pred * actual))
hinge_y_vals = tf.maximum(0., 1. - tf.multiply(target, x_vals))
hinge_y_out = hinge_y_vals
Cross Entropy损失函数
cross entropy loss是测量分类目标与输出模型的logits的损失的非常流行的方法。你可以在这里详细的了解: https://en.wikipedia.org/wiki/Cross_entropy
# Cross entropy loss
# L = -actual * (log(pred)) - (1-actual)(log(1-pred))
xentropy_y_vals = - tf.multiply(target, tf.math.log(x_vals)) - tf.multiply((1. - target), tf.math.log(1. - x_vals))
xentropy_y_out = xentropy_y_vals
Sigmoid Entropy损失函数
TensorFlow也有sigmoid-entropy loss函数。它与前面的cross-entropy函数相似,不同的是我们在函数里取预测值的sigmoid。
# L = -actual * (log(sigmoid(pred))) - (1-actual)(log(1-sigmoid(pred)))
# or
# L = max(actual, 0) - actual * pred + log(1 + exp(-abs(actual)))
x_val_input = tf.expand_dims(x_vals, 1)
target_input = tf.expand_dims(targets, 1)
xentropy_sigmoid_y_vals = tf.nn.sigmoid_cross_entropy_with_logits(logits=x_val_input, labels=target_input)
xentropy_sigmoid_y_out = xentropy_sigmoid_y_vals
Weighted (Softmax) Cross Entropy损失函数
Tensorflow也有一个与sigmoid cross entropy
loss函数相似的损失函数, 但是我们用真实值和加权的预测值的softmax替代。
# Weighted (softmax) cross entropy loss
# L = -actual * (log(pred)) * weights - (1-actual)(log(1-pred))
# or
# L = (1 - pred) * actual + (1 + (weights - 1) * pred) * log(1 + exp(-actual))
weight = tf.constant(0.5)
xentropy_weighted_y_vals = tf.nn.weighted_cross_entropy_with_logits(logits=x_vals,labels=targets,pos_weight=weight)
xentropy_weighted_y_out = xentropy_weighted_y_vals
绘制分类的损失
# Plot the output
x_array = x_vals
plt.plot(x_array, hinge_y_out, 'b-', label='Hinge Loss')
plt.plot(x_array, xentropy_y_out, 'r--', label='Cross Entropy Loss')
plt.plot(x_array, xentropy_sigmoid_y_out, 'k-.', label='Cross Entropy Sigmoid Loss')
plt.plot(x_array, xentropy_weighted_y_out, 'g:', label='Weighted Cross Entropy Loss (x0.5)')
plt.ylim(-1.5, 3)
plt.legend(loc='lower right', prop={'size': 11})
plt.grid()
plt.show()
图3-16
Softmax entropy和Sparse Entropy
因为很难绘制多分类的损失函数,我们用如何获得输出来展示而不是绘图。
# Softmax entropy loss
# L = -actual * (log(softmax(pred))) - (1-actual)(log(1-softmax(pred)))
unscaled_logits = tf.constant([[1., -3., 10.]])
target_dist = tf.constant([[0.1, 0.02, 0.88]])
softmax_xentropy = tf.nn.softmax_cross_entropy_with_logits(logits=unscaled_logits,labels=target_dist)
print(softmax_xentropy)
# Sparse entropy loss
# Use when classes and targets have to be mutually exclusive
# L = sum( -actual * log(pred) )
unscaled_logits = tf.constant([[1., -3., 10.]])
sparse_target_dist = tf.constant([2])
sparse_xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=unscaled_logits,labels=sparse_target_dist)
print(sparse_xentropy)