逻辑回归:一个二分类器

一个用于二分类任务的逻辑回归类。

> 从 mlxtend.classifier 导入 LogisticRegression

概述

感知器Adaline 相关,逻辑回归模型是一个用于二分类的线性模型。然而,与 Adaline 中最小化平方误差和(SSE)这样的线性成本函数不同,我们最小化的是一个 sigmoid 函数,即 logit 函数:

$$\phi(z) = \frac{1}{1 + e^{-z}},$$

其中 $z$ 定义为净输入

$$z = w_0x_0 + w_1x_1 + ... + w_mx_m = \sum_{j=0}^{m} w_j x_j= \mathbf{w}^T\mathbf{x}.$$

净输入又基于 logit 函数

$$logit(p(y=1 \mid \mathbf{x})) = z.$$

这里,$p(y=1 \mid \mathbf{x})$ 是给定特征 $\mathbf{x}$ 一个特定样本属于类 1 的条件概率。logit 函数接收在 [0, 1] 范围内的输入,并将其转换为整个实数范围的值。相反,logistic 函数接受整个实数范围的输入值,并将其转换为 [0, 1] 范围内的值。换句话说,logistic 函数是 logit 函数的反函数,它使我们能够预测某个特定样本属于类 1(或类 0)的条件概率。

在模型拟合后,条件概率 $p(y=1 \mid \mathbf{x})$ 通过阈值函数 $g(\cdot)$ 转换为二进制类别标签:

$$y = g({z}) = \begin{cases} 1 & \text{如果 $\phi(z) \ge 0.5$}\ 0 & \text{否则.} \end{cases} $$

或者等效地:

$$y = g({z}) = \begin{cases} 1 & \text{如果 z $\ge$ 0}\ 0 & \text{否则.} \end{cases} $$

目标函数 -- 对数似然函数

为了参数化逻辑回归模型,我们最大化似然函数 $L(\cdot)$(或最小化逻辑成本函数)。

我们将似然函数写为

$$L(\mathbf{w}) = P(\mathbf{y} \mid \mathbf{x};\mathbf{w}) = \prod_{i=1}^{n} P\big(y^{(i)} \mid x^{(i)}; \mathbf{w}\big) = \prod^{n}_{i=1}\bigg(\phi\big(z^{(i)}\big)\bigg)^{y^{(i)}} \bigg(1-\phi\big(z^{(i)}\big)\bigg)^{1-y^{(i)}},$$

在训练样本彼此独立的假设下。

在实践中,最大化这个方程的(自然)对数更为容易,这被称为对数似然函数:

$$l(\mathbf{w}) = \log L(\mathbf{w}) = \sum^{n}_{i=1} y^{(i)} \log \bigg(\phi\big(z^{(i)}\big)\bigg) + \big( 1 - y^{(i)}\big) \log \big(1-\phi\big(z^{(i)}\big)\big)$$

采取对数的一个好处是可以避免在非常小的似然值下发生数值下溢(以及浮点数学的挑战)。另一个好处是我们可以更容易地获得导数,使用加法技巧将因子的乘积重写为求和项,然后可以利用诸如梯度上升法等优化算法对其进行最大化。

目标函数 -- 逻辑成本函数

一个最大化对数似然的替代方案是定义一个需要最小化的代价函数 $J(\cdot)$;我们可以将对数似然重写为:

$$J(\mathbf{w}) = \sum_{i=1}^{m} - y^{(i)} \log \bigg( \phi\big(z^{(i)}\big) \bigg) - \big(1 - y^{(i)}\big) \log\bigg(1-\phi\big(z^{(i)}\big)\bigg)$$

$$ J\big(\phi(z), y; \mathbf{w}\big) =\begin{cases} -\log\big(\phi(z) \big) & \text{如果 $y = 1$}\ -\log\big(1- \phi(z) \big) & \text{如果 $y = 0$} \end{cases} $$

正如我们在上面的图中所看到的,我们对错误预测施加越来越大的惩罚成本。

梯度下降(GD)和随机梯度下降(SGD)优化

梯度上升与对数似然函数

通过基于梯度的优化来学习逻辑回归模型的权重系数,我们计算对数似然函数关于第 j 个权重的偏导数,具体如下:

$$\frac{\partial}{\partial w_j} l(\mathbf{w}) = \bigg(y \frac{1}{\phi(z)} - (1-y) \frac{1}{1-\phi{(z)}} \bigg) \frac{\partial}{\partial w_j}\phi(z)$$

作为一个中间步骤,我们计算sigmoid函数的偏导数,这将在后面派上用场:

\begin{align}
&\frac{\partial}{\partial z} \phi(z) = \frac{\partial}{{\partial z}} \frac{1}{1+e^{-z}} \\\\
&= \frac{1}{(1 + e^{-z})^{2}} e^{-z}\\\\
&= \frac{1}{1+e^{-z}} \bigg(1 - \frac{1}{1+e^{-z}} \bigg)\\\\
&= \phi(z)\big(1-\phi(z)\big)
\end{align}

现在,我们将 $$\frac{\partial}{\partial z} \phi(z) = \phi(z) \big(1 - \phi(z)\big)$$ 重新代入对数似然偏导数方程中,并得到下面所示的方程:

\begin{align} & \bigg(y \frac{1}{\phi{(z)}} - (1 - y) \frac{1}{1 - \phi(z)} \bigg) \frac{\partial}{\partial w_j} \phi(z) \\ &= \bigg(y \frac{1}{\phi{(z)}} - (1 - y) \frac{1}{1 - \phi(z)} \bigg) \phi(z) \big(1 - \phi(z)\big) \frac{\partial}{\partial w_j}z\\ &= \big(y(1-\phi(z)\big) - (1 - y) \phi(z)\big)x_j\\ &=\big(y - \phi(z)\big)x_j \end{align}

现在,为了找到模型的权重,我们沿着梯度的正方向采取一个与之成比例的步骤,以最大化对数似然。此外,我们将一个系数,即学习率 $\eta$ 添加到权重更新中:

\begin{align} & w_j := w_j + \eta \frac{\partial}{\partial w_j} l(\mathbf{w})\\ & w_j := w_j + \eta \sum^{n}_{i=1} \big( y^{(i)} - \phi\big(z^{(i)}\big)\big)x_j^{(i)} \end{align}

注意,与随机梯度上升/下降相比,梯度(和权重更新)是从训练集中的所有样本中计算得出的。有关梯度下降和随机梯度下降之间差异的更多信息,请参见相关文章 梯度下降与随机梯度下降

前一个方程显示了单个权重 $j$ 的权重更新。在基于梯度的优化中,所有权重系数同时更新;权重更新可以更简洁地写为

$$\mathbf{w} := \mathbf{w} + \Delta\mathbf{w},$$ 其中

$$\Delta{\mathbf{w}} = \eta \nabla l(\mathbf{w})$$

梯度下降法与逻辑代价函数

在前一节中,我们推导了对数似然函数的梯度,可以通过梯度上升进行优化。同样,我们可以获得逻辑代价函数 $J(\cdot)$ 的代价值梯度,并通过梯度下降来最小化它,以便学习逻辑回归模型。

单个权重的更新规则:

\begin{align} & \Delta{w_j} = -\eta \frac{\partial J}{\partial w_j} \ & = - \eta \sum_{i=1}^{n}\big(y^{(i)} - \phi\big(z^{(i)}\big) x^{(i)} \big) \end{align}

同时的权重更新:

$$\mathbf{w} := \mathbf{w} + \Delta\mathbf{w}$$

其中

$$\Delta{\mathbf{w}} = - \eta \nabla J(\mathbf{w}).$$

洗牌

随机洗牌实现如下:

正则化

作为解决过拟合的一种方法,我们可以通过正则化项向逻辑回归模型添加额外的偏差。通过L2正则化项,我们通过惩罚大的权重系数来减少模型的复杂性:

$$L2: \frac{\lambda}{2}\lVert \mathbf{w} \lVert_2 = \frac{\lambda}{2} \sum_{j=1}^{m} w_j^2$$

$$L2: \frac{\lambda}{2}\lVert \mathbf{w} \lVert_2 = \frac{\lambda}{2} \sum_{j=1}^{m} w_j^2$$

为了应用正则化,我们只需要将正则化项添加到我们为逻辑回归定义的成本函数中,以缩小权重:

$$J(\mathbf{w}) = \sum_{i=1}^{m} \Bigg[ - y^{(i)} \log \bigg( \phi\big(z^{(i)}\big) \bigg) - \big(1 - y^{(i)}\big) \log\bigg(1-\phi\big(z^{(i)}\big)\bigg) \Bigg] + \frac{\lambda}{2} \sum_{j=1}^{m} w_j^2$$

单个权重的更新规则:

\begin{align} & \Delta{w_j} = -\eta \bigg( \frac{\partial J}{\partial w_j} + \lambda w_j\bigg)\ & = - \eta \sum_{i=1}^{n}\big(y^{(i)} - \phi\big(z^{(i)}\big) x^{(i)} \big) - \eta \lambda w_j \end{align}

同时的权重更新:

$$\mathbf{w} := \mathbf{w} + \Delta\mathbf{w}$$

其中

$$\Delta{\mathbf{w}} = - \eta \big( \nabla J(\mathbf{w}) + \lambda \mathbf{w}\big).$$

有关正则化的更多信息,请参见 广义线性模型的正则化

参考文献

示例 1 - 梯度下降

from mlxtend.data import iris_data
from mlxtend.plotting import plot_decision_regions
from mlxtend.classifier import LogisticRegression
import matplotlib.pyplot as plt

# 加载数据

X, y = iris_data()
X = X[:, [0, 3]] # 萼片长度和花瓣宽度
X = X[0:100] # 0类和1类
y = y[0:100] # 0类和1类

# 标准化
X[:,0] = (X[:,0] - X[:,0].mean()) / X[:,0].std()
X[:,1] = (X[:,1] - X[:,1].mean()) / X[:,1].std()

lr = LogisticRegression(eta=0.1, 
                        l2_lambda=0.0, 
                        epochs=100,
                        minibatches=1, # 对于梯度下降
                        random_seed=1,
                        print_progress=3)
lr.fit(X, y)

plot_decision_regions(X, y, clf=lr)
plt.title('Logistic Regression - Gradient Descent')
plt.show()

plt.plot(range(len(lr.cost_)), lr.cost_)
plt.xlabel('Iterations')
plt.ylabel('Cost')
plt.show()

Iteration: 100/100 | Cost 0.32 | Elapsed: 0:00:00 | ETA: 0:00:00

png

png

预测类别标签

y_pred = lr.predict(X)
print('Last 3 Class Labels: %s' % y_pred[-3:])

Last 3 Class Labels: [1 1 1]

预测类别概率

y_pred = lr.predict_proba(X)
print('Last 3 Class Labels: %s' % y_pred[-3:])

Last 3 Class Labels: [ 0.99997968  0.99339873  0.99992707]

示例 2 - 随机梯度下降

from mlxtend.data import iris_data
from mlxtend.plotting import plot_decision_regions
from mlxtend.classifier import LogisticRegression
import matplotlib.pyplot as plt

# 加载数据

X, y = iris_data()
X = X[:, [0, 3]] # 萼片长度和花瓣宽度
X = X[0:100] # 0类和1类
y = y[0:100] # 0类和1类

# 标准化
X[:,0] = (X[:,0] - X[:,0].mean()) / X[:,0].std()
X[:,1] = (X[:,1] - X[:,1].mean()) / X[:,1].std()

lr = LogisticRegression(eta=0.5, 
                        epochs=30, 
                        l2_lambda=0.0, 
                        minibatches=len(y), # 对于随机梯度下降学习 
                        random_seed=1,
                        print_progress=3)
lr.fit(X, y)

plot_decision_regions(X, y, clf=lr)
plt.title('Logistic Regression - Stochastic Gradient Descent')
plt.show()

plt.plot(range(len(lr.cost_)), lr.cost_)
plt.xlabel('Iterations')
plt.ylabel('Cost')
plt.show()

Iteration: 30/30 | Cost 0.27 | Elapsed: 0:00:00 | ETA: 0:00:00

png

png

示例 3 - 使用小批量的随机梯度下降

在这里,我们将minibatches设置为5,这将导致小批量学习,批量大小为20个样本(因为100个鸢尾花样本除以5个小批量等于20)。

from mlxtend.data import iris_data
from mlxtend.plotting import plot_decision_regions
from mlxtend.classifier import LogisticRegression
import matplotlib.pyplot as plt

# 加载数据

X, y = iris_data()
X = X[:, [0, 3]] # 萼片长度和花瓣宽度
X = X[0:100] # 0类和1类
y = y[0:100] # 0类和1类

# 标准化
X[:,0] = (X[:,0] - X[:,0].mean()) / X[:,0].std()
X[:,1] = (X[:,1] - X[:,1].mean()) / X[:,1].std()

lr = LogisticRegression(eta=0.5, 
                        epochs=30, 
                        l2_lambda=0.0, 
                        minibatches=5, # 100/5 = 20 -> 小批次大小 
                        random_seed=1,
                        print_progress=3)
lr.fit(X, y)

plot_decision_regions(X, y, clf=lr)
plt.title('Logistic Regression - Stochastic Gradient Descent')
plt.show()

plt.plot(range(len(lr.cost_)), lr.cost_)
plt.xlabel('Iterations')
plt.ylabel('Cost')
plt.show()

Iteration: 30/30 | Cost 0.25 | Elapsed: 0:00:00 | ETA: 0:00:00

png

png

API

LogisticRegression(eta=0.01, epochs=50, l2_lambda=0.0, minibatches=1, random_seed=None, print_progress=0)

Logistic regression classifier.

Note that this implementation of Logistic Regression expects binary class labels in {0, 1}.

Parameters

Attributes

Examples

For usage examples, please see https://rasbt.github.io/mlxtend/user_guide/classifier/LogisticRegression/

Methods


fit(X, y, init_params=True)

Learn model from training data.

Parameters

Returns


predict(X)

Predict targets from X.

Parameters

Returns


predict_proba(X)

Predict class probabilities of X from the net input.

Parameters

Returns


score(X, y)

Compute the prediction accuracy

Parameters

Returns