1.机器学习建模流程
- 获取数据:搜集与完成机器学习任务相关的数据集
- 数据的预处理:数据集中异常值,缺失值的处理
- 特征工程:对数据特征进行提取,转成向量,让模型达到最好的效果
- 模型训练:选择合适的算法对模型进行训练,根据不同的任务来选中不同的算法,有监督学习,无监督学习,半监督学习,强化学习
- 模型评估:评估效果好上线服务,评估效果不好则重复上诉步骤
- 模型预测
2.特征工程介绍

-
知道特征工程是什么:利用专业背景知识和技巧处理数据,让机器学习算法效果最好。这个过程就是特征工程。数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已。
-
理解特征提取的作用:原始数据中提取与任务相关的特征,构成特征向量。新数据 = 特征提取(原数据)
-
理解特征预处理的作用:特征对模型产生影响,因量纲(单位)问题,有些特征对模型影响大,有些影响小。有两种思路,归一化和标准化(重点),归一化公式为:(当前值-最小值)/(最大值-最小值),使映射的值域在(0~1)之间
-
了解特征降维,特征选择,特征组合。特征降维 将原始数据的维度降低,叫做特征降维,一般会对原始数据产生影响。特征选择 :原始数据特征很多,从原特征中挑子集,通常不改原值。特征组合 (Feature Combination / Feature Interaction)是指:把两个或多个已有特征通过某种规则组合成一个新特征 ,用来表达单个特征无法表达的交互关系、非线性关系或业务含义。
3.模型拟合问题
模型拟合问题 ,本质是:模型对训练数据中的"真实规律"和"噪声"学习得是否恰当。拟合得太差叫欠拟合 ,拟合得太好但把噪声也学进去叫过拟合 ,理想状态是恰当拟合。
1. 三种拟合状态
| 状态 | 训练误差 | 验证/测试误差 | 本质 | 表现 |
|---|---|---|---|---|
| 欠拟合 | 高 | 高 | 模型太简单,没学到规律 | 训练集都做不好,预测偏差大 |
| 恰当拟合 | 低 | 低且接近训练误差 | 学到了可泛化规律 | 训练、验证表现稳定 |
| 过拟合 | 很低 | 明显高 | 模型太复杂,记住了噪声和偶然样本 | 训练集很好,新数据很差 |
一句话:
欠拟合是"没学会",过拟合是"学太死",恰当拟合是"学会了还能举一反三"。
2. 为什么会欠拟合
常见原因:
-
模型太简单:线性模型拟合强非线性关系。
-
特征不足或特征表达差:关键信息没提供,或原始特征不适合任务。
-
正则化过强:把模型约束得太狠。
-
训练不充分:迭代次数少、学习率不合适、优化没收敛。
-
数据质量问题:标签噪声大、特征缺失严重、采样偏差。
典型表现:
-
训练误差和验证误差都高;
-
增加数据后效果提升不明显;
-
残差呈现明显系统模式,而不是随机分布;
-
学习曲线在高误差处趋于平台。
解决方向:
-
增加模型复杂度;
-
构造更有表达力的特征,如特征组合、分桶、交互项;
-
减少正则化;
-
训练更充分、调学习率;
-
换更合适的模型;
-
检查数据和标签质量。
3. 为什么会过拟合
常见原因:
-
模型太复杂:参数太多、树太深、网络太大。
-
训练数据太少:样本不足以约束模型。
-
噪声和异常值被学习:模型把偶然现象当规律。
-
特征太多且无效:尤其大量稀疏类别交叉特征。
-
特征组合过细:如"城市×性别×商品×小时"组合爆炸,训练集覆盖好,验证集稀疏。
-
数据泄漏:训练时用了预测时拿不到的信息,造成"假的好效果"。
-
反复调参看测试集:测试集信息泄漏到模型选择中。
典型表现:
-
训练误差持续下降,验证误差先降后升;
-
训练集指标远好于验证集;
-
换一批数据效果波动大;
-
模型对少数样本或异常值非常敏感;
-
特征重要性不稳定。
解决方向:
-
增加数据、数据增强;
-
降低模型复杂度;
-
L1/L2 正则、Dropout、树剪枝、早停;
-
特征选择、降维、减少过细特征组合;
-
交叉验证选参;
-
集成学习;
-
严格划分训练/验证/测试,防止数据泄漏。
4. 一句话总结
模型拟合问题就是模型对数据规律学习程度是否合适的问题:欠拟合是模型太简单没学会,过拟合是模型太复杂学太死;诊断看训练与验证误差差距及学习曲线,解决靠调整模型复杂度、特征表达、正则化、数据量和验证方式,最终目标是获得稳定可泛化的模型。
4. KNN算法
1. KNN算法简介
1.1 KNN思想
KNN(K-Nearest Neighbor,K-近邻算法)的核心思想可以概括为"近朱者赤,近墨者黑"或"物以类聚":如果一个样本在特征空间中的 k 个最相似(即距离最近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。
KNN 是一种非参数统计方法 ,不对数据分布做任何先验假设;同时它也是一种惰性学习算法------训练阶段仅存储数据,真正的计算推迟到预测阶段才进行。这一特性使 KNN 原理直观、易于实现,但在数据规模较大时预测开销较高。
分类问题的处理流程:
-
计算未知样本到每一个训练样本的距离(通常使用欧氏距离)。
-
将训练样本按距离升序排列。
-
取距离最近的 k 个训练样本。
-
多数表决:统计这 k 个样本中哪个类别出现次数最多。
-
将未知样本归为该类别。
数学上,给定测试样本 xq,预测类别 y^为:

其中 Nk(xq)是 xq的 k 个最近邻,I(⋅)为指示函数。
回归问题的处理流程:
前 3 步与分类相同;第 4 步不再投票,而是计算这 k 个邻居目标值的平均值(或加权平均值),作为预测值。
1.2 K值的影响
K 是 KNN 唯一的核心超参数,其选择直接决定模型的偏差---方差权衡:
| K 值 | 模型特点 | 风险 |
|---|---|---|
| 过小(如 K=1) | 决策边界高度复杂,对噪声和异常点敏感 | 过拟合 |
| 过大 | 决策边界平滑,受样本均衡影响大 | 欠拟合 |
| K=N(训练样本总数) | 预测结果恒为训练集中最多的类别 | 完全忽略数据信息,严重欠拟合 |
实际工作中通常使用交叉验证从较小的奇数 K 值(如 3、5、7)中选取最优值。
2. KNN算法API介绍
sklearn.neighbors 提供了分类与回归两个对偶 API,两者参数体系几乎完全一致。
2.1 分类:KNeighborsClassifier
python
"""
代码实现思路:
1. 导包
2. 准备数据集(测试集和训练集)
3. 创建(KNN分类模型)模型对象
4. 模型训练
5. 模型预测
"""
# 1. 导包
from sklearn.neighbors import KNeighborsClassifier
# 2. 准备数据集(测试集和训练集)
x_train = [[0],[1],[2],[3]]
y_train = [0,0,1,1]
x_test = [[5]]
# 3. 创建(KNN分类模型)模型对象
estimator = KNeighborsClassifier(n_neighbors=2)
# 4. 模型训练
estimator.fit(x_train, y_train)
# 5. 模型预测
y_pred = estimator.predict(x_test)
# 6. 打印预测结果
print(y_pred)
关键参数:
-
n_neighbors:近邻个数 K,默认 5。 -
weights:投票权重。'uniform'(默认)表示所有近邻等权;'distance'表示权重与距离成反比,距离越近权重越大。 -
metric与p:metric='minkowski'为默认距离度量;p=1退化为曼哈顿距离,p=2退化为欧氏距离。 -
algorithm:近邻搜索算法,'auto'根据数据自动在'kd_tree'、'ball_tree'、'brute'中选择。
注意 :predict 的输入必须是二维数组 ,即形状为 (n_samples, n_features),单样本预测应写成 [[x1, x2, ...]] 而非 [x1, x2, ...]。
2.2 回归:KNeighborsRegressor
python
from sklearn.neighbors import KNeighborsRegressor
estimator = KNeighborsRegressor(n_neighbors=3)
estimator.fit(X_train, y_train)
y_pred = estimator.predict(X_test) # 返回 k 个邻居目标值的平均
参数含义与分类器完全对应,差异仅在于输出:分类输出类别标签,回归输出连续数值。
3. 距离度量
KNN 的"近"由距离函数定义,距离度量的选择直接影响哪些样本成为邻居。
3.1 欧氏距离(Euclidean Distance)
最常用的距离度量,即 n 维空间中的直线距离:

优点:直观、计算简单;缺点:对特征尺度敏感------量纲大的特征会主导距离计算结果。
3.2 曼哈顿距离(Manhattan Distance)
又称"城市街区距离",沿坐标轴方向移动的总距离(对应维度差值的绝对值,求和):

适合特征之间存在"网格式"约束或希望降低单个维度极端差异影响的场景。
3.3 闵可夫斯基距离(Minkowski Distance)
闵可夫斯基距离是一个"距离家族",通过参数 pp 统一生成多种距离:

-
p=1:曼哈顿距离
-
p=2:欧氏距离
-
p→∞:切比雪夫距离
在 sklearn 的 KNeighborsClassifier / KNeighborsRegressor 中,metric='minkowski' 配合 p 参数即可切换上述距离。
3.4 余弦相似度(Cosine Similarity)
衡量两个向量在方向上的一致程度,而非数值大小:

取值范围 −1,1,越接近 1 方向越一致。特别适合文本数据(如 TF-IDF 向量),因为文本分类中词频的量级往往不如词语分布模式重要。
3.5 距离度量的选择要点
| 距离 | 适用场景 | 主要注意事项 |
|---|---|---|
| 欧氏距离 | 连续特征、低维、尺度相近 | 必须配合归一化/标准化 |
| 曼哈顿距离 | 高维稀疏特征、希望抑制极端差值 | 对尺度同样敏感 |
| 余弦相似度 | 文本、方向性特征 | 忽略向量模长 |
| 闵可夫斯基距离 | 需要通过 pp 调参的实验场景 | pp 本身成为额外超参数 |
当各特征量纲不一致时,标准欧氏距离或先做标准化再使用普通欧氏距离是必要的修正手段。
4. 特征预处理
4.1 为什么 KNN 必须做特征预处理
KNN 完全依赖距离进行决策。如果某一特征的单位远大于其他特征(例如"收入"以元计,而"年龄"以岁计),该特征在距离计算中将占据压倒性权重,其他特征的信息被"淹没"。因此,在 KNN 中特征缩放不是可选项,而是必要步骤。
4.2 归一化(MinMaxScaler)
将每个特征线性映射到指定区间,默认 0,1:

python
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler(feature_range=(0, 1))
X_scaled = scaler.fit_transform(X)
特点 :保留原始分布形状;但对异常值敏感------一个极端最大值或最小值会压缩其余样本的缩放范围,鲁棒性较差。适合传统精确小数据场景。
4.3 标准化(StandardScaler)
将特征转换为均值为 0、标准差为 1 的分布:

μ 为特征的平均值
σ 为特征的标准差
python
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
特点:对异常值的鲁棒性优于归一化,是 KNN 中更常用的预处理方式,尤其当特征分布近似正态或存在一定程度离群值时。
4.4 归一化 vs 标准化
| MinMaxScaler | StandardScaler | |
|---|---|---|
| 输出范围 | 固定区间(如 0,1) | 均值 0,标准差 1,范围不固定 |
| 异常值影响 | 大(被极值主导) | 相对较小 |
| 适用场景 | 数据边界明确、无强异常值 | 一般场景,KNN 默认推荐 |
4.5 鸢尾花识别案例(KNN + 标准化)
python
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
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.2, random_state=42, stratify=y
)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test) # 注意:测试集用 transform,不用 fit
knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train_scaled, y_train)
print("测试集准确率:", knn.score(X_test_scaled, y_test))
流程要点:先划分训练/测试集,再在训练集上 fit 预处理器 ,测试集只做 transform,避免数据泄漏。
5. 超参数选择方法
KNN 的核心超参数是 n_neighbors(K),辅以 weights、p 等。手动逐个尝试既不可靠也不高效,标准做法是交叉验证 + 网格搜索。
5.1 交叉验证(Cross-Validation)
K 折交叉验证将训练数据划分为 K 份,每次用其中 K−1 份训练、剩余 1 份验证,循环 K 次后取平均性能作为该组超参数的评估结果。这样每个样本都恰好被验证一次,评估结果比单次训练/验证划分更稳定,尤其适合数据量有限时使用。
5.2 网格搜索(GridSearchCV)
GridSearchCV 将交叉验证封装为自动化流程:给定超参数候选值构成的"网格",对每一个组合执行 K 折交叉验证,最终返回平均得分最高的参数组合。
python
from sklearn.model_selection import GridSearchCV
param_grid = {
"n_neighbors": [3, 5, 7, 9, 11],
"weights": ["uniform", "distance"],
}
grid = GridSearchCV(
estimator=KNeighborsClassifier(),
param_grid=param_grid,
cv=5,
scoring="accuracy",
)
grid.fit(X_train_scaled, y_train)
print("最佳参数:", grid.best_params_)
print("最佳交叉验证得分:", grid.best_score_)
print("测试集得分:", grid.score(X_test_scaled, y_test))
GridSearchCV 在 fit 时自动完成所有参数组合的交叉验证;之后调用 predict 或 score 时,自动使用 best_estimator_,无需手动重建模型。
5.3 手写数字识别案例(digits + GridSearchCV)
python
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import Pipeline
digits = load_digits()
X, y = digits.data, digits.target # 8×8 像素展平为 64 维
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
pipe = Pipeline([
("scaler", StandardScaler()),
("knn", KNeighborsClassifier()),
])
param_grid = {
"knn__n_neighbors": [3, 4, 5, 6, 7],
"knn__weights": ["uniform", "distance"],
"knn__p": [1, 2], # 1=曼哈顿,2=欧氏
}
grid = GridSearchCV(pipe, param_grid, cv=5, scoring="accuracy", n_jobs=-1)
grid.fit(X_train, y_train)
print("最佳参数:", grid.best_params_)
print("测试集准确率:", grid.score(X_test, y_test))
使用 Pipeline 的关键优势:交叉验证的每一折中,StandardScaler 只在当折的训练部分上 fit ,验证部分仅做 transform,彻底避免预处理阶段的数据泄漏。digits 数据集下 KNN 配合合理的 K 与距离度量,测试准确率通常可达 97% 以上。