一、模型选择与调优
在机器学习中,模型的选择和调优是一个重要的步骤,它直接影响到最终模型的性能
1、交叉验证
在任何有监督机器学习项目的模型构建阶段,我们训练模型的目的是从标记的示例中学习所有权重和偏差的最佳值
如果我们使用相同的标记示例来测试我们的模型,那么这将是一个方法论错误,因为一个只会重复刚刚看到的样本标签的模型将获得完美的结果,但无法预测数据,这种情况称为过拟合,为了克服过度拟合的问题,我们使用交叉验证
交叉验证(Cross-validation)是一种统计学上的方法,用于评估机器学习模型的性能,并帮助避免过拟合。它的主要思想是在有限的数据集上划分出一部分数据用于测试模型的泛化能力
1.1 保留交叉验证HoldOut
保留交叉验证(Holdout Cross Validation)是最简单的一种交叉验证方法。在这种方法中,数据集被一次性划分为两个互斥的部分:一个较大的部分作为训练集(training set),用于训练模型;另一个较小的部分作为验证集(validation set)或测试集(test set),用于评估模型的泛化能力
Holdout 方法的步骤:
数据划分:从原始数据集中随机抽取一部分数据作为测试集,剩余的数据作为训练集。通常的比例为70%的数据作为训练集,30%的数据作为测试集
训练模型:使用训练集数据来训练模型
评估模型:使用测试集数据来评估模型的性能,如准确率、召回率等指标
Holdout 方法的优点:
简单易行:只需要一次划分即可完成训练和测试
计算效率高:相较于K折交叉验证等方法,Holdout方法的计算开销较低
Holdout 方法的缺点:
不适用于不平衡的数据集:假设我们有一个不平衡的数据集,有 0 类和 1 类。假设80%的数据属于 0 类,其余 20% 的数据属于 1 类。这种情况下,训练集的大小为 80%,测试数据的大小为数据集的 20%。可能发生的情况是,所有 80% 的 0 类数据都在训练集中,而所有 1 类数据都在测试集中。因此,我们的模型将不能很好地概括我们的测试数据,因为它之前没有见过 1 类的数据
大块数据被剥夺了训练模型的机会:在小数据集的情况下,有一部分数据将被保留下来用于测试模型,这些数据可能具有重要的特征,而我们的模型可能会因为没有在这些数据上进行训练而错过
1.2 K-折交叉验证K-Fold
K-折交叉验证(K-Fold Cross Validation)是一种评估机器学习模型性能的方法
在这种 K 折交叉验证技术中,整个数据集被划分为 K 个相等大小的部分。每个分区称为一个折叠。因为有 K 个部分,所以我们称之为 K-Fold 。一个 Fold 折用作验证集,其余 K-1 个 Fold 用作训练集
图示:
执行步骤:
数据划分:
首先,将整个数据集随机分成 K 个子集或者 Folds,尽量保证每个子集的大小相同
模型训练与测试:
对于每一个子集,将其保留作为测试集,而其他 K-1个子集合并作为训练集。这样,就可以训练一次模型并评估其性能
这个过程会被重复 K 次,每次选择不同的子集作为测试集
性能评估:
在所有 K 次迭代结束后,计算每次测试的结果(如准确率、召回率、F1分数等),然后求这些结果的平均值,以此作为模型性能的估计
cross_val_score
是 scikit-learn 中的一个函数,用于执行交叉验证并返回模型在不同折叠上的得分,cross_val_score
函数的主要参数如下:estimator:一个 scikit-learn 的估算器(estimator),通常是模型类的实例,如
LogisticRegression
、SVC
等X:特征数据,可以是 NumPy 数组、Pandas DataFrame 或其他支持索引的数据结构
y:目标数据,通常是一个一维数组,表示每个样本的标签
cv:交叉验证的折叠数或交叉验证生成器。可以是一个整数(表示 K 折交叉验证的 K 值),也可以是一个交叉验证生成器(如
StratifiedKFold
)
优点:
减少过拟合:由于模型是整个数据集既用作训练集又用作验证集,这种方法可以帮助减少模型的过拟合倾向
提高模型稳定性:通过多次迭代,K-折交叉验证能够提供更稳定的模型性能评估,因为它考虑了不同数据划分对模型的影响
利用有限数据:当可用的数据量较小的时候,这种方法允许更有效地利用数据来评估模型性能
缺点:
计算成本较高:需要训练和验证K次模型,因此需要更多的计算资源和时间
1.3 分层K-折交叉验证Stratified k-fold
分层 K 折交叉验证(Stratified K-Fold Cross Validation)是一种改进的交叉验证方法,它特别适用于类别不平衡的数据集。这种技术确保在每个折叠(fold)中,不同类别的样本比例保持一致,从而使得每次训练和测试集的类别分布尽可能与整体数据集的类别分布相同,比如说:原始数据有 3 类,比例为 1:2:1,采用 3 折分层交叉验证,那么划分的 3 折中,每一折中的数据类别保持着 1:2:1 的比例
StratifiedKFold
是scikit-learn
库中的一个类,用于实现分层 K 折交叉验证(Stratified K-Fold Cross Validation),构造函数StratifiedKFold
的参数如下:n_splits (int, default=5):定义将数据集分割成多少个折叠(folds)。默认值为 5,意味着将数据集分成 5 个子集
shuffle (bool, default=False):如果设置为
True
,则在划分数据之前会对数据进行随机化。这有助于提高结果的随机性和公平性random_state (int, RandomState instance or None, default=None):如果
shuffle=True
,则可以设置random_state
来确保每次分割的结果可复现。它可以是一个整数(作为随机种子)、一个RandomState
实例,或者None
(表示不使用固定的随机种子)
StratifiedKFold
返回的对象中,可以调用skf.split(X, y)
方法,skf.split(X, y)
方法是StratifiedKFold
类的一个重要方法,用于生成训练集和测试集的索引。该方法返回一个迭代器,每次迭代返回一个元组(train_index, test_index)
,分别对应训练集和测试集的索引,参数和返回值如下:X:特征数据,可以是任何支持索引访问的数据结构,如 NumPy 数组、列表或 Pandas DataFrame
y:标签数据,通常是一个一维数组,表示每个样本的类别标签
split
方法返回一个迭代器,每次迭代返回一个元组(train_index, test_index)
,其中:train_index
:一个 NumPy 数组,包含训练集样本的索引test_index
:一个 NumPy 数组,包含测试集样本的索引
案例:
from sklearn.metrics import accuracy_score from sklearn.model_selection import StratifiedKFold from sklearn.datasets import load_iris from sklearn.neighbors import KNeighborsClassifier from sklearn.preprocessing import StandardScaler def stratified_k_fold():iris = load_iris()X = iris.datay = iris.target# 模型对象model = KNeighborsClassifier()# K-折交叉验证skf = StratifiedKFold(n_splits=5,shuffle=True,random_state=42)# 存储每次验证的结果scores = []for train_index,test_index in skf.split(X,y):X_train,X_test = X[train_index],X[test_index]y_train,y_test = y[train_index],y[test_index]# 特征工程:标准化standardScaler = StandardScaler()X_train = standardScaler.fit_transform(X_train)X_test = standardScaler.transform(X_test)# 训练模型model.fit(X_train,y_train)# 在测试集上评估模型y_predict = model.predict(X_test)score = accuracy_score(y_test, y_predict)scores.append(score)print(scores)
StratifiedKFold
的工作原理
目标:在划分数据时,保持每一折的
y_train
和y_test
中各类别的比例与原始数据y
相同。实现方式
:
对每个类别单独计算分位数,确保每个类别的样本均匀分布在每一折中。
例如,如果原始数据中类别 A 占 40%,B 占 60%,则每一折的
y_train
和y_test
也会保持 4:6 的比例。
2、超参数搜索
超参数是在建立模型时用于控制算法行为的参数。这些参数不能从常规训练过程中获得。在对模型进行训练之前,需要对它们进行赋
超参数的选择对模型的最终性能有着至关重要的影响。不同的超参数组合可能导致模型在训练集上过拟合或者欠拟合。因此,寻找合适的超参数组合是一项关键任务
常见的方式:
手工调参
网格搜索
随机搜索
贝叶斯搜索
2.1 网格搜索
网格搜索是一种基本的超参数调优技术。它为网格中指定的所有给定超参数值的每个排列构建模型,评估并选择最佳模型
GridSearchCV
是scikit-learn
库提供的一个用于执行网格搜索的类,它可以在给定的超参数网格中自动寻找最佳的超参数组合。GridSearchCV
通过交叉验证来评估每种超参数组合的效果,并最终返回性能最好的模型GridSearchCV
的构造函数的参数如下:estimator:scikit-learn估计器实例
param_grid:以参数名称(str)作为键,将参数设置列表尝试作为值的字典
cv:确定交叉验证切分策略,None 默认5折,integer 设置多少折
GridSearchCV
的对象中,有如下几个重要的属性:best_params_ 最佳参数
best_score_ 在训练集中的准确率
best_estimator_ 最佳估计器
cv_results_ 交叉验证结果
案例:
from sklearn.datasets import load_iris from sklearn.metrics import accuracy_score from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.neighbors import KNeighborsClassifier from sklearn.model_selection import GridSearchCV # 用KNN算法对鸢尾花进行分类,添加网格搜索和交叉验证 def knn_iris_gridSearchCV():iris = load_iris()X = iris.datay = iris.targetX_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)# 特征工程:标准化transfer = StandardScaler()X_train = transfer.fit_transform(X_train)X_test = transfer.transform(X_test)# 创建KNN模型, 不设置n_neighbors的值,后期让GridSearchCV来设置knn = KNeighborsClassifier()# 加入网格搜索与交叉验证, GridSearchCV会让k分别等于1,2,5,7,9,11进行网格搜索model = GridSearchCV(knn, param_grid={"n_neighbors": [1, 2, 5, 7, 9, 11]})# 训练模型model.fit(X_train, y_train)# 模型评估# 比对真实值和预测值y_predict = model.predict(X_test)print("y_predict:\n", y_predict)print("真实值和预测值的准确率:\n", y_test == y_predict)# 计算预测值和真实值的准确率print("预测值和真实值的准确率:\n", accuracy_score(y_test, y_predict))# 计算准确率score = model.score(X_test, y_test)print("在测试集中的准确率为:\n", score)# 最佳参数:best_params_print("最佳参数:\n", model.best_params_)# 最佳结果:best_score_print("在训练集中的准确率:\n", model.best_score_)# 最佳估计器:best_estimator_print("最佳估计器:\n", model.best_estimator_)# 交叉验证的平均得分:cv_results_['mean_test_score']print("交叉验证的平均得分:\n", model.cv_results_['mean_test_score'])# 超参数的所有组合:model.cv_results_['params']print("超参数的所有组合:\n", model.cv_results_['params'])# 交叉验证结果:cv_results_print("交叉验证结果:\n", model.cv_results_)
3、模型保存与加载
joblib
是一个 Python 库,主要用于并行计算和持久化存储(即保存和加载)大型 NumPy 数组和模型。joblib
提供了dump
和load
两个函数,用于保存和恢复 Python 对象,特别是机器学习模型
函数名 | 参数 | 说明 |
---|---|---|
joblib.dump() | obj:要保存的对象 filename:保存文件的路径 | 将 Python 对象保存到磁盘文件中。它可以有效地压缩数据,并且支持并行写入,适合用于保存大型数据集或模型 |
joblib.load() | filename:保存对象的文件路径 | 从磁盘文件中恢复之前保存的对象 |