一、D1-D6内容复习

https://blog.csdn.net/m0_48133999/article/details/150448968?spm=1011.2124.3001.6209

二、D8-D14内容复习

1. 标签编码与连续变量处理

1. 字典的简单介绍

  • 在 Python 中,字典(dictionary)是一种非常实用的数据结构,用于存储键值对(key-value pairs)。它具有高效的查找性能,是 Python 中最常用的数据类型之一。
  • 字典的基本特点
    1. 字典使用大括号 {} 定义
    2. 每个元素由键(key)和值(value)组成,格式为 key: value
    3. 键和值之间用冒号分隔,元素之间用逗号分隔
    4. 字典中的键必须是不可变类型(如字符串、数字、元组),且不能重复
    5. 字典中的值可以是任何数据类型,包括列表、字典等
    6. 字典是无序的(在 Python 3.7+ 中实际保留插入顺序)

2. 字典常用方法

  • keys():返回所有键的视图
  • values():返回所有值的视图
  • items():返回所有键值对的视图
  • pop(key):删除并返回指定键的值
  • popitem():删除并返回最后插入的键值对
  • update():用另一个字典更新当前字典

(以上由AI解答,仅做记录)

【头哥-Python】字典自学引导

3. 标签编码

day-5 学习了离散数据 如果是不存在顺序,则采用独热编码,函数为pd.get_dummies()

现在学习了对于存在顺序和大小关系的离散特征,做好标签编码,借助dataframe的map函数即可实现。

标签编码的适用场景
  • 适用于有序分类特征(Ordinal Features),即类别之间存在明确的顺序关系(如:学历 “小学”<“中学”<“大学”)
  • 不适用于无序分类特征(Nominal Features),如颜色(红、蓝、绿)、职业(医生、教师、工程师)等,因为会人为引入不存在的顺序关系
  • 二分类的问题不需要独热编码,三分类以上才涉及独热编码

比如性别这个特征,男女不需要变成2个特征,性别男 性别女 。因为他们二者自由度为1,如果是2个特征的话,性别男=1,那么性别女必定等于0.这样特征高度相关,没有价值。

此时这个特征的含义不是性别,而是:是否为男性,1是男性,0是非男。

实现方式
  • scikit-learn 的 LabelEncoder 类实现标签编码,也可以通过自定义字典手动实现。
标签编码的优缺点
  • 优点:
    – 实现简单,转换后数据维度不变
    – 适用于有序分类特征,能保留类别间的顺序关系
  • 缺点:
    – 会给无序分类特征引入虚假的数值关系(如将 “红 = 0”、“蓝 = 1” 误认为红 < 蓝)
    – 可能导致模型误解类别间的距离关系(如认为 1 和 2 的距离比 1 和 3 更近)

4. 连续特征的处理:归一化和标准化

归一化

在这里插入图片描述

标准化

在这里插入图片描述

选择建议
  • 归一化:特征边界明确且需保留比例关系时优先使用。
  • 标准化:数据存在异常值或算法对分布有要求时更有效。
  • 树模型(如随机森林):通常无需缩放,因分裂基于特征排序。
实现方式
  • 自己写函数
  • sklearn 库
    –StandardScaler:标准化工具(将数据转换为均值为 0、标准差为 1 的分布)
    – MinMaxScaler:归一化工具(将数据缩放到 [0, 1] 区间)

2. 热力图和子图的绘制

1. 热力图的绘制方法

热力图是通过颜色深浅来直观展示数据矩阵中数值大小的图表,常用语展示相关性矩阵、混淆矩阵等。在python中,常用的绘制库是seabornmatplotlib

绘制步骤

  1. 准备数据: 先通过corr()函数来计算数据集的相关系数矩阵,corr() 通常用于特征选择(分析自变量与因变量的相关性)、多重共线性检测(分析自变量之间的相关性)、探索数据中潜在的关联模式。corr()函数的参数说明如下:
  • method:指定相关系数的计算方法(默认 'pearson'):
    • 'pearson':皮尔逊相关系数(最常用,适用于线性相关)。
    • 'kendall':肯德尔等级相关系数(适用于有序分类数据)。
    • 'spearman':斯皮尔曼等级相关系数(适用于非线性或有序数据)。
  • min_periods:计算相关系数所需的最小非缺失值数量(默认 1)。
  • 结果解释:
    • 相关系数的取值范围是 [-1, 1]:
      • 接近 1:强正相关(一个变量增加,另一个变量也增加)。
      • 接近 -1:强负相关(一个变量增加,另一个变量减少)。
      • 接近 0:几乎无线性相关。
    • 对角线元素恒为 1(变量与自身的相关性)。
    • 矩阵是对称的(A与B的相关性 = B与A的相关性)
  • 注意事项:
    • corr() 会自动排除包含缺失值(NaN)的行 / 列(或根据min_periods调整)。
    • 相关系数仅衡量线性关系,无法捕捉非线性关联(如二次关系)。
    • 相关性不等于因果关系,需结合业务逻辑解读结果。
  1. 使用seaborn绘制

    示例代码:

import seaborn as sns
import matplotlib.pyplot as plt

sns.heatmap(data,  # 数据矩阵
            cmap="YlGnBu",  # 颜色映射(如"viridis"、"coolwarm")
            annot=True,  # 是否在单元格中显示数值
            fmt=".2f",  # 数值格式(保留2位小数)
            linewidths=.5)  # 单元格边框宽度
plt.title("热力图示例")
plt.show()
  • 应用场景:
    • 相关性分析(如特征间的相关系数矩阵)
    • 数据分布可视化(如不同时间段的数值变化)

2. enumerate()函数

enumerate()是 Python 内置函数,用于将可迭代对象(如列表、元组)转换为枚举对象,同时返回元素的索引,简化了 “遍历元素并记录位置” 的操作。
语法:

enumerate(iterable, start=0)
  • iterable:需遍历的可迭代对象(如列表、字符串)。
  • start:索引的起始值(默认从 0 开始)。
    优势:
  • 避免手动定义计数器(如 i = 0; for value in iterable: ...; i += 1),代码更简洁。
  • 适用于需要同时获取元素位置和值的场景(如遍历列表并修改指定索引的元素)。

3. 子图的绘制方法

子图(Subplot)是在同一画布上绘制多个图表,便于对比数据。常用matplotlibplt.subplots()——一次性创建多个子图或plt.subplot()——逐个创建子图实现。

方法一:plt.subplots ()
语法:

fig, axes = plt.subplots(nrows, ncols, figsize=(,))
  • nrows:子图行数;ncols:子图列数。
  • fig:画布对象;axes:子图数组(若单图则为单个对象)。

示例代码:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
y3 = x**2
y4 = np.exp(x)

# 创建2行2列的子图,画布大小10x8
fig, axes = plt.subplots(2, 2, figsize=(10, 8))

# 第1行第1列子图(索引[0,0])
axes[0, 0].plot(x, y1)
axes[0, 0].set_title("sin(x)")

# 第1行第2列子图(索引[0,1])
axes[0, 1].plot(x, y2, color="orange")
axes[0, 1].set_title("cos(x)")

# 第2行第1列子图(索引[1,0])
axes[1, 0].plot(x, y3, color="green")
axes[1, 0].set_title("x²")

# 第2行第2列子图(索引[1,1])
axes[1, 1].plot(x, y4, color="red")
axes[1, 1].set_title("e^x")

plt.tight_layout()  # 自动调整子图间距,避免重叠
plt.show()

方法二:plt.subplot ()
语法:

plt.subplot(nrows, ncols, index)  # index从1开始,按行优先排序

示例代码:

plt.subplot(1, 2, 1)  # 第1个位置(1行2列中的第1个)
plt.plot(x, y1)
plt.title("图1")

plt.subplot(1, 2, 2)  # 第2个位置
plt.plot(x, y2)
plt.title("图2")

plt.tight_layout()
plt.show()

注意事项:

  • 子图索引在subplots()中是0 开始的数组索引,在subplot()中是1 开始的位置编号
  • 使用plt.tight_layout()可避免子图标题、标签重叠。
  • 可通过axes对象单独设置每个子图的标题(set_title())、坐标轴标签(set_xlabel())等。

3. 机器学习建模与评估

1. 数据集的划分

在机器学习中,通常将数据集划分为训练集(用于模型训练)和测试集(用于评估模型性能),有时还会增加验证集(用于调参)。常用的工具是scikit-learntrain_test_split
示例:

from sklearn.model_selection import train_test_split

# X为特征数据,y为标签数据
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42  # 测试集占20%,固定随机种子确保结果可复现
)

2. 机器学习模型建模的三行代码

以逻辑回归(分类问题)为例,核心步骤通常是:实例化模型训练模型预测结果
示例:

from sklearn.linear_model import LogisticRegression

# 1. 实例化模型(可设置超参数)
model = LogisticRegression(random_state=42)

# 2. 用训练集训练模型
model.fit(X_train, y_train)

# 3. 用训练好的模型预测测试集
y_pred = model.predict(X_test)

3. 机器学习模型分类问题的评估

分类问题的常用评估指标包括:准确率(Accuracy)、精确率(Precision)、召回率(Recall)、F1 分数、混淆矩阵、ROC 曲线等。
示例:

from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix, classification_report

# 准确率:正确预测的样本占总样本的比例
accuracy = accuracy_score(y_test, y_pred)
print(f"准确率:{accuracy:.4f}")

# 精确率:预测为正例的样本中实际为正例的比例
precision = precision_score(y_test, y_pred)
print(f"精确率:{precision:.4f}")

# 召回率:实际为正例的样本中被正确预测的比例
recall = recall_score(y_test, y_pred)
print(f"召回率:{recall:.4f}")

# F1分数:精确率和召回率的调和平均
f1 = f1_score(y_test, y_pred)
print(f"F1分数:{f1:.4f}")

# 混淆矩阵:展示预测结果与真实标签的对应关系
print("混淆矩阵:")
print(confusion_matrix(y_test, y_pred))

# 综合报告:包含上述所有指标(适用于多分类)
print("分类报告:")
print(classification_report(y_test, y_pred))

说明:

  • 二分类问题中,需指定average参数(如average='binary');
  • 多分类问题需根据需求选择average(如macromicroweighted)。

4. 常见的调参方式

1. 网格搜索(Grid Search)

网格搜索是最常用的超参数调优方法,通过穷举所有预设的超参数组合,结合交叉验证评估性能,选择最优组合。其核心思想是将超参数空间转化为 “网格”,逐点搜索最优解。

原理与特点

  • 优点:确定性强,能找到预设范围内的最优组合;实现简单。
  • 缺点:当超参数数量多或范围大时,计算成本极高(时间复杂度随超参数数量呈指数增长)。

实战实现(以 scikit-learn 为例)

from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC

# 1. 定义超参数网格
param_grid = {
    'C': [0.1, 1, 10],  # 正则化参数
    'kernel': ['linear', 'rbf'],  # 核函数
    'gamma': ['scale', 'auto']  # 核系数
}

# 2. 实例化网格搜索(结合5折交叉验证)
grid_search = GridSearchCV(
    estimator=SVC(random_state=42),
    param_grid=param_grid,
    cv=5,  # 5折交叉验证
    scoring='f1',  # 评估指标
    n_jobs=-1  # 并行计算(使用所有CPU核心)
)

# 3. 训练与搜索最优参数
grid_search.fit(X_train, y_train)

# 4. 输出最优结果
print("最优超参数:", grid_search.best_params_)
print("最优交叉验证分数:", grid_search.best_score_)

# 5. 用最优模型评估测试集
best_model = grid_search.best_estimator_
print("测试集F1分数:", f1_score(y_test, best_model.predict(X_test)))

2. 随机搜索(Random Search)

随机搜索与网格搜索类似,但不遍历所有组合,而是在超参数空间中随机采样一定数量的组合,通过交叉验证选择最优。

原理与特点

  • 优点:计算成本低于网格搜索(无需遍历所有组合),在超参数数量多或范围大时更高效。
  • 缺点:结果具有随机性,可能错过最优组合;确定性差。

简单实现(以 scikit-learn 为例)

from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint, uniform  # 用于生成随机分布

# 1. 定义超参数随机分布(而非固定网格)
param_dist = {
    'C': uniform(0.1, 10),  # 0.1~10.1的均匀分布
    'kernel': ['linear', 'rbf'],
    'gamma': ['scale', 'auto'] + list(uniform(0.01, 1).rvs(5))  # 混合固定值和随机值
}

# 2. 实例化随机搜索(采样20组组合)
random_search = RandomizedSearchCV(
    estimator=SVC(random_state=42),
    param_distributions=param_dist,
    n_iter=20,  # 随机采样20组
    cv=5,
    scoring='f1',
    n_jobs=-1,
    random_state=42
)

# 3. 训练与评估(后续步骤同网格搜索)
random_search.fit(X_train, y_train)

3. 贝叶斯优化(Bayesian Optimization)

贝叶斯优化是更高效的超参数调优方法,基于贝叶斯定理,通过不断更新 “代理模型”(概率模型)来预测超参数组合的性能,优先探索可能更优的区域,减少无效搜索。

核心逻辑

与网格 / 随机搜索的 “无记忆” 不同,贝叶斯优化会利用历史搜索结果指导下一次采样:

  1. 初始化:随机采样少量超参数组合,计算性能。
  2. 构建代理模型:用历史数据拟合超参数与性能的概率关系(如 “超参数 A=10 时,性能有 80% 概率≥0.8”)。
  3. 选择下一组超参数:基于代理模型,选择 “最可能提升性能” 的组合(通过 “采集函数”,如期望提升)。
  4. 迭代:重复步骤 2-3,直到达到最大迭代次数。

两种实现逻辑

(1)基于高斯过程(Gaussian Process, GP)的贝叶斯优化

scikit-optimize库为例,用高斯过程作为代理模型,适合低维超参数空间(≤20 维)。

from skopt import BayesSearchCV
from skopt.space import Real, Categorical, Integer

# 1. 定义超参数空间(支持连续/离散/分类类型)
search_space = {
    'C': Real(0.1, 10, 'log-uniform'),  # 对数均匀分布(适合数值范围大的参数)
    'kernel': Categorical(['linear', 'rbf']),
    'gamma': Real(0.001, 1, 'log-uniform')
}

# 2. 实例化贝叶斯搜索(结合5折交叉验证)
bayes_search = BayesSearchCV(
    estimator=SVC(random_state=42),
    search_spaces=search_space,
    n_iter=30,  # 总迭代次数
    cv=5,
    scoring='f1',
    random_state=42
)

# 3. 训练与评估
bayes_search.fit(X_train, y_train)

(2)基于树结构 Parzen 估计器(Tree-structured Parzen Estimator, TPE)的贝叶斯优化

hyperopt库为例,TPE 是更高效的代理模型,适合高维超参数空间,在工业界更常用。

from hyperopt import fmin, tpe, hp, Trials, STATUS_OK

# 1. 定义超参数空间
space = {
    'C': hp.loguniform('C', np.log(0.1), np.log(10)),  # 对数均匀分布
    'kernel': hp.choice('kernel', ['linear', 'rbf']),
    'gamma': hp.loguniform('gamma', np.log(0.001), np.log(1))
}

# 2. 定义目标函数(输入超参数,输出性能分数的负值,因为fmin是最小化)
def objective(params):
    model = SVC(**params, random_state=42)
    model.fit(X_train, y_train)
    y_pred = model.predict(X_val)  # 用验证集评估(而非交叉验证)
    f1 = f1_score(y_val, y_pred)
    return {'loss': -f1, 'status': STATUS_OK}  # 最小化负F1等价于最大化F1

# 3. 初始化试验记录器
trials = Trials()

# 4. 执行优化(用TPE算法,最大迭代30次)
best = fmin(
    fn=objective,
    space=space,
    algo=tpe.suggest,  # TPE算法
    max_evals=30,
    trials=trials,
    rstate=np.random.default_rng(42)
)

print("最优超参数:", best)

如何避开 “必须用交叉验证” 的问题?

交叉验证能减少结果波动,但计算成本高(尤其大数据 / 复杂模型)。贝叶斯优化中可通过以下方式替代:

  1. 留出验证集(Hold-out Validation:将训练集再拆分出一个固定验证集(如 70% 训练,20% 验证,10% 测试),用验证集性能指导贝叶斯优化,而非 k 折交叉验证。
  • 适用场景:数据量大且分布稳定(验证集能代表整体分布),如工业级大数据。
  1. 减少交叉验证折数:如用 2-3 折交叉验证,平衡稳定性与效率(比 5-10 折快,但波动略大)。

  2. 早期停止(Early Stopping):对迭代式模型(如 LightGBM、神经网络),用单折训练 + 验证集早停代替交叉验证,快速评估超参数效果。

4. time库的计时模块

time库可用于记录代码运行时间,方便分析效率瓶颈(尤其超参数调优这类耗时任务)。

常用方法

  1. time.time()返回当前时间戳(秒),适合粗略计时。
  2. time.perf_counter():返回高精度计时器(包含睡眠耗时),适合精细计时。

示例

import time

# 记录代码块1的运行时间
start_time = time.perf_counter()

# 示例:训练模型
model = SVC(C=1, kernel='rbf')
model.fit(X_train, y_train)
y_pred = model.predict(X_test)

end_time = time.perf_counter()
print(f"模型训练+预测耗时:{end_time - start_time:.4f}秒")


# 记录超参数搜索的运行时间
start_search = time.perf_counter()

# 示例:网格搜索
grid_search = GridSearchCV(SVC(), param_grid={'C': [0.1, 1]}, cv=3)
grid_search.fit(X_train, y_train)

end_search = time.perf_counter()
print(f"网格搜索总耗时:{end_search - start_search:.2f}秒")  # 保留2位小数

总结

简单的调参方法

  1. 随机搜索 : 在参数空间中随机选择参数组合,然后使用交叉验证来评估每个组合的性能:
  2. 网格搜索
  3. 贝叶斯优化
  • 基线模型(基准模型): 首先运行一个使用默认参数的 RandomForestClassifier,记录其性能作为比较的基准。

1. 网格搜索 (GridSearchCV):

  • 需要定义参数的网格(param_grid),包含所有你想要尝试的特定值的列表。它会尝试网格中所有可能的参数组合。
  • 缺点: 计算成本非常高,参数和值的数量稍多,组合数就会呈指数级增长(维度灾难)。因此,网格通常设置得比较小或集中在认为最优参数可能存在的区域(可能基于随机搜索的初步结果)。

2. 随机搜索 (RandomizedSearchCV):

  • 需要定义参数的分布,而不是固定的列表。这是它与网格搜索的主要区别,它不会尝试所有组合,而是在指定次数内随机采样。通常,用相对较少的迭代次数(如 50-100)就能找到相当好的参数。
  • 对于给定的计算预算,随机搜索通常比网格搜索更有效,尤其是在高维参数空间中。

3. 贝叶斯优化 (BayesSearchCV from skopt):

  • 需要定义参数的搜索空间,与随机搜索类似,当搜索空间非常大时,它通常比网格搜索和随机搜索更有效。
  • 核心优势: 它不是随机选择下一个点,而是根据先前评估的结果建立一个概率模型(通常是高斯过程),预测哪些参数组合可能产生更好的结果,并据此选择下一个评估点。这使得它在寻找最优解方面通常比随机搜索更高效(用更少的迭代次数达到相似或更好的性能),特别是当模型训练(单次评估)非常耗时的时候。

正常情况下,计算资源够用网格,计算资源不够用贝叶斯优化。随机搜索不常用。

5. 启发式算法

1. 模拟退火算法(Simulated Annealing, SA)

灵感来源

源于物理化学中的 “退火过程”—— 将固体加热至高温后缓慢冷却,使原子排列趋于能量最低的稳定状态。

基本思想
  • 模拟退火过程中 “温度越高,原子越易脱离局部稳定状态;温度降低,原子逐渐稳定” 的特性,用于解决优化问题。
  • 核心是概率性接受较差解:
    • 初始时 “温度” 较高,允许以较大概率接受质量较差的解(类似高温下原子自由运动),避免过早陷入局部最优。
    • 随着 “温度” 逐渐降低(按一定冷却速率下降),接受较差解的概率越来越小,最终收敛到稳定的最优解(类似低温下原子趋于稳定排列)。
  • 通过控制温度下降过程,平衡 “探索”(寻找新解空间)和 “利用”(优化当前解),实现全局寻优。
伪代码
算法:模拟退火算法
输入:目标函数f(x),解空间范围,初始温度T₀,冷却系数α,终止温度T_end
输出:最优解x_best,最优值f_best

1. 初始化:
   随机生成初始解x_current
   计算初始值f_current = f(x_current)
   设置x_best = x_current,f_best = f_current
   设置当前温度T = T₀
   
2. 迭代优化(直到T ≤ T_end):
   a. 生成邻域解:
      x_new = 基于x_current的随机微小扰动(如x_current ± 随机步长)
      (需限制x_new在解空间范围内)
   
   b. 计算差值:
      Δf = f(x_new) - f_current (目标是最大化,故Δf为新解-当前解)
   
   c. 接受准则:
      若Δf > 0(新解更优):
          接受x_new,即x_current = x_new,f_current = f(x_new)
      否则(新解较差):
          计算接受概率p = exp(Δf / T)
          若随机数r ∈ [0,1) < p,则接受x_new
   
   d. 更新最优解:
      若f_current > f_best,则x_best = x_current,f_best = f_current
   
   e. 降温:
      T = T × α (α通常取0.8~0.99)
   
3. 返回x_best和f_best

2. 粒子群算法(Particle Swarm Optimization, PSO)

灵感来源

源于对鸟类群体觅食行为的观察,模拟了鸟群在寻找食物时的群体协作与信息共享机制。

基本思想
  • 将每个可能解视为 “粒子”,所有粒子在解空间中 “飞行”,每个粒子的位置代表一个候选解,速度代表移动方向和距离。
  • 粒子的飞行行为受两个因素引导:
    • 个体经验:粒子自身历史找到的最优位置(“个体最优”)。
    • 群体经验:整个粒子群目前找到的最优位置(“全局最优”)。
  • 每个粒子通过动态调整速度(结合个体最优和全局最优的方向)更新位置,最终群体向全局最优解聚集。
  • 核心是通过群体内的信息共享与协作,实现高效寻优,无需复杂的交叉、变异操作,原理更简洁。
伪代码
算法:粒子群算法
输入:目标函数f(x),解空间维度d,粒子数N,最大迭代次数T
输出:最优解x_best,最优值f_best

1. 初始化粒子群:
   对每个粒子i(1≤i≤N):
      随机生成初始位置xᵢ = [xᵢ₁, xᵢ₂, ..., xᵢd]
      随机生成初始速度vᵢ = [vᵢ₁, vᵢ₂, ..., vᵢd]
      设置个体最优位置pbestᵢ = xᵢ,个体最优值f_pbestᵢ = f(xᵢ)
   
2. 初始化全局最优:
   找到所有pbest中最优的解,设为gbest,对应值为f_gbest
   
3. 迭代优化(t从1到T):
   对每个粒子i:
      a. 更新速度:
         vᵢₜ₊₁ = ω·vᵢₜ + c₁·r₁·(pbestᵢ - xᵢₜ) + c₂·r₂·(gbest - xᵢₜ)
         (ω为惯性权重,c₁、c₂为学习因子,r₁、r₂为[0,1]随机数)
      
      b. 更新位置:
         xᵢₜ₊₁ = xᵢₜ + vᵢₜ₊₁(需限制在解空间范围内)
      
      c. 更新个体最优:
         若f(xᵢₜ₊₁) > f_pbestᵢ,则pbestᵢ = xᵢₜ₊₁,f_pbestᵢ = f(xᵢₜ₊₁)
      
      d. 更新全局最优:
         若f_pbestᵢ > f_gbest,则gbest = pbestᵢ,f_gbest = f_pbestᵢ
   
4. 返回gbest和f_gbest

3. 遗传算法(Genetic Algorithm, GA)

灵感来源

源于生物进化理论中的自然选择与遗传学原理,模拟了生物在自然环境中 “物竞天择、适者生存” 的进化过程。

基本思想
  • 将优化问题的每个可能解抽象为 “染色体”(通常用二进制或实数编码表示),多个解构成 “种群”。
  • 通过模拟生物进化的三大核心操作迭代寻优:
    • 选择:根据解的 “适应度”(对目标函数的满足程度)筛选优质个体,适应度高的解更易被保留(类似自然界中优势基因的传递)。
    • 交叉:随机选取两个优质解,交换部分 “基因片段”(如二进制编码中的位交换),生成新解(类似生物繁殖中的基因重组)。
    • 变异:对新解的某些 “基因” 进行随机微小调整(如二进制位翻转),引入新的可能性,避免种群陷入局部最优(类似基因突变)。
  • 迭代多代后,种群中适应度最高的个体即为近似最优解。
伪代码
算法:遗传算法
输入:目标函数f(x),解空间范围,种群大小N,最大迭代次数T
输出:最优解x_best,最优值f_best

1. 初始化种群:
   生成N个随机解{x₁, x₂, ..., x_N}(每个解为染色体编码)
   
2. 计算适应度:
   对每个解xᵢ,计算适应度fitnessᵢ = f(xᵢ)
   
3. 记录初始最优解:
   x_best = 种群中适应度最大的解
   f_best = 对应的适应度值
   
4. 迭代优化(t从1到T):
   a. 选择操作:
      根据适应度概率选择N个父代(适应度高的解被选中概率高)
   
   b. 交叉操作:
      对选中的父代随机配对,以交叉概率p_c交换部分基因,生成子代
   
   c. 变异操作:
      对子代中的每个基因,以变异概率p_m随机改变其值
   
   d. 形成新种群:
      合并父代和子代,选择适应度最高的N个解组成新种群
   
   e. 更新最优解:
      若新种群中存在解x',其适应度>f_best,则更新x_best=x',f_best=f(x')
   
5. 返回x_best和f_best

总结

三种算法均通过模拟自然现象实现优化,核心差异在于

  • 遗传算法:强调 “进化与选择”,通过 “选择 - 交叉 - 变异” 模拟进化,依赖种群多样性寻优。
  • 粒子群算法:强调 “群体协作”,通过个体与群体的信息共享调整搜索方向,收敛速度较快。
  • 模拟退火算法:强调 “渐进稳定”,通过温度控制平衡探索与利用,适合跳出局部最优。

其他优秀解答

模拟退火算法
粒子群算法
遗传算法

6. 不平衡数据的处理

1. 不平衡数据集处理策略

在处理不平衡数据集时,常用的策略包括:

  • 1. 过采样(Oversampling):增加少数类样本数量,使其与多数类平衡
    • 随机过采样:随机复制少数类样本
    • SMOTE 算法:生成少数类的合成样本,避免过拟合
  • 2. 修改类别权重:在模型训练时为少数类分配更高的权重
    • 多数类错误分类的代价较低,少数类错误分类的代价较高
    • 许多算法(如 SVMXGBoost随机森林)都支持 class_weight 参数
  • 3. 修改决策阈值:调整分类阈值以提高少数类的识别率
    • 默认阈值通常为 0.5,但可以根据业务需求调整
    • 可通过 ROC曲线或精确率 - 召回率曲线找到最优阈值

2. 交叉验证

交叉验证(Cross Validation)是一种评估机器学习模型性能的统计方法,它通过将数据集多次分割为训练集和验证集,多次训练和评估模型,从而获得更稳健的性能估计。这种方法可以有效避免单次划分带来的随机性影响,尤其适用于数据集较小的情况

常见的交叉验证方法

1. K 折交叉验证(K-Fold Cross Validation)

  • 将数据集均匀分成 K 个互斥的子集(fold)
  • 每次用 K-1 个子集作为训练集,1 个子集作为验证集
  • 重复 K 次,最终结果取 K 次评估的平均值

2. 分层 K 折交叉验证(Stratified K-Fold)

  • 对 K 折交叉验证的改进,确保每个折中各类别的比例与原始数据集一致
  • 特别适合不平衡分类问题

3. 留一交叉验证(Leave-One-Out)

  • 每次只留一个样本作为验证集,其余作为训练集
  • 数据集有 N 个样本时,需要训练 N 次
  • 结果稳定但计算成本高,适合小型数据集

4. 时间序列交叉验证(Time Series CV)

  • 考虑时间顺序,确保训练集总是在验证集之前
  • 适用于时间序列数据,避免数据泄露
代码示例
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import (
    KFold, StratifiedKFold, LeaveOneOut,
    cross_val_score, TimeSeriesSplit
)
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

# 生成示例数据集
X, y = make_classification(
    n_samples=200, n_features=10,
    n_informative=5, random_state=42
)

# 初始化模型
model = LogisticRegression(max_iter=1000, random_state=42)

# 1. K折交叉验证
def k_fold_cv():
    kf = KFold(n_splits=5, shuffle=True, random_state=42)
    scores = cross_val_score(model, X, y, cv=kf, scoring='accuracy')
    
    print(f"K折交叉验证 (K=5) 准确率: {scores.mean():.4f} ± {scores.std():.4f}")
    print(f"各折准确率: {['%.4f' % s for s in scores]}")
    return kf

# 2. 分层K折交叉验证
def stratified_k_fold_cv():
    skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
    scores = cross_val_score(model, X, y, cv=skf, scoring='accuracy')
    
    print(f"\n分层K折交叉验证 (K=5) 准确率: {scores.mean():.4f} ± {scores.std():.4f}")
    print(f"各折准确率: {['%.4f' % s for s in scores]}")
    return skf

# 3. 留一交叉验证 (只展示前10次结果,避免输出过多)
def leave_one_out_cv():
    loo = LeaveOneOut()
    scores = []
    
    # 只计算前10个样本,完整计算请移除切片
    for train_idx, test_idx in list(loo.split(X))[:10]:
        X_train, X_test = X[train_idx], X[test_idx]
        y_train, y_test = y[train_idx], y[test_idx]
        
        model.fit(X_train, y_train)
        y_pred = model.predict(X_test)
        scores.append(accuracy_score(y_test, y_pred))
    
    print(f"\n留一交叉验证 (前10次) 准确率: {np.mean(scores):.4f}")
    return loo

# 4. 时间序列交叉验证
def time_series_cv():
    # 创建示例时间序列数据索引
    n_samples = 100
    X_ts = np.random.randn(n_samples, 5)
    y_ts = np.random.randint(0, 2, size=n_samples)
    
    tscv = TimeSeriesSplit(n_splits=5)
    scores = []
    
    for train_idx, test_idx in tscv.split(X_ts):
        X_train, X_test = X_ts[train_idx], X_ts[test_idx]
        y_train, y_test = y_ts[train_idx], y_ts[test_idx]
        
        model.fit(X_train, y_train)
        y_pred = model.predict(X_test)
        scores.append(accuracy_score(y_test, y_pred))
    
    print(f"\n时间序列交叉验证 准确率: {np.mean(scores):.4f} ± {np.std(scores):.4f}")
    return tscv

# 可视化不同交叉验证的划分方式
def plot_cv_splits(cv, X, name):
    plt.figure(figsize=(10, 6))
    n_splits = cv.get_n_splits(X)
    
    for i, (train_idx, test_idx) in enumerate(cv.split(X)):
        # 绘制训练集
        plt.scatter(train_idx, [i] * len(train_idx), 
                   c='blue', marker='o', label='训练集' if i == 0 else "")
        # 绘制验证集
        plt.scatter(test_idx, [i] * len(test_idx), 
                   c='red', marker='o', label='验证集' if i == 0 else "")
    
    plt.title(f'{name} 划分方式')
    plt.xlabel('样本索引')
    plt.ylabel('折数')
    plt.legend()
    plt.tight_layout()
    plt.show()

# 运行所有示例
if __name__ == "__main__":
    kf = k_fold_cv()
    skf = stratified_k_fold_cv()
    loo = leave_one_out_cv()
    tscv = time_series_cv()
    
    # 可视化划分方式 (可选)
    # plot_cv_splits(kf, X, "K折交叉验证")
    # plot_cv_splits(skf, X, "分层K折交叉验证")
    # plot_cv_splits(tscv, np.random.randn(100, 5), "时间序列交叉验证")
    
代码说明

1. 数据集与模型: 使用make_classification生成分类数据集,以逻辑回归作为示例模型

2. 交叉验证实现:

  • K 折交叉验证:使用KFold,通过shuffle=True随机打乱数据
  • 分层 K 折:使用StratifiedKFold,保持各折中类别比例一致
  • 留一法:使用LeaveOneOut,计算成本高但结果可靠
  • 时间序列交叉验证:使用TimeSeriesSplit,确保训练集在验证集之前

3. 可视化功能: plot_cv_splits函数可以直观展示不同交叉验证方法的数据集划分方式

选择建议
  • 一般机器学习问题:优先使用分层 K 折交叉验证(尤其分类问题)
  • 平衡数据集:普通 K 折交叉验证即可
  • 小型数据集:留一交叉验证(计算资源允许的话)
  • 时间序列数据:必须使用时间序列交叉验证
  • 超参数调优:可结合交叉验证使用网格搜索或随机搜索

交叉验证的核心价值在于提供对模型泛化能力的更可靠估计,帮助我们选择更优的模型和参数配置。

7. SHAP图的绘制

SHAP 图全解析:原理、类型与应用

SHAP(SHapley Additive exPlanations)是基于博弈论中 Shapley Value 的模型解释工具,核心是通过计算每个特征对模型输出的边际贡献,打破机器学习 “黑盒”,从局部(单样本)和全局(全数据集)两个维度解释预测逻辑。其可视化图表(SHAP 图)是实现这一目标的关键载体,以下从核心原理、图表类型、解读方法及典型应用场景展开介绍。

一、SHAP 图的核心原理基础

在理解 SHAP 图前,需先明确两个核心概念:

  1. Shapley Value(沙普利值)
    起源于合作博弈论,用于公平分配 “多方合作产生的总收益”。在机器学习中,可类比为:

    • 所有输入特征(如年龄、收入)是 “合作者”,模型预测结果(如信贷违约概率、单车骑行量)是 “总收益”;
    • 每个特征的 Shapley Value 即其对预测结果的 “贡献值”(正贡献 = 推动预测值上升,负贡献 = 拉低预测值)。
  2. 基值(Base Value)
    每个特征的 Shapley Value 即其对预测结果的 “贡献值”(正贡献 = 推动预测值上升,负贡献 = 拉低预测值)。

二、SHAP图种类
1. 全局解释图 - 理解模型的整体行为

这类图从整个数据集的宏观角度展示模型的特征重要性以及特征如何影响预测。
a. 特征重要性的条形图

  • 是什么: 对每个特征的全局平均SHAP值(取绝对值后平均)进行排序的条形图。
  • 作用: 回答“哪些特征对模型输出最重要?”。它比基于特征系数的排序或树模型自带的“feature_importance”更一致、更准确。
  • 解读: 条形越长,特征对模型预测的整体影响越大。

b. 摘要图

  • 是什么: SHAP最核心、信息最丰富的图之一。它是每个样本的每个特征的SHAP值(影响力)的散点图。

  • 作用: 同时展示特征重要性、特征影响的方向(正面/负面)以及特征值与影响力的关系。

    • Y轴: 特征按重要性排序。

    • X轴: SHAP值(影响力大小和方向)。

    • 颜色: 表示特征值本身的大小(红色为高值,蓝色为低值)。

  • 解读:

    • 特征重要性: 特征在Y轴上的位置越高越重要。

    • 影响方向: 点越靠右,对预测结果的正面影响越大;越靠左,负面影响越大。

    • 关系趋势: 看颜色梯度。例如,如果某个特征的点从左到右颜色由蓝变红,说明特征值越大,SHAP值越大(正面影响越强),即正相关。

2. 局部解释图 - 理解单个样本的预测

这类图专注于解释模型为什么会为某一个特定的样本做出某个特定的预测。

a. 力图

  • 是什么: 展示单个样本的预测值是如何由基础值(所有样本预测的平均值)一步步被每个特征的特征值推高或拉低的。

  • 作用: 清晰、直观地展示单个预测的决策过程。

  • 解读:

    • 最底部的 base_value 是模型输出的平均值。

    • 红色箭头:将预测值从基础值推高的特征(正面影响)。

    • 蓝色箭头:将预测值从基础值拉低的特征(负面影响)。

    • 箭头长度代表影响力大小。

    • 最终箭头指向f(x),即模型对该样本的预测值。

b. 决策图

  • 是什么: 与力图类似,但它以线条的形式展示了多个特征影响的累积效应。

  • 作用: 解释单个预测,尤其适用于特征较多或特征间存在交互作用的情况。 比力图能更清晰地显示多条可能的“决策路径”。

  • 解读:

    • 从最底部的 base_value 开始,每条线代表一个特征对预测值的累积影响过程。

    • 线的顺序通常按SHAP值大小排列。

    • 最终所有线汇聚到该样本的预测值 f(x)

    • 可以很清楚地看到哪些特征在早期起到了决定性作用。

3. 依赖与交互图 - 理解特定特征的影响方式

这类图用于深入分析某一个或两个特征是如何影响模型输出的。

a. 依赖图

  • 是什么: 类似于PDP(部分依赖图),显示一个特征的变化如何影响模型的平均预测输出。

  • 作用:分析单个特征与模型预测结果之间的宏观关系。

  • 解读: X轴是特征值,Y轴是该特征值对应的平均SHAP值(即对预测的平均影响)。可以看到是线性关系、非线性关系还是有阈值效应。

b. 交互作用图

  • 是什么: 分析两个特征之间的交互效应如何影响输出。

  • 作用:回答“特征A的影响是否依赖于特征B的值?”

  • 解读: 通常先选择一个待分析的特征,然后自动选择另一个与它有最强交互效应的特征,用颜色进行区分。如果不同颜色的线明显不平行,则说明存在交互效应。

4. 其他高级与组合图

a. 聚类散点图

  • 作用:将样本进行聚类,然后绘制摘要图。 用于发现模型决策模式中的不同群体。例如,可以聚类样本的SHAP值,然后发现一组样本主要受特征A和B影响,另一组主要受特征C和D影响。

b. 热力图

  • 作用:用于时间序列等有序数据。 将每个样本(如每个时间点)的每个特征的SHAP值用热力图表示,可以追踪特征影响力随时间的变化趋势。

c. 小提琴图 / 蜂群图

  • 作用: 是摘要图的变体,用于显示每个特征的SHAP值分布(小提琴图),或者显示每个样本点的具体位置(蜂群图,即默认的摘要图)。
总结表
图表类型英文名解释范围核心问题
条形图Bar Plot全局哪些特征最重要?
摘要图Summary Plot全局特征如何影响预测?值与影响的关系?
力图Force Plot局部这个样本的预测是如何得出的?
决策图Decision Plot局部这个样本的预测路径是怎样的?
依赖图Dependence Plot全局+特定特征这个特征的值如何影响预测?
交互图Interaction Plot全局+交互这两个特征如何共同影响预测?
热力图Heatmap局部(时序)特征影响力如何随时间变化?

如何选择?

  • 想了解模型的整体行为:从条形图和摘要图开始。

  • 想解释为什么某个样本预测异常:使用力图或决策图。

  • 想深入分析某个特定特征:使用依赖图和交互图。

三、SHAP 各绘图函数的参数尺寸要求

下表整理核心 SHAP 绘图函数的关键参数(shap_valuesfeatures等)的形状要求,按 “函数用途” 分类:

绘图函数核心参数参数尺寸要求(以二分类 / 回归为例)多分类适配(信贷数据集)
单样本解释
shap.force_plot(单样本)expected_value标量(模型基值)标量(指定类别,如expected_value[1]
shap.force_plot(单样本)shap_values(n_features,) 或 (1, n_features)(n_features,)(取某类的 SHAP 值,如shap_values[1][i]
shap.force_plot(单样本)X_display(n_features,) 或 (1, n_features)(原始特征)同左
shap.plots.waterfallshap_values(n_features,)(单样本的 shap.Explanation对象)同左(取某类的单样本 SHAP 值)
多样本 / 全局解释
shap.force_plot(多样本)shap_values(n_samples, n_features)(n_samples, n_features)(取某类,如shap_values[1]
shap.force_plot(多样本)X_display(n_samples, n_features)同左
shap.summary_plot(默认) shap_values(n_samples, n_features)(n_classes, n_samples, n_features)(显示所有类)
shap.summary_plot(默认)X(n_samples, n_features)同左
shap.summary_plot(交互)shap_interaction_values(n_samples, n_features, n_features)(n_classes, n_samples, n_features, n_features)
shap.plots.beeswarmshap_values(n_samples, n_features)(shap.Explanation 对象)(n_samples, n_features)(取某类)
shap.plots.bar(全局)shap_values(n_samples, n_features)(shap.Explanation 对象)同左(取某类)
shap.plots.heatmapshap_values(n_samples, n_features)(shap.Explanation 对象)同左(取某类)
特征关系解释
shap.dependence_plotfeature特征名(str)或索引(int)同左
shap.dependence_plotshap_values(n_samples, n_features)(n_samples, n_features)(取某类)
shap.dependence_plotX(n_samples, n_features)同左
shap.plots.scattershap_values(n_samples, 1)(单特征的 shap.Explanation 切片,如sv[:, "Age"])同左(取某类)
shap.plots.scattercolor(n_samples, 1)(另一特征的切片)或 (n_samples,)同左
决策路径 / 异常检测
shap.decision_plot shap_values(n_samples, n_features)(n_samples, n_features)(取某类)
shap.decision_plotfeatures_display(n_samples, n_features)(原始特征,可选)同左
shap.decision_plothighlight(n_samples,)(布尔数组,标记异常 / 错误分类)同左

关键通用规则

  1. shap.Explanation 对象 vs numpy 数组:
    • 多数函数(如plots.barplots.waterfall)优先接受shap.Explanation对象(含datavaluesdisplay_data等元信息),尺寸与 numpy 数组一致(如单样本为 (n_features,));
    • 旧版函数(如shap.force_plot)支持 numpy 数组,需手动匹配形状。
  2. 多分类特殊处理:
    • 树模型的shap_values默认返回 (n_classes, n_samples, n_features),需通过索引选择某一类(如shap_values[1]表示第二类的 SHAP 值);
    • 二分类可简化为 (n_samples, n_features)(默认返回正类)。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐