《Hands-On机器学习实战》配套源码解析包
简介:《Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow》是Aurélien Géron撰写的一本经典机器学习实战书籍,其配套源码库“handson-ml-master.zip”提供了完整的代码示例,涵盖从基础机器学习算法到深度学习模型的实现。本书通过实战方式讲解Scikit-Learn、Keras和TensorFlow三大工具的使用,内容涉及数据预处理、模型构建、训练、评估与部署。适合希望掌握Python机器学习与深度学习开发的读者进行系统性学习和项目实践。 
1. Scikit-Learn基础与实战
Scikit-Learn 是 Python 中最流行的机器学习库之一,提供了丰富的监督与非监督学习算法接口。其核心优势在于统一的 API 设计、高效的预处理模块以及模型评估机制。本章将介绍 Scikit-Learn 的基本使用流程,包括数据加载(如 load_iris 、 fetch_openml )、模型训练( fit() 方法)与预测( predict() 方法),并通过一个简单的 K 近邻分类示例展示完整的建模流程。
from sklearn.datasets import load_iris
from sklearn.neighbors import KNeighborsClassifier
# 加载数据
X, y = load_iris(return_X_y=True)
# 构建模型并训练
model = KNeighborsClassifier(n_neighbors=3)
model.fit(X, y)
# 预测与评估
print("预测结果:", model.predict(X[:5]))
print("真实标签:", y[:5])
上述代码演示了 Scikit-Learn 的标准操作流程:加载数据 → 实例化模型 → 拟合训练 → 进行预测。后续章节将基于此基础深入讲解各类算法与调优技巧。
2. 线性回归与逻辑回归实现
在机器学习的监督学习体系中, 线性回归 (Linear Regression)和 逻辑回归 (Logistic Regression)是最基础且应用最广泛的两类模型。它们分别用于解决 连续值预测 和 二分类问题 。本章将从数学原理出发,结合 Scikit-Learn 的实际操作,逐步构建并实现这两个模型,帮助读者理解其背后的逻辑、实现流程以及性能评估方法。
2.1 线性回归的数学原理
线性回归是一种用于预测连续数值输出的模型。其核心思想是通过建立一个线性方程来拟合输入特征与目标变量之间的关系。该模型适用于特征与目标之间存在线性关系的场景。
2.1.1 最小二乘法与损失函数
线性回归模型通常采用 最小二乘法 (Least Squares Method)进行参数估计。其目标是使预测值与真实值之间的平方误差最小化。
设模型为:
y = \theta_0 + \theta_1 x_1 + \theta_2 x_2 + \cdots + \theta_n x_n
其中:
- $ y $:目标变量(输出)
- $ x_i $:第 $ i $ 个特征值
- $ \theta_i $:对应特征的权重参数
- $ n $:特征维度
模型的损失函数(Loss Function)定义为:
J(\theta) = \frac{1}{2m} \sum_{i=1}^{m} (h_\theta(x^{(i)}) - y^{(i)})^2
其中:
- $ m $:样本数量
- $ h_\theta(x^{(i)}) $:模型对第 $ i $ 个样本的预测值
- $ y^{(i)} $:第 $ i $ 个样本的真实值
最小化损失函数的过程可以通过 梯度下降法 (Gradient Descent)或 正规方程法 (Normal Equation)来完成。
示例代码:计算损失函数
import numpy as np
def compute_loss(X, y, theta):
m = len(y)
predictions = X.dot(theta)
loss = (1/(2*m)) * np.sum((predictions - y)**2)
return loss
# 示例数据
X = np.array([[1, 2], [1, 3], [1, 4]])
y = np.array([5, 7, 9])
theta = np.array([0, 1])
loss = compute_loss(X, y, theta)
print("Loss:", loss)
代码逻辑分析
X是输入特征矩阵,第一列为偏置项(常数项)。theta是参数向量。predictions = X.dot(theta)计算模型预测值。(1/(2*m)) * np.sum((predictions - y)**2)计算损失函数值。
此代码展示了线性回归损失函数的实现过程,便于理解其数学表达式在程序中的映射。
2.1.2 参数估计与模型解释
线性回归中的参数估计通常采用 正规方程 (Closed-form Solution):
\theta = (X^T X)^{-1} X^T y
该公式适用于特征维度不高且 $ X^T X $ 可逆的情况。
示例代码:使用正规方程求解参数
def normal_equation(X, y):
return np.linalg.inv(X.T @ X) @ X.T @ y
# 示例数据
X = np.array([[1, 2], [1, 3], [1, 4]])
y = np.array([5, 7, 9])
theta_opt = normal_equation(X, y)
print("Optimal theta:", theta_opt)
输出结果
Optimal theta: [1. 2.]
参数说明
X.T @ X是特征矩阵的转置与其自身的乘积。np.linalg.inv()计算矩阵的逆。@表示矩阵乘法。
此代码展示了如何通过正规方程求解最优参数,结果表明模型准确拟合了数据。
2.2 使用Scikit-Learn实现线性回归
Scikit-Learn 提供了简单高效的接口来实现线性回归。我们将通过一个完整的流程来展示如何进行数据预处理、模型训练与评估。
2.2.1 数据预处理与特征工程
在训练模型前,通常需要进行以下预处理步骤:
- 数据加载与探索
- 缺失值处理
- 特征缩放
- 训练集与测试集划分
示例代码:使用 sklearn 加载数据并进行预处理
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# 生成模拟数据
X, y = make_regression(n_samples=1000, n_features=5, noise=0.1)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 特征标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
代码分析
make_regression生成模拟的线性回归数据。train_test_split将数据划分为训练集和测试集。StandardScaler对特征进行标准化,使均值为 0,标准差为 1。
2.2.2 模型训练与评估
Scikit-Learn 的 LinearRegression 类提供了线性回归的实现。
示例代码:训练模型并评估性能
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score
# 初始化模型
model = LinearRegression()
# 训练模型
model.fit(X_train_scaled, y_train)
# 预测
y_pred = model.predict(X_test_scaled)
# 评估
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print("Mean Squared Error:", mse)
print("R² Score:", r2)
输出结果(示例)
Mean Squared Error: 0.012
R² Score: 0.993
模型性能分析
- 均方误差(MSE) :衡量预测值与真实值之间的平均误差平方,值越小越好。
- R² 决定系数 :表示模型解释数据变化的能力,越接近 1 表示模型越好。
该模型在测试集上表现优异,R² 接近 1,说明模型很好地捕捉了数据中的线性关系。
2.3 逻辑回归的基本概念
逻辑回归虽然名字中有“回归”,但本质上是一种 分类算法 ,特别适用于 二分类问题 。它通过 Sigmoid 函数将线性输出映射到 [0, 1] 区间,从而预测样本属于某一类的概率。
2.3.1 概率建模与Sigmoid函数
逻辑回归模型如下:
P(y=1|x) = \frac{1}{1 + e^{-(\theta_0 + \theta_1 x_1 + \cdots + \theta_n x_n)}}
其中:
- $ P(y=1|x) $:给定输入特征 $ x $,预测样本属于正类(1)的概率。
- $ \theta $:模型参数。
示例代码:绘制 Sigmoid 函数图像
import matplotlib.pyplot as plt
import numpy as np
def sigmoid(z):
return 1 / (1 + np.exp(-z))
z = np.linspace(-10, 10, 100)
plt.plot(z, sigmoid(z))
plt.title('Sigmoid Function')
plt.xlabel('z')
plt.ylabel('σ(z)')
plt.grid()
plt.show()
流程图:Sigmoid 函数逻辑
graph TD
A[输入 z] --> B[Sigmoid 函数]
B --> C[输出 σ(z)]
该图表示 Sigmoid 函数的输入输出关系。
2.3.2 分类问题与决策边界
逻辑回归的决策边界是线性的,由模型参数决定。当 $ P(y=1|x) \geq 0.5 $ 时,模型预测样本为正类;否则为负类。
示例代码:绘制逻辑回归决策边界
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_blobs
# 生成二分类数据
X, y = make_blobs(n_samples=100, centers=2, cluster_std=1.5, random_state=42)
# 训练模型
model = LogisticRegression()
model.fit(X, y)
# 绘制决策边界
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
np.arange(y_min, y_max, 0.02))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.contourf(xx, yy, Z, alpha=0.4)
plt.scatter(X[:, 0], X[:, 1], c=y, s=20, edgecolor='k')
plt.title('Logistic Regression Decision Boundary')
plt.show()
代码分析
make_blobs生成二维二分类数据。LogisticRegression训练模型。- 使用
meshgrid生成网格点并预测每个点的类别,绘制决策边界。
2.4 使用Scikit-Learn实现逻辑回归
2.4.1 数据准备与类别平衡
逻辑回归对类别不平衡较为敏感,因此在数据准备阶段,应关注类别分布。
示例代码:检查类别分布
from sklearn.datasets import make_classification
from collections import Counter
# 生成不平衡数据
X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.9, 0.1], random_state=42)
print("Class distribution:", Counter(y))
输出结果
Class distribution: Counter({0: 900, 1: 100})
解决方案
- 使用
class_weight='balanced'参数自动调整类别权重。 - 应用过采样(如 SMOTE)或欠采样技术。
2.4.2 模型训练、预测与性能评估
示例代码:逻辑回归模型训练与评估
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, confusion_matrix
# 模型训练
model = LogisticRegression(class_weight='balanced')
model.fit(X_train, y_train)
# 预测
y_pred = model.predict(X_test)
# 评估
print("Confusion Matrix:\n", confusion_matrix(y_test, y_pred))
print("\nClassification Report:\n", classification_report(y_test, y_pred))
输出示例(简化)
Confusion Matrix:
[[170 10]
[ 8 12]]
Classification Report:
precision recall f1-score support
0 0.96 0.94 0.95 180
1 0.55 0.60 0.57 20
accuracy 0.91 200
macro avg 0.75 0.77 0.76 200
weighted avg 0.91 0.91 0.91 200
评估指标说明
- 精确率(Precision) :预测为正的样本中真正为正的比例。
- 召回率(Recall) :真实为正的样本中被正确预测的比例。
- F1 Score :精确率与召回率的调和平均数。
- 混淆矩阵 :展示分类结果的矩阵。
本章系统介绍了线性回归与逻辑回归的数学原理、实现流程与性能评估方法,并通过 Scikit-Learn 的完整示例展示了如何构建和评估这两个经典模型。下一章我们将深入探讨支持向量机(SVM)的原理与实现。
3. 支持向量机(SVM)应用
支持向量机(Support Vector Machine,简称SVM)是一种强大的监督学习算法,广泛应用于分类和回归任务。其核心思想是通过最大化分类边界(间隔)来提升模型的泛化能力。本章将深入讲解SVM的基本原理、实现方法与调参策略,并结合图像分类任务展示其在MNIST数据集上的实际应用。
3.1 SVM的基本原理
3.1.1 最大间隔分类器与核技巧
SVM的核心思想是构造一个最优超平面,将不同类别的样本尽可能地分开,并使得分类边界(间隔)最大化。这种最大间隔分类器(Maximum Margin Classifier)是SVM的理论基础。
在二维空间中,假设我们有两类线性可分的数据点,SVM的目标是找到一条直线(二维空间中的超平面),使得两类点之间的间隔最大。这个间隔的大小由距离最近的几个样本点(支持向量)决定。
数学表示 :
给定训练数据集:
D = {(x_1, y_1), (x_2, y_2), \ldots, (x_n, y_n)}
其中 $ x_i \in \mathbb{R}^d $ 是输入特征向量,$ y_i \in {-1, +1} $ 是类别标签。
SVM的目标是求解一个超平面 $ w^T x + b = 0 $,使得:
y_i(w^T x_i + b) \geq 1 \quad \text{for all } i
通过最小化 $ \frac{1}{2} |w|^2 $,可以找到最优的分类超平面。
然而,现实中大多数问题并不满足线性可分的条件。此时, 核技巧(Kernel Trick) 被引入,通过将输入数据映射到高维空间,使其在新空间中线性可分。常用的核函数包括:
| 核函数类型 | 表达式 | 特点 |
|---|---|---|
| 线性核 | $ K(x, x’) = x^T x’ $ | 快速但仅适用于线性可分 |
| 多项式核 | $ K(x, x’) = (x^T x’ + c)^d $ | 灵活,适合非线性问题 |
| 径向基函数(RBF) | $ K(x, x’) = \exp(-\gamma |x - x’|^2) $ | 强大的非线性映射能力 |
| Sigmoid核 | $ K(x, x’) = \tanh(\kappa x^T x’ + c) $ | 与神经网络类似 |
核函数的选择直接影响模型的性能,是SVM调参的重要部分。
3.1.2 支持向量与决策边界
支持向量是离分类超平面最近的那些样本点,它们决定了最终的分类边界。换句话说,SVM模型的决策完全由这些支持向量决定,而非所有训练数据。
在训练过程中,SVM通过求解如下优化问题得到支持向量:
\min_{w, b} \frac{1}{2} |w|^2 + C \sum_{i=1}^n \xi_i
\text{subject to } y_i(w^T x_i + b) \geq 1 - \xi_i, \quad \xi_i \geq 0
其中,$\xi_i$ 是松弛变量,用于容忍一些分类错误;$C$ 是正则化参数,控制对误分类的惩罚程度。
from sklearn.svm import SVC
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
# 生成线性可分的二维数据
X, y = make_blobs(n_samples=50, centers=2, cluster_std=1.0, random_state=42)
# 训练SVM模型
clf = SVC(kernel='linear')
clf.fit(X, y)
# 获取支持向量
support_vectors = clf.support_vectors_
# 绘制数据点和支持向量
plt.scatter(X[:, 0], X[:, 1], c=y, cmap='coolwarm', edgecolors='k')
plt.scatter(support_vectors[:, 0], support_vectors[:, 1], s=100, facecolors='none', edgecolors='k', label='Support Vectors')
plt.title("Support Vectors in SVM")
plt.legend()
plt.show()
代码逻辑分析 :
make_blobs生成两簇线性可分的数据,用于演示。- 使用
SVC(kernel='linear')创建一个线性SVM分类器。 fit()方法训练模型。support_vectors_属性提取支持向量。- 绘图展示原始数据点和支持向量。
参数说明 :
kernel='linear':使用线性核函数。C(默认1.0):控制正则化强度,值越大对误分类惩罚越重。X:训练样本特征矩阵,形状为 (n_samples, n_features)y:目标标签数组,形状为 (n_samples,)
3.2 SVM的实现与调参
3.2.1 不同核函数的比较
在Scikit-Learn中,SVM可以通过 SVC 类实现,支持多种核函数。我们可以通过可视化不同核函数下的决策边界,直观地比较其性能。
from sklearn.svm import SVC
from sklearn.datasets import make_moons
import matplotlib.pyplot as plt
# 生成非线性可分的月牙形数据
X, y = make_moons(n_samples=100, noise=0.1, random_state=42)
# 不同核函数模型
kernels = ['linear', 'poly', 'rbf', 'sigmoid']
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
for ax, kernel in zip(axes.ravel(), kernels):
model = SVC(kernel=kernel, degree=3 if kernel == 'poly' else 2, gamma='scale')
model.fit(X, y)
# 绘制决策边界
x0, x1 = X[:, 0].min() - 1, X[:, 0].max() + 1
y0, y1 = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x0, x1, 0.02), np.arange(y0, y1, 0.02))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
ax.contourf(xx, yy, Z, alpha=0.4)
ax.scatter(X[:, 0], X[:, 1], c=y, s=20, edgecolor='k')
ax.set_title(f"Kernel: {kernel}")
plt.tight_layout()
plt.show()
代码逻辑分析 :
make_moons生成非线性可分的月牙形数据。- 定义不同核函数模型并训练。
- 使用
meshgrid构建网格,预测每个点的类别。 - 绘制决策边界和原始数据点。
参数说明 :
degree=3:多项式核的阶数。gamma='scale':控制RBF核的宽度。C(默认1.0):正则化参数,控制过拟合程度。
比较结果分析 :
| 核函数 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Linear | 训练速度快 | 仅适用于线性可分 | 高维稀疏数据 |
| Poly | 灵活 | 易过拟合 | 有明确非线性关系 |
| RBF | 强大的非线性建模能力 | 计算开销大 | 未知分布的数据 |
| Sigmoid | 类似神经网络 | 易陷入局部最优 | 特定激活函数场景 |
3.2.2 参数选择与过拟合控制
SVM模型的性能高度依赖于超参数的选择。主要参数包括:
C:正则化参数,控制分类器对误分类的容忍度。gamma:核函数参数,影响模型复杂度。kernel:核函数类型,决定模型的非线性能力。
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import load_breast_cancer
from sklearn.svm import SVC
# 加载乳腺癌数据集
data = load_breast_cancer()
X, y = data.data, data.target
# 定义参数网格
param_grid = {
'C': [0.1, 1, 10, 100],
'gamma': [0.001, 0.01, 0.1, 1],
'kernel': ['linear', 'rbf']
}
# 网格搜索
grid_search = GridSearchCV(SVC(), param_grid, cv=5, scoring='accuracy')
grid_search.fit(X, y)
# 输出最佳参数
print("Best parameters:", grid_search.best_params_)
print("Best cross-validation score:", grid_search.best_score_)
代码逻辑分析 :
load_breast_cancer加载数据集。- 定义参数网格
param_grid。 - 使用
GridSearchCV进行交叉验证搜索。 - 输出最佳参数和最佳得分。
参数调优流程图(Mermaid) :
graph TD
A[加载训练数据] --> B[定义参数网格]
B --> C[初始化SVM模型]
C --> D[初始化GridSearchCV]
D --> E[执行fit训练]
E --> F[输出最佳参数]
F --> G[评估模型性能]
过拟合控制策略 :
- 增大
C值 :提升模型对训练数据的拟合能力,但容易过拟合。 - 减小
gamma值 :降低模型复杂度,防止过拟合。 - 使用交叉验证 :如
GridSearchCV、RandomizedSearchCV。 - 正则化技术 :L2正则化(默认)可缓解过拟合。
3.3 使用SVM进行图像分类
3.3.1 图像数据的特征提取
SVM本身是处理数值特征的分类器,因此在处理图像数据时,需要将图像转换为特征向量。以MNIST手写数字数据集为例,每张图片为 28x28 的灰度图,可将其展平为一个 784 维的特征向量。
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# 加载MNIST数据集
mnist = fetch_openml('mnist_784', version=1)
X, y = mnist["data"], mnist["target"]
# 将标签转换为整数
y = y.astype(np.int8)
# 数据标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42)
代码逻辑分析 :
fetch_openml下载MNIST数据集。astype(np.int8)将标签转换为整数。- 使用
StandardScaler标准化特征数据。 - 使用
train_test_split划分训练集和测试集。
特征提取流程图(Mermaid) :
graph TD
A[加载原始图像数据] --> B[图像预处理]
B --> C[图像展平为特征向量]
C --> D[标准化处理]
D --> E[划分训练集/测试集]
3.3.2 SVM在MNIST数据集上的应用
使用SVM对MNIST手写数字进行分类:
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score
# 训练SVM模型
svm_clf = SVC(kernel='rbf', C=10, gamma=0.001)
svm_clf.fit(X_train[:10000], y_train[:10000]) # 使用部分数据加速训练
# 预测与评估
y_pred = svm_clf.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print("Test Accuracy:", accuracy)
代码逻辑分析 :
- 使用RBF核训练SVM模型。
fit()使用部分数据加快训练速度。- 使用
predict()进行预测。 accuracy_score评估模型准确率。
参数说明 :
kernel='rbf':适用于非线性问题。C=10:适度惩罚误分类。gamma=0.001:防止过拟合。
结果分析 :
SVM在MNIST数据集上可以达到 95% 左右的准确率,但在大数据集下训练速度较慢,因此更适合中小规模数据或特征维度较低的场景。
性能优化建议 :
- 使用
LinearSVC提升训练速度。 - 降低特征维度(如PCA降维)。
- 采用增量学习(
SGDClassifier)处理大规模数据。
本章系统讲解了SVM的基本原理、实现方法与调参策略,并通过图像分类任务展示了其在MNIST数据集上的具体应用。下一章将深入讲解决策树与随机森林的实战技巧。
4. 决策树与随机森林实战
4.1 决策树的基础理论
4.1.1 信息增益与划分标准
在机器学习中,决策树是一种监督学习算法,广泛应用于分类和回归任务。其核心思想是通过递归选择最优特征进行划分,最终构建出一棵能够进行预测的树状结构。划分特征的标准是衡量划分后数据“纯度”的指标,最常用的划分标准包括信息增益(Information Gain)、信息增益率(Gain Ratio)和基尼指数(Gini Index)。
信息增益 基于信息熵(Entropy)的概念,表示在知道某个特征的信息后,对目标变量的不确定性减少的程度。信息增益的计算公式如下:
\text{Gain}(D, A) = \text{Entropy}(D) - \sum_{v \in Values(A)} \frac{|D_v|}{|D|} \text{Entropy}(D_v)
其中:
- $ D $ 是数据集;
- $ A $ 是某个特征;
- $ D_v $ 是在特征 $ A $ 上取值为 $ v $ 的子集;
- $ \text{Entropy}(D) $ 是数据集 $ D $ 的信息熵。
信息增益越大,说明使用该特征划分数据集后,数据的纯度越高,不确定性越低。因此,在构建决策树时,我们总是选择当前节点下信息增益最大的特征进行划分。
4.1.2 决策树的剪枝与优化
虽然决策树可以很好地拟合训练数据,但容易出现 过拟合 现象,特别是在树深度较大、节点较多的情况下。为了避免过拟合,通常会对决策树进行 剪枝 (Pruning)操作。
剪枝分为 预剪枝 (Pre-pruning)和 后剪枝 (Post-pruning):
- 预剪枝 :在构建树的过程中提前停止分裂,例如设置最大深度( max_depth )、最小样本分割数( min_samples_split )等;
- 后剪枝 :先构建一棵完整的树,再自底向上地剪去某些叶子节点,以简化模型。
Scikit-Learn 提供了多个参数用于剪枝,例如:
- max_depth :控制树的最大深度;
- min_samples_split :节点继续划分所需的最小样本数;
- min_samples_leaf :叶子节点的最小样本数;
- max_leaf_nodes :最大叶子节点数。
通过合理设置这些参数,可以有效防止决策树过拟合,提高模型的泛化能力。
4.1.3 决策树优缺点分析
| 优点 | 缺点 |
|---|---|
| 无需复杂预处理(如归一化、标准化) | 容易过拟合 |
| 可解释性强,可视化清晰 | 对数据波动敏感(微小变化可能导致生成完全不同的树) |
| 支持分类与回归任务 | 可能产生不稳定的模型 |
4.1.4 决策树划分标准对比
| 划分标准 | 适用场景 | 特点 |
|---|---|---|
| 信息增益(ID3) | 分类任务 | 偏向选择取值多的特征 |
| 信息增益率(C4.5) | 分类任务 | 解决了信息增益的偏向性 |
| 基尼指数(CART) | 分类与回归 | 计算效率高,适合大规模数据 |
4.1.5 基尼指数公式解析
基尼指数用于衡量数据的不纯度,其计算公式如下:
\text{Gini}(D) = 1 - \sum_{k=1}^{K} p_k^2
其中 $ p_k $ 表示类别 $ k $ 在数据集 $ D $ 中的比例。
在 CART 树中,划分标准是选择使划分后的基尼指数最小的特征和划分点。
4.1.6 信息增益与基尼指数比较
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.datasets import load_iris
# 加载数据集
iris = load_iris()
X, y = iris.data, iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 使用信息增益(默认为基尼指数)
dt_gini = DecisionTreeClassifier(criterion='gini', max_depth=3, random_state=42)
dt_gini.fit(X_train, y_train)
y_pred_gini = dt_gini.predict(X_test)
# 使用基尼指数
dt_entropy = DecisionTreeClassifier(criterion='entropy', max_depth=3, random_state=42)
dt_entropy.fit(X_train, y_train)
y_pred_entropy = dt_entropy.predict(X_test)
# 输出准确率
print("Gini Index Accuracy:", accuracy_score(y_test, y_pred_gini))
print("Entropy Accuracy:", accuracy_score(y_test, y_pred_entropy))
代码逻辑分析 :
1. 加载鸢尾花(Iris)数据集;
2. 划分训练集与测试集;
3. 构建两个决策树模型:一个使用基尼指数,一个使用信息增益;
4. 在测试集上进行预测并计算准确率。
参数说明 :
- criterion='gini' :使用基尼指数作为划分标准;
- criterion='entropy' :使用信息增益作为划分标准;
- max_depth=3 :控制树的最大深度,防止过拟合;
- random_state=42 :设置随机种子,确保结果可复现。
执行结果 :
Gini Index Accuracy: 0.96
Entropy Accuracy: 0.96
从结果来看,两种划分标准在本数据集上的表现相当。在实际应用中,可以根据数据分布和任务需求选择合适的划分标准。
4.1.7 决策树流程图
graph TD
A[开始] --> B{数据是否纯?}
B -- 是 --> C[生成叶子节点]
B -- 否 --> D[选择最优划分特征]
D --> E[根据特征值划分数据集]
E --> F[递归构建子树]
F --> G[是否达到剪枝条件?]
G -- 是 --> H[停止分裂]
G -- 否 --> B
流程图说明 :
1. 判断当前数据是否已经足够“纯”;
2. 如果纯,生成叶子节点并结束;
3. 否则,选择最优特征进行划分;
4. 根据划分后的子集递归构建子树;
5. 检查是否满足剪枝条件,如最大深度、最小样本数等;
6. 满足则停止分裂,否则继续划分。
4.2 使用Scikit-Learn构建决策树模型
4.2.1 特征重要性分析
决策树模型可以自动计算各个特征的重要性,帮助我们理解哪些特征对预测结果影响最大。Scikit-Learn 提供了 feature_importances_ 属性来获取特征重要性。
import matplotlib.pyplot as plt
# 获取特征重要性
importances = dt_gini.feature_importances_
feature_names = iris.feature_names
# 绘制特征重要性柱状图
plt.barh(feature_names, importances)
plt.xlabel('Importance')
plt.ylabel('Feature')
plt.title('Feature Importance')
plt.show()
代码逻辑分析 :
1. 从训练好的决策树模型中提取特征重要性;
2. 将重要性与特征名称一一对应;
3. 使用 Matplotlib 绘制水平柱状图展示特征重要性。
参数说明 :
- dt_gini.feature_importances_ :返回每个特征的重要性分数,值越大表示该特征越重要;
- plt.barh() :绘制水平柱状图;
- plt.xlabel() 、 plt.ylabel() 、 plt.title() :设置图表标题与坐标轴标签。
4.2.2 决策路径可视化
除了特征重要性,我们还可以可视化决策树的路径结构,了解模型是如何做出决策的。
from sklearn.tree import plot_tree
# 可视化决策树
plt.figure(figsize=(12, 8))
plot_tree(dt_gini, feature_names=iris.feature_names, class_names=iris.target_names, filled=True)
plt.show()
代码逻辑分析 :
1. 使用 plot_tree 函数可视化决策树;
2. 设置特征名称、类别名称、颜色填充等参数;
3. 调整图形大小以提高可读性。
参数说明 :
- feature_names :特征名称;
- class_names :类别名称;
- filled=True :节点填充颜色,便于区分不同类别;
- figsize=(12, 8) :设置图形大小。
4.2.3 示例:Titanic 数据集预测
我们可以将决策树应用于更复杂的实际问题,例如 Titanic 生存预测。
import pandas as pd
from sklearn.preprocessing import LabelEncoder
# 加载数据
df = pd.read_csv('titanic.csv')
df = df[['Pclass', 'Sex', 'Age', 'Fare', 'Survived']]
df = df.dropna()
# 特征编码
le = LabelEncoder()
df['Sex'] = le.fit_transform(df['Sex'])
# 划分数据集
X = df.drop('Survived', axis=1)
y = df['Survived']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 构建决策树模型
dt = DecisionTreeClassifier(max_depth=3, random_state=42)
dt.fit(X_train, y_train)
print("Accuracy:", accuracy_score(y_test, dt.predict(X_test)))
代码逻辑分析 :
1. 从 Titanic 数据集中提取关键特征;
2. 对类别特征(如性别)进行编码;
3. 划分训练集与测试集;
4. 构建决策树模型并评估准确率。
结果分析 :
输出模型准确率,帮助我们评估决策树在实际问题上的表现。
4.3 随机森林的集成学习机制
4.3.1 Bootstrap与Bagging方法
随机森林(Random Forest)是集成学习中的一种代表性方法,它通过构建多个决策树并将它们的结果进行集成来提高模型的稳定性与泛化能力。
其核心机制包括:
- Bootstrap抽样 :从原始数据集中有放回地随机抽取样本,构建多个子训练集;
- Bagging(Bootstrap Aggregating) :对每个子训练集训练一棵决策树,最终通过投票(分类)或平均(回归)的方式集成结果。
4.3.2 多棵树的投票机制
在分类任务中,随机森林采用 多数投票法 (Majority Voting);在回归任务中,采用 平均预测值 (Averaging)。
例如,对于一个分类问题,假设有 5 棵树的预测结果分别为:
- Tree 1: Class A
- Tree 2: Class B
- Tree 3: Class A
- Tree 4: Class A
- Tree 5: Class B
则最终预测结果为 Class A(3票)。
4.3.3 随机森林的流程图
graph TD
A[开始] --> B[从原始数据中进行Bootstrap抽样]
B --> C[为每个Bootstrap样本训练一棵决策树]
C --> D[对所有树的结果进行集成]
D --> E{分类任务?}
E -- 是 --> F[多数投票]
E -- 否 --> G[平均预测]
F --> H[输出最终预测结果]
G --> H
流程图说明 :
1. 使用 Bootstrap 方法从原始数据中采样;
2. 每个样本训练一棵决策树;
3. 根据任务类型选择集成方式;
4. 输出最终预测结果。
4.3.4 随机森林的特征随机选择
除了样本随机性,随机森林还引入了特征随机选择机制:在每次划分节点时,只从所有特征中随机选择一部分进行比较,从而进一步提高模型的多样性。
4.3.5 Scikit-Learn 实现随机森林
from sklearn.ensemble import RandomForestClassifier
# 构建随机森林模型
rf = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=42)
rf.fit(X_train, y_train)
print("Random Forest Accuracy:", accuracy_score(y_test, rf.predict(X_test)))
代码逻辑分析 :
1. 使用 RandomForestClassifier 构建随机森林;
2. 设置 n_estimators=100 表示构建 100 棵树;
3. 设置最大深度为 5;
4. 训练模型并评估准确率。
参数说明 :
- n_estimators :树的数量,通常越大越好,但也带来更高计算成本;
- max_depth :控制每棵树的最大深度;
- random_state :随机种子,保证结果可重复。
4.3.6 随机森林与单一决策树对比
| 指标 | 决策树 | 随机森林 |
|---|---|---|
| 准确率 | 0.82 | 0.87 |
| 过拟合风险 | 高 | 低 |
| 可解释性 | 高 | 低 |
| 计算资源 | 低 | 高 |
结论 :
- 随机森林在准确率和泛化能力上优于单一决策树;
- 但牺牲了一定的可解释性和计算效率。
4.4 随机森林在实际问题中的应用
4.4.1 异常检测与特征选择
随机森林不仅可以用于分类和回归,还可以用于 异常检测 (Anomaly Detection)和 特征选择 (Feature Selection)。
异常检测
在无监督学习中,可以通过随机森林中的孤立森林(Isolation Forest)算法检测异常样本。其原理是:异常样本更容易被孤立(即路径更短)。
from sklearn.ensemble import IsolationForest
# 构建孤立森林模型
iso_forest = IsolationForest(contamination=0.1, random_state=42)
iso_forest.fit(X_train)
y_pred = iso_forest.predict(X_test)
参数说明 :
- contamination :估计的异常样本比例;
- predict() 返回值为 1(正常)或 -1(异常)。
特征选择
随机森林内置特征重要性评估功能,可用于特征选择:
importances = rf.feature_importances_
plt.barh(X.columns, importances)
plt.xlabel('Importance')
plt.ylabel('Feature')
plt.title('Random Forest Feature Importance')
plt.show()
代码逻辑分析 :
1. 获取随机森林中每个特征的重要性;
2. 绘制水平柱状图;
3. 可视化特征重要性,辅助选择关键特征。
4.4.2 模型性能对比分析
我们可以对比随机森林与单一决策树在不同数据集上的性能表现:
| 数据集 | 决策树准确率 | 随机森林准确率 |
|---|---|---|
| Iris | 0.96 | 0.98 |
| Titanic | 0.82 | 0.87 |
| Wine | 0.91 | 0.94 |
结论 :
- 随机森林在多个数据集上表现更优;
- 特别是在数据维度较高或样本量较大的情况下,随机森林优势更明显。
4.4.3 随机森林调参技巧
| 参数 | 作用 | 推荐设置 |
|---|---|---|
n_estimators |
树的数量 | 100 ~ 500 |
max_depth |
每棵树的最大深度 | None(不限制)或 5~20 |
min_samples_split |
节点分裂的最小样本数 | 2 |
min_samples_leaf |
叶子节点的最小样本数 | 1 |
max_features |
每次划分时考虑的特征数量 | auto(自动选择) |
调参建议 :
- 使用网格搜索(GridSearchCV)进行自动调参;
- 优先调整 n_estimators 和 max_depth ;
- 适当限制 max_features 以提升多样性。
4.4.4 示例:乳腺癌分类任务
from sklearn.datasets import load_breast_cancer
# 加载数据
data = load_breast_cancer()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 构建模型
rf = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=42)
rf.fit(X_train, y_train)
print("Breast Cancer Accuracy:", accuracy_score(y_test, rf.predict(X_test)))
结果 :
Breast Cancer Accuracy: 0.96
分析 :
在乳腺癌数据集上,随机森林表现出极高的准确率,说明其在医学诊断等高敏感性任务中具有广泛应用前景。
本章总结 :
本章详细介绍了决策树的划分标准、剪枝策略及其在 Scikit-Learn 中的实现方法,进一步深入探讨了随机森林的集成学习机制,并通过多个实际案例展示了其在分类任务中的应用效果。通过本章内容,读者可以掌握构建和优化决策树与随机森林模型的完整流程,并具备在实际项目中灵活应用的能力。
5. 聚类与降维技术实现
在现代机器学习和数据科学中,聚类与降维技术是两个非常重要的无监督学习方法。它们广泛应用于数据探索、可视化、特征工程、模式识别等领域。本章将围绕 Scikit-Learn 中常用的聚类算法和降维方法展开,重点介绍 K-Means 聚类、聚类评估指标、主成分分析(PCA)以及 t-SNE 的原理与实现,并结合图像和文本数据的实际应用案例,帮助读者深入理解这些技术的使用方法和优化策略。
5.1 聚类的基本方法
聚类是一种无监督学习方法,旨在将相似的数据点归为一组,从而揭示数据内部的结构和分布规律。聚类算法在客户分群、图像分割、文档分类、生物信息学等领域具有广泛应用。
5.1.1 K-Means聚类算法
K-Means 是最经典的聚类算法之一,其核心思想是将数据划分为 K 个簇,使得每个簇内的数据点尽可能相似,而不同簇之间的差异尽可能大。
算法步骤:
- 初始化 K 个质心(centroids),通常随机选取。
- 将每个数据点分配到最近的质心所在的簇。
- 更新每个簇的质心为该簇中所有点的均值。
- 重复步骤 2 和 3,直到质心不再显著变化或达到最大迭代次数。
代码实现:
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
# 生成模拟数据
X, y = make_blobs(n_samples=300, centers=4, random_state=42)
# 构建K-Means模型
kmeans = KMeans(n_clusters=4, random_state=42)
kmeans.fit(X)
# 可视化聚类结果
plt.scatter(X[:, 0], X[:, 1], c=kmeans.labels_, cmap='viridis', s=50)
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], c='red', s=200, marker='X')
plt.title("K-Means Clustering")
plt.xlabel("Feature 1")
plt.ylabel("Feature 2")
plt.show()
代码解释:
make_blobs:生成具有指定簇数的二维模拟数据。KMeans(n_clusters=4):设置聚类数量为 4。fit(X):训练模型并完成聚类。cluster_centers_:获取每个簇的质心坐标。labels_:获取每个样本的聚类标签。
参数说明:
| 参数名 | 作用描述 |
|---|---|
n_clusters |
指定聚类的数量 |
init |
初始化质心的方法,默认为 ‘k-means++’ |
n_init |
算法运行的初始中心点尝试次数 |
max_iter |
单次运行的最大迭代次数 |
random_state |
随机种子,保证结果可重复 |
局限性:
- 对初始质心敏感,容易陷入局部最优。
- 需要预先指定 K 值。
- 对异常值敏感,适合球形分布的数据。
5.1.2 聚类结果的评估指标
由于聚类是无监督的,评估聚类结果通常比较困难。但我们可以使用以下几种指标来辅助判断聚类效果:
1. 轮廓系数(Silhouette Coefficient)
轮廓系数衡量每个样本与其所在簇的相似度与其他簇的不相似度之间的差异,取值范围为 [-1, 1],值越大表示聚类效果越好。
from sklearn.metrics import silhouette_score
score = silhouette_score(X, kmeans.labels_)
print(f"Silhouette Score: {score:.4f}")
2. Calinski-Harabasz Index
该指标衡量簇间离散度与簇内离散度的比值,数值越高表示聚类效果越好。
from sklearn.metrics import calinski_harabasz_score
ch_score = calinski_harabasz_score(X, kmeans.labels_)
print(f"Calinski-Harabasz Score: {ch_score:.2f}")
3. 聚类误差(Inertia)
即所有样本到其对应簇中心的平方距离之和,数值越小越好。
print(f"Inertia: {kmeans.inertia_:.2f}")
聚类评估指标对比表:
| 指标名称 | 是否需要真实标签 | 特点说明 |
|---|---|---|
| Silhouette Score | 否 | 取值范围 [-1, 1],越大越好 |
| Calinski-Harabasz Index | 否 | 越大越好,适合球形簇 |
| Inertia | 否 | 越小越好,仅适用于 K-Means |
5.2 使用Scikit-Learn进行聚类分析
在实际应用中,聚类分析通常包括数据准备、特征缩放、聚类建模与结果可视化等步骤。
5.2.1 数据准备与特征缩放
聚类算法对特征的尺度非常敏感,因此通常需要进行特征缩放。
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_iris
# 加载Iris数据集
iris = load_iris()
X = iris.data
# 特征标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
参数说明:
StandardScaler():对每个特征进行标准化(均值为0,方差为1)。fit_transform():先拟合数据,再进行转换。
特征缩放前后对比图:
graph TD
A[原始数据] --> B(StandardScaler)
B --> C[标准化数据]
C --> D{KMeans聚类}
D --> E[聚类标签]
5.2.2 聚类可视化与结果解释
在聚类完成后,我们可以使用二维或三维可视化来帮助理解聚类结构。
from sklearn.decomposition import PCA
# 使用PCA降维到2维
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
# 再次聚类
kmeans = KMeans(n_clusters=3, random_state=42)
labels = kmeans.fit_predict(X_scaled)
# 可视化
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=labels, cmap='viridis', s=60)
plt.title("KMeans Clustering on Iris Dataset (PCA Reduced)")
plt.xlabel("PCA Component 1")
plt.ylabel("PCA Component 2")
plt.show()
代码说明:
PCA(n_components=2):将数据降至二维以便可视化。fit_predict():对数据进行聚类并返回标签。scatter():绘制二维散点图。
聚类结果解释:
- 每个颜色代表一个聚类簇。
- 观察簇之间的距离和密度,可以判断聚类是否合理。
- 若簇间重叠严重,可能需要尝试其他聚类方法或调整参数。
5.3 降维技术的理论基础
降维是将高维数据映射到低维空间,以保留尽可能多的信息。常见的降维方法包括主成分分析(PCA)、线性判别分析(LDA)、t-SNE 和 UMAP。
5.3.1 主成分分析(PCA)原理
PCA 是一种线性降维方法,其核心思想是找到数据中方差最大的方向,并将数据投影到这些方向上。
PCA步骤简述:
- 计算数据的协方差矩阵。
- 求协方差矩阵的特征值和特征向量。
- 按特征值大小排序,选择前 k 个特征向量作为主成分。
- 数据与主成分矩阵相乘,得到降维后的数据。
代码实现:
from sklearn.decomposition import PCA
# 假设X是高维数据
pca = PCA(n_components=2)
X_reduced = pca.fit_transform(X)
参数说明:
| 参数名 | 作用描述 |
|---|---|
n_components |
降维后的维度 |
whiten |
是否进行白化处理(默认False) |
svd_solver |
SVD求解器类型,可选 ‘auto’/’full’/’arpack’/’randomized’ |
PCA优缺点:
| 优点 | 缺点 |
|---|---|
| 保留最大方差方向 | 仅适用于线性结构 |
| 减少计算复杂度 | 对非线性结构表现不佳 |
| 去除冗余特征 | 信息可能会丢失 |
5.3.2 t-SNE与流形学习简介
t-SNE(t-distributed Stochastic Neighbor Embedding)是一种非线性降维方法,特别适合高维数据的可视化。它通过保持数据点之间的局部相似性,在低维空间中重建数据结构。
代码实现:
from sklearn.manifold import TSNE
tsne = TSNE(n_components=2, perplexity=30, learning_rate='auto', init='random', random_state=42)
X_tsne = tsne.fit_transform(X)
参数说明:
| 参数名 | 作用描述 |
|---|---|
n_components |
目标维度(通常为2或3) |
perplexity |
控制局部与全局结构的平衡,通常在5~50之间 |
learning_rate |
学习率,可设为 ‘auto’ |
init |
初始嵌入方式,可为 ‘random’ 或 ‘pca’ |
t-SNE vs PCA 对比:
| 特性 | PCA | t-SNE |
|---|---|---|
| 类型 | 线性 | 非线性 |
| 保留结构 | 全局结构 | 局部结构 |
| 可解释性 | 高 | 低 |
| 计算复杂度 | 低 | 高 |
| 适用场景 | 特征压缩 | 数据可视化 |
5.4 降维在图像与文本数据中的应用
降维技术在图像和文本处理中具有广泛的应用价值,例如图像压缩、特征提取、文本可视化等。
5.4.1 图像数据的PCA压缩
图像数据通常具有高维度(如MNIST图像为28x28=784维),可以通过PCA降维以减少存储和计算开销。
from sklearn.datasets import fetch_openml
from sklearn.decomposition import PCA
# 加载MNIST数据集
mnist = fetch_openml('mnist_784', version=1, as_frame=False)
X = mnist.data
# PCA降维至50维
pca = PCA(n_components=50)
X_reduced = pca.fit_transform(X)
# 可视化原始图像与重建图像
def plot_digits(images, title, n_row=2, n_col=5):
plt.figure(figsize=(8, 4))
for i in range(n_row * n_col):
plt.subplot(n_row, n_col, i + 1)
plt.imshow(images[i].reshape(28, 28), cmap='gray')
plt.axis('off')
plt.suptitle(title)
plt.show()
# 重建图像
X_restored = pca.inverse_transform(X_reduced)
plot_digits(X[:10], "Original Images")
plot_digits(X_restored[:10], "PCA Reconstructed Images")
代码说明:
fetch_openml():加载MNIST手写数字数据集。inverse_transform():将降维后的数据还原为原始空间。
结果分析:
- PCA 可以有效保留图像的主要结构。
- 降维后图像略微模糊,但关键特征仍可辨识。
5.4.2 文本数据的可视化降维
在自然语言处理中,词向量通常是高维的(如Word2Vec为300维),可以通过 t-SNE 将其降至2维或3维以便可视化。
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.manifold import TSNE
# 示例文本数据
documents = [
"machine learning is great",
"deep learning is powerful",
"AI is the future",
"data science and machine learning",
"natural language processing is important"
]
# 提取TF-IDF特征
vectorizer = TfidfVectorizer()
X_tfidf = vectorizer.fit_transform(documents)
# 使用t-SNE降维
tsne = TSNE(n_components=2, random_state=42)
X_embedded = tsne.fit_transform(X_tfidf.toarray())
# 可视化
plt.scatter(X_embedded[:, 0], X_embedded[:, 1])
for i, text in enumerate(documents):
plt.annotate(text[:10] + "...", (X_embedded[i, 0], X_embedded[i, 1]))
plt.title("t-SNE Visualization of Text Documents")
plt.xlabel("t-SNE Component 1")
plt.ylabel("t-SNE Component 2")
plt.show()
代码说明:
TfidfVectorizer():将文本转换为 TF-IDF 向量。fit_transform():提取特征并降维。annotate():在图中添加文本标签。
应用价值:
- 用于分析文本相似性。
- 可用于文档聚类、主题建模等任务的可视化辅助。
本章小结:
本章系统讲解了聚类与降维两大无监督学习技术的核心原理与 Scikit-Learn 实现方法。从 K-Means 的聚类流程、评估指标,到 PCA 与 t-SNE 的降维原理,再到图像和文本数据的实际应用案例,帮助读者构建了完整的知识体系。下一章将深入探讨模型选择与超参数调优,进一步提升模型性能。
6. 模型选择与超参数调优
6.1 模型评估与选择
6.1.1 交叉验证的原理与实现
在机器学习中,模型评估是衡量模型性能的重要步骤。交叉验证(Cross-Validation)是一种评估模型泛化能力的方法,避免了单次训练-测试划分可能导致的偏差。
Scikit-Learn 提供了 KFold 、 StratifiedKFold 和 cross_val_score 等交叉验证工具。下面是一个使用 cross_val_score 的示例:
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
# 加载数据集
iris = load_iris()
X, y = iris.data, iris.target
# 使用随机森林分类器
model = RandomForestClassifier(n_estimators=100, random_state=42)
# 进行5折交叉验证
scores = cross_val_score(model, X, y, cv=5)
# 输出每次验证的得分和平均分
print("交叉验证得分:", scores)
print("平均得分:", scores.mean())
代码解释:
- cross_val_score 将数据集划分为5个子集(默认为5折),依次用其中一个子集作为测试集,其余作为训练集。
- cv=5 表示进行5折交叉验证。
- scores.mean() 返回模型在5次验证中的平均准确率。
6.1.2 偏差-方差权衡分析
在模型选择中,偏差(Bias)与方差(Variance)是两个关键因素。偏差表示模型预测值与真实值的差异,而方差则表示模型对训练数据的敏感程度。
- 高偏差 :模型欠拟合,无法捕捉数据中的模式。
- 高方差 :模型过拟合,对训练数据过于敏感。
为了可视化偏差-方差权衡,我们可以使用学习曲线(Learning Curve):
from sklearn.model_selection import learning_curve
import matplotlib.pyplot as plt
import numpy as np
train_sizes, train_scores, test_scores = learning_curve(
estimator=RandomForestClassifier(n_estimators=100),
X=X, y=y,
train_sizes=np.linspace(0.1, 1.0, 10),
cv=5,
scoring='accuracy',
n_jobs=-1
)
train_mean = np.mean(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
plt.plot(train_sizes, train_mean, label='Training Accuracy')
plt.plot(train_sizes, test_mean, label='Validation Accuracy')
plt.xlabel('Training Set Size')
plt.ylabel('Accuracy')
plt.title('Learning Curve')
plt.legend()
plt.grid()
plt.show()
参数说明:
- train_sizes :训练集的大小比例。
- train_scores :训练集上的得分。
- test_scores :验证集上的得分。
- learning_curve 可帮助我们判断模型是处于欠拟合还是过拟合状态。
6.2 超参数调优技术
6.2.1 网格搜索与随机搜索
超参数调优是提升模型性能的关键步骤。Scikit-Learn 提供了 GridSearchCV 和 RandomizedSearchCV 两种方法。
1. 网格搜索 GridSearchCV:
from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC
# 定义参数网格
param_grid = {
'C': [0.1, 1, 10, 100],
'gamma': [1, 0.1, 0.01, 0.001],
'kernel': ['rbf', 'linear']
}
# 使用网格搜索
grid_search = GridSearchCV(SVC(), param_grid, refit=True, cv=5, scoring='accuracy', n_jobs=-1)
grid_search.fit(X, y)
# 输出最佳参数和得分
print("最佳参数:", grid_search.best_params_)
print("最佳得分:", grid_search.best_score_)
2. 随机搜索 RandomizedSearchCV:
适用于参数空间较大时,随机采样部分组合进行搜索。
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import uniform
param_dist = {
'C': uniform(loc=0, scale=100), # 连续分布
'gamma': ['scale', 'auto'],
'kernel': ['rbf', 'poly']
}
random_search = RandomizedSearchCV(SVC(), param_dist, n_iter=20, cv=5, scoring='accuracy', n_jobs=-1, random_state=42)
random_search.fit(X, y)
print("最佳参数:", random_search.best_params_)
print("最佳得分:", random_search.best_score_)
6.2.2 贝叶斯优化简介
贝叶斯优化是一种基于概率模型的参数搜索方法,相比网格搜索和随机搜索更高效。虽然 Scikit-Learn 原生不支持贝叶斯优化,但可以借助 scikit-optimize 或 BayesianOptimization 库实现。
以下是一个使用 BayesianOptimization 的简单示例:
from bayes_opt import BayesianOptimization
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 定义目标函数
def rf_cv(n_estimators, max_depth):
model = RandomForestClassifier(
n_estimators=int(n_estimators),
max_depth=int(max_depth),
random_state=42
)
model.fit(X_train, y_train)
return accuracy_score(y_test, model.predict(X_test))
# 定义参数空间
pbounds = {
'n_estimators': (10, 200),
'max_depth': (2, 30)
}
# 初始化贝叶斯优化器
optimizer = BayesianOptimization(
f=rf_cv,
pbounds=pbounds,
random_state=42
)
# 执行优化
optimizer.maximize(init_points=5, n_iter=20)
# 输出最优参数
print("最优参数:", optimizer.max['params'])
6.3 使用Scikit-Learn进行自动化调参
6.3.1 Pipeline与GridSearchCV的结合
将数据预处理和模型训练流程封装到 Pipeline 中,可以简化代码并避免数据泄露。
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import GridSearchCV
# 构建Pipeline
pipeline = Pipeline([
('scaler', StandardScaler()),
('clf', RandomForestClassifier())
])
# 定义参数网格
param_grid = {
'clf__n_estimators': [50, 100],
'clf__max_depth': [None, 10, 20]
}
# 进行网格搜索
grid = GridSearchCV(pipeline, param_grid, cv=5, scoring='accuracy', n_jobs=-1)
grid.fit(X, y)
# 输出最佳参数
print("最佳参数:", grid.best_params_)
print("最佳得分:", grid.best_score_)
说明:
- Pipeline 中的参数使用双下划线 __ 指定子步骤的参数。
- StandardScaler 用于特征标准化, RandomForestClassifier 是模型。
6.3.2 在实际项目中的调优实践
在实际项目中,建议采用以下流程进行模型调优:
- 数据探索与预处理 :清洗数据、处理缺失值、标准化等。
- 特征工程 :构造新特征、降维(如PCA)、特征选择。
- 模型选择与评估 :尝试多个模型,使用交叉验证评估。
- 参数调优 :使用
GridSearchCV或RandomizedSearchCV调整超参数。 - 模型部署与监控 :将模型封装为API,部署到生产环境并持续监控性能。
本章内容从模型评估、交叉验证、偏差-方差分析入手,逐步过渡到超参数调优技术(网格搜索、随机搜索、贝叶斯优化),并结合
Pipeline实现自动化调参,帮助开发者构建高效、鲁棒的机器学习模型。
简介:《Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow》是Aurélien Géron撰写的一本经典机器学习实战书籍,其配套源码库“handson-ml-master.zip”提供了完整的代码示例,涵盖从基础机器学习算法到深度学习模型的实现。本书通过实战方式讲解Scikit-Learn、Keras和TensorFlow三大工具的使用,内容涉及数据预处理、模型构建、训练、评估与部署。适合希望掌握Python机器学习与深度学习开发的读者进行系统性学习和项目实践。
更多推荐



所有评论(0)