总的来说,监督机器学习的核心原理莫过于如下公式:
该公式可谓是机器学习中最核心最关键最能概述监督学习的核心思想的公式了:所有的有监督机器学习,无非就是正则化参数的同时最小化经验误差函数。最小化经验误差是为了极大程度的拟合训练数据,正则化参数是为了防止过分的拟合训练数据。你看,多么简约数学哲学。正如之前所说,监督机器学习是为了让我们建立的模型能够发现数据中普遍的一般的规律,这个普遍的一般的规律无论对于训练集还是未知的测试集,都具有较好的拟合性能。通俗点举例就是,考试能力很强,应用能力很差,或者是模拟考很强,高考却一般。
先不扯远了,继续回到公式。第一项经验误差函数在机器学习中无疑地位重要,但它不是笔者今天要讲的,今天要讲的是公式的第二项:正则化项。第二项中 λ 为正则化系数,通常是大于 0 的,是一种调整经验误差项和正则化项之间关系的系数。λ = 0 时相当于该公式没有正则化项,模型全力讨好第一项,将经验误差进行最小化,往往这也是最容易发生过拟合的时候。随着 λ 逐渐增大,正则化项在模型选择中的话语权越来越高,对模型的复杂性的惩罚也越来越厉害。所以,在实际的训练过程中,λ 作为一种超参数很大程度上决定了模型生死。
系数 λ 说完了,然后就是正则化项,正则化项形式有很多,但常见的也就是 L1 和 L2 正则化。下面笔者就带大家好好拾掇拾掇这些个 L1 L2。
在说常见的 L1 和 L2 之前,先来看一下 L0 正则化。L0 正则化也就是 L0 范数,即矩阵中所有非 0 元素的个数。如何我们在正则化过程中选择了 L0 范数,那该如何理解这个 L0 呢?其实非常简单,L0 范数就是希望要正则化的参数矩阵 W 大多数元素都为 0。如此简单粗暴,让参数矩阵 W 大多数元素为 0 就是实现稀疏而已。说到这里,权且打住,想必同样在机器学习领域摸爬滚打的你一定想问,据我所知稀疏性不通常都是用 L1 来实现的吗?这里个中缘由笔者不去细讲了,简单说结论:在机器学习领域,L0 和 L1 都可以实现矩阵的稀疏性,但在实践中,L1 要比 L0 具备更好的泛化求解特性而广受青睐。先说了 L1,但还没解释 L1 范数是什么,L1 范数就是矩阵中各元素绝对值之和,正如前述所言,L1 范数通常用于实现参数矩阵的稀疏性。至于为啥要稀疏,稀疏有什么用,通常是为了特征选择和易于解释方面的考虑。
再来看 L2 范数。相较于 L0 和 L1,其实 L2 才是正则化中的天选之子。在各种防止过拟合和正则化处理过程中,L2 正则化可谓风头无二。L2 范数是指矩阵中各元素的平方和后的求根结果。采用 L2 范数进行正则化的原理在于最小化参数矩阵的每个元素,使其无限接近于 0 但又不像 L1 那样等于 0,也许你又会问了,为什么参数矩阵中每个元素变得很小就能防止过拟合?这里我们就拿深度神经网络来举例说明吧。在 L2 正则化中,如何正则化系数变得比较大,参数矩阵 W 中的每个元素都在变小,线性计算的和 Z 也会变小,激活函数在此时相对呈线性状态,这样就大大简化了深度神经网络的复杂性,因而可以防止过拟合。
至于 L1 和 L2,江湖上还有一些混名,L1 就是江湖上著名的 lasso,L2 呢则是岭回归。二者都是对回归损失函数加一个约束形式,lasso 加的是 L1 范数,岭回归加的是 L2 范数。可以从几何直观上看看二者的区别。
L1 和 L2 的模型空间
说了半天的范数,下面我们就来看看在神经网络中如何进行正则化操作防止过拟合。为了跟前面笔记保持一致,我们在神经网络训练过程中继续采用交叉熵损失函数:
加了正则化项之后,损失函数形式如上所示,损失函数变了,反向传播的梯度计算也就变了,相应的反向传播也需要重新定义函数。
带正则化项的损失函数的定义:
def compute_cost_with_regularization(A3, Y, parameters, lambd): """
Implement the cost function with L2 regularization. See formula (2) above.
Arguments:
A3 -- post-activation, output of forward propagation, of shape (output size, number of examples)
Y -- "true" labels vector, of shape (output size, number of examples)
parameters -- python dictionary containing parameters of the model
Returns:
cost - value of the regularized loss function (formula (2))
"""
m = Y.shape[1]
W1 = parameters["W1"]
W2 = parameters["W2"]
W3 = parameters["W3"]
cross_entropy_cost = compute_cost(A3, Y) # This gives you the cross-entropy part of the cost
L2_regularization_cost = 1/m * lambd/2 * (np.sum(np.square(W1))+np.sum(np.square(W2))+np.sum(np.square(W3)))
cost = cross_entropy_cost + L2_regularization_cost
return cost
反向传播的函数定义:
def backward_propagation_with_regularization(X, Y, cache, lambd): """
Implements the backward propagation of our baseline model to which we added an L2 regularization.
Arguments:
X -- input dataset, of shape (input size, number of examples)
Y -- "true" labels vector, of shape (output size, number of examples)
cache -- cache output from forward_propagation()
lambd -- regularization hyperparameter, scalar
Returns:
gradients -- A dictionary with the gradients with respect to each parameter, activation and pre-activation variables
"""
m = X.shape[1]
(Z1, A1, W1, b1, Z2, A2, W2, b2, Z3, A3, W3, b3) = cache
dZ3 = A3 - Y
dW3 = 1./m * np.dot(dZ3, A2.T) + lambd/m * W3
db3 = 1./m * np.sum(dZ3, axis=1, keepdims = True)
dA2 = np.dot(W3.T, dZ3)
dZ2 = np.multiply(dA2, np.int64(A2 > 0))
dW2 = 1./m * np.dot(dZ2, A1.T) + lambd/m * W2
db2 = 1./m * np.sum(dZ2, axis=1, keepdims = True)
dA1 = np.dot(W2.T, dZ2)
dZ1 = np.multiply(dA1, np.int64(A1 > 0))
dW1 = 1./m * np.dot(dZ1, X.T) + lambd/m * W1
db1 = 1./m * np.sum(dZ1, axis=1, keepdims = True)
gradients = {"dZ3": dZ3, "dW3": dW3, "db3": db3,"dA2": dA2, "dZ2": dZ2, "dW2": dW2, "db2": db2, "dA1": dA1,
"dZ1": dZ1, "dW1": dW1, "db1": db1}
return gradients
在实例中,加了正则化项和没加正则化项的模型分类结果可如图所见:
未经正则化处理的分类模型结果
加上正则化后的模型分类结果
效果显而易见,加了正则化之后,神经网络的过拟合情况得到极大的缓解。
参考资料:
https://www.coursera.org/learn/machine-learning
https://blog.csdn.net/zouxy09/article/details/24971995
https://www.deeplearning.ai/
往期精彩:
一个数据科学从业者的学习历程