AI_机器学习-2.Scikit-learn实战
Scikit-learn 实战
标签: #Python #Scikit-learn #机器学习 #数据预处理 #模型训练
学习周期:3 天 | 核心目标:掌握 Scikit-learn 完整机器学习流程,能独立完成数据预处理、模型训练、评估与调参
5.2 Scikit-learn 实战
Scikit-learn(sklearn)是 Python 机器学习工业级标准库,提供统一的 API 接口、丰富的数据预处理工具和经典算法实现。本章通过实战代码,带你快速上手完整机器学习流程。
安装与导入
pip install scikit-learn
import numpy as np
import pandas as pd
from sklearn import datasets, preprocessing, model_selection, linear_model, tree, ensemble, svm, cluster
import matplotlib.pyplot as plt
5.2.1 数据预处理
机器学习三大定律:数据决定上限,模型逼近上限。 原始数据通常需要预处理才能用于模型训练。
1. 标准化(StandardScaler)
一、什么是标准化?
标准化(Standardization):是一种数据预处理方法,将原始数据按特征列转换为均值为0、标准差为1的分布。其计算公式为:
x' = \frac{x - \mu}{\sigma}
其中:
- x 是原始特征值
- \mu 是该特征列的均值
- \sigma 是该特征列的标准差
标准化后的数据服从标准正态分布(但不要求原始数据为正态分布)。
二、为什么要做标准化?
在实际数据中,不同特征的取值范围可能差异巨大。例如:
- 特征A:年龄(0~100)
- 特征B:年收入(0~1,000,000)
如果不进行标准化,直接使用原始数据进行机器学习会导致以下问题:
1. 量纲大的特征主导模型
基于距离的算法(KNN、K-Means、SVM)或基于梯度的优化算法(线性回归、逻辑回归、神经网络)中,取值大的特征会贡献更大的误差项。模型会“偏向”大数值特征,而忽略小数值特征中包含的有用信息。
2. 梯度下降收敛缓慢
损失函数的等高线在特征尺度不一致时呈椭圆形,梯度更新方向会来回震荡,需要更多迭代才能收敛。标准化后等高线更接近圆形,梯度方向更直接,加速收敛。
3. 正则化效果失衡
L1/L2正则化对每个特征的惩罚力度相同。如果特征尺度不同,大尺度特征的自然系数会更小,导致正则化不公平地影响了不同特征。
4. 某些算法强制要求标准化
主成分分析(PCA)需要数据中心化;线性判别分析(LDA)、SVM、逻辑回归等对特征尺度敏感。
三、标准化后的数据性质
标准化是一种线性变换,它改变数据的中心位置和分散程度,但保持内部关系不变。
| 性质 | 说明 |
|---|---|
| 均值变为0 | 所有特征都中心化到原点 |
| 标准差变为1 | 所有特征具有相同的离散程度(方差齐性) |
| 相对顺序不变 | 标准化不改变数据的排序 |
| 相关系数不变 | 任意两个特征之间的相关系数与标准化前完全相同 |
| 分布形状不变 | 只是平移和缩放,不改变分布形态(如偏度、峰度) |
| 不同特征可比 | 消除量纲后,可以直接比较系数或计算距离 |
示例:原始数据 [1,2,3,4],均值 2.5,样本标准差 \approx 1.291,标准化后得到 [-1.161, -0.387, 0.387, 1.161](近似值),均值 0,标准差 1。
四、标准差的详细计算
标准差是衡量数据离散程度的统计量,表示各数据点偏离平均值的平均距离。
总体标准差(Population Standard Deviation)
当数据代表整个总体时使用,记为 \sigma:
\sigma = \sqrt{\frac{1}{n} \sum_{i=1}^{n} (x_i - \mu)^2}
其中 \mu 是总体均值,n 是数据个数。
样本标准差(Sample Standard Deviation)
当数据是从总体中抽取的样本时使用,记为 s:
s = \sqrt{\frac{1}{n-1} \sum_{i=1}^{n} (x_i - \bar{x})^2}
分母使用 n-1 称为贝塞尔校正,目的是使样本标准差成为总体标准差的无偏估计。
计算步骤(以样本标准差为例)
- 计算均值 \bar{x} = \frac{1}{n}\sum_{i=1}^{n} x_i
- 计算每个数据与均值的差 x_i - \bar{x}
- 计算差的平方 (x_i - \bar{x})^2
- 求和 \sum (x_i - \bar{x})^2
- 除以 n-1 得到样本方差 s^2
- 开平方根得到样本标准差 s
手动计算示例
数据集:[1, 2, 3, 4]
- 均值 \bar{x} = 2.5
- 差:[-1.5, -0.5, 0.5, 1.5]
- 平方:[2.25, 0.25, 0.25, 2.25]
- 平方和:5.0
- 样本方差:5.0 / (4-1) = 1.6667
- 样本标准差:\sqrt{1.6667} \approx 1.291
标准差 vs 方差
| 指标 | 公式 | 特点 |
|---|---|---|
| 方差 | \sigma^2 或 s^2 | 单位是原始单位的平方,不便直接解释 |
| 标准差 | \sigma 或 s | 与原始数据同单位,更直观 |
例如:身高数据若以厘米为单位,方差是平方厘米,而标准差就是厘米,可以直接说明“数据平均偏离均值多少厘米”。
五、标准化与标准差的联系
在标准化公式 x' = (x - \bar{x}) / s 中,分母使用的是样本标准差(即 s = \sqrt{\frac{1}{n-1}\sum (x_i - \bar{x})^2})。因此标准化后的数据:
- 均值 \bar{x}' = 0
- 样本标准差 s' = 1
这正是通过除以标准差实现的:它将原始数据的离散程度缩放为单位尺度。
六、什么时候不需要标准化?
标准化不是万能的,以下情况可以不进行标准化:
- 基于树的模型:决策树、随机森林、梯度提升树(XGBoost、LightGBM)对特征尺度不敏感,因为它们基于特征值分割,不受单调变换影响。
- 特征具有明确物理意义且需要原始单位:例如线性回归中需要解释“每增加1个单位,目标变化多少”,此时不应标准化。
- 数据已经是同一尺度:如图像像素值都在0~255之间。
七、总结
| 问题 | 答案 |
|---|---|
| 标准化做了什么? | 将每个特征的均值变为0,标准差变为1 |
| 为什么需要? | 消除量纲影响,加速收敛,公平正则化,满足算法假设 |
| 数据性质变化? | 均值、标准差改变,但相关性、顺序、分布形态不变 |
| 标准差如何计算? | 总体用 n,样本用 n-1,开平方根 |
| 是否总是需要? | 不是,树模型和需要原始单位的场景可不做 |
标准化是数据预处理的基石之一,掌握其原理和使用场景,能显著提升机器学习模型的效果与稳定性。
八、代码示例
# 从 sklearn.preprocessing 模块导入 StandardScaler 类,用于标准化特征(使每个特征均值为0,方差为1)
from sklearn.preprocessing import StandardScaler
# 定义一个原始数据集,包含4个样本,每个样本有2个特征
# 第一列特征:[1, 2, 3, 4];第二列特征:[2, 3, 4, 5]
data = [[1, 2], [2, 3], [3, 4], [4, 5]]
# 创建 StandardScaler 对象,用于计算训练数据的均值和标准差,并执行标准化
scaler = StandardScaler()
# 调用 fit_transform 方法:先拟合数据(计算每个特征的均值和标准差),再转换数据(减去均值并除以标准差)
# 返回标准化后的数组,形状与 data 相同
scaled_data = scaler.fit_transform(data)
# 打印标准化后数据的均值(按列计算),由于标准化操作,每个特征的均值应接近于0(可能有浮点误差)
print("标准化后均值:", scaled_data.mean(axis=0))
# 打印标准化后数据的标准差(按列计算),每个特征的标准差应为1
print("标准化后标准差:", scaled_data.std(axis=0))
2. 归一化(MinMaxScaler)
一、什么是归一化?
归一化(Normalization),也称最小-最大缩放(Min-Max Scaling),是一种将特征数据按线性比例缩放到指定区间(通常为 [0, 1])的方法。其核心公式为:
x' = \frac{x - \min(x)}{\max(x) - \min(x)}
如果希望缩放到任意区间 [a, b],则:
x' = a + \frac{(x - \min(x)) \cdot (b - a)}{\max(x) - \min(x)}
其中:
- x 是原始特征值
- \min(x) 和 \max(x) 分别是该特征列的最小值和最大值
- x' 是缩放后的值
归一化不改变数据的内部相对关系,仅通过平移和缩放将数据“压缩”到目标区间。
二、为什么要归一化?—— 核心原因
在实际数据集中,不同特征的取值范围往往差异悬殊。例如:
- 年龄:0~100
- 年收入:0~1,000,000
- 商品评分:1~5
如果不进行归一化,直接使用原始数据训练模型会导致一系列问题:
1. 基于距离的算法被大尺度特征主导
K-近邻(KNN)、K-Means 聚类、支持向量机(SVM)等算法依赖样本间的距离计算(如欧氏距离)。若某一特征的取值范围远大于其他特征,该特征在距离计算中会占据绝对主导地位,相当于其他特征被“淹没”。
2. 梯度下降收敛缓慢或不稳定
在神经网络、线性回归等使用梯度下降优化的模型中,不同特征的尺度差异会导致损失函数的等高线呈现极端椭圆形。梯度下降更新方向会来回震荡,需要更多迭代才能收敛。归一化后等高线更接近圆形,加速收敛。
3. 正则化惩罚不均衡
L1/L2 正则化对所有特征的系数施加相同力度的惩罚。但大尺度特征的自然系数通常更小(因为数值大,乘上小系数就能达到同样效果),导致正则化实际上对大小尺度特征的惩罚力度不均衡。归一化后所有特征处于同一量纲,正则化更公平。
4. 激活函数饱和问题
神经网络中,若输入值过大或过小,经过 Sigmoid、Tanh 等有界激活函数时容易进入饱和区(梯度接近零),导致梯度消失。归一化将输入限制在 [0,1] 或 [-1,1],能有效缓解这一问题。
5. 算法内部假设
某些算法(如 PCA、线性判别分析)假设数据已中心化;另一些算法(如逻辑回归)虽然不强制要求归一化,但归一化后能显著提升数值稳定性。
三、归一化的优势
| 优势 | 说明 |
|---|---|
| 保留原始分布形态 | 归一化是线性变换,不改变数据的偏度、峰度、相关性等内在结构 |
| 输出范围可控 | 可任意指定缩放区间(如 [0,1] 或 [-1,1]),便于可视化和后续处理 |
| 对边界值敏感(双刃剑) | 能够充分利用整个数据范围,但这也意味着对异常值敏感 |
| 计算简单高效 | 只需计算每列的最大值和最小值,复杂度 O(n) |
| 保持稀疏性 | 对于稀疏矩阵(如文本数据),归一化后零值仍保持为零(因为减去最小值可能破坏稀疏性,需小心) |
四、归一化的必要性
归一化并非所有场景必需,但在以下情况下非常必要:
- 使用欧氏距离、曼哈顿距离等受尺度影响的度量
- 使用梯度下降优化的模型(线性回归、逻辑回归、神经网络、SVM)
- 使用正则化(L1/L2)的模型
- 特征的单位或量纲完全不同(例如“千米”和“克”混用)
如果特征本身已经处于同一尺度(例如图像像素值 0~255),归一化可能不是必须的,但仍有帮助(例如将像素值缩放到 0~1 可加速神经网络收敛)。
五、算法细节与实现注意事项
5.1 基本计算流程
- 对每个特征列,计算最小值 x_{\min} 和最大值 x_{\max}。
- 对每个值 x,计算 x' = (x - x_{\min}) / (x_{\max} - x_{\min})。
- (可选)乘以目标区间长度并加上下界。
5.2 处理新数据(测试集或预测样本)
必须使用训练集计算出的 x_{\min} 和 x_{\max} 来转换新数据,而不是重新计算新数据的极值。原因:
- 保持数据分布一致性
- 防止数据泄露(测试集信息不应影响训练过程)
若新数据中的值超出训练集的范围,转换后可能不在 [0,1] 区间内(例如大于 1 或小于 0),这是正常现象,但需要注意模型可能未见过这些范围的值。
5.3 边界情况处理
当 x_{\max} = x_{\min} 时(该特征所有值相同),分母为零。常见的处理方式:
- 将该特征的所有值设为 0(或目标区间的中点,如 0.5)
- 丢弃该特征(因为它不提供任何信息)
许多库(如 scikit-learn)会发出警告并默认将结果设为 0。
5.4 对异常值的敏感性
归一化使用极值(min 和 max)作为缩放基准,因此对异常值非常敏感。例如:
原始数据:[1, 2, 3, 4, 100]
- min=1, max=100
- 正常值 2、3、4 被压缩到接近 0 的狭窄区间,大部分信息丢失
解决方案: - 先进行异常值处理(截断、替换)
- 改用稳健缩放(RobustScaler,使用中位数和四分位距)
- 改用标准化(Z-score,使用均值和标准差,受异常值影响相对较小)
六、适用场景
| 场景 | 说明 |
|---|---|
| 基于距离的算法 | KNN、K-Means、层次聚类、SVM(核函数依赖距离) |
| 神经网络与深度学习 | 加快收敛,避免激活函数饱和;常与批归一化配合 |
| 主成分分析(PCA) | 需要数据居中,但若特征尺度差异大,应先归一化或标准化 |
| 图像像素值处理 | 将 0~255 缩放到 0~1 或 -1~1 |
| 需要固定输入范围的模型 | 某些物理模拟或控制系统中,要求输入在特定区间 |
| 可视化 | 将多特征缩放到同一尺度后,可用平行坐标图等展示 |
七、不适用场景
| 场景 | 原因 |
|---|---|
| 存在明显异常值 | 异常值会拉大 min-max 范围,压缩正常数据 |
| 基于树的模型(随机森林、XGBoost、LightGBM) | 树模型基于特征值划分,不受单调变换影响,归一化无帮助 |
| 需要保留原始方差信息 | 归一化丢失了绝对方差,若算法依赖方差(如某些统计检验)则不适用 |
| 稀疏数据(如文本词频矩阵) | 归一化可能破坏稀疏结构(减去 min 会产生非零值),通常应使用逐样本归一化(如 Tf-idf 或 L2 归一化) |
| 特征具有明确物理含义且需解释 | 归一化后系数失去原始单位的意义,不利于解释(例如“每增加1单位原始值,目标变化多少”) |
八、归一化 vs 标准化
| 对比维度 | 归一化(Min-Max) | 标准化(Z-score) |
|---|---|---|
| 公式 | x' = \frac{x - \min}{\max - \min} | x' = \frac{x - \mu}{\sigma} |
| 输出范围 | 固定区间(如 [0,1]) | 无界,通常约 [-3,3](正态分布下) |
| 对异常值敏感度 | 高(使用极值) | 中等(使用均值和标准差) |
| 保留零值 | 若 \min \neq 0,则零值被改变 | 零值可能变为负值 |
| 适用数据分布 | 近似均匀分布,且无极端异常值 | 近似正态分布,或有异常值 |
| 常见应用 | 神经网络输入、图像处理 | PCA、线性回归、SVM、K-Means |
选择建议:
- 优先使用标准化,除非有明确理由需要将数据限定在 [0,1] 区间。
- 若数据有明显边界(例如像素值 0~255),使用归一化。
- 若存在异常值,考虑 RobustScaler 或标准化。
- 若后续使用 PCA,标准化通常比归一化更合适(因为 PCA 对方差敏感)。
九、总结
归一化是一种简单而强大的数据预处理技术,通过线性缩放将特征映射到固定区间。它能消除量纲影响、加速梯度收敛、平衡正则化惩罚,特别适用于基于距离和梯度下降的算法。然而,它对异常值敏感,且不适用于树模型或需要保留原始方差信息的场景。在实际应用中,应根据数据特性和算法需求选择归一化、标准化或其他缩放方法。
关键要点:
- 归一化用极值(min/max)缩放
- 测试集使用训练集的极值
- 异常值会严重干扰归一化效果
- 与标准化各有优劣,标准化通常更通用
十、代码示例
# 从 sklearn.preprocessing 模块导入 MinMaxScaler 类,用于将特征缩放到指定的最小值和最大值之间(默认 [0, 1])
from sklearn.preprocessing import MinMaxScaler
# 定义一个原始数据集,包含 4 个样本,每个样本有 2 个特征
# 第一列特征值:[1, 2, 3, 4];第二列特征值:[2, 3, 4, 5]
data = [[1, 2], [2, 3], [3, 4], [4, 5]]
# 创建 MinMaxScaler 对象,指定缩放范围为 [0, 1](即最小值映射为 0,最大值映射为 1)
scaler = MinMaxScaler(feature_range=(0, 1))
# 调用 fit_transform 方法:先拟合数据(计算每列的最小值和最大值),再对数据进行最小-最大缩放
# 返回缩放后的数组,形状与 data 相同
normalized_data = scaler.fit_transform(data)
# 打印原始数据和缩放后的数据,用于对比效果
# 注意:print 中使用 f-string,但实际内容为多行字符串,Python 会正常输出
print(f"====\ndata:{data}\nnormalized_data:{normalized_data}")
3. 编码分类变量(OneHotEncoder)
一、什么是独热编码?
独热编码(One‑Hot Encoding)是一种将分类变量(Categorical Variable)转换为数值形式的方法。它用一组二进制向量表示每个类别,其中只有一个位为 1(“热”),其余位均为 0(“冷”)。
设有类别集合 C = \{c_1, c_2, \dots, c_k\},对任意类别 c_i,其独热编码为长度为 k 的向量:
\mathbf{e}_i = [0, 0, \dots, 1, \dots, 0] \quad \text{(第 $i$ 个位置为 1,其余为 0)}
例如,颜色类别 \{\text{红}, \text{绿}, \text{蓝}\} 可编码为:
- 红 → [1,0,0]
- 绿 → [0,1,0]
- 蓝 → [0,0,1]
二、为什么要编码分类变量?—— 核心原因
大多数机器学习算法(线性回归、逻辑回归、神经网络、SVM 等)只能处理数值输入,不能直接接受字符串或枚举值。因此,必须将分类变量转化为数值。
2.1 直接映射为整数的问题
一种简单做法是将类别映射为整数,例如:
- 红 → 0
- 绿 → 1
- 蓝 → 2
这会产生两个严重问题:
- 引入虚假的顺序关系:算法会认为 1 > 0 且 2 > 1,暗示绿 > 红、蓝 > 绿。对于名义变量(如颜色、国家、性别),这种顺序毫无意义。
- 距离计算失真:基于距离的模型(KNN、K‑Means)会认为“红”与“绿”的距离为 1,“红”与“蓝”的距离为 2,这暗示“蓝”比“绿”更远离“红”,但实际类别间并无此关系。
2.2 独热编码的优势
- 消除顺序假设:所有类别在几何上相互正交,彼此之间距离相等(若使用欧氏距离,任意两个不同类别的编码向量距离均为 \sqrt{2})。
- 表达能力完全:每个类别独立占用一个维度,不会强迫类别间存在线性关系。
- 兼容线性模型:线性模型可以学习到每个类别的独立权重,实现类似“哑变量”的效果。
三、独热编码的优势与必要性
| 优势 | 说明 |
|---|---|
| 无顺序假设 | 适合名义变量(Nominal),不引入虚假的数值顺序。 |
| 距离意义明确 | 不同类别的距离恒为 \sqrt{2}(二值向量),避免了整数编码的距离歧义。 |
| 模型表达能力 | 线性模型可为每个类别学习独立的偏置(即类别效应),相当于引入了哑变量。 |
| 简单高效 | 编码与解码均为线性操作,计算复杂度 O(nk)。 |
| 可解释性 | 编码后的特征直接对应“是否为某类别”,含义清晰。 |
必要性:当算法无法处理类别字符串且简单整数映射会引入错误假设时,独热编码是标准且必要的预处理步骤。尤其在以下情况:
- 使用线性模型(逻辑回归、线性回归)
- 使用基于距离的模型(KNN、K‑Means、SVM)
- 使用神经网络(尤其是嵌入层的前置替代方案)
四、算法细节
4.1 基本流程
- 扫描所有训练数据,收集每个分类特征的所有可能取值,构成类别集合 C。
- 为每个类别分配一个唯一整数索引(通常按出现顺序或字母顺序)。
- 将每个样本的原始类别转换为长度为 |C| 的二进制向量,在对应索引处置 1,其余置 0。
- 若有多个分类特征,则每个特征的独热向量拼接在一起形成最终的特征矩阵。
4.2 数学表达
设样本 x 在第 j 个分类特征上的取值为 c,该特征的可能类别为 \{c_{j1}, c_{j2}, \dots, c_{jk_j}\}。则独热编码函数为:
\text{OneHot}_j(c) = [\mathbb{1}(c = c_{j1}), \mathbb{1}(c = c_{j2}), \dots, \mathbb{1}(c = c_{jk_j})]
其中 \mathbb{1}(\cdot) 是指示函数(真为 1,假为 0)。
若共有 m 个分类特征,则最终编码为:
\text{OneHot}(x) = \text{OneHot}_1(c_1) \oplus \text{OneHot}_2(c_2) \oplus \cdots \oplus \text{OneHot}_m(c_m)
这里 \oplus 表示向量拼接。
4.3 处理新数据(测试集/生产环境)
必须使用训练集上得到的类别集合来编码新样本:
- 若新样本的类别出现在训练集中 → 正常编码
- 若新样本的类别未在训练集中出现(未知类别):
- 默认行为:抛出错误(sklearn 中handle_unknown='error')
- 可选忽略:将所有未知类别映射为全零向量(handle_unknown='ignore')
- 可选归为“其他”类:预先指定一个infrequent类别(需设置min_frequency或max_categories)
4.4 维度爆炸与稀疏表示
当类别数量 k 很大时(例如邮政编码有 10000 个唯一值),独热编码会产生极高维的特征空间。此时:
- 使用稀疏矩阵存储(如 scipy.sparse.csr_matrix),只存储非零元素的位置和值,内存占用从 O(nk) 降为 O(n)。
- sklearn 的
OneHotEncoder默认sparse_output=True输出稀疏矩阵。
4.5 与 Pandas get_dummies 的区别
| 特性 | OneHotEncoder |
pd.get_dummies |
|---|---|---|
| 输出格式 | NumPy 数组或稀疏矩阵 | DataFrame |
| 处理未知类别 | 支持 handle_unknown |
自动增加新列(或忽略) |
| 支持多特征同时编码 | 是 | 需分别处理或使用 columns |
| 支持 Pipeline 集成 | 是 | 否(需单独使用) |
| 内存效率 | 可输出稀疏矩阵 | 总是密集矩阵 |
五、适用场景
| 场景 | 说明 |
|---|---|
| 线性模型(逻辑回归、线性回归、岭回归、Lasso) | 独热编码后每个类别有独立权重,等价于哑变量回归。 |
| 基于距离的模型(KNN、K‑Means、层次聚类、SVM) | 避免了整数编码的距离陷阱,所有类别等距。 |
| 神经网络(MLP、CNN) | 作为类别特征的输入层;若类别很多,通常改用嵌入层(Embedding),但独热编码仍可用于小基数特征。 |
| 朴素贝叶斯 | 可处理二值特征,适合独热编码后的数据。 |
| 决策树/随机森林(可选) | 树模型能直接处理类别(如 LightGBM 的 categorical_feature),但若库不支持,独热编码也可用(不过会损失一些效率)。 |
| 特征交叉(多项式特征) | 独热编码后可以方便地进行特征组合(如 PolynomialFeatures)。 |
六、不适用场景
| 场景 | 原因 |
|---|---|
| 类别基数极高(> 1000) | 特征维度爆炸,模型容易过拟合,训练和推理内存/时间开销大。替代方案:目标编码、频率编码、嵌入、特征哈希。 |
| 有序类别(Ordinal) | 若类别有天然顺序(如学历:小学<中学<大学),独热编码丢弃了顺序信息。应使用整数编码(保留顺序)或 label encoding。 |
| 树模型(原生支持分类) | LightGBM、CatBoost 等能直接处理字符串类别,且效果优于独热编码(避免分裂时的维度灾难)。 |
| 高稀疏数据且内存受限 | 即使使用稀疏矩阵,当类别数极大时仍会消耗较多内存(因为需要存储列索引)。特征哈希(FeatureHasher)更节省内存。 |
| 需要可解释的类别权重 | 独热编码后每个类别是一个独立的系数,但当类别很多时解释性下降。目标编码可能提供更直观的“类别均值”解释。 |
七、实践建议与常见陷阱
7.1 避免哑变量陷阱(Dummy Variable Trap)
在线性回归等模型中,若使用独热编码的全部 k 列,并与截距项(偏置)同时使用,会导致完全多重共线性(因为每一列之和等于截距列)。解决方案:
- 删除其中一列(保留 k-1 列),这等价于将某一类别设为基准(baseline)。
- 或使用正则化模型(岭回归、Lasso)处理共线性。
- sklearn 的
OneHotEncoder可以设置drop='first'自动删除第一列。
7.2 处理低频类别
当某些类别出现次数极少时,独热编码会导致:
- 模型难以学习到有意义的权重(样本不足)
- 增加过拟合风险
常用策略: - 将低频类别归并为 “other” 类别(设置
min_frequency或max_categories) - 使用目标编码(Target Encoding)对低频类别进行平滑
7.3 与标准化/归一化的配合
独热编码产生的二值特征通常不需要进一步缩放(因为已经处于 0/1 尺度)。但如果与其他连续特征一同使用,连续特征可能需要标准化。此时建议对连续特征做标准化,而对独热编码特征不做额外处理。
7.4 Pipeline 中的使用
# 修正后的完整代码
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.linear_model import LogisticRegression # 补充导入
import pandas as pd
import numpy as np
# 创建示例数据(仅用于演示)
data = pd.DataFrame({
'年龄': [25, 32, 47, 51, 23],
'收入': [50000, 80000, 120000, 150000, 45000],
'城市': ['北京', '上海', '广州', '深圳', '北京'],
'目标': [0, 1, 0, 1, 0]
})
# 定义数值列和分类列名称(需要根据实际数据定义)
numerical_cols = ['年龄', '收入']
categorical_cols = ['城市']
# 创建预处理器:数值列标准化,分类列独热编码(drop='first'避免哑变量陷阱)
preprocessor = ColumnTransformer([
('num', StandardScaler(), numerical_cols),
('cat', OneHotEncoder(drop='first', sparse_output=False), categorical_cols) # 注意参数名 sparse_output
])
# 创建完整的 Pipeline:先预处理,再逻辑回归
model = Pipeline([
('pre', preprocessor),
('clf', LogisticRegression())
])
# 准备特征 X 和目标 y
X = data[numerical_cols + categorical_cols]
y = data['目标']
# 训练模型
model.fit(X, y)
# 预测示例
print(model.predict(X))
八、总结
| 维度 | 结论 |
|---|---|
| 本质 | 将名义变量转换为正交的二值向量 |
| 核心优势 | 消除顺序假设,距离意义明确,兼容线性模型 |
| 主要缺点 | 类别多时维度爆炸,对低频类别不鲁棒 |
| 必要场景 | 线性模型、距离模型、神经网络处理名义变量 |
| 不推荐场景 | 高基数类别、有序变量、原生支持类别的树模型 |
| 替代方案 | 目标编码、频率编码、嵌入、特征哈希、整数编码(有序时) |
独热编码是数据预处理中最基础、最重要的工具之一。理解它的原理、适用边界和替代方案,能够帮助数据科学家在特征工程中做出更合理的选择,从而提升模型性能与可解释性。
九、代码示例
# 从 sklearn.preprocessing 模块导入 OneHotEncoder 类,用于将分类特征转换为独热编码(One-Hot Encoding)
from sklearn.preprocessing import OneHotEncoder
# 定义一个类别数据的列表,每个元素是一个包含单个类别值的列表(因为 OneHotEncoder 默认期望输入为二维数组,每一行是一个样本,每一列是一个特征)
# 这里共有 4 个样本,每个样本有一个特征:颜色值分别为 'red', 'blue', 'green', 'red'
categories = [['red'], ['blue'], ['green'], ['red']]
# 创建 OneHotEncoder 对象,参数 sparse_output=False 表示输出密集数组(即普通的 NumPy 二维数组),而不是稀疏矩阵
# 如果设为 True(默认),输出为稀疏矩阵,节省内存
encoder = OneHotEncoder(sparse_output=False)
# 调用 fit_transform 方法:先拟合数据(确定所有可能的类别),再将原始类别转换为独热编码
# 返回编码后的数组,形状为 (n_samples, n_categories)
encoded = encoder.fit_transform(categories)
# 打印原始类别列表
print("原始类别:", categories)
# 打印独热编码结果
print("独热编码:\n", encoded)
4. 划分训练集/测试集
一、为什么要划分训练集和测试集?
在机器学习中,我们不仅希望模型能记住训练数据(低偏差),更希望模型能对未见过的数据做出正确预测(低方差,即泛化能力)。因此,需要将全部数据划分为:
- 训练集(Training Set):用于训练模型,学习参数。
- 测试集(Test Set):用于评估模型在全新数据上的表现,不能用于训练。
如果不划分,直接在训练数据上评估,会得到过于乐观的准确率(过拟合),无法反映真实泛化能力。
二、典型划分比例
- 常见比例:70% 训练,30% 测试 或 80% 训练,20% 测试。
- 当数据量很大时(例如百万级),测试集比例可以更低(如 10% 或 5%)。
- 当数据量很少时(几百个样本),常采用 交叉验证 代替单次划分。
三、train_test_split 函数详解
sklearn.model_selection.train_test_split 是 scikit-learn 中最常用的数据划分工具。
函数签名与参数
train_test_split(X, y, test_size=None, train_size=None, random_state=None, shuffle=True, stratify=None)
| 参数 | 类型 | 含义 |
|---|---|---|
X |
array-like | 特征矩阵,形状 (n_samples, n_features) |
y |
array-like | 标签向量,形状 (n_samples,) |
test_size |
float 或 int | 测试集比例(0.0~1.0)或绝对数量(整数)。若为 None,则自动补全为 1 - train_size |
train_size |
float 或 int | 训练集比例或绝对数量。通常不指定,由 test_size 决定 |
random_state |
int 或 None | 随机种子。固定后多次运行划分结果相同,便于复现 |
shuffle |
bool | 划分前是否打乱数据顺序。默认为 True,避免原始顺序导致偏差 |
stratify |
array-like 或 None | 若传入标签 y,则进行分层抽样,保证训练集和测试集中各类别比例与原始数据一致(用于分类任务) |
返回值
返回四个对象:X_train, X_test, y_train, y_test,顺序固定。
四、注意事项与最佳实践
- 分层抽样(stratify)
对于分类问题,若原始数据类别不平衡(例如 90% 正例,10% 负例),随机划分可能导致某个类别在训练集或测试集中缺失。stratify=y可保证划分后各类别比例与原数据一致。 - 随机种子(random_state)
设置后,每次运行代码划分结果相同,便于调试和复现。不设置则每次随机不同。 - 先划分,再预处理
标准化、归一化等需要计算统计量的操作,必须在划分后用训练集拟合(fit),再转换训练集和测试集(transform),避免数据泄露。 - 回归任务不需要 stratify
回归的标签是连续值,分层无意义。若仍想保持标签分布,可对标签分箱后传入stratify。 - 时间序列数据:不能随机打乱,需按时间顺序划分(例如前 80% 训练,后 20% 测试)。
五、代码示例
以下代码使用鸢尾花数据集,演示如何划分训练集和测试集。
# 从 sklearn 的 model_selection 模块导入 train_test_split 函数
from sklearn.model_selection import train_test_split
# 从 sklearn 的 datasets 模块导入 load_iris 函数,用于加载鸢尾花数据集
from sklearn.datasets import load_iris
# 调用 load_iris 函数,参数 return_X_y=True 表示以 (X, y) 元组的形式返回特征和标签
# 默认情况下 load_iris 返回一个 Bunch 对象(类似字典),设置 return_X_y=True 后直接返回两个数组:
# X: 特征矩阵,形状 (150, 4),包含 150 个样本,每个样本有 4 个特征(花萼长度、花萼宽度、花瓣长度、花瓣宽度)
# y: 标签数组,形状 (150,),每个元素是 0、1 或 2,代表三种鸢尾花品种
X, y = load_iris(return_X_y=True)
# 划分训练集和测试集
# test_size=0.2:测试集占全部数据的 20%(即 30 个样本),训练集占 80%(120 个样本)
# random_state=42:固定随机种子,保证每次运行划分结果一致,便于复现
# stratify=y:按照标签 y 进行分层抽样,确保训练集和测试集中各类别(0,1,2)的比例与原始数据集相同
# 原始数据中每类 50 个样本(各占 1/3),划分后训练集每类约 40 个(120 * 1/3),测试集每类约 10 个(30 * 1/3)
# 返回值:X_train 训练集特征,X_test 测试集特征,y_train 训练集标签,y_test 测试集标签
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
# 可选:查看划分结果
print(f"训练集特征形状: {X_train.shape}")
print(f"测试集特征形状: {X_test.shape}")
print(f"训练集各类别数量: {np.bincount(y_train)}")
print(f"测试集各类别数量: {np.bincount(y_test)}")
六、load_iris(return_X_y=True) 说明
- 默认用法:
iris = load_iris(),返回一个Bunch对象,可通过iris.data和iris.target访问特征和标签。 return_X_y=True:直接返回一个元组(X, y),省去了中间对象,代码更简洁。适用于只需要特征和标签的场景。
等价写法:
# 方式一:Bunch 对象
iris = load_iris()
X = iris.data
y = iris.target
# 方式二:直接解包
X, y = load_iris(return_X_y=True)
七、常见错误与解决方案
| 错误 | 后果 | 解决方案 |
|---|---|---|
| 先预处理再划分 | 预处理时使用了全部数据的统计信息(如均值、最值),导致测试集信息泄露,评估结果过于乐观 | 先 train_test_split,再用训练集 fit 预处理器,最后 transform 训练集和测试集 |
忘记设置 random_state |
代码不可复现,实验结果无法对比 | 设置固定整数,如 random_state=42 |
分类任务未设置 stratify=y |
小数据下某个类别可能完全分到训练集或测试集,模型无法学习或评估 | 总是为分类任务添加 stratify=y |
| 将测试集用于调参 | 测试集被间接用于模型选择,导致泛化能力被高估 | 使用验证集(从训练集再分出一部分)进行调参,测试集只能使用一次 |
八、总结
- 核心目标:评估模型的泛化能力,防止过拟合。
- 标准工具:
sklearn.model_selection.train_test_split。 - 关键参数:
test_size(划分比例)、random_state(复现性)、stratify(分类任务分层)。 - 黄金法则:划分后,测试集绝不参与任何训练或调参过程。
掌握训练集/测试集划分是机器学习实践的基础,正确使用可以避免大量常见错误,使模型评估更可靠。
5.2.2 模型训练统一 API
sklearn 最强大的设计:所有模型用法完全一致
| 方法 | 用途 | 适用对象 |
|---|---|---|
fit(X, y) |
训练模型(监督学习) | 所有监督模型 |
fit(X) |
训练模型(无监督学习) | 聚类、降维等 |
predict(X) |
预测标签/值 | 所有模型 |
predict_proba(X) |
预测类别概率(分类) | 分类器 |
score(X, y) |
返回默认评估指标(分类:准确率,回归:R²) | 所有模型 |
# 示例(逻辑回归)
# 从 sklearn.linear_model 模块导入 LogisticRegression 类,用于构建逻辑回归分类模型
from sklearn.linear_model import LogisticRegression
# 创建逻辑回归模型对象,使用默认参数(例如 solver='lbfgs', max_iter=100, C=1.0 等)
model = LogisticRegression()
# 使用训练集数据训练模型:X_train 是训练集特征矩阵,y_train 是对应的训练集标签
# fit() 方法会学习特征与类别之间的线性关系,并通过最大似然估计或梯度下降求解模型参数(系数和截距)
model.fit(X_train, y_train)
# 对测试集特征 X_test 进行类别预测,返回预测的离散标签(如 0, 1, 2 等)
# predict() 内部先计算线性得分,再通过 softmax(多分类)或 sigmoid(二分类)得到概率,最后取概率最大的类别
y_pred = model.predict(X_test)
# 对测试集特征 X_test 进行概率预测,返回每个样本属于各个类别的概率值
# 对于二分类,返回形状为 (n_samples, 2) 的数组,第一列是类别 0 的概率,第二列是类别 1 的概率
# 对于多分类,返回 (n_samples, n_classes) 的数组,每行概率之和为 1
y_proba = model.predict_proba(X_test) # 每个类别的概率
# 计算模型在测试集上的分类准确率:预测正确的样本数除以测试集总样本数
# score() 内部调用 predict(X_test) 并与 y_test 比较,返回平均准确率
accuracy = model.score(X_test, y_test)
5.2.3 经典算法实现
以下代码全部使用鸢尾花数据集,可直接复制运行。
# 从 sklearn.datasets 模块导入 load_iris 函数,用于加载鸢尾花数据集
from sklearn.datasets import load_iris
# 从 sklearn.model_selection 模块导入 train_test_split 函数,用于将数据集划分为训练集和测试集
from sklearn.model_selection import train_test_split
# 加载鸢尾花数据集,返回一个 Bunch 对象(类似字典),包含 data(特征)、target(标签)、feature_names 等属性
iris = load_iris()
# 提取特征矩阵 X(形状 150×4)和标签数组 y(形状 150,取值 0,1,2 代表三种鸢尾花)
X, y = iris.data, iris.target
# 将数据集划分为训练集和测试集
# test_size=0.2:测试集占 20%(30 个样本),训练集占 80%(120 个样本)
# random_state=42:固定随机种子,保证每次运行划分结果相同,便于复现
# stratify=y:按标签 y 进行分层抽样,确保训练集和测试集中各类别比例与原始数据集一致(每类约 1/3)
# 返回值:X_train 训练集特征,X_test 测试集特征,y_train 训练集标签,y_test 测试集标签
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
1. 线性回归(LinearRegression)—— 回归任务
# 从 sklearn.linear_model 模块导入 LinearRegression 类,用于构建线性回归模型
from sklearn.linear_model import LinearRegression
# 从 sklearn.metrics 模块导入均方误差(MSE)和决定系数(R²)评估指标
from sklearn.metrics import mean_squared_error, r2_score
# 创建线性回归模型对象(使用默认参数,如 fit_intercept=True,即计算截距项)
lr = LinearRegression()
# 使用训练集数据训练线性回归模型
# X_train:训练集特征矩阵,y_train:训练集目标值(连续变量)
# fit() 方法通过最小二乘法或梯度下降求解最优的回归系数(权重)和截距
lr.fit(X_train, y_train)
# 使用训练好的模型对测试集特征 X_test 进行预测,返回预测的目标值数组
y_pred = lr.predict(X_test)
# 计算并打印决定系数 R²(R-squared),取值范围 (-∞, 1],越接近 1 表示模型拟合效果越好
# R² = 1 - (SS_res / SS_tot),其中 SS_res 是残差平方和,SS_tot 是总平方和(与均值之差的平方和)
print("R²:", r2_score(y_test, y_pred))
# 计算并打印均方误差(Mean Squared Error),即预测值与真实值之差的平方的平均值
# MSE 越小表示模型预测误差越小,但对大误差惩罚较重(单位是目标变量单位的平方)
print("MSE:", mean_squared_error(y_test, y_pred))
2. 逻辑回归(LogisticRegression)—— 分类
# 从 sklearn.linear_model 模块导入 LogisticRegression 类,用于构建逻辑回归分类模型
from sklearn.linear_model import LogisticRegression
# 创建逻辑回归模型对象,参数 max_iter=200 设置最大迭代次数为 200
# 逻辑回归使用迭代优化算法(如 lbfgs)求解参数,max_iter 控制算法收敛前的最大迭代步数
# 默认值为 100,当数据复杂或特征较多时可能需要增加该值以确保收敛
lr_clf = LogisticRegression(max_iter=200)
# 使用训练集数据训练逻辑回归模型
# X_train:训练集特征矩阵,y_train:训练集标签(离散类别,如 0,1,2 等)
# fit() 方法会学习特征与类别之间的线性决策边界,并通过最大似然估计或梯度下降求解模型参数
lr_clf.fit(X_train, y_train)
# 计算并打印模型在测试集上的分类准确率
# score() 方法内部调用 predict(X_test) 得到预测标签,然后与 y_test 比较,计算正确预测的比例
# 准确率 = (预测正确的样本数) / (测试集总样本数)
print("逻辑回归准确率:", lr_clf.score(X_test, y_test))
3. 决策树(DecisionTreeClassifier)—— 分类
# 从 sklearn.tree 模块导入 DecisionTreeClassifier(决策树分类器)和 plot_tree(用于绘制决策树结构)
from sklearn.tree import DecisionTreeClassifier, plot_tree
# 创建决策树分类器对象,设置最大深度为 3(限制树的最大层数,防止过拟合),随机种子为 42 保证结果可复现
dt = DecisionTreeClassifier(max_depth=3, random_state=42)
# 使用训练集数据训练决策树模型:学习特征中的非线性划分规则,生成一棵二叉树
dt.fit(X_train, y_train)
# 计算并打印决策树模型在测试集上的分类准确率
print("决策树准确率:", dt.score(X_test, y_test))
# 可视化决策树结构(需要导入 matplotlib.pyplot 并创建图形)
# 创建一个新图形,设置画布大小为 12 英寸宽、8 英寸高
plt.figure(figsize=(12,8))
# 绘制决策树
# feature_names:特征名称列表(使用鸢尾花数据集的 feature_names)
# class_names:类别名称列表(使用鸢尾花数据集的 target_names)
# filled=True:用颜色填充节点,表示多数类别或纯度
plot_tree(dt, feature_names=iris.feature_names, class_names=iris.target_names, filled=True)
# 显示图形窗口
plt.show()
4. 随机森林(RandomForestClassifier)—— 最强 baseline
工业界最常用、效果最稳、几乎不需要调参
# 从 sklearn.ensemble 模块导入 RandomForestClassifier 类,用于构建随机森林分类模型
from sklearn.ensemble import RandomForestClassifier
# 创建随机森林分类器对象
# n_estimators=100:指定森林中决策树的数量为 100 棵(树越多通常性能越好,但训练和预测时间增加)
# random_state=42:固定随机种子,确保每次运行结果可复现(包括每棵树的训练样本和特征选择)
rf = RandomForestClassifier(n_estimators=100, random_state=42)
# 使用训练集数据训练随机森林模型
# 随机森林会在训练集上通过 Bootstrap 采样生成多个子数据集,分别训练多棵决策树,并综合投票结果
rf.fit(X_train, y_train)
# 计算并打印随机森林模型在测试集上的分类准确率
# score() 内部调用 predict(X_test) 并与 y_test 比较,返回平均准确率
print("随机森林准确率:", rf.score(X_test, y_test))
# 打印特征重要性数组,形状为 (n_features,)
# feature_importances_ 表示每个特征在随机森林中对预测的贡献程度,值越高越重要
# 所有特征重要性之和为 1,基于节点不纯度减少(基尼系数或信息增益)的平均值计算
print("特征重要性:", rf.feature_importances_)
5. 支持向量机(SVC)—— 分类
# 从 sklearn.svm 模块导入 SVC 类,用于构建支持向量机分类模型
from sklearn.svm import SVC
# 创建支持向量机分类器对象
# kernel='rbf':使用径向基函数(RBF)作为核函数,将数据映射到高维空间,适用于非线性分类
# C=1.0:正则化参数,控制对误分类样本的惩罚力度。C 越大,模型对训练数据的拟合越严格(可能过拟合);C 越小,模型对误分类的容忍度越高(可能欠拟合)
# gamma='scale':RBF 核函数的系数,影响单个训练样本的影响力范围。'scale' 表示使用 1/(n_features * X.var()) 作为 gamma 值,是 scikit-learn 的默认值
# random_state=42:固定随机种子,确保结果可复现(SVC 中某些求解器涉及随机过程)
svm = SVC(kernel='rbf', C=1.0, gamma='scale', random_state=42)
# 使用训练集数据训练支持向量机模型
# 模型寻找一个超平面(或在高维空间中的超平面)使得不同类别的间隔最大化,同时允许少量误分类(由 C 控制)
svm.fit(X_train, y_train)
# 计算并打印 SVM 模型在测试集上的分类准确率
# score() 内部调用 predict(X_test) 并与 y_test 比较,返回平均准确率
print("SVM 准确率:", svm.score(X_test, y_test))
6. K-Means 聚类(KMeans)—— 无监督学习
```python
# 从 sklearn.cluster 模块导入 KMeans 类,用于执行 K-Means 聚类算法(无监督学习)
from sklearn.cluster import KMeans
# 从 sklearn.datasets 模块导入 make_blobs 函数,用于生成各向同性的高斯分布簇数据(聚类数据集)
from sklearn.datasets import make_blobs
# 生成聚类数据:300 个样本,3 个中心点(真实簇数),随机种子 42 保证结果可重复
# 返回值:X_blob 是特征矩阵(300, 2),_ 是真实标签(此处用下划线忽略,因为无监督学习不使用标签)
X_blob, _ = make_blobs(n_samples=300, centers=3, random_state=42)
# 创建 K-Means 聚类模型对象,指定聚类数量为 3,随机种子 42(用于初始化簇中心)
kmeans = KMeans(n_clusters=3, random_state=42)
# 仅使用特征矩阵 X_blob 进行聚类拟合(无监督学习,不传入标签)
# K-Means 会将样本分配到 3 个簇中,并学习每个簇的中心点
kmeans.fit(X_blob)
# 打印前 10 个样本的聚类标签(kmeans.labels_ 是一个数组,存储每个样本所属的簇编号,范围 0~2)
print("聚类标签:", kmeans.labels_[:10])
# 打印聚类中心坐标(cluster_centers_ 是一个形状为 (3, 2) 的数组,每行是一个簇的中心点)
print("聚类中心:\n", kmeans.cluster_centers_)
# 可视化聚类结果(需要提前导入 matplotlib.pyplot 并简写为 plt)
# 绘制散点图:X_blob[:,0] 是第一列特征(横坐标),X_blob[:,1] 是第二列特征(纵坐标)
# c=kmeans.labels_ 按聚类标签着色,cmap='viridis' 使用 viridis 颜色映射
plt.scatter(X_blob[:,0], X_blob[:,1], c=kmeans.labels_, cmap='viridis')
# 在散点图上叠加绘制聚类中心:横坐标 cluster_centers_[:,0],纵坐标 cluster_centers_[:,1]
# marker='x' 使用叉号标记,s=200 设置标记大小,linewidths=3 设置边框宽度,color='red' 红色
plt.scatter(kmeans.cluster_centers_[:,0], kmeans.cluster_centers_[:,1], marker='x', s=200, linewidths=3, color='red')
# 设置图表标题
plt.title("K-Means 聚类结果")
# 显示图形窗口
plt.show()
5.2.4 模型选择与调优
1. 交叉验证(cross_val_score)
更可靠的评估方式,避免单次划分的偶然性。
from sklearn.model_selection import cross_val_score
scores = cross_val_score(LogisticRegression(max_iter=200), X, y, cv=5, scoring='accuracy')
print("交叉验证准确率:", scores)
print("平均准确率: {:.3f} ± {:.3f}".format(scores.mean(), scores.std()))
2. 网格搜索(GridSearchCV)
自动寻找最优超参数组合,是调参的核心工具。
from sklearn.model_selection import GridSearchCV
param_grid = {
'n_estimators': [50, 100, 200],
'max_depth': [3, 5, 10],
'min_samples_split': [2, 5, 10]
}
rf = RandomForestClassifier(random_state=42)
grid_search = GridSearchCV(rf, param_grid, cv=5, scoring='accuracy', n_jobs=-1)
grid_search.fit(X_train, y_train)
print("最佳参数:", grid_search.best_params_)
print("最佳交叉验证得分:", grid_search.best_score_)
print("测试集得分:", grid_search.score(X_test, y_test))
📚 学习资料(Obsidian 可直接收藏)
🎯 学习建议(3 天计划)
- 第 1 天:掌握数据预处理(标准化、归一化、独热编码)和训练/测试集划分。
- 第 2 天:学习经典算法 API(线性回归、逻辑回归、决策树、随机森林、SVM、K-Means),理解统一 API 设计。
- 第 3 天:掌握交叉验证和网格搜索,完成下面的练习项目。
✅ 核心要点总结
- 统一 API:
fit()→predict()→score(),所有模型一致。 - 必须做预处理:标准化(
StandardScaler)或归一化(MinMaxScaler),分类变量用独热编码。 - 随机森林 = 最强 baseline:效果稳、几乎不调参、特征重要性天然可用。
- 调参 = GridSearchCV + 交叉验证:自动寻找最优参数,避免手动试错。
- 项目流程:数据加载 → 划分 → 预处理 → 模型训练 → 评估 → 调参 → 最终预测。
练习项目:完整机器学习流程(鸢尾花分类)
面试标准模板,可直接运行
# ---------------------- 1. 加载数据 ----------------------
from sklearn.datasets import load_iris
X, y = load_iris(return_X_y=True)
# ---------------------- 2. 划分数据集 ----------------------
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, stratify=y)
# ---------------------- 3. 数据预处理(标准化)--------------------
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# ---------------------- 4. 基础模型(随机森林)--------------------
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(random_state=42)
# ---------------------- 5. 网格搜索调参 ----------------------
from sklearn.model_selection import GridSearchCV
param_grid = {
'n_estimators': [50, 100],
'max_depth': [3, 5]
}
grid_search = GridSearchCV(rf, param_grid, cv=5, scoring='accuracy')
grid_search.fit(X_train_scaled, y_train)
# ---------------------- 6. 评估 ----------------------
from sklearn.metrics import accuracy_score, classification_report
y_pred = grid_search.predict(X_test_scaled)
print("最佳参数:", grid_search.best_params_)
print("测试集准确率:", accuracy_score(y_test, y_pred))
print("\n分类报告:\n", classification_report(y_test, y_pred))
# ---------------------- 7. 输出最终模型性能 ----------------------
print("最终模型交叉验证得分:", grid_search.best_score_)
扩展:波士顿房价(回归)练习
from sklearn.datasets import load_diabetes
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error, r2_score
# 加载糖尿病数据集(回归)
X, y = load_diabetes(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 标准化
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
# 岭回归(L2正则化)
ridge = Ridge(alpha=1.0)
ridge.fit(X_train, y_train)
y_pred = ridge.predict(X_test)
print("R²:", r2_score(y_test, y_pred))
print("RMSE:", mean_squared_error(y_test, y_pred, squared=False))
练习题(自测)
- 使用
make_classification生成一个二分类数据集,分别训练逻辑回归、决策树、随机森林,比较它们的准确率和 AUC。 - 对同一数据集,使用
GridSearchCV优化随机森林的max_depth和n_estimators,绘制参数与得分的关系图。 - 使用 K-Means 对鸢尾花数据(仅特征)进行聚类,并与真实标签对比,计算调整兰德指数(
adjusted_rand_score)。 - 实现一个完整的机器学习管道(
Pipeline),将标准化和随机森林串联起来,并用交叉验证评估。 - 下载 Kaggle 的 Titanic 数据集,完成数据清洗、特征编码、模型训练和预测提交。
建议将以上代码保存为
.py文件或在 Jupyter Notebook 中逐段运行,观察输出并修改参数。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)