18  分类和预测

分类预测是机器学习中一个核心任务,其目标是构建一个能够准确识别未知样本类别的模型。在分类问题中,我们通常拥有一组已知类别的样本数据,这些数据包括样本的属性(如特征或输入变量)和相应的类别标签。例如,在Iris数据集中,样本的属性包括花瓣和花萼的长度和宽度,而类别则是花的品种。

建模过程首先涉及使用这些已知类别的样本(训练数据)来训练一个分类模型。通过这种方式,模型学习如何根据样本的属性来预测样本的类别。一旦模型被训练完成,它就可以用来评估其在新的、未见过的数据(测试数据)上的准确率,从而验证模型的泛化能力。长远来看,这个训练好的模型将用于预测那些只有属性但没有类别标签的新样本的类别,实现对未知数据的分类。这种能力使得分类预测在许多实际应用中成为一种强大的工具,例如在生物学、金融风控、图像识别等领域。

说人话:我们有一大堆样本和每个样本所属类别。我们对现有样本进行建模,以判断新样本可能是什么类别。

18.1 Python分类实现

  1. 数据集简介

Iris 数据集是机器学习和统计分类技术中广泛使用的一个经典数据集。它包含150个样本,每个样本有四个特征:花萼长度(sepal_length)、花萼宽度(sepal_width)、花瓣长度(petal_length)和花瓣宽度(petal_width)。这些特征被用来预测样本属于三种鸢尾花(Setosa、Versicolor、和Virginica)中的哪一种。

  1. 数据预处理

数据加载

使用 seaborn 库加载 Iris 数据集:

import seaborn as sns
import pandas as pd
from sklearn.model_selection import train_test_split

# 使用seaborn加载数据集
iris = sns.load_dataset('iris')

# 显示数据集的前几行以确认加载成功
iris.head()
sepal_length sepal_width petal_length petal_width species
0 5.1 3.5 1.4 0.2 setosa
1 4.9 3.0 1.4 0.2 setosa
2 4.7 3.2 1.3 0.2 setosa
3 4.6 3.1 1.5 0.2 setosa
4 5.0 3.6 1.4 0.2 setosa

数据探索

探索数据的基本特性,包括每种类型的样本分布和基本统计描述:

# 输出数据集的基本描述性统计
print(iris.describe())

# 查看各品种的数量
print(iris['species'].value_counts())
       sepal_length  sepal_width  petal_length  petal_width
count    150.000000   150.000000    150.000000   150.000000
mean       5.843333     3.057333      3.758000     1.199333
std        0.828066     0.435866      1.765298     0.762238
min        4.300000     2.000000      1.000000     0.100000
25%        5.100000     2.800000      1.600000     0.300000
50%        5.800000     3.000000      4.350000     1.300000
75%        6.400000     3.300000      5.100000     1.800000
max        7.900000     4.400000      6.900000     2.500000
species
setosa        50
versicolor    50
virginica     50
Name: count, dtype: int64

数据准备

为了验证模型的性能,我们需要将数据集分为训练集和测试集:

# 特征和目标变量分离
X = iris.drop('species', axis=1)
y = iris['species']

# 划分数据集为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 注意:random_state即随机数种子。同一个种子数,每次进行随机分隔的结果都会完全一样,可以实现“可重现研究”
# 不写这个参数问题也不大
  1. 逻辑回归模型
  2. 逻辑回归是一种统计模型,常用于处理分类问题,尤其适用于二分类问题。在本例中,虽然Iris数据集包含三个类别,逻辑回归可以通过技术手段如“一对多”(One-vs-Rest)来适应多类别分类任务。

模型建立

首先,我们使用 scikit-learn 的 LogisticRegression 类来创建逻辑回归模型:

from sklearn.linear_model import LogisticRegression

# 创建逻辑回归模型实例
model = LogisticRegression(max_iter=200)

# 这里的 max_iter 参数设置为200,以确保算法有足够的迭代次数来收敛,特别是在处理多分类问题时。

模型训练

接下来,我们将使用训练数据集来训练逻辑回归模型:

# 训练模型
model.fit(X_train, y_train)
LogisticRegression(max_iter=200)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.

模型预测

模型训练完成后,我们使用测试集来进行预测:

# 在测试集上进行预测
y_pred = model.predict(X_test)

性能评估

为了评估模型的性能,我们将计算几个关键的指标,如准确率、混淆矩阵和分类报告:

from sklearn.metrics import accuracy_score

# 计算准确率
accuracy = accuracy_score(y_test, y_pred)
print("准确率:", accuracy)
准确率: 1.0
y_pred
array(['versicolor', 'setosa', 'virginica', 'versicolor', 'versicolor',
       'setosa', 'versicolor', 'virginica', 'versicolor', 'versicolor',
       'virginica', 'setosa', 'setosa', 'setosa', 'setosa', 'versicolor',
       'virginica', 'versicolor', 'versicolor', 'virginica', 'setosa',
       'virginica', 'setosa', 'virginica', 'virginica', 'virginica',
       'virginica', 'virginica', 'setosa', 'setosa', 'setosa', 'setosa',
       'versicolor', 'setosa', 'setosa', 'virginica', 'versicolor',
       'setosa', 'setosa', 'setosa', 'virginica', 'versicolor',
       'versicolor', 'setosa', 'setosa'], dtype=object)

准确率100%,一般不会这么高。

如果测试数据是全新的,未知分类的数据,那么我们可以把测试数据和预测到的分类进行合并。

# y_pred是一个ndarray,合并前先构造一个Series
y_series = pd.Series(y_pred,name = 'species')

# 粘贴到一起。注意X_test经过抽样,因此index是乱序
# 需要把index重置成0,1,2 ... ,才能正确粘贴到一起

print('分类结果如下:')
pd.concat([X_test.reset_index(drop=True), 
           y_series],axis=1).head()
分类结果如下:
total_bill size sex smoker day species
0 19.82 2 1 0 1 versicolor
1 8.77 2 1 0 2 setosa
2 24.55 4 1 0 2 virginica
3 25.89 4 1 1 1 versicolor
4 13.00 2 0 1 3 versicolor

18.2 其他分类模型

在机器学习中,有多种分类算法适用于不同的问题和数据集。下面是一些常用的分类算法以及对应的 scikit-learn 模块或函数,同时也提供了每种算法的典型应用场景:

  1. 决策树(Decision Trees)
    • 模块/函数from sklearn.tree import DecisionTreeClassifier
    • 适用场合:当你需要一个模型可以直观展示和解释决策过程时,决策树是一个好选择。它们适用于特征关系较为简单且不需要大量数据预处理的情形。
  2. 支持向量机(SVM)
    • 模块/函数from sklearn.svm import SVC
    • 适用场合:适用于高维空间的数据分类,尤其在数据维度高于样本数的情况下表现良好。支持向量机对于边界清晰的分类问题特别有效。
  3. 随机森林(Random Forests)
    • 模块/函数from sklearn.ensemble import RandomForestClassifier
    • 适用场合:适合处理存在大量数据且数据特征含有多个类别变量的问题。随机森林对于过拟合的抵抗力较强,适用于多分类问题。
  4. K-最近邻(K-Nearest Neighbors, KNN)
    • 模块/函数from sklearn.neighbors import KNeighborsClassifier
    • 适用场合:适用于数据集较小,问题简单直观的场景。KNN易于理解和实现,但对大规模数据处理效率较低。
  5. 朴素贝叶斯(Naive Bayes)
    • 模块/函数from sklearn.naive_bayes import GaussianNB
    • 适用场合:适合于文本数据和多类别问题,特别是在文本分类和垃圾邮件检测等领域表现优秀。朴素贝叶斯对于数据的分布有一定的假设。
  6. 梯度提升树(Gradient Boosting)
    • 模块/函数from sklearn.ensemble import GradientBoostingClassifier
    • 适用场合:适用于各种分类问题,特别是需要高精度预测时。梯度提升树在提升模型的准确率方面非常有效,但可能需要较长的训练时间和调参。
  7. 神经网络(Neural Networks)
    • 模块/函数from sklearn.neural_network import MLPClassifier
    • 适用场合:适合处理大规模复杂数据,如图像和声音处理。神经网络强大的模型容量使其在处理高度非线性问题时特别有效,但也容易过拟合且参数调整复杂。

18.3 更复杂的例子

因子可能不完全是数值,可能还包括分类变量。

在tips数据集中,如果我们选择一个因子来预测类别变量,可以将time(用餐时间,如午餐或晚餐)作为预测目标,即类别变量。使用其他变量如total_bill(总账单金额)、size(用餐人数)、sex(性别)、smoker(是否吸烟)等作为预测因子。

可以构建一个分类模型来预测顾客是在午餐时间还是晚餐时间用餐,基于他们的账单金额、用餐人数、性别和是否吸烟等信息。这样的模型可以帮助了解哪些因素与用餐时间有较强的关联性,进而对餐厅的运营策略提供数据支持。

import seaborn as sns
import pandas as pd

# 加载数据集
tips = sns.load_dataset('tips')

# 查看数据集的前几行
tips.head()
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4

注意到,有几个变量是分类变量,因此我们需要转为数字。

import seaborn as sns
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import accuracy_score

# 加载数据集
tips = sns.load_dataset('tips')

# 将分类变量转换为数字
encoder = LabelEncoder()
tips['sex'] = encoder.fit_transform(tips['sex'])
tips['smoker'] = encoder.fit_transform(tips['smoker'])
tips['day'] = encoder.fit_transform(tips['day'])
tips['time'] = encoder.fit_transform(tips['time'])

tips.head()
total_bill tip sex smoker day time size
0 16.99 1.01 0 0 2 0 2
1 10.34 1.66 1 0 2 0 3
2 21.01 3.50 1 0 2 0 3
3 23.68 3.31 1 0 2 0 2
4 24.59 3.61 0 0 2 0 4

其他过程就和第一个例子相同


# 选择特征和标签
X = tips[['total_bill', 'size', 'sex', 'smoker', 'day']]
y = tips['time']

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 创建并训练模型
model = LogisticRegression()
model.fit(X_train, y_train)

# 预测测试集
predictions = model.predict(X_test)

# 计算准确率
accuracy = accuracy_score(y_test, predictions)
print("准确率:", accuracy)
准确率: 0.9459459459459459

18.4 模型的对比

我们可以比较逻辑回归和支持向量机这两个模型,看看哪个预测效果更好。

注意,不同的模型的接口是一样的(函数和参数的形式一样),因此代码几乎相同。

from sklearn.svm import SVC

# 创建并训练模型,为了和前面的变量区分,这里的全部变量都加入svc的后缀
model_svc = SVC()
model_svc.fit(X_train, y_train)

# 预测测试集
predictions_svc = model_svc.predict(X_test)

# 计算准确率
accuracy_svc = accuracy_score(y_test, predictions_svc)
print("SVC准确率:", accuracy_svc)
SVC准确率: 0.7432432432432432

可见,在这个数据上,逻辑回归模型模型的准确率高于SVC模型。后续的预测可能前者更好。

18.5 竞赛题

作为作业。