更新了机器学习部分的文档
This commit is contained in:
parent
af045f6493
commit
cdccb69e24
|
|
@ -1,2 +1,281 @@
|
|||
## 朴素贝叶斯算法
|
||||
|
||||
我们继续为大家介绍解决分类任务的算法,本章介绍一种概率模型贝叶斯分类器。贝叶斯分类器是一类分类算法的总称,这类算法均以贝叶斯定理为基础,因而统称为贝叶斯分类器。在介绍贝叶斯定理之前,我们先讲一个故事:从 2015 年到 2020 年期间,某位李姓女士凭借自己对航班是否会延误的分析,购买了大约 900 次飞机延误险并获得延误赔偿,累计获得理赔金高达 300 多万元,真可谓“航班延误,发家致富”。当然,这套骚操作本身不是我们探讨的重点,我们的问题是:李女士是怎么决定要不要购买延误险的呢?航班延误最主要的原因就是天气(包括起飞地和降落地的天气)、机场(起飞机场和降落机场)和航司,由于李女士有过航空服务类工作的经历,有获得机场和航司相关数据的途径(天气数据相对更容易获取),集齐相关的数据再利用贝叶斯定理,她可以能够计算出当前航班延误的概率并决定是否购买延误险。接下来,李女士通过虚构不同身份购票并大量投保(每个身份购买 30 到 40 份延误险),这样一旦航班延误,她就可以向保险公司进行索赔。那么,我们要探讨的就是贝叶斯定理是如何利用现有数据计算出航班延误的概率。
|
||||
|
||||
### 贝叶斯定理
|
||||
|
||||
贝叶斯定理是概率论中的一个重要定理,它描述了如何从主观经验或已知事实出发,通过收集到的样本数据(证据)来更新对事件发生概率的认知(信念)。贝叶斯定理的数学表达式为:
|
||||
$$
|
||||
P(A|B) = \frac{P(B|A)}{P(B)} \cdot P(A)
|
||||
$$
|
||||
其中,$\small{P(A)}$是事件$\small{A}$发生的**先验概率**,我们可以理解为已知事实或主观经验(**主观概率**);$\small{P(B|A)}$是在事件$\small{A}$发生的条件下事件$\small{B}$发生的 条件概率,通常也称之为**似然性**(likelihood),$\small{P(B)}$是事件$\small{B}$发生的(全)概率,这两个概率可以通过我们收集到的样本数据(证据)获得;$\small{P(A|B)}$是在事件$\small{B}$发生的条件下事件$\small{A}$发生的条件概率,即收集到样本数据后对事件$\small{A}$发生概率的重新认知,称之为**后验概率**。贝叶斯定理告诉我们一个重要的事实:可以从已知的事实或主观经验出发,通过收集到的证据来更新我们对某个事件发生概率的认知,简单的说就是**可以通过已知的事实和收集的证据来推断出未知的真相**。
|
||||
|
||||
回到上面李女士购买飞机延误险的例子,假设本次航班是从成都双流国际机场飞往北京首都国际机场,执飞的航空公司是四川航空,起飞地天气为雨天(小雨),温度为8°C,东北风2级,降落地天气为晴天,温度4°C,西北风2级。为了更简单的让大家理解贝叶斯定理,我们对这里的条件稍作简化,只保留天气中的降水信息,暂不考虑温度和风速等其他因素,对应到上面的贝叶斯定理有:
|
||||
$$
|
||||
P(延误|起飞机场=双流,到达机场=首都,起飞天气=小雨,降落天气=晴天,执飞航司=川航) = \\
|
||||
\frac{P(起飞机场=双流,到达机场=首都,起飞天气=小雨,降落天气=晴天,执飞航司=川航|延误)}{P(起飞机场=双流,到达机场=首都,起飞天气=小雨,降落天气=晴天,执飞航司=川航)} \cdot P(延误)
|
||||
$$
|
||||
上面公式等号左边就是李女士想知道的当前航班延误的概率,等号右边的部分其实就是历史数据和当前信息,计算这个概率的关键在于计算出似然性,即$\small{P(起飞机场=双流,到达机场=首都,起飞天气=小雨,降落天气=晴天,执飞航司=川航|延误)}$到底是多少,那么这个条件概率又该如何计算呢?
|
||||
|
||||
### 朴素贝叶斯
|
||||
|
||||
朴素贝叶斯算法是基于贝叶斯定理和特征条件独立性假设的分类算法,因其简单高效而受到广泛应用。朴素贝叶斯算法的关键在于“朴素”二字,就是刚才我们说到特征条件独立性假设,条件独立性假设是说用于分类的特征在类确定的条件下都是独立的,该假设使得朴素贝叶斯的学习成为可能。
|
||||
|
||||
假设我们有一个特征集合$\small{X = \{x_1, x_2, \ldots, x_n\}}$和一个类别$\small{C}$,朴素贝叶斯算法假设:
|
||||
|
||||
$$
|
||||
P(X|C) = P(x_1|C) \cdot P(x_2|C) \cdot \ldots \cdot P(x_n|C)
|
||||
$$
|
||||
这个假设大大简化了计算复杂性,使得我们可以只计算每个特征在给定类别下的概率,而不需要考虑特征之间的相互作用,对应到上面购买飞机延误险的例子,我们可以用下面的方式来计算似然性:
|
||||
$$
|
||||
P(起飞机场=双流,到达机场=首都,起飞天气=小雨,降落天气=晴天,执飞航司=川航|延误) = \\
|
||||
P(起飞机场=双流|延误) \times P(到达机场=首都|延误) \times P(起飞天气=小雨|延误) \times P(降落天气=晴天|延误) \times P(执飞航司=川航|延误)
|
||||
$$
|
||||
|
||||
|
||||
### 算法原理
|
||||
|
||||
#### 训练阶段
|
||||
|
||||
在训练阶段,朴素贝叶斯算法首先需要计算每个类别的先验概率和每个特征在各个类别下的条件概率。
|
||||
|
||||
1. **计算先验概率**:
|
||||
$$
|
||||
P(C_{i}) = \frac{n_{i}}{n}
|
||||
$$
|
||||
其中,$\small{C_{i}}$表示类别,$\small{n_{i}}$是类别$\small{C_{i}}$的样本数量,$\small{n}$是总的样本容量。
|
||||
|
||||
2. **计算条件概率**:
|
||||
$$
|
||||
P(x_{j}|C_{i}) = \frac{n_{i,j}}{n_{i}}
|
||||
$$
|
||||
其中,$\small{n_{i,j}}$是在类别$\small{C_{i}}$中,特征$\small{x_{j}}$出现的次数。
|
||||
|
||||
#### 预测阶段
|
||||
|
||||
在预测阶段,给定一个待分类样本$\small{X}$,朴素贝叶斯算法通过以下步骤来计算其属于每个类别的后验概率:
|
||||
$$
|
||||
P(C_{i}|X) = \frac{P(X|C_{i})}{P(X)} \cdot P(C_{i})
|
||||
$$
|
||||
|
||||
上面的公式中,$\small{P(X)}$对应到每个类别都是一个常量,可以忽略掉它,再结合独立性假设有:
|
||||
$$
|
||||
P(C_{i}|X) \propto P(C_{i}) \cdot P(x_1|C_{i}) \cdot P(x_2|C_{i}) \cdot \ldots \cdot P(x_n|C_{i})
|
||||
$$
|
||||
这样,我们可以选择具有最高后验概率的类别作为预测结果。
|
||||
|
||||
#### 代码实现
|
||||
|
||||
我们还是以鸢尾花数据集为例,按照上面的讲解的原理,用 NumPy 来实现一个朴素贝叶斯分类器,我们还是从加载数据开始,代码如下所示。
|
||||
|
||||
```python
|
||||
from sklearn.datasets import load_iris
|
||||
from sklearn.model_selection import train_test_split
|
||||
|
||||
iris = load_iris()
|
||||
X, y = iris.data, iris.target
|
||||
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8, random_state=3)
|
||||
```
|
||||
|
||||
训练阶段我们要获得类别标签和对应的先验概率,此外还要计算出似然性,似然性的计算用到了上面提到的“朴素”假设,我们对鸢尾花连续的特征值进行了简单的离散化处理(大家先不考虑这种处理方式是否合理),代码如下所示。
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def naive_bayes_fit(X, y):
|
||||
"""
|
||||
:param X: 样本特征
|
||||
:param Y: 样本标签
|
||||
:returns: 二元组 - (先验概率, 似然性)
|
||||
"""
|
||||
# 计算先验概率
|
||||
clazz_labels, clazz_counts = np.unique(y, return_counts=True)
|
||||
prior_probs = pd.Series({k: v / y.size for k, v in zip(clazz_labels, clazz_counts)})
|
||||
# 拷贝数组创建副本
|
||||
X = np.copy(X)
|
||||
# 保存似然性计算结果的字典
|
||||
likelihoods = {}
|
||||
for j in range(X.shape[1]): # 对特征的循环
|
||||
# 对特征进行等宽分箱(离散化处理)
|
||||
X[:, j] = pd.cut(X[:, j], bins=5, labels=np.arange(1, 6))
|
||||
for i in prior_probs.index:
|
||||
# 按标签类别拆分数据并统计每个特征值出现的频次
|
||||
x_prime = X[y == i, j]
|
||||
x_values, x_counts = np.unique(x_prime, return_counts=True)
|
||||
for k, value in enumerate(x_values): # 对不同特征值的循环
|
||||
# 计算似然性并保存在字典中(字典的键是一个三元组 - (标签, 特征序号, 特征值))
|
||||
likelihoods[(i, j, value)] = x_counts[k] / x_prime.size
|
||||
return prior_probs, likelihoods
|
||||
```
|
||||
|
||||
调用上面的函数,我们可以得到一个二元组,解包之后分别是类别标签$\small{C_{i}}$对应的先验概率和在类别$\small{C_{i}}$中,第$\small{j}$个特征取到某个值`value`(上面的代码中,我们用 pandas 的`cut`函数对特征值分箱,`value`的取值为`1` 到`5`)的似然性,前者是一个`Series`对象,后者是一个`dict`对象,如下所示:
|
||||
|
||||
```python
|
||||
p_ci, p_x_ci = naive_bayes_fit(X_train, y_train)
|
||||
print('先验概率: ', p_ci, sep='\n')
|
||||
print('似然性: ', p_x_ci, sep='\n')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
先验概率:
|
||||
0 0.333333
|
||||
1 0.333333
|
||||
2 0.333333
|
||||
dtype: float64
|
||||
似然性:
|
||||
{(0, 0, 1.0): 0.525, (0, 0, 2.0): 0.45, (0, 0, 3.0): 0.025, (1, 0, 1.0): 0.05, (1, 0, 2.0): 0.375, (1, 0, 3.0): 0.425, (1, 0, 4.0): 0.15, (2, 0, 1.0): 0.025, (2, 0, 2.0): 0.025, (2, 0, 3.0): 0.45, (2, 0, 4.0): 0.3, (2, 0, 5.0): 0.2, (0, 1, 1.0): 0.025, (0, 1, 3.0): 0.325, (0, 1, 4.0): 0.45, (0, 1, 5.0): 0.2, (1, 1, 1.0): 0.175, (1, 1, 2.0): 0.325, (1, 1, 3.0): 0.475, (1, 1, 4.0): 0.025, (2, 1, 1.0): 0.025, (2, 1, 2.0): 0.35, (2, 1, 3.0): 0.525, (2, 1, 4.0): 0.05, (2, 1, 5.0): 0.05, (0, 2, 1.0): 1.0, (1, 2, 2.0): 0.025, (1, 2, 3.0): 0.525, (1, 2, 4.0): 0.45, (2, 2, 4.0): 0.525, (2, 2, 5.0): 0.475, (0, 3, 1.0): 0.975, (0, 3, 2.0): 0.025, (1, 3, 2.0): 0.125, (1, 3, 3.0): 0.75, (1, 3, 4.0): 0.125, (2, 3, 3.0): 0.05, (2, 3, 4.0): 0.525, (2, 3, 5.0): 0.425}
|
||||
```
|
||||
|
||||
> **说明**:字典中的第一个元素`(0, 0, 1.0): 0.525`表示标签为`0`,第`0`个特征(花萼长度)取值为`1.0`的似然性为`0.525`;最后一个元素`(2, 3, 5.0): 0.425`表示标签为`2`,第`3`个特征(花瓣宽度)取值为`5.0`的似然性为`0.425`。
|
||||
|
||||
预测阶段我们利用上面函数得到的先验概率和似然性计算后验概率,然后根据后验概率的最大值为样本赋予预测的类别标签。
|
||||
|
||||
```python
|
||||
def naive_bayes_predict(X, p_ci, p_x_ci):
|
||||
"""
|
||||
朴素贝叶斯分类器预测
|
||||
:param X: 样本特征
|
||||
:param p_ci: 先验概率
|
||||
:param p_x_ci: 似然性
|
||||
:return: 预测的标签
|
||||
"""
|
||||
# 对特征进行等宽分箱(离散化处理)
|
||||
X = np.copy(X)
|
||||
for j in range(X.shape[1]):
|
||||
X[:, j] = pd.cut(X[:, j], bins=5, labels=np.arange(1, 6))
|
||||
# 保存每个样本对应每个类别后验概率的二维数组
|
||||
results = np.zeros((X.shape[0], p_ci.size))
|
||||
clazz_labels = p_ci.index.values
|
||||
for k in range(X.shape[0]):
|
||||
for i, label in enumerate(clazz_labels):
|
||||
# 获得先验概率(训练的结果)
|
||||
prob = p_ci.loc[label]
|
||||
# 计算获得特征数据后的后验概率
|
||||
for j in range(X.shape[1]):
|
||||
# 如果没有对应的似然性就取值为0
|
||||
prob *= p_x_ci.get((i, j, X[k, j]), 0)
|
||||
results[k, i] = prob
|
||||
# 根据每个样本对应类别最大的概率选择预测标签
|
||||
return clazz_labels[results.argmax(axis=1)]
|
||||
```
|
||||
|
||||
将上面的函数作用于测试集进行预测,比较预测值和真实值,如下所示。
|
||||
|
||||
```python
|
||||
y_pred = naive_bayes_predict(X_test, p_ci, p_x_ci)
|
||||
y_pred == y_test
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([ True, True, True, True, True, True, True, True, True,
|
||||
True, True, True, True, True, True, True, True, True,
|
||||
False, True, True, True, True, True, True, True, False,
|
||||
True, True, True])
|
||||
```
|
||||
|
||||
上面两个函数希望能帮助大家理解朴素贝叶斯的工作原理,实际工作中我们还是推荐大家使用 scikit-learn 库的`navie_bayes`模块封装的类来创建朴素贝叶斯模型,该模块下有五个朴素贝叶斯算法的变体,每种变体针对不同类型的数据和特征分布,对应的五种朴素贝叶斯分类器分别是:`BernoulliNB`、`CategoricalNB`、`ComplementNB`、`GaussianNB`和`MultinomialNB`,它们之间的差别如下表所示:
|
||||
|
||||
| 分类器 | 特征类型 | 主要假设 | 对应公式 |
|
||||
| --------------- | -------- | ------------------------------------ |----|
|
||||
| `BernoulliNB` | 二元特征 | 特征服从 Bernoulli 分布 | $\small{P(x_{i}|C) = p^{x_{i}} \cdot (1 - p)^{1 - x_{i}}}$ |
|
||||
| `CategoricalNB` | 类别特征 | 特征服从多项式分布,常用于处理类别数据 | $\small{P(x_{i}|C)} = \frac{N_{i,j} + \alpha}{N_{i} + {\alpha}K}$ |
|
||||
| `ComplementNB` | 计数特征 | 利用补集概率,常用于处理不平衡数据集 | $\small{P}$ |
|
||||
| `GaussianNB` | 连续特征 | 特征服从高斯分布 | |
|
||||
| `MultinomialNB` | 计数特征 | 特征服从多项式分布,常用于文本分类 | |
|
||||
|
||||
对于鸢尾花数据集,由于其特征是连续值,我们可以用`GaussianNB`来创建模型,代码如下所示。
|
||||
|
||||
```python
|
||||
from sklearn.naive_bayes import GaussianNB
|
||||
|
||||
model = GaussianNB()
|
||||
model.fit(X_train, y_train)
|
||||
y_pred = model.predict(X_test)
|
||||
```
|
||||
|
||||
我们看看模型评估的结果。
|
||||
|
||||
```python
|
||||
from sklearn.metrics import classification_report
|
||||
|
||||
print(classification_report(y_test, y_pred))
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
precision recall f1-score support
|
||||
|
||||
0 1.00 1.00 1.00 10
|
||||
1 0.91 1.00 0.95 10
|
||||
2 1.00 0.90 0.95 10
|
||||
|
||||
accuracy 0.97 30
|
||||
macro avg 0.97 0.97 0.97 30
|
||||
weighted avg 0.97 0.97 0.97 30
|
||||
```
|
||||
|
||||
如果想看看朴素贝叶斯模型给每个样本对应到每个标签给出的概率值,可以使用下面的代码。
|
||||
|
||||
```python
|
||||
model.predict_proba(X_test).round(2)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[1. , 0. , 0. ],
|
||||
[1. , 0. , 0. ],
|
||||
[1. , 0. , 0. ],
|
||||
[1. , 0. , 0. ],
|
||||
[1. , 0. , 0. ],
|
||||
[0. , 0. , 1. ],
|
||||
[0. , 1. , 0. ],
|
||||
[1. , 0. , 0. ],
|
||||
[0. , 0. , 1. ],
|
||||
[0. , 0.98, 0.02],
|
||||
[0. , 1. , 0. ],
|
||||
[1. , 0. , 0. ],
|
||||
[0. , 1. , 0. ],
|
||||
[0. , 1. , 0. ],
|
||||
[0. , 0. , 1. ],
|
||||
[1. , 0. , 0. ],
|
||||
[0. , 0.93, 0.07],
|
||||
[0. , 0. , 1. ],
|
||||
[0. , 0.02, 0.98],
|
||||
[1. , 0. , 0. ],
|
||||
[0. , 0.22, 0.78],
|
||||
[0. , 0. , 1. ],
|
||||
[0. , 0. , 1. ],
|
||||
[0. , 0.92, 0.08],
|
||||
[1. , 0. , 0. ],
|
||||
[0. , 0. , 1. ],
|
||||
[0. , 0.54, 0.46],
|
||||
[0. , 1. , 0. ],
|
||||
[0. , 1. , 0. ],
|
||||
[0. , 0.81, 0.19]])
|
||||
```
|
||||
|
||||
### 算法优缺点
|
||||
|
||||
朴素贝叶斯算法的优缺包括:
|
||||
|
||||
1. **逻辑简单容易实现,适合大规模数据集**。
|
||||
2. **运算开销较小**。预测需要用到的概率在训练阶段都已经准好了,当新数据来了之后,只需要获取对应的概率值并进行简单的运算就能获得预测的结果。
|
||||
3. **受噪声和无关属性影响小**。
|
||||
|
||||
当然,由于做了“特征相互独立”这个假设,朴素贝叶斯算法的缺点也相当明显,因为在实际应用中,特征之间很难做到完全独立,尤其是维度很高的数据,如果特征之间的相关性较大,那么分类的效果就会变得很差。为了解决这个问题,在朴素贝叶斯算法的基础上又衍生出了一些新的方法,包括:半朴素贝叶斯(One Dependent Estimator)、AODE(Averaged One Dependent Estimator)、K依赖朴素贝叶斯、朴素贝叶斯网络、高斯混合朴素贝叶斯等,有兴趣的读者可以自行了解。
|
||||
|
||||
### 总结
|
||||
|
||||
朴素贝叶斯算法在多个领域有广泛应用,以下是一些常见的应用场景:
|
||||
|
||||
- **文本分类**:如垃圾邮件检测、情感分析等。
|
||||
- **推荐系统**:根据用户行为和喜好进行个性化推荐。
|
||||
- **医药诊断**:根据症状预测疾病。
|
||||
|
|
|
|||
|
|
@ -0,0 +1,549 @@
|
|||
## 回归模型
|
||||
|
||||
回归模型是机器学习和统计学中的一种基本模型,用于预测连续型输出变量。简单的说,给定一组输入变量(自变量)和对应的输出变量(因变量),回归模型旨在找到输入变量和输出变量之间的映射关系。回归模型的形式可能比较简单,但它确包含了机器学习中最主要的建模思想。通常,我们建立回归模型主要有两个目标:
|
||||
|
||||
1. **描述数据之间的关系**。我们之前讲过**机器学习的关键就是要通过历史数据掌握如何从特征映射到目标值**,这个过程不需要我们事先设置任何的规则,而是让机器通过对历史数据的学习来获得。回归模型可以帮助我们通过模型表达输入和输出之间的关系。
|
||||
2. **对未知数据做出预测**。通过学习到的映射关系,模型可以对新的输入数据进行预测。
|
||||
|
||||
回归模型的应用非常广泛,我们为大家举几个具体的例子:
|
||||
|
||||
1. **零售行业**。全球最大的电商平台亚马逊(Amazon)会根据历史销量、商品属性(价格、折扣、品牌、类别等)、时间特征(季节、工作日、节假日等)、外部因素(天气、社媒等)等特征创建回归模型预测未来一段时间内不同商品的需求量。在促销活动期间,会使用多元回归结合交互项来分析促销对销量的影响。
|
||||
2. **汽车行业**。为了优化电池的充电策略,延长电池的使用寿命,为电车用户提供更准确的电量预警,特斯拉(Tesla)使用回归模型,通过电池充放电次数、环境温度、放电深度、电池物理参数等建立回归模型,预测电池的剩余寿命。
|
||||
3. **房地产行业**。美国最大的在线房地产平台 Zillow 曾经使用回归模型帮助用户评估房屋的价值,通过房屋的面积、房龄、地理位置、房屋类型、社区安全等级、学校评分等对房屋的市场价格做出预测。
|
||||
|
||||
### 回归模型的分类
|
||||
|
||||
根据模型的复杂程度和假设,回归模型可以分为以下几类:
|
||||
|
||||
1. **线性回归**(Linear Regression):假设输入变量和输出变量之间是线性关系。
|
||||
|
||||
- 一元线性回归:建立一个因变量与单个自变量之间线性关系的模型。
|
||||
$$
|
||||
y = \beta_0 + \beta_1 x + \varepsilon
|
||||
$$
|
||||
其中,$\small{y}$ 是目标变量(因变量),$\small{x}$ 是输入变量(自变量),$\small{\beta_{0}}$ 是截距,表示$\small{x=0}$ 时的预测值,$\small{\beta_{1}}$ 是回归系数(斜率),表示输入变量对输出变量影响的大小,$\small{\varepsilon}$ 是误差项,用于表示数据中的随机噪声或无法解释的部分。
|
||||
|
||||
- 多元线性回归:建立一个因变量与多个自变量之间线性关系的模型。
|
||||
$$
|
||||
y = \beta_{0} + \beta_{1} x_{1} + \beta_{2} x_{2} + \cdots + \beta_{n} x_{n} + \varepsilon
|
||||
$$
|
||||
上面的公式也可以用向量的形式简化表示为:
|
||||
$$
|
||||
y = \mathbf{x}^{T} \mathbf{\beta} + \varepsilon
|
||||
$$
|
||||
其中,$\small{\mathbf{x} = [1, x_{1}, x_{2}, \dots, x_{n}]^{T}}$ 是包含截距的输入向量,$\small{\mathbf{\beta} = [\beta_{0}, \beta_{1}, \beta_{2}, \dots, \beta_{n}]^{T}}$ 是模型参数(包括截距 $\small{\beta_{0}}$ 和回归系数 $\small{\beta_{1}, \beta_{2}, \cdots, \beta_{n}}$),$\small{\varepsilon}$ 是误差项。
|
||||
|
||||
2. **多项式回归**(Polynomial Regression):引入高阶特征,使模型能拟合更复杂的非线性关系,属于线性模型的扩展,因为因为它对参数 $\small{\beta}$ 的求解仍然是线性形式,如下面所示的二次关系:
|
||||
$$
|
||||
y = \beta_{0} + \beta_{1} x + \beta_{2} x^{2} + \varepsilon
|
||||
$$
|
||||
|
||||
3. **非线性回归**(Nonlinear Regression):非线性回归完全放弃了线性假设,模型形式可以是任意非线性函数。
|
||||
|
||||
4. **岭回归**(Ridge Regression)、**套索回归**(Lasso Regression)、**弹性网络回归**(Elastic Net Regression):在线性回归基础上加入正则化项,用于处理过拟合、多重共线性和特征筛选问题。
|
||||
|
||||
5. **逻辑回归**(Logistic Regression):逻辑回归虽然名字中带“回归”,但实际上是用于分类问题的模型。它通过 Sigmoid 函数将线性组合的输入值映射到区间$\small{(0, 1)}$,表示分类概率,适用于二分类问题;也可以扩展为 Softmax 回归,解决多分类问题。
|
||||
$$
|
||||
P(y=1|x) = \frac{1}{1 + e^{-(\beta_{0} + \beta_{1} x_{1} + \cdots + \beta_{n} x_{n})}}
|
||||
$$
|
||||
|
||||
### 回归系数的计算
|
||||
|
||||
建立回归模型的关键是找到最佳的回归系数 $\small{\mathbf{\beta}}$,所谓最佳回归系数是指让模型对数据的拟合效果达到最好的模型参数,即能够最小化模型的预测值 $\small{\hat{y}_{i}}$ 与实际观测值 $\small{y_{i}}$ 之间差异的模型参数。为此,我们先定义如下所示的损失函数。
|
||||
$$
|
||||
L(\mathbf{\beta}) = \sum_{i=1}^{m}(y_{i} - \hat{y}_{i})^{2}
|
||||
$$
|
||||
其中,$\small{m}$表示样本容量,代入回归模型,有:
|
||||
$$
|
||||
L(\mathbf{\beta}) = \sum_{i=1}^{m}(y_{i} - \mathbf{x}_{i}^{T}\mathbf{\beta})^{2}
|
||||
$$
|
||||
如果用矩阵形式表示,有:
|
||||
$$
|
||||
L(\mathbf{\beta}) = (\mathbf{y} - \mathbf{X\beta})^{T}(\mathbf{y} - \mathbf{X\beta})
|
||||
$$
|
||||
其中,$\mathbf{y}$ 是目标值的向量,大小为 $\small{m \times 1}$,$\mathbf{X}$ 是特征矩阵,大小为 $\small{m \times n}$,$\small{\mathbf{\beta}}$ 是回归系数的向量,大小为 $\small{n \times 1}$。
|
||||
|
||||
通过最小化损失函数 $\small{L(\mathbf{\beta})}$,我们可以得到线性回归模型的解析解。对 $\small{L(\mathbf{\beta})}$ 求导并令其为 0,有:
|
||||
$$
|
||||
\frac{\partial{L(\mathbf{\beta})}}{\partial{\mathbf{\beta}}} = -2\mathbf{X}^{T}(\mathbf{y} - \mathbf{X\beta}) = 0
|
||||
$$
|
||||
整理后得到:
|
||||
$$
|
||||
\mathbf{\beta} = (\mathbf{X}^{T}\mathbf{X})^{-1}\mathbf{X}^{T}\mathbf{y}
|
||||
$$
|
||||
对于矩阵 $\small{\mathbf{X}^{T}\mathbf{X}}$ 不满秩的情况,可以通过添加正则化项的方式使得矩阵可逆,如下所示,这个就是线性回归的解析解。
|
||||
$$
|
||||
\mathbf{\beta} = (\mathbf{X}^{T}\mathbf{X} + \mathbf{\lambda \mit{I}})^{-1}\mathbf{X}^{T}\mathbf{y}
|
||||
$$
|
||||
> **说明**:如果你对这里提到的正则化不理解可以先放放,后面我们再来讨论这个问题。
|
||||
|
||||
上述方法适用于小规模数据集,当数据体量不大(样本和特征数量较少)时,计算效率是没有问题的。对于大规模数据集或更为复杂的优化问题,我们可以使用**梯度下降法**,通过迭代更新参数来逐步逼近最优解。梯度下降法的目标也是最小化损失函数,该方法通过计算梯度方向进行参数更新。梯度是一个向量,包含了目标函数在各个参数方向上的偏导数。对于上面的损失函数 $\small{L(\mathbf{\beta})}$,梯度可以表示为:
|
||||
$$
|
||||
\nabla L(\mathbf{\beta}) = \left[ \frac{\partial{L}}{\partial{\beta_{1}}}, \frac{\partial{L}}{\partial{\beta_{2}}}, \cdots, \frac{\partial{L}}{\partial{\beta_{n}}} \right]
|
||||
$$
|
||||
梯度下降法通过以下更新规则来更新参数 $\small{\mathbf{\beta}}$:
|
||||
$$
|
||||
\mathbf{\beta}^{\prime} = \mathbf{\beta} - \alpha \nabla L(\mathbf{\beta}) \\
|
||||
\mathbf{\beta} = \mathbf{\beta^{\prime}}
|
||||
$$
|
||||
其中,$\small{\alpha}$ 是学习率(step size),通常是一个较小的正数,用于控制每次更新的幅度。如果学习率 $\small{\alpha}$ 选择得当,梯度下降法将收敛到目标函数的局部最小值。如果学习率过大,可能导致震荡不收敛;如果学习率过小,则收敛的速度缓慢,需要更多次的迭代。
|
||||
|
||||
### 新数据集介绍
|
||||
|
||||
之前介绍的鸢尾花数据集并不适合讲解回归模型,为此我们引入另一个经典的汽车 MPG 数据集。汽车 MPG 数据集最初由美国汽车协会提供,我们可以通过该数据集预测车辆的燃油效率,即每加仑燃料行驶的里程(Miles Per Gallon, MPG)。需要注意的是,scikit-learn 库没有内置该数据集,我们可以直接从 [UCI 机器学习仓库](https://archive.ics.uci.edu/dataset/9/auto+mpg) 网站下载数据集,也可以通过执行下面的代码联网加载该数据集。
|
||||
|
||||
```python
|
||||
import ssl
|
||||
import pandas as pd
|
||||
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
df = pd.read_csv('https://archive.ics.uci.edu/static/public/9/data.csv')
|
||||
df.info()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
<class 'pandas.core.frame.DataFrame'>
|
||||
RangeIndex: 398 entries, 0 to 397
|
||||
Data columns (total 9 columns):
|
||||
# Column Non-Null Count Dtype
|
||||
--- ------ -------------- -----
|
||||
0 car_name 398 non-null object
|
||||
1 cylinders 398 non-null int64
|
||||
2 displacement 398 non-null float64
|
||||
3 horsepower 392 non-null float64
|
||||
4 weight 398 non-null int64
|
||||
5 acceleration 398 non-null float64
|
||||
6 model_year 398 non-null int64
|
||||
7 origin 398 non-null int64
|
||||
8 mpg 398 non-null float64
|
||||
dtypes: float64(4), int64(4), object(1)
|
||||
memory usage: 28.1+ KB
|
||||
```
|
||||
|
||||
根据上面的输出,我们简单介绍下数据集的九个属性,前面八个都是输入变量(第一个暂不使用),最后一个是输出变量,具体如下表所示。
|
||||
|
||||
| 属性名称 | 描述 |
|
||||
| -------------- | ------------------------------------------------------------ |
|
||||
| *car_name* | 汽车的名称,字符串,这个属性对建模暂时没有帮助 |
|
||||
| *cylinders* | 气缸数量,整数 |
|
||||
| *displacement* | 发动机排量(立方英寸),浮点数 |
|
||||
| *horsepower* | 马力,浮点数,有空值需要提前处理 |
|
||||
| *weight* | 汽车重量(磅),整数 |
|
||||
| *acceleration* | 加速(0 - 60 mph所需时间),浮点数 |
|
||||
| *model_year* | 模型年份(1970年 - 1982年),这里用的是两位的年份 |
|
||||
| *origin* | 汽车来源(1 = 美国, 2 = 欧洲, 3 = 日本),这里的`1`、`2`、`3`应该视为三种类别而不是整数 |
|
||||
| *mpg* | 车辆的燃油效率,每加仑行驶的里程(目标变量) |
|
||||
|
||||
我们先删除`car_name`这个暂时用不上的属性,然后使用`DataFrame`对象的`corr`方法检查输入变量(特征)与输出变量(目标值)之间是否存在相关性。通过相关性分析我们可以选择相关性强的特征,剔除掉那些与目标值相关性较弱的特征,这有助于减少模型的复杂性和过拟合的风险。在多元回归中,多重共线性(即输入变量之间高度相关)可能会影响回归系数的估计,导致模型不稳定。可以通过计算特征之间的相关性和方差膨胀因子(VIF)等来检测共线性问题。
|
||||
|
||||
```python
|
||||
# 删除指定的列
|
||||
df.drop(columns=['car_name'], inplace=True)
|
||||
# 计算相关系数矩阵
|
||||
df.corr()
|
||||
```
|
||||
|
||||
> **说明**:`DataFrame`对象的`corr`方法默认计算皮尔逊相关系数,皮尔逊相关系数适合来自于正态总体的连续值,对于等级数据之间相关性的判定,可以通过修改`method`参数为`spearmean`或`kendall`来计算斯皮尔曼秩相关或肯德尔系数。当然,连续值也可以通过分箱操作处理成等级数据,然后再进行相关性的判定。
|
||||
|
||||
在使用该数据集建模之前,我们需要做一些准备工作,首先处理掉`horsepower`字段的空值,然后将`origin`字段处理成**独热编码**(One-Hot Encoding)。独热编码是一种用于处理分类变量的常见编码方式,通常分类数据(如性别、颜色、季节等)无法直接输入机器学习模型进行训练,因为大多数算法只能处理数值数据。独热编码通过将每个分类变量转换为若干个新的二元特征(`0`或` 1`)来表示,从而使得这些变量可以输入到机器学习模型中。假设我们有一个叫“颜色”的特征列,可能的取值有`红色`、`绿色`和`蓝色`,我们可以将其通过独热编码转换成三个二元特征,如下表所示:
|
||||
|
||||
| 红色 | 绿色 | 蓝色 |
|
||||
| :--: | :--: | :--: |
|
||||
| 1 | 0 | 0 |
|
||||
| 0 | 1 | 0 |
|
||||
| 0 | 0 | 1 |
|
||||
| 0 | 1 | 0 |
|
||||
| 1 | 0 | 0 |
|
||||
|
||||
> **说明**:我们也可以只保留绿色和蓝色两个列,如果两个列的取值都为`0`,那么说明我们的颜色是红色。
|
||||
|
||||
独热编码方法简单直观,容易理解和实现。对于无序类别独热编码是非常有效的,因为它不会引入任何虚假的顺序关系,处理后的数据类型是数值型的,很多机器学习算法都能很好的处理。当然,如果类别特征有大量不同的类别取值,独热编码会生成大量的新特征,可能导致数据的维度大幅增加,从而影响计算性能和存储效率,尤其是在数据中有很多稀疏类别时。
|
||||
|
||||
下面的代码实现了对数据的预处理。
|
||||
|
||||
```python
|
||||
# 删除有缺失值的样本
|
||||
df.dropna(inplace=True)
|
||||
# 将origin字段处理为类别类型
|
||||
df['origin'] = df['origin'].astype('category')
|
||||
# 将origin字段处理为独热编码
|
||||
df = pd.get_dummies(df, columns=['origin'], drop_first=True)
|
||||
df
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
cylinders displacement horsepower weight ... model_year mpg origin_2 origin_3
|
||||
0 8 307.0 130.0 3504 ... 70 18.0 False False
|
||||
1 8 350.0 165.0 3693 ... 70 15.0 False False
|
||||
2 8 318.0 150.0 3436 ... 70 18.0 False False
|
||||
3 8 304.0 150.0 3433 ... 70 16.0 False False
|
||||
4 8 302.0 140.0 3449 ... 70 17.0 False False
|
||||
.. ... ... ... ... ... ... ... ... ...
|
||||
393 4 140.0 86.0 2790 ... 82 27.0 False False
|
||||
394 4 97.0 52.0 2130 ... 82 44.0 True False
|
||||
395 4 135.0 84.0 2295 ... 82 32.0 False False
|
||||
396 4 120.0 79.0 2625 ... 82 28.0 False False
|
||||
397 4 119.0 82.0 2720 ... 82 31.0 False False
|
||||
|
||||
[392 rows x 9 columns]
|
||||
```
|
||||
|
||||
> **说明**:上面调用 pandas 的`get_dummies`函数将`origin`列处理成了独热编码,由于将`drop_first`参数设置为`True`,所以原来的取值`1`、`2`、`3`只保留了两个列,分别叫`origin_2`和`origin_3`。Scikit-learn 库中`preprocessing`模块的`OneHotEncoder`也支持将类别特征处理成独热编码。
|
||||
|
||||
接下来,我们还是将数据集拆分为训练集和测试集,代码如下所示。
|
||||
|
||||
```python
|
||||
from sklearn.model_selection import train_test_split
|
||||
|
||||
X, y = df.drop(columns='mpg').values, df['mpg'].values
|
||||
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8, random_state=3)
|
||||
```
|
||||
|
||||
### 线性回归代码实现
|
||||
|
||||
我们首先使用 scikit-learn 库`linear_model`模块的`LinearRegression`来创建线性回归模型,`LinearRegression`使用最小二乘法计算回归模型的参数,代码如下所示。
|
||||
|
||||
```python
|
||||
from sklearn.linear_model import LinearRegression
|
||||
|
||||
model = LinearRegression()
|
||||
model.fit(X_train, y_train)
|
||||
y_pred = model.predict(X_test)
|
||||
```
|
||||
|
||||
如果想查看线性回归模型的参数(回归系数和截距),可以通过下面的代码来实现。
|
||||
|
||||
```python
|
||||
print('回归系数:', model.coef_)
|
||||
print('截距:', model.intercept_)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
回归系数: [-0.70865621 0.03138774 -0.03034065 -0.0064137 0.06224274 0.82866534
|
||||
3.20888265 3.68252848]
|
||||
截距: -21.685482718950933
|
||||
```
|
||||
|
||||
### 回归模型的评估
|
||||
|
||||
回归模型的预测效果到底如何,我们可以通过下面的指标对其进行评估。
|
||||
|
||||
1. 均方误差(Mean Squared Error, MSE)。MSE 是回归模型最常用的评估指标之一,定义为预测值与真实值误差的平方平均值。
|
||||
$$
|
||||
\text{MSE} = \frac{1}{m} \sum_{i=1}^{m}(y_{i} - \hat{y}_{i})^{2}
|
||||
$$
|
||||
|
||||
2. 均方根误差(Root Mean Squared Error, RMSE)。RMSE 是 MSE 的平方根形式,用于更直观地衡量误差的实际尺度(单位与目标变量一致)。
|
||||
$$
|
||||
\text{RMSE} = \sqrt{\text{MSE}} = \sqrt{\frac{1}{m} \sum_{i=1}^{m}(y_{i} - \hat{y}_{i})^{2}}
|
||||
$$
|
||||
|
||||
3. 平均绝对误差(Mean Absolute Error, MAE)。MAE 是另一个常用的误差度量指标,定义为预测值与真实值误差的绝对值平均值。
|
||||
$$
|
||||
\text{MAE} = \frac{1}{m} \sum_{i=1}^{m}|y_{i} - \hat{y}_{i}|
|
||||
$$
|
||||
|
||||
4. 决定系数(R-Squared, $\small{R^2}$)。$\small{R^2}$ 是一个相对指标,用于衡量模型对数据的拟合程度,其值越接近 1 越好。$\small{R^2}$ 的计算公式为:
|
||||
$$
|
||||
R^{2} = 1 - \frac{\text{SS}_{\text{res}}}{\text{SS}_{\text{tot}}}
|
||||
$$
|
||||
其中,$\text{SS}_{\text{res}} = \sum_{i=1}^{m}(y_i - \hat{y}_i)^2$ 为残差平方和,$\text{SS}_{\text{tot}} = \sum_{i=1}^{m} (y_i - \bar{y})^{2}$ 为总平方和,如下图所示。下图左边红色正方形的面积之和代表总平方和,右边蓝色正方形的面积之和代表残差的平方和,很显然,模型拟合的效果越好,$\small{\frac{\text{SS}_{\text{res}}}{\text{SS}_{\text{tot}}}}$的值就越接近 0,$\small{R^{2}}$的值就越接近 1。通常$\small{R^{2} \ge 0.8}$时,我们认为模型的拟合效果已经很不错了。
|
||||
|
||||
<img src="res/05_regression_r2.png" style="zoom:40%;">
|
||||
|
||||
可以使用 scikit-learn 中封装好的函数计算出均方误差、平均绝对误差和$\small{R^{2}}$的值,代码如下所示。
|
||||
|
||||
```python
|
||||
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
|
||||
|
||||
mse = mean_squared_error(y_test, y_pred)
|
||||
mae = mean_absolute_error(y_test, y_pred)
|
||||
r2 = r2_score(y_test, y_pred)
|
||||
|
||||
print(f'均方误差: {mse:.4f}')
|
||||
print(f'平均绝对误差: {mae:.4f}')
|
||||
print(f'决定系数: {r2:.4f}')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
均方误差: 13.1215
|
||||
平均绝对误差: 2.8571
|
||||
决定系数: 0.7848
|
||||
```
|
||||
|
||||
### 引入正则化项
|
||||
|
||||
岭回归是在线性回归的基础上引入 $\small{L2}$ 正则化项,目的是防止模型过拟合,尤其是当特征数较多或特征之间存在共线性时。岭回归的损失函数如下所示:
|
||||
$$
|
||||
L(\mathbf{\beta}) = \sum_{i=1}^{m}(y_{i} - \hat{y}_{i})^{2} + \lambda \sum_{j=1}^{n}\beta_{j}^{2}
|
||||
$$
|
||||
其中,$\small{L2}$ 正则化项 $\small{\lambda \sum_{j=1}^{n} \beta_{j}^{2}}$ 会惩罚较大的回归系数,相当于缩小了回归系数的大小,但不会使系数为 0(即不会进行特征选择)。可以通过 scikit-learn 库`linear_model`模块的`Ridge`类实现岭回归,代码如下所示。
|
||||
|
||||
```python
|
||||
from sklearn.linear_model import Ridge
|
||||
|
||||
model = Ridge()
|
||||
model.fit(X_train, y_train)
|
||||
y_pred = model.predict(X_test)
|
||||
print('回归系数:', model.coef_)
|
||||
print('截距:', model.intercept_)
|
||||
mse = mean_squared_error(y_test, y_pred)
|
||||
r2 = r2_score(y_test, y_pred)
|
||||
print(f'均方误差: {mse:.4f}')
|
||||
print(f'决定系数: {r2:.4f}')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
回归系数: [-0.68868217 0.03023126 -0.0291811 -0.00642523 0.06312298 0.82583962
|
||||
3.04105754 3.49988826]
|
||||
截距: -21.390402697674855
|
||||
均方误差: 12.9604
|
||||
决定系数: 0.7874
|
||||
```
|
||||
|
||||
套索回归引入 $\small{L1}$ 正则化项,不仅防止过拟合,还具有特征选择的功,特别适用于高维数据。套索回归的损失函数如下所示:
|
||||
$$
|
||||
L(\mathbf{\beta}) = \sum_{i=1}^{m}(y_{i} - \hat{y}_{i})^{2} + \lambda \sum_{j=1}^{n}|\beta_{j}|
|
||||
$$
|
||||
其中,$\small{L1}$ 正则化项 $\small{\lambda \sum_{j=1}^{n}|\beta_{j}|}$ 会将某些不重要的回归系数缩减为 0,从而实现特征选择。可以通过 scikit-learn 库`linear_model`模块的`Lasso`类实现岭回归,代码如下所示。
|
||||
|
||||
```python
|
||||
from sklearn.linear_model import Lasso
|
||||
|
||||
model = Lasso()
|
||||
model.fit(X_train, y_train)
|
||||
y_pred = model.predict(X_test)
|
||||
print('回归系数:', model.coef_)
|
||||
print('截距:', model.intercept_)
|
||||
mse = mean_squared_error(y_test, y_pred)
|
||||
r2 = r2_score(y_test, y_pred)
|
||||
print(f'均方误差: {mse:.4f}')
|
||||
print(f'决定系数: {r2:.4f}')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
回归系数: [-0.00000000e+00 4.46821248e-04 -1.22830326e-02 -6.29725191e-03
|
||||
0.00000000e+00 6.91590631e-01 0.00000000e+00 0.00000000e+00]
|
||||
截距: -9.109888229245005
|
||||
均方误差: 11.1035
|
||||
决定系数: 0.8179
|
||||
```
|
||||
|
||||
> **注意**:上面代码运行结果中的回归系数,有四个特征的回归系数被设置为 0,相当于从 8 个特征中选出了 4 个重要的特征。模型的拟合效果是优于之间的回归模型的,这一点从均方误差和决定系数可以看出。
|
||||
|
||||
弹性网络回归结合了岭回归和套索回归的优点,通过同时引入 $\small{L1}$ 和 $\small{L2}$ 正则化项,适用于高维数据且特征之间存在相关的情况,其损失函数如下所示:
|
||||
$$
|
||||
L(\mathbf{\beta}) = \sum_{i=1}^{m}(y_{i} - \hat{y}_{i})^{2} + \alpha \lambda \sum_{j=1}^{n}|\beta_{j}| + (1 - \alpha) \lambda \sum_{j=1}^{n}\beta_{j}^{2}
|
||||
$$
|
||||
其中,$\small{\alpha}$ 是控制 $\small{L1}$ 和 $\small{L2}$ 正则化的权重比例。
|
||||
|
||||
### 线性回归另一种实现
|
||||
|
||||
上面我们提到过,除了最小二乘法我们还可以使用梯度下降法来求解回归模型的参数,scikit-learn 库`linear_model`模块的`SGDRegressor`就使用了这种方法,SGD 就是 Stochastic Gradient Descent 的缩写。随机梯度下降每次迭代只使用一个随机样本来计算梯度,计算速度快,适合大规模数据集,而且可以跳出局部最优解。需要注意的是它的学习率,如果学习率设置得不合理,它的收敛性可能会发生波动,通常需要使用学习率衰减策略来促进收敛。此外,随机梯度下降对特征的尺度非常敏感,通常需要在训练之前对特征进行标准化或归一化处理,完整的代码如下所示。
|
||||
|
||||
```python
|
||||
from sklearn.linear_model import SGDRegressor
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
|
||||
# 对特征进行选择和标准化处理
|
||||
scaler = StandardScaler()
|
||||
scaled_X = scaler.fit_transform(X[:, [1, 2, 3, 5]])
|
||||
# 重新拆分训练集和测试集
|
||||
X_train, X_test, y_train, y_test = train_test_split(scaled_X, y, train_size=0.8, random_state=3)
|
||||
|
||||
# 模型的创建、训练和预测
|
||||
model = SGDRegressor()
|
||||
model.fit(X_train, y_train)
|
||||
y_pred = model.predict(X_test)
|
||||
print('回归系数:', model.coef_)
|
||||
print('截距:', model.intercept_)
|
||||
|
||||
# 模型评估
|
||||
mse = mean_squared_error(y_test, y_pred)
|
||||
r2 = r2_score(y_test, y_pred)
|
||||
print(f'均方误差: {mse:.4f}')
|
||||
print(f'决定系数: {r2:.4f}')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
回归系数: [-0.25027084 -0.41349219 -4.9559786 2.83009217]
|
||||
截距: [23.48707219]
|
||||
均方误差: 11.3853
|
||||
决定系数: 0.8133
|
||||
```
|
||||
|
||||
这里,我们还需要强调一下`SGDRegressor`构造函数几个重要的参数,也是回归模型比较重要的超参数,如下所示:
|
||||
|
||||
1. `loss`:指定优化目标(损失函数),默认值为`'squared_error'`(最小二乘法),其他可以选择的值有:`'huber'`、`'epsilon_insensitive'` 和 `'squared_epsilon_insensitive'`,其中`'huber'`适用于对异常值更鲁棒的回归模型。
|
||||
|
||||
2. `penalty`:指定正则化方法,用于防止过拟合,默认为`'l2'`(L2 正则化),其他可以选择的值有:`'l1'`(L1正则化)、`'elasticnet'`(弹性网络,L1 和 L2 的组合)、`None`(不使用正则化)。
|
||||
|
||||
3. `alpha`:正则化强度的系数,控制正则化项的权重,默认值为`0.0001`。较大的 `alpha` 值会加重正则化的影响,从而限制模型复杂度;较小的值会让模型更关注训练数据的拟合。
|
||||
4. `l1_ratio`:当 `penalty='elasticnet'` 时,控制 L1 和 L2 正则化之间的权重,默认值为`0.15`,取值范围为`[0, 1]`(`0` 表示完全使用 L2,`1` 表示完全使用 L1)。
|
||||
5. `tol`:优化算法的容差,即判断收敛的阈值,默认值为`1e-3`。当目标函数的改变量小于 `tol` 时,训练会提前终止;如果希望训练更加精确,可以适当降低 `tol`。
|
||||
6. `learning_rate`:指定学习率的调节策略,默认值为`'constant'`,表示使用固定学习率,具体的值由 `eta0` 指定;其他可选项包括:
|
||||
- `'optimal'`:基于公式`eta = 1.0 / (alpha * (t + t0))`自动调整。
|
||||
- `'invscaling'`:按 `eta = eta0 / pow(t, power_t)` 缩放学习率。
|
||||
- `'adaptive'`:动态调整,误差减少时保持当前学习率,否则减小学习率。
|
||||
|
||||
7. `eta0`:初始学习率,默认值为`0.01`,当 `learning_rate='constant'` 或其他策略使用时,`eta0` 决定了初始更新步长。
|
||||
8. `power_t`:当 `learning_rate='invscaling'` 时,控制学习率衰减速度,默认值为`0.25`。较小的值会让学习率下降得更慢,从而更长时间地关注全局优化。
|
||||
9. `early_stopping`:是否启用早停机制,默认值为`False`。如果设置为 `True`,模型会根据验证集性能自动停止训练,防止过拟合。
|
||||
10. `validation_fraction`:指定用作验证集的训练数据比例,默认值为`0.1`。当 `early_stopping=True` 时,该参数会起作用。
|
||||
11. `max_iter`:训练的最大迭代次数,默认值为`1000`。当数据较大或学习率较小时,可能需要增加迭代次数以保证收敛。
|
||||
12. `shuffle`:是否在每个迭代轮次开始时打乱训练数据,默认值为`True`,表示打乱数据。打乱数据有助于提高模型的泛化能力。
|
||||
13. `warm_start`:是否使用上次训练的参数继续训练,默认值为`False`。当设置为 `True` 时,可以在已有模型的基础上进一步优化。
|
||||
14. `verbose`:控制训练过程的日志输出,默认值为`0`,可以设置为更高值以观察训练进度。
|
||||
|
||||
### 多项式回归
|
||||
|
||||
有的时候,我们关心的自变量和因变量之间并不是简单的线性关系,例如广告投入与销售额增长的关系、设备的使用时间与故障率之间的关系等。除此以外,如果数据中存在明显的拐点或者要通过简单的公式来近似某些复杂的现象,线性回归模型可能并不能满足这样的需求,这个时候我们就需要建立多项式回归模型。
|
||||
|
||||
下面我们用一个简单的例子对多项式回归加以说明,我们先生成一组数据点并绘制出对应的散点图,代码如下所示。
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
x = np.linspace(0, 6, 150)
|
||||
y = x ** 2 - 4 * x + 3 + np.random.normal(1, 1, 150)
|
||||
plt.scatter(x, y)
|
||||
plt.show()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/05_polynomial_scatter.png" style="zoom:50%;">
|
||||
|
||||
显然,这样的一组数据点是很难通过线性模型进行拟合的,下面的代码可以证明这一点。
|
||||
|
||||
```Python
|
||||
x_ = x.reshape(-1, 1)
|
||||
|
||||
model = LinearRegression()
|
||||
model.fit(x_, y)
|
||||
a, b = model.coef_[0], model.intercept_
|
||||
y_pred = a * x + b
|
||||
plt.scatter(x, y)
|
||||
plt.plot(x, y_pred, color='r')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/05_polynomial_line_fit.png" style="zoom:50%;">
|
||||
|
||||
我们再看看 $\small{R^{2}}$ 的值:
|
||||
|
||||
```python
|
||||
r2 = r2_score(y, y_pred)
|
||||
print(f'决定系数: {r2:.4f}')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
决定系数: 0.5933
|
||||
```
|
||||
|
||||
Scikit-learn 库`preprocessing`模块中的`PolynomialFeatures`类可以将原始特征扩展为多项式特征,从而将线性模型转换为具有高次项的模型。创建`PolynomialFeatures`对象时有几个重要的参数:
|
||||
|
||||
1. `degree`:设置多项式的最高次项。例如,`degree=3` 会生成包含一次项、二次项和三次项的特征。
|
||||
2. `interaction_only`:默认值为`False`,如果设置为`True`,则只生成交互项(如$\small{x_{1}x_{2}}$),不生成单独的高次项(如$\small{x_{1}^{2}}$、$\small{x_{2}^{2}}$)。
|
||||
3. `include_bias`:默认值为`True`,表示包括常数项(通常为 1),设置为`False`则不包括常数项。
|
||||
|
||||
下面我们通过代码来演示如何通过`PolynomialFeatures`类进行特征预处理,实现多项式回归。
|
||||
|
||||
```python
|
||||
from sklearn.preprocessing import PolynomialFeatures
|
||||
|
||||
poly = PolynomialFeatures(degree=2)
|
||||
x_ = poly.fit_transform(x_)
|
||||
|
||||
model = LinearRegression()
|
||||
model.fit(x_, y)
|
||||
y_pred = model.predict(x_)
|
||||
r2 = r2_score(y, y_pred)
|
||||
print(f'决定系数: {r2:.4f}')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
决定系数: 0.9497
|
||||
```
|
||||
|
||||
通过特征预处理引入了高次项之后,模型拟合的效果得到了明显的改善。如果希望只对一部分特征添加高次项,可以使用 scikit-learn 库`compose`模块的`ColumnTransformer`来定义处理规则,有兴趣的读者可以自行研究。需要注意的是,多项式回归时随着高次项的引入,模型发生过拟合的风险增加会大大增加,尤其在数据量较小时;另一方面,高次项的值可能会导致特征范围变得很大,要考虑对特征进行标准化处理。
|
||||
|
||||
### 逻辑回归
|
||||
|
||||
逻辑回归尽管名字中含有“回归”,但逻辑回归实际上是一种分类算法,用于处理二分类问题,例如电子邮件是不是垃圾邮件、用户是否会点击广告、信用卡客户是否存在违约风险等。逻辑回归的核心思想是通过 Sigmoid 函数将线性回归的输出映射到$\small{(0, 1)}$区间,作为对分类概率的预测。Sigmoid 函数的曲线如下图所示。
|
||||
|
||||
<img src="res/05_sigmoid_function.png" style="zoom:58%;">
|
||||
|
||||
下面,我们用 scikit-learn 库`datasets`模块提供的`make_classification`函数生成一组模拟数据, 通过逻辑回归来构建分类预测模型,代码如下所示。
|
||||
|
||||
```python
|
||||
from sklearn.datasets import make_classification
|
||||
from sklearn.linear_model import LogisticRegression
|
||||
from sklearn.metrics import classification_report
|
||||
|
||||
# 生成1000条样本数据,每个样本包含6个特征
|
||||
X, y = make_classification(n_samples=1000, n_features=6, random_state=3)
|
||||
# 将1000条样本数据拆分为训练集和测试集
|
||||
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8, random_state=3)
|
||||
|
||||
# 创建和训练逻辑回归模型
|
||||
model = LogisticRegression()
|
||||
model.fit(X_train, y_train)
|
||||
|
||||
# 对测试集进行预测并评估
|
||||
y_pred = model.predict(X_test)
|
||||
print(classification_report(y_test, y_pred))
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
precision recall f1-score support
|
||||
|
||||
0 0.95 0.93 0.94 104
|
||||
1 0.93 0.95 0.94 96
|
||||
|
||||
accuracy 0.94 200
|
||||
macro avg 0.94 0.94 0.94 200
|
||||
weighted avg 0.94 0.94 0.94 200
|
||||
```
|
||||
|
||||
这里,我们再强调一下`LogisticRegression`构造函数几个重要的参数,也是逻辑回归模型比较重要的超参数,如下所示:
|
||||
|
||||
1. `penalty`:指定正则化类型,用于控制模型复杂度,防止过拟合,默认值为`l2`。
|
||||
2. `C`:正则化强度的倒数,默认值为`1.0`。较小的 `C` 值会加强正则化(更多限制模型复杂度),较大的 `C` 值会减弱正则化(更注重拟合训练数据)。
|
||||
3. `solver`:指定优化算法,默认值为`lbfgs`,可选值包括:
|
||||
- `'newton-cg'`、`'lbfgs'`、`'sag'`、`'saga'`:支持 L2 和无正则化。
|
||||
- `'liblinear'`:支持 L1 和 L2 正则化,适用于小型数据集。
|
||||
- `'saga'`:支持 L1、L2 和 ElasticNet,适用于大规模数据。
|
||||
4. `multi_class`:指定多分类问题的处理方式,默认值为`'auto'`,根据数据选择 `'ovr'` 或 `'multinomial'`,前者表示一对多策略,适合二分类或多分类的基础情况,后者表示多项式回归策略,适用于多分类问题,需与 `'lbfgs'`、`'sag'` 或 `'saga'` 搭配使用。
|
||||
5. `fit_intercept`:是否计算截距(偏置项),默认值为`True`。
|
||||
6. `class_weight`:类别权重,处理类别不平衡问题,默认值为`None`,设置为`'balanced'`可以根据类别频率自动调整权重。
|
||||
|
||||
> **说明**:逻辑回归有些超参数跟我们之前讲的`SGDRegressor`是类似的,此处不再进行赘述。
|
||||
|
||||
### 总结
|
||||
|
||||
回归模型是一种统计分析方法,用于建立自变量与因变量之间的关系。它通过拟合数据来预测目标变量的值,在经济学、工程、医学等领域有着广泛的应用,可以帮助决策者进行数据驱动的预测和分析。
|
||||
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
## K-均值聚类
|
||||
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
## 支持向量机
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
## 聚类算法
|
||||
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
## 回归分析
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
## 集成学习算法
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
## 深度学习概述
|
||||
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
## Tensorflow入门
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
## PyTorch深度学习入门
|
||||
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
## PyTorch概述
|
||||
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
## PyTorch实战
|
||||
## PyTorch深度学习实战
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 83 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
49
README.md
49
README.md
|
|
@ -572,21 +572,60 @@ Python在以下领域都有用武之地。
|
|||
|
||||
#### Day81 - [浅谈机器学习](./Day81-90/81.浅谈机器学习.md)
|
||||
|
||||
- 人工智能发展史
|
||||
- 什么是机器学习
|
||||
- 机器学习应用领域
|
||||
- 机器学习的分类
|
||||
- 机器学习的步骤
|
||||
- 第一次机器学习
|
||||
|
||||
#### Day82 - [k最近邻算法](./Day81-90/82.k最近邻算法.md)
|
||||
|
||||
- 距离的度量
|
||||
- 数据集介绍
|
||||
- kNN分类的实现
|
||||
- 模型评估
|
||||
- 参数调优
|
||||
- kNN回归的实现
|
||||
|
||||
#### Day83 - [决策树和随机森林](./Day81-90/83.决策树和随机森林.md)
|
||||
|
||||
- 决策树的构建
|
||||
- 特征选择
|
||||
- 数据分裂
|
||||
- 树的剪枝
|
||||
- 实现决策树模型
|
||||
- 随机森林概述
|
||||
|
||||
#### Day84 - [朴素贝叶斯算法](./Day81-90/84.朴素贝叶斯算法.md)
|
||||
|
||||
#### Day85 - [聚类算法](./Day81-90/85.聚类算法.md)
|
||||
- 贝叶斯定理
|
||||
- 朴素贝叶斯
|
||||
- 算法原理
|
||||
- 训练阶段
|
||||
- 预测阶段
|
||||
- 代码实现
|
||||
- 算法优缺点
|
||||
|
||||
#### Day86 - [支持向量机](./Day81-90/86.支持向量机.md)
|
||||
#### Day85 - [回归模型](./Day81-90/85.回归模型.md)
|
||||
|
||||
#### Day87 - [回归模型](./Day81-90/87.回归模型.md)
|
||||
- 回归模型的分类
|
||||
- 回归系数的计算
|
||||
- 新数据集介绍
|
||||
- 线性回归代码实现
|
||||
- 回归模型的评估
|
||||
- 引入正则化项
|
||||
- 线性回归另一种实现
|
||||
- 多项式回归
|
||||
- 逻辑回归
|
||||
|
||||
#### Day88 - [集成学习算法](./Day81-90/88.集成学习算法.md)
|
||||
#### Day86 - [聚类算法](./Day81-90/86.聚类算法.md)
|
||||
|
||||
#### Day89 - [PyTorch深度学习概述](./Day81-90/89.PyTorch深度学习概述.md)
|
||||
#### Day87 - [集成学习算法](./Day81-90/87.集成学习算法.md)
|
||||
|
||||
#### Day88 - [深度学习概述](./Day81-90/88.深度学习概述.md)
|
||||
|
||||
#### Day89 - [PyTorch深度学习入门](./Day81-90/89.PyTorch深度学习入门.md)
|
||||
|
||||
#### Day90 - [PyTorch深度学习实战](./Day81-90/90.PyTorch深度学习实战.md)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue