质量保证-模型的评估与验证
模型的评估和验证是机器学习和数据分析流程中的关键步骤,用于确保模型的有效性和可靠性。它们帮助我们理解模型的性能、准确性和可靠性,确保模型在实际应用中能够做出正确的预测或决策。
评估专注于衡量模型在未见数据上的最终性能,通常在模型开发的最后阶段进行。验证聚焦于模型训练过程中的性能监测与调优,以确保模型既不过拟合也不欠拟合。
模型的评估与验证是一个迭代过程,可能需要多次循环直到达到满意的性能水平。在整个过程中,重要的是要保持数据的清洁和预处理的一致性,以及模型评估的公正性和可重复性。
以下是模型评估与验证的一些关键概念和方法
数据集拆分
在训练过程中,假设数据量比较小,我们不可能将一整个数据集都用在训练上,这样我们想使用一些新的数据去验证这个模型是否过拟合时,就没有数据了。
这个时候,我们就需要对数据进行拆分,分为如下三个数据集:
训练集(Training Set):用于训练模型的数据。
验证集(Validation Set):在训练过程中用于调整模型参数和选择最佳模型版本的数据。
测试集(Test Set):独立于训练集和验证集的数据,用于最终评估模型的泛化能力。
通常来说,在原始数据中拆分只会拆成训练集(验证集会包含在里面)和测试集。个人建议按照8:2的比例进行拆分,即80%作为训练集,20%作为测试集。
例如,假设拿到的数据集有8000条,那么训练集就是6400条,测试集就是1600条。
当然,这个比例不是固定的,可以根据自己的数据量进行调整。常见的也有7:3的比例拆分数据集。
拆分数据集的方法有很多,这里使用sklearn中的sklearn.model_selection.train_test_split
去进行拆分,例子如下:
from sklearn.model_selection import train_test_split
X = np.array([np.random.randint(0, 100000000) for _ in range(8000)])
Y = np.array([np.random.randint(0, 100000000) for _ in range(8000)])
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X, Y, test_size=0.2, random_state=42)
其中test_size
就是定义测试集的长度,输入值在[0, 1]之间。
random_state
的作用是控制随机数生成器的状态,从而确保每次运行代码时,数据分割的结果都是相同的。该参数建议固定为42(原因是官方案例为42),固定为42不会影响到使用,在实际案例时请放心抄。
train_test_split
也支持对多维数组进行拆分,例如:
from sklearn.model_selection import train_test_split
X = np.array([[np.random.randint(0, 100000000) for _ in range(100)] for _ in range(8000)])
Y = np.array([np.random.randint(0, 100000000) for _ in range(8000)])
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X, Y, test_size=0.2, random_state=42)
当模型仅需要X,不需要Y时,train_test_split
可以只切割X,例如:
from sklearn.model_selection import train_test_split
X = np.array([np.random.randint(0, 100000000) for _ in range(8000)])
Xtrain, Xtest = train_test_split(X, test_size=0.2, random_state=42)
交叉验证(Cross-validation)
前面提到了对数据进行拆分,分为三个数据集。而训练集会包含验证集,这个会怎么用上呢?这里就会用到交叉验证这个方法
交叉验证是一种统计学方法,用于评估模型的性能,特别是当可用数据量非常有限时。
常见的有k折交叉验证(k-Fold CV),其中数据被分为k个子集,每次将其中一个子集作为验证集,其余作为训练集,重复k次。
k折交叉验证会将训练集分块拆分,拆分多少块记为K(像折纸一样),并用不同的折块进行验证,得出来的所有结果取平均数为最终结果
比较常用的是sklearn.model_selection.cross_val_score
,例子如下
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split, cross_val_score
X = np.array([np.random.randint(0, 100000000) for _ in range(8000)])
Y = np.array([np.random.randint(0, 100000000) for _ in range(8000)])
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X, Y, test_size=0.2, random_state=42)
clf = DecisionTreeClassifier(random_state=42, max_depth=4, criterion="entropy").fit(Xtrain, Ytrain)
score = cross_val_score(clf, X, Y, cv=10).mean()
这个例子中,首先在cross_val_score
中输入模型,然后将未被拆分的数据X和Y输入进去,而后定义cv=10
表示将数据集拆分成10份进行交叉验证。所得的分数取平均最终得到结果
该函数得到的分数区间(置信区间)为[0, 1],其分数越接近1,置信度(也就是该模型的泛化程度)越高。
cross_val_score
函数目前只支持sklearn的模型,如果需要使用自定义模型(特别是神经网路模型),可以使用sklearn.model_selection.KFold
,例子如下:
from sklearn.model_selection import KFold
X = np.array([np.random.randint(0, 100000000) for _ in range(8000)])
Y = np.array([np.random.randint(0, 100000000) for _ in range(8000)])
kf = KFold(n_splits=10, shuffle=True, random_state=42)
scores = []
for train_index, test_index in kf.split(X):
Xtrain, Xtest = X[train_index], X[test_index]
Ytrain, Ytest = Y[train_index], Y[test_index]
clf = DecisionTreeClassifier(random_state=42, max_depth=4, criterion="entropy").fit(Xtrain, Ytrain)
score = clf.score(Xtest, Ytest)
scores.append(score)
score = np.mean(scores)
这个例子中,首先定义了KFold
,然后使用for
循环遍历KFold
,每次将数据集拆分成训练集和测试集,然后训练模型并计算分数,最后取平均得到结果
评估指标
对于每种不同的模型,对应有不同的评估指标,通常使用sklearn.metrics
下的包
详细请看: https://scikit-learn.org/stable/modules/model_evaluation.html
回归问题
均方误差(MSE):metrics.mean_squared_error
均方根误差(RMSE):sklearn没有提供RMSE,但它可以通过先计算MSE再取平方根来获得。
平均绝对误差(MAE):sklearn.metrics.mean_absolute_error
R^2分数:metrics.r2_score
分类问题
准确率(Accuracy):sklearn.metrics.accuracy_score
精确率(Precision):sklearn.metrics.precision_score
召回率(Recall):sklearn.metrics.recall_score
F1分数:sklearn.metrics.f1_score
AUC-ROC曲线下的面积(AUC-ROC):sklearn.metrics.roc_auc_score
其他问题:
对于聚类,可以使用如下指标
轮廓系数(Silhouette Coefficient):sklearn.metrics.silhouette_score
Calinski-Harabasz指数:sklearn.metrics.calinski_harabasz_score
强化学习通常使用累计奖励(Cumulative Reward)或策略评估(Policy Evaluation)来衡量,它们通常不在scikit-learn的范围内。