弹性网络介于 Ridge 回归和 Lasso 回归之间。它的正则项是 Ridge 回归和 Lasso 回归正则项的简单混合,同时你可以控制它们的混合率 $r$,当 $r=0$ 时,弹性网络就是 Ridge 回归,当 $r=1$ 时,其就是 Lasso 回归。具体表示如公式 4-12。
公式 4-12:弹性网络损失函数
$$ J(\theta)=MSE(\theta)+r\alpha\sum\limits_{i=1}^n\left|\theta_i \right|+\frac{1-r}{2}\alpha\sum\limits_{i=1}^n\theta_i^2 $$
那么我们该如何选择线性回归,岭回归,Lasso 回归,弹性网络呢?一般来说有一点正则项的表现更好,因此通常你应该避免使用简单的线性回归。岭回归是一个很好的首选项,但是如果你的特征仅有少数是真正有用的,你应该选择 Lasso 和弹性网络。就像我们讨论的那样,它两能够将无用特征的权重降为零。一般来说,弹性网络的表现要比 Lasso 好,因为当特征数量比样本的数量大的时候,或者特征之间有很强的相关性时,Lasso 可能会表现的不规律。下面是一个使用 Scikit-LearnElasticNet
(l1_ratio
指的就是混合率 $r$)的简单样本:
>>> from sklearn.linear_model import ElasticNet
>>> elastic_net = ElasticNet(alpha=0.1, l1_ratio=0.5)
>>> elastic_net.fit(X, y)
>>> elastic_net.predict([[1.5]])
array([ 1.54333232])
对于迭代学习算法,有一种非常特殊的正则化方法,就像梯度下降在验证错误达到最小值时立即停止训练那样。我们称为早期停止法。图 4-20 表示使用批量梯度下降来训练一个非常复杂的模型(一个高阶多项式回归模型)。随着训练的进行,算法一直学习,它在训练集上的预测误差(RMSE)自然而然的下降。然而一段时间后,验证误差停止下降,并开始上升。这意味着模型在训练集上开始出现过拟合。一旦验证错误达到最小值,便提早停止训练。这种简单有效的正则化方法被 Geoffrey Hinton 称为“完美的免费午餐”
提示
随机梯度和小批量梯度下降不是平滑曲线,你可能很难知道它是否达到最小值。 一种解决方案是,只有在验证误差高于最小值一段时间后(你确信该模型不会变得更好了),才停止,之后将模型参数回滚到验证误差最小值。
下面是一个早期停止法的基础应用:
from sklearn.base import clone
sgd_reg = SGDRegressor(n_iter=1, warm_start=True, penalty=None,learning_rate="constant", eta0=0.0005)
minimum_val_error = float("inf")
best_epoch = None
best_model = None
for epoch in range(1000):
sgd_reg.fit(X_train_poly_scaled, y_train)
y_val_predict = sgd_reg.predict(X_val_poly_scaled)
val_error = mean_squared_error(y_val_predict, y_val)
if val_error < minimum_val_error:
minimum_val_error = val_error
best_epoch = epoch
best_model = clone(sgd_reg)
注意:当warm_start=True
时,调用fit()
方法后,训练会从停下来的地方继续,而不是从头重新开始。
正如我们在第1章中讨论的那样,一些回归算法也可以用于分类(反之亦然)。 Logistic 回归(也称为 Logit 回归)通常用于估计一个实例属于某个特定类别的概率(例如,这电子邮件是垃圾邮件的概率是多少?)。 如果估计的概率大于 50%,那么模型预测这个实例属于当前类(称为正类,标记为“1”),反之预测它不属于当前类(即它属于负类 ,标记为“0”)。 这样便成为了一个二元分类器。
那么它是怎样工作的? 就像线性回归模型一样,Logistic 回归模型计算输入特征的加权和(加上偏差项),但它不像线性回归模型那样直接输出结果,而是把结果输入logistic()
函数进行二次加工后进行输出(详见公式 4-13)。
公式 4-13:逻辑回归模型的概率估计(向量形式)
$$ \hat{p}=h_\theta(\mathbf{x})=\sigma(\theta^T \cdot \mathbf{x}) $$
Logistic 函数(也称为 logit),用 $\sigma()$ 表示,其是一个 sigmoid 函数(图像呈 S 型),它的输出是一个介于 0 和 1 之间的数字。其定义如公式 4-14 和图 4-21 所示。
公式 4-14:逻辑函数
$$ \sigma(t)=\frac{1}{1+exp(-t)} $$
一旦 Logistic 回归模型估计得到了 $\mathbf{x}$ 属于正类的概率 $\hat{p}=h_\theta(\mathbf{x})$,那它很容易得到预测结果 $\hat{y}$(见公式 4-15)。
公式 4-15:逻辑回归预测模型
$$ \hat{y}= \begin{cases} 0, &\hat{p}<0.5 \ 1,&\hat{p}\geq0.5 \ \end{cases} $$
注意当 $t<0$ 时 $\sigma(t)<0.5$,当 $t\geq0$时$\sigma(t)\geq0.5$,因此当$\theta^T \cdot \mathbf{x}$ 是正数的话,逻辑回归模型输出 1,如果它是负数的话,则输出 0。
好,现在你知道了 Logistic 回归模型如何估计概率并进行预测。 但是它是如何训练的? 训练的目的是设置参数向量 $\theta$,使得正例($y=1$)概率增大,负例($y=0$)的概率减小,其通过在单个训练实例 $\mathbf{x}$ 的损失函数来实现(公式 4-16)。
公式 4-16:单个样本的损失函数
$$ c(\theta)= \begin{cases} -log(\hat{p}), &y=1 \ -log(1-\hat{p}),&y=0 \ \end{cases} $$
这个损失函数是合理的,因为当 $t$ 接近 0 时,$-log(t)$ 变得非常大,所以如果模型估计一个正例概率接近于 0,那么损失函数将会很大,同时如果模型估计一个负例的概率接近 1,那么损失函数同样会很大。 另一方面,当 $t$ 接近于 1 时,$-log(t)$ 接近 0,所以如果模型估计一个正例概率接近于 0,那么损失函数接近于 0,同时如果模型估计一个负例的概率接近 0,那么损失函数同样会接近于 0, 这正是我们想的。
整个训练集的损失函数只是所有训练实例的平均值。可以用一个表达式(你可以很容易证明)来统一表示,称为对数损失,如公式 4-17 所示。
公式 4-17:逻辑回归的损失函数(对数损失)
$$ J(\theta)=-\frac{1}{m}\sum\limits_{i=1}^m\left[y^{(i)}log\left(\hat{p}^{(i)}\right)+\left(1-y^{(i)}\right)log\left(1-\hat{p}^{(i)}\right)\right] $$
但是这个损失函数对于求解最小化损失函数的 $\theta$ 是没有公式解的(没有等价的正态方程)。 但好消息是,这个损失函数是凸的,所以梯度下降(或任何其他优化算法)一定能够找到全局最小值(如果学习速率不是太大,并且你等待足够长的时间)。公式 4-18 给出了损失函数关于第 $j$ 个模型参数 $\theta_j$ 的偏导数。
公式 4-18:逻辑回归损失函数的偏导数
$$ \frac{\partial}{\partial \theta_j}J(\theta_j)=\frac{1}{m} \sum\limits_{i=1}^m{\left(\sigma\left(\theta^T \cdot \mathbf{x}^{(i)}\right)-y^{(i)}\right)}{x_j}^{(i)} $$
这个公式看起来非常像公式 4-5:首先计算每个样本的预测误差,然后误差项乘以第 $j$ 项特征值,最后求出所有训练样本的平均值。 一旦你有了包含所有的偏导数的梯度向量,你便可以在梯度向量上使用批量梯度下降算法。 也就是说:你已经知道如何训练 Logistic 回归模型。 对于随机梯度下降,你当然只需要每一次使用一个实例,对于小批量梯度下降,你将每一次使用一个小型实例集。
我们使用鸢尾花数据集来分析 Logistic 回归。 这是一个著名的数据集,其中包含 150 朵三种不同的鸢尾花的萼片和花瓣的长度和宽度。这三种鸢尾花为:Setosa,Versicolor,Virginica(如图 4-22)。
让我们尝试建立一个分类器,仅仅使用花瓣的宽度特征来识别 Virginica,首先让我们加载数据:
>>> from sklearn import datasets
>>> iris = datasets.load_iris()
>>> list(iris.keys())
['data', 'target_names', 'feature_names', 'target', 'DESCR']
>>> X = iris["data"][:, 3:] # petal width
>>> y = (iris["target"] == 2).astype(np.int)
接下来,我们训练一个逻辑回归模型:
from sklearn.linear_model import LogisticRegression
log_reg = LogisticRegression()
log_reg.fit(X, y)
我们来看看模型估计的花瓣宽度从 0 到 3 厘米的概率估计(如图 4-23):
X_new = np.linspace(0, 3, 1000).reshape(-1, 1)
y_proba = log_reg.predict_proba(X_new)
plt.plot(X_new, y_proba[:, 1], "g-", label="Iris-Virginica")
plt.plot(X_new, y_proba[:, 0], "b--", label="Not Iris-Virginica"
Virginica 花的花瓣宽度(用三角形表示)在 1.4 厘米到 2.5 厘米之间,而其他种类的花(由正方形表示)通常具有较小的花瓣宽度,范围从 0.1 厘米到 1.8 厘米。注意,它们之间会有一些重叠。在大约 2 厘米以上时,分类器非常肯定这朵花是Virginica花(分类器此时输出一个非常高的概率值),而在1厘米以下时,它非常肯定这朵花不是 Virginica 花(不是 Virginica 花有非常高的概率)。在这两个极端之间,分类器是不确定的。但是,如果你使用它进行预测(使用predict()
方法而不是predict_proba()
方法),它将返回一个最可能的结果。因此,在 1.6 厘米左右存在一个决策边界,这时两类情况出现的概率都等于 50%:如果花瓣宽度大于 1.6 厘米,则分类器将预测该花是 Virginica,否则预测它不是(即使它有可能错了):
>>> log_reg.predict([[1.7], [1.5]])
array([1, 0])
图 4-24 表示相同的数据集,但是这次使用了两个特征进行判断:花瓣的宽度和长度。 一旦训练完毕,Logistic 回归分类器就可以根据这两个特征来估计一朵花是 Virginica 的可能性。 虚线表示这时两类情况出现的概率都等于 50%:这是模型的决策边界。 请注意,它是一个线性边界。每条平行线都代表一个分类标准下的两两个不同类的概率,从 15%(左下角)到 90%(右上角)。越过右上角分界线的点都有超过 90% 的概率是 Virginica 花。
就像其他线性模型,逻辑回归模型也可以 $\ell_1$ 或者 $\ell_2$ 惩罚使用进行正则化。Scikit-Learn 默认添加了 $\ell_2$ 惩罚。
注意
在 Scikit-Learn 的
LogisticRegression
模型中控制正则化强度的超参数不是 $\alpha$(与其他线性模型一样),而是它的逆:$C$。 $C$ 的值越大,模型正则化强度越低。
Logistic 回归模型可以直接推广到支持多类别分类,不必组合和训练多个二分类器(如第 3 章所述), 其称为 Softmax 回归或多类别 Logistic 回归。
这个想法很简单:当给定一个实例 $\mathbf{x}$ 时,Softmax 回归模型首先计算 $k$ 类的分数 $s_k(\mathbf{x})$,然后将分数应用在Softmax
函数(也称为归一化指数)上,估计出每类的概率。 计算 $s_k(\mathbf{x})$ 的公式看起来很熟悉,因为它就像线性回归预测的公式一样(见公式 4-19)。
公式 4-19:k
类的 Softmax 得分
$$ s_k(\mathbf{x})= \theta^T \cdot \mathbf{x} $$
注意,每个类都有自己独一无二的参数向量 $\theta_k$。 所有这些向量通常作为行放在参数矩阵 $\Theta$ 中。
一旦你计算了样本 $\mathbf{x}$ 的每一类的得分,你便可以通过Softmax
函数(公式 4-20)估计出样本属于第 $k$ 类的概率 $\hat{p}_k$:通过计算 $e$ 的 $s_k(\mathbf{x})$ 次方,然后对它们进行归一化(除以所有分子的总和)。
公式 4-20:Softmax 函数
$$ \hat{p_k}=\sigma{(\mathbf{s}(\mathbf{x}))}k= \frac{exp\left(s_k(\mathbf{x})\right)} {\sum_{j=1}^{K}exp\left(s_j(\mathbf{x})\right)} $$
$K$ 表示有多少类
$\mathbf{s}(\mathbf{x})$ 表示包含样本 $\mathbf{x}$ 每一类得分的向量
$\sigma{(\mathbf{s}(\mathbf{x}))_k}$ 表示给定每一类分数之后,实例 $\mathbf{x}$ 属于第 $k$ 类的概率
和 Logistic 回归分类器一样,Softmax 回归分类器将估计概率最高(它只是得分最高的类)的那类作为预测结果,如公式 4-21 所示。
公式 4-21:Softmax 回归模型分类器预测结果
$$ \hat{y}=argmax\ \sigma{(\mathbf{s}(\mathbf{x}))_k}=argmax \ s_k(\mathbf{x})=argmax \ \left( \theta_k^T \cdot \mathbf{x}\right) $$
argmax
运算返回一个函数取到最大值的变量值。 在这个等式,它返回使 $\sigma{(\mathbf{s}(\mathbf{x}))_k}$ 最大时的 $k$ 的值
注意
Softmax 回归分类器一次只能预测一个类(即它是多类的,但不是多输出的),因此它只能用于判断互斥的类别,如不同类型的植物。 你不能用它来识别一张照片中的多个人。
现在我们知道这个模型如何估计概率并进行预测,接下来将介绍如何训练。我们的目标是建立一个模型在目标类别上有着较高的概率(因此其他类别的概率较低),最小化公式 4-22 可以达到这个目标,其表示了当前模型的损失函数,称为交叉熵,当模型对目标类得出了一个较低的概率,其会惩罚这个模型。 交叉熵通常用于衡量待测类别与目标类别的匹配程度(我们将在后面的章节中多次使用它)
公式 4-22:交叉熵
$$ J(\Theta)=-\frac{1}{m}\sum\limits_{i=1}^m\sum\limits_{k=1}^Ky_k^{(i)}log\left(\hat{p}_k^{(i)}\right) $$
如果对于第 $i$ 个实例的目标类是 $k$,那么 $y_k^{(i)}=1$,反之 $y_k^{(i)}=0$。
可以看出,当只有两个类($K=2$)时,此损失函数等同于 Logistic 回归的损失函数(对数损失;请参阅公式 4-17)。
交叉熵
交叉熵源于信息论。假设你想要高效地传输每天的天气信息。如果有八个选项(晴天,雨天等),则可以使用3位对每个选项进行编码,因为 $2^3=8$。但是,如果你认为几乎每天都是晴天,更高效的编码“晴天”的方式是:只用一位(0)。剩下的七项使用四位(从 1 开始)。交叉熵度量每个选项实际发送的平均比特数。 如果你对天气的假设是完美的,交叉熵就等于天气本身的熵(即其内部的不确定性)。 但是,如果你的假设是错误的(例如,如果经常下雨)交叉熵将会更大,称为 Kullback-Leibler 散度(KL 散度)。
两个概率分布 $p$ 和 $q$ 之间的交叉熵定义为:$H(p,q)=-\sum_xp(x)\log q(x)$(分布至少是离散的)
这个损失函数关于 $\theta_k$ 的梯度向量为公式 4-23:
公式 4-23:k
类交叉熵的梯度向量
$$ \nabla_{\theta_k}J(\Theta)=\frac{1}{m}\sum\limits_{i=1}^m\left(\hat{p}_k^{(i)}-y_k^{(i)}\right)\mathbf{x}^{(i)} $$
现在你可以计算每一类的梯度向量,然后使用梯度下降(或者其他的优化算法)找到使得损失函数达到最小值的参数矩阵 $\Theta$。
让我们使用 Softmax 回归对三种鸢尾花进行分类。当你使用LogisticRregression
对模型进行训练时,Scikit Learn 默认使用的是一对多模型,但是你可以设置multi_class
参数为“multinomial”来把它改变为 Softmax 回归。你还必须指定一个支持 Softmax 回归的求解器,例如“lbfgs”求解器(有关更多详细信息,请参阅 Scikit-Learn 的文档)。其默认使用 $\ell_12$ 正则化,你可以使用超参数 $C$ 控制它。
X = iris["data"][:, (2, 3)] # petal length, petal width
y = iris["target"]
softmax_reg = LogisticRegression(multi_class="multinomial",solver="lbfgs", C=10)
softmax_reg.fit(X, y)
所以下次你发现一个花瓣长为 5 厘米,宽为 2 厘米的鸢尾花时,你可以问你的模型你它是哪一类鸢尾花,它会回答 94.2% 是 Virginica 花(第二类),或者 5.8% 是其他鸢尾花。
>>> softmax_reg.predict([[5, 2]])
array([2])
>>> softmax_reg.predict_proba([[5, 2]])
array([[ 6.33134078e-07, 5.75276067e-02, 9.42471760e-01]])
图 4-25 用不同背景色表示了结果的决策边界。注意,任何两个类之间的决策边界是线性的。 该图的曲线表示 Versicolor 类的概率(例如,用 0.450 标记的曲线表示 45% 的概率边界)。注意模型也可以预测一个概率低于 50% 的类。 例如,在所有决策边界相遇的地方,所有类的估计概率相等,分别为 33%。