【机器学习【9】】评估算法:数据集划分与算法泛化能力评估
文章目录
- 一、 数据集划分:训练集与评估集
- 二、 K 折交叉验证:提升评估可靠性
- 1. 基本原理
- 1.1. K折交叉验证基本原理
- 1.2. 逻辑回归算法与L2
- 2. 基于K折交叉验证L2算法
- 三、弃一交叉验证(Leave-One-Out)
- 1、基本原理
- 2、代码实现
- 四、ShuffleSplit交叉验证
- 1、基本原理
- 2、为什么能降低方差
- 3、代码测试
- 五、 选择建议
在机器学习中,评估算法的核心目标是衡量模型在“未知数据”上的表现,而不是仅仅追求训练集上的高分。只有通过科学的评估方法,我们才能判断模型是否具备良好的泛化能力,避免“过拟合”陷阱。
因此,合理划分数据集并选择合适的评估策略,是每一个机器学习项目的基础环节。
一、 数据集划分:训练集与评估集
常见做法是将原始数据集分为训练集和评估集(也称测试集),常用比例为67%训练,33%评估。这种划分方式简单高效,适合数据量较大的场景。
from pandas import read_csv
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
# 导入数据
filename = 'pima_data.csv'
names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class']
data = read_csv(filename, names=names)
# 将数据分为输入数据和输出结果
array = data.values
X = array[:, 0:8]
Y = array[:, 8]
test_size = 0.33
seed = 4 # 将数据随机分离
X_train, X_test, Y_traing, Y_test =
train_test_split(X, Y, test_size=test_size, random_state=seed)model = LogisticRegression() # 初始化逻辑回归模型。
model.fit(X_train, Y_traing) # 模型进行训练
result = model.score(X_test, Y_test)
print("算法评估结果:%.3f%%" % (result * 100))算法评估结果:80.709%
test_size=0.33
:指定33%数据作为评估集。random_state=42
:设置随机种子,保证每次划分结果一致,便于复现实验。
注意:评估结果(如准确率)只能代表模型在未见过数据上的表现,不能用训练集直接评估,否则会高估模型能力。
二、 K 折交叉验证:提升评估可靠性
1. 基本原理
1.1. K折交叉验证基本原理
当数据量有限时,单次划分可能导致评估结果不稳定。K 折交叉验证(K-Fold Cross Validation)通过多次划分和训练,有效提升评估的稳健性。
原理简述:
- 将数据分为 K 份,每次用 K-1 份训练,剩下1份评估。
- 重复 K 次,每份都做一次评估,最后取平均分。
1.2. 逻辑回归算法与L2
逻辑回归是一种用于二分类问题的机器学习算法。它通过一个特殊的函数(sigmoid函数)将线性组合转换为0到1之间的概率值。
P(y=1)=1/(1+e(−z))P(y=1) = 1 / (1 + e^{(-z)}) P(y=1)=1/(1+e(−z))
其中 z = w₁x₁ + w₂x₂ + … + wₙxₙ + b,这里的权重 w₁, w₂, ..., wₙ
决定了每个特征的重要性。当某个特征的权重很大时,这个特征对预测结果的影响就很大。
而L2正则化是防止它过拟合的关键技术。
当特征很多或数据量较小时,逻辑回归容易过拟合。模型可能学习到一些噪声模式,导致在训练集上表现很好,但在新数据上表现很差。这是因为模型"记住"了训练数据中的随机波动,而不是学习到真正的规律。
L2正则化通过惩罚大的权重值来防止过拟合。
总损失=交叉熵损失+λ×Σ(权重2)总损失 = 交叉熵损失 + λ × Σ(权重²)总损失=交叉熵损失+λ×Σ(权重2)
λ × Σ(权重²):衡量所有权重的平方和。
交叉熵损失=−Σ[y×log(p)+(1−y)×log(1−p)]交叉熵损失 = -Σ[y × log(p) + (1-y) × log(1-p)] 交叉熵损失=−Σ[y×log(p)+(1−y)×log(1−p)]
- y 是真实标签(0或1)
- p 是模型预测的概率
现在举例说明L2原理,假设模型有如下4个权重,现在对不同模型引入惩罚系数,
# 模型A:权重较大
权重_A = [5.0, -4.0, 6.0, -5.0]
预测准确率 = 95%
交叉熵损失 = 0.1
L2惩罚项 = 0.1 × (25+16+36+25) = 0.1 × 102 = 10.2
总损失 = 0.1 + 10.2 = 10.3# 模型B:权重较小
权重_B = [2.0, -1.5, 2.5, -2.0]
预测准确率 = 94%
交叉熵损失 = 0.15
L2惩罚项 = 0.1 × (4+2.25+6.25+4) = 0.1 × 16.5 = 1.65
总损失 = 0.15 + 1.65 = 1.8
结果分析
L2正则化的目标是最小化总损失,而不是最大化预测准确率。虽然模型A的预测准确率更高(95% vs 94%),但它的总损失更大(10.3 vs 1.8)。那为什么总损失更重要?
- 过拟合风险:模型A的权重很大,说明它可能"记住"了训练数据中的噪声,而不是学习到真正的规律。虽然现在预测准确,但在新数据上可能表现很差。
- 泛化能力:模型B的权重较小,说明它更"保守",不会过分依赖某些特征。虽然准确率稍低,但更稳定,在新数据上表现更好。
所以,L2正则化选择总损失更小的模型,即使它的预测准确率稍低。这是因为较小的权重意味着模型更稳定,泛化能力更强,不容易过拟合。这就是为什么模型B虽然准确率94%,但总损失1.8,比模型A的10.3更优。
2. 基于K折交叉验证L2算法
from pandas import read_csv
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression。。。数据集划分见上
# 创建K折交叉验证对象
# n_splits=num_folds: 将数据集分成num_folds份,进行num_folds次训练和验证
# random_state=seed: 设置随机种子,确保每次运行结果一致
# shuffle=True: 在分割前先打乱数据,避免数据顺序对结果的影响
kfold = KFold(n_splits=num_folds, random_state=seed, shuffle=True)# 创建逻辑回归模型
# LogisticRegression(): 默认使用L2正则化,适用于二分类问题
# 可以通过参数调整正则化强度、求解器等
model = LogisticRegression()# 使用交叉验证评估模型性能
# cross_val_score(): 自动进行K折交叉验证,返回每折的得分
# model: 要评估的模型
# X: 特征矩阵
# Y: 目标变量
# cv=kfold: 使用上面定义的K折交叉验证策略
result = cross_val_score(model, X, Y, cv=kfold)# 输出评估结果
print("算法评估结果:%.3f%% (%.3f%%)" % (result.mean() * 100, result.std() * 100))算法评估结果:77.864% (4.735%)
优缺点:
- 优点:充分利用数据,评估更稳健。
- 缺点:计算量较大,尤其是K值较高时。
三、弃一交叉验证(Leave-One-Out)
1、基本原理
弃一交叉验证(LOOCV)是一种特殊的交叉验证方法,专门用于极小数据集。它的核心思想很简单:每次只留一个样本做测试,其余全部用于训练,这样循环N次(N为样本总数)。
当数据集很小时,传统的K折交叉验证可能不够可靠。比如只有10个样本,分成5折意味着每折只有2个样本,这样的评估结果波动很大,不够稳定。LOOCV通过最大化训练数据来解决这个问题。
每次训练时,模型都能使用N-1个样本,这在小数据集上是非常宝贵的。同时,每个样本都会被用作测试集一次,确保了评估的全面性。
2、代码实现
from sklearn.model_selection import LeaveOneOut# 创建弃一交叉验证对象
# loocv:自动实现每次留一个样本的验证策略
# 如果数据集有N个样本,会进行N次训练和测试
loocv = LeaveOneOut()# 使用弃一交叉验证评估模型性能
# cross_val_score:自动进行N次训练和测试,返回每次的得分
# model:要评估的模型(如逻辑回归、随机森林等)
# X:特征矩阵
# Y:目标变量
# cv=loocv:使用弃一交叉验证策略
result = cross_val_score(model, X, Y, cv=loocv)# 输出评估结果
# result.mean():计算所有N次评估的平均得分
# result.std():计算所有N次评估的标准差,衡量模型稳定性
print("算法评估结果:%.3f%% (%.3f%%)" % (result.mean() * 100, result.std() * 100))
弃一交叉验证是极小数据集上最可靠的评估方法。它通过最大化训练数据利用,为每个样本提供最全面的评估,从而获得最稳定的性能指标。虽然计算成本高,但在数据稀缺的情况下,这种"奢侈"是值得的。
四、ShuffleSplit交叉验证
1、基本原理
传统的K折交叉验证虽然有效,但在某些情况下可能不够灵活。比如当数据分布不均匀(某折训练集某些特征数据很少),或者我们希望获得更稳健的评估结果时,需要一种更灵活的验证方法。
ShuffleSplit通过随机划分数据来解决这个问题。每次随机选择一部分数据作为测试集,其余作为训练集,然后重复这个过程多次。这样既保证了评估的随机性,又通过多次重复降低了结果的方差。
假设设置10次划分,测试集比例为33%:
- 第1次:随机选择33%数据做测试,67%做训练
- 第2次:重新随机选择33%数据做测试,67%做训练
- 第3次:再次重新随机选择…
- …重复10次
2、为什么能降低方差
1. 减少偶然性
传统K折使用固定划分,如果某折恰好包含异常数据,整个评估结果就会受影响。而ShuffleSplit通过多次随机划分,将异常数据的影响分散到不同次评估中,从而降低了单次异常对整体结果的影响。
2. 更全面的评估
传统K折中每个样本只在固定的某折中测试一次,而ShuffleSplit中每个样本可能在不同次评估中被测试,获得更全面的性能评估。这种随机性确保了评估结果更加客观和全面。
3. 统计稳定性
传统K折通常只进行5次评估,结果可能波动很大。而ShuffleSplit可以进行10次或更多次评估,通过增加评估次数来提高统计稳定性,使结果更加可靠。
 
3、代码测试
from sklearn.model_selection import ShuffleSplit# 创建ShuffleSplit对象
# n_splits=10:重复10次划分,获得10个评估结果
# test_size=0.33:每次33%数据做测试,67%数据做训练
# random_state=7:设置随机种子,确保结果可重现
kfold = ShuffleSplit(n_splits=10, test_size=0.33, random_state=7)# 使用ShuffleSplit评估模型性能
# cross_val_score:自动进行10次训练和测试,返回每次的得分
# model:要评估的模型(如逻辑回归、随机森林等)
# X:特征矩阵
# Y:目标变量
# cv=kfold:使用ShuffleSplit验证策略
result = cross_val_score(model, X, Y, cv=kfold)# 输出评估结果
# result.mean():计算10次评估的平均得分
# result.std():计算10次评估的标准差,衡量模型稳定性
# *100:将小数转换为百分比显示
# %.3f:保留3位小数
print("算法评估结果:%.3f%% (%.3f%%)" % (result.mean() * 100, result.std() * 100))
优缺点分析
优点:通过多次随机划分降低方差,提高评估结果的稳定性;同时具有很高的灵活性,可以自由设置划分次数和测试集比例,适应各种数据分布情况。
缺点:需要多次训练模型,计算成本较高;不同次划分的测试集可能有重叠,影响评估的独立性。
ShuffleSplit通过随机重复划分的方式,在保持计算效率的同时,显著降低了评估结果的方差。它特别适合需要稳健评估结果的场景,是传统交叉验证方法的有益补充。
五、 选择建议
数据规模 | 推荐方法 | 说明 |
---|---|---|
数据量大 | 67%/33%分割或K折 | K=3、5、10均可 |
数据量小 | 弃一交叉或ShuffleSplit | 最大化数据利用,降低方差 |
需最终评估 | K折(K=10) | 行业“黄金准则” |
最佳实践:
- 每次划分都要设置
random_state
,保证实验可复现。 - 评估指标要与业务目标一致(如类别不平衡时优先关注AUC等)。
- 不要用训练集直接评估模型。