逻辑回归的数学模型

基本模型

逻辑回归主要用于处理二分类问题。二分类问题对于模型的输出包含 0 和 1,是一个不连续的值。分类问题的结果一般不能由线性函数求出。这里就需要一个特别的函数来求解,这里引入一个新的函数 Sigmoid 函数,也成为逻辑函数。
$$
h_\theta(x) = g(\theta^Tx) \
z = \theta^Tx \
g(z) = \frac{1}{1 + e^{-z}}
$$
这里函数 $g(z)$ 将任何实数映射到了 $(0, 1)$ 区间中,从而将任何值函数转换为适合分类的函数。这里我们将线性回归模型函数插入到这个函数中形成新的逻辑回归模型。

图 1 Sigmoid 函数

如图所示,转换后可以看到在 $x = 0$ 处有一个明显的变化,两边的函数值无限接近于 0 和 1,而中间的交界处则根据输出来判断如何分类,例如 $h_\theta(x) = 0.7$ 则表示有 70% 的概率输出为 1。

决策边界

决策边界(Decision boundary)即为输出的分界点。二分类问题的输出是离散的零一分类,也就是说:
$$
h_\theta(x) \ge 0.5 \rarr y = 1 \
h_\theta(x) < 0.5 \rarr y = 0
$$
那么此处由 Sigmoid 函数的性质可以得到:
$$
\theta^T x \ge 0 \Rightarrow y = 1 \
\theta^T x < 0 \Rightarrow y = 0
$$
那么此处根据输入 $x$ 来判断输出从当前值跳变到另一个值的边界,即为决策边界。在上面 Sigmoid 函数的实例图中,假设输入函数仅是简单的 $z = x$,并且认为当 $h_\theta(x) \ge 0.5$ 时,输出 $y = 1$,那么可以看到,$x = 0$​ 即为其决策边界。

在更复杂的情况下,假设

$$
\theta^T x = \theta_0 + \theta_1 x_1 + \theta_2 x_2
$$

那么通过变形可得到
$$
\theta_0 + \theta_1 \cdot x = - \theta_2 \cdot y \
y = \frac{\theta_0 + \theta_1 \cdot x}{\theta_2}
$$

代价函数

根据模型的代价函数(Cost function)即可根据对当前参数的评估最后找到最优解,逻辑回归的代价函数定义为:
$$
J(\theta) = \frac{1}{m}\sum^m_{i = 1}\mathrm{Cost}(h_\theta(x^{(i)}), y^{(i)}) \
\mathrm{Cost}(h_\theta(x), y) = -\log(h_\theta(x)) & \text{if } y = 1 \
\mathrm{Cost}(h_\theta(x), y) = -\log(1 - h_\theta(x)) & \text{if } y = 0 \
$$

图 2 Sigmoid 的损失函数

这里可以看出,当 $y = 1 \text{ and } h_\theta(x) \rarr 0$ 时,损失函数的值会趋向于无穷,可以直观看到损失函数对模型预测与实际值的差距评估。机器学习的主要目标就是要将损失函数降到最低,以求得最优模型。

梯度下降

通过梯度下降(Gradient descent)找到最优解,首先将代价函数转化为如下形式。不难看出在某一情况时,另一种情况会被化为 0,这样做的目的是方便编程:
$$
\mathrm{Cost}(h_\theta(x), y) = - y \log(\theta(x)) - (1 - y) \log(1 - h_\theta(x))
$$
那么整个代价函数如下:
$$
J(\theta) = -\frac{1}{m}\sum_{i = 1}^{m}[y^{(i)}\log(h_\theta(x^{(i)})) + (1 - y^{(i)}) \log(1 - h\theta(x^{(i)}))]
$$
则可以求出梯度下降迭代的步骤:
$$
\theta_j := \theta_j - \alpha\frac{\partial{J(\theta)}}{\partial{\theta_j}} \
\text{即 } \theta_j := \theta_j - \frac{\alpha}{m}\sum^m_{i = 1}(h_\theta(x^{(i)}) - y^{(i)})x_j^{(i)}
$$

Sklearn 逻辑回归模型

数据整理

假设有一份学生的成绩单和大学录取的名单,学生们通过两门考试的两门分数来被决定是否被录取。这是一个两个特征的二分类问题,首先整理一下数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
data = pd.read_csv('ex2data1.txt', names=['exam1', 'exam2', 'is_admitted'])
print(data.head())

# 将数据拆分成是否录取的两批,绘制散点
positive = data[data['is_admitted'] == 1]
negative = data[data['is_admitted'] == 0]

fig, ax = plt.subplots(figsize=(12, 8))
ax.scatter(positive['exam1'], positive['exam2'], s=50, c='b', alpha=0.5, label='Admitted')
ax.scatter(negative['exam1'], negative['exam2'], s=50, c='r', alpha=0.5, label='Not Admitted')
ax.legend()
ax.set_xlabel('Exam 1 Score')
ax.set_ylabel('Exam 2 Score')
plt.show()
1
2
3
4
5
6
       exam1      exam2  admitted
0 34.623660 78.024693 0
1 30.286711 43.894998 0
2 35.847409 72.902198 0
3 60.182599 86.308552 1
4 79.032736 75.344376 1
图 3 数据预览

逻辑回归模型

这里将从上面读取的数据传递给定义的逻辑回归的模型,并训练得到模型参数。

1
2
3
4
5
6
7
8
9
X = data[['exam1', 'exam2']].values
Y = data['is_admitted'].values

# 定义并训练模型
model = LogisticRegression()
model.fit(X, Y)

print("Model Coefficients:", model.coef_)
print("Intercept:", model.intercept_)
1
2
Model Coefficients: [[0.20535491 0.2005838 ]]
Intercept: [-25.05219314]

验证

验证模型的准确性,首先从模型中取出相关参数,即为 $\theta$ 。这里需要说明一下数学模型中与 Sklearn 逻辑回归模型的属性,首先求出决策边界:
$$
y = \frac{\theta_0 + \theta_1 \cdot x}{\theta_2}
$$
这里 $\theta_0$ 为偏置,$\theta_1$ 和 $\theta_2$ 是每个特征的系数。两者分别对应了两个属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
coef = model.coef_[0]
intercept = model.intercept_[0]
x = np.linspace(30, 100, 1000)
y = -(coef[0] * x + intercept) / coef[1]

fig, ax = plt.subplots(figsize=(12, 8))
ax.scatter(positive['exam1'], positive['exam2'], s=50, c='b', alpha=0.5, label='Admitted')
ax.scatter(negative['exam1'], negative['exam2'], s=50, c='r', alpha=0.5, label='Not Admitted')
ax.plot(x, y, label='Decision Boundary', c='grey')
ax.legend()
ax.set_xlabel('Exam 1 Score')
ax.set_ylabel('Exam 2 Score')
plt.show()

最后可以看出决策边界较好的分割了两类点集。

图 4 决策边界

线性回归的数学模型

假设单变量回归模型:
$$
h_\theta(x) = \theta^T x = \theta_0 + \theta_1 x_1
$$
这里的 $\theta_0$ 就是偏置,而 $\theta_1$ 就是权重,而 $x_1$ 就是特征。

线性回归方程的代价函数为:
$$
J(\theta) = \frac{1}{2m} \sum^{m}{i = 1}(h_\theta(x^{(i)}) - y^{(i)})^2
$$
这里根据代价函数来计算当前权重与偏置得到的方程与实际情况差距有多大,线性回归问题就是要通过一定方法找到最合适的参数来构造这个方程,也就是找到合适的 $\theta_0$ 和 $\theta_1$ 来使代价函数的值最小,也就是最符合实际情况。

一般来说通过梯度下降的方法来让参数不断接近最优解:
$$
\theta_j:= \theta_j - \alpha \frac{1}{m}\sum^m_{i = 1}(h_\theta(x^{i}) - y^{(i)})x_j^{(i)}
$$
每次跟新都会根据代价函数来更改参数,代价函数在这里反应了与最优解的差距。而式中 $\alpha$ 为学习率,也就是每次更新的幅度。学习率过大会导致下降可能会跳过最优解甚至导致当前值与最优解越来越远,太小会导致循环次数过多靠近最优解的速度太慢。
cost_function

图 1 代建函数与两个参数的关系

当然,在这里我们使用 python scikit-learn 库来构建线性回归模型,不需要完全读懂数学模型。只需要理解这个模型需要进行什么操作得到最终结果即可。

Sklearn 单变量线性回归

假设有一份食品供应商的数据,包含了城市的人口(population)和在这个城市的利润(profit),需要以人口作为特征来预测在某个城市的利润。

数据处理

假设数据保存在本地的 ex1data1.txt 中并以 csv 文件格式保存,那么需要首先用 pandas 读取数据。

1
2
3
4
5
import numpy as np
import pandas as pd

data = pd.read_csv('ex1data1.txt', header=none, names=['population', 'profit'])
print(data.head())

查看到数据的头部信息:

1
2
3
4
5
6
   population   profit
0 6.1101 17.5920
1 5.5277 9.1302
2 8.5186 13.6620
3 7.0032 11.8540
4 5.8598 6.8233

然后需要将特征和答案传入 Sklearn 线性回归模型,需要注意的是,这里是单变量回归,而一般的线性回归模型接受的参数是一个特征矩阵,那么这里需要将一维的特征向量转换成矩阵形式才行。

1
2
X = data['population'].values.reshape(-1, 1) # 重构特征的数据形状
y = data['profit'].values

构建模型

首先利用构造函数初始化一个线性回归模型对象 model ,然后利用现有的训练数据训练模型。

1
2
3
4
from sklearn.linear_model import LinearRegression

model = LinearRegression()
model.fit(X, y) # 输入训练数据训练模型

验证

从模型中取出最终训练后的参数:

1
2
3
slope = model.coef_[0]
intercept = model.intercept_
print(f"Linear model equations: y = {slope:.2f}x + {intercept:.2f}")

查看最终的模型:

1
Linear model equations: y = 1.19x + -3.90

通过取出的参数来绘制图形,查看模型的拟合效果是否良好:

1
2
3
4
5
6
import matplotlib.pyplot as plt

data.plot(kind='scatter', x='population', y='profit', label='Data')
plt.plot(X, model.predict(X), color='red', label='Linear Fit')
plt.legend()
plt.show()
图 2 线性回归模型的拟合效果

Sklearn 多变量线性回归

多变量回归模型的构建大同小异,也是一样的步骤。这里假设有数据 ex1data2.txt,数据包含了房屋面积,卧室数量和房屋的价格,以前两者为特征来预测房屋价格。

数据处理与训练

首先读取数据:

1
2
data = pd.read_csv('ex1data2.txt', header=None, names=['area', 'bedrooms', 'price'])
print(data.head())
1
2
3
4
5
6
   area  bedrooms   price
0 2104 3 399900
1 1600 3 329900
2 2400 3 369000
3 1416 2 232000
4 3000 4 539900

训练模型

1
2
3
4
5
X = data[['area', 'bedrooms']].values
y = data['price'].values

model = LinearRegression()
model.fit(X, y)

验证

查看训练后的模型的参数:

1
2
3
4
5
coefficients = model.coef_
intercept = model.intercept_

print(f"Linear model equation: y = {coefficients[0]:.2f} * area + {coefficients[1]:.2f} * bedrooms + {intercept:.2f}")

1
Linear model equation: y = 139.21 * area + -8738.02 * bedrooms + 89597.91

这里如果需要画图,则需要绘制一个三维图片,因为此处的特征是二维的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')

ax.scatter(data['area'], data['bedrooms'], data['price'], color='blue')

area_range = np.linspace(data['area'].min(), data['area'].max(), 100)
bedrooms_range = np.linspace(data['bedrooms'].min(), data['bedrooms'].max(), 100)
area_grid, bedrooms_grid = np.meshgrid(area_range, bedrooms_range)

# 使用模型的预测方法
price_grid = model.predict(np.c_[area_grid.ravel(), bedrooms_grid.ravel()]).reshape(area_grid.shape)

ax.plot_surface(area_grid, bedrooms_grid, price_grid, color='red', alpha=0.5, rstride=100, cstride=100)

ax.set_xlabel('Area')
ax.set_ylabel('Bedrooms')
ax.set_zlabel('Price')
ax.legend()
plt.show()
图 3 多变量模型的拟合效果
0%