准确预测Fitbit的睡眠得分
在本文的前两部分中,我获取了Fitbit的睡眠数据并对其进行预处理,将这些数据分为训练集、验证集和测试集,除此之外,我还训练了三种不同的机器学习模型并比较了它们的性能。
在第2部分中,我们看到使用随机森林和xgboost默认超参数,并在验证集上评估模型性能会导致多元线性回归表现最佳,而随机森林和xgboost回归的表现稍差一些。
在本文的这一部分中,我将讨论只使用一个验证集的缺点。除此之外,我们还会谈到如何解决这些缺点以及如何调优模型超参数以提高性能。就让我们一探究竟吧。
交叉验证
简单训练、验证和测试分割的缺点
在本文的第2部分中,我们将数据分为训练、验证和测试集,在训练集上训练我们的模型并在验证集上对模型进行评估。我们还没有接触到测试集,因为它是保留集,它代表的是从未见过的数据,一旦我们觉得机器学习模型有能力进行最终测试,这些数据将用于评估它们的泛化程度。
因为我们只将数据分成了一组训练数据和一组验证数据,所以模型的性能指标高度依赖于这两组数据。机器学习模型只进行一次训练和评估,因此它的性能就取决于那一次评估。而且在对同一数据的不同子集进行训练和评估时,学习模型的表现可能会非常不同,这仅仅是因为选取的子集不同。
如果我们把这个过程分解为多次训练和验证测试,每次训练和评估我们的模型都是在不同的数据子集上,最后在多次评估中观察模型的平均表现会怎么样呢?这就是K-fold交叉验证背后的想法。
K-fold交叉验证
在K-fold交叉验证(CV)中,我们仍然要先从需要被处理的数据集中分离出一个测试/保留集,以用于模型的最终评估。剩下的数据,即除测试集之外的所有数据,将被分割成K个折叠数(子集)。然后交叉验证迭代这些折叠,在每次迭代中使用一个K折叠作为验证集,同时使用所有剩余的折叠作为训练集。重复这个过程,直到每个折叠都被用作验证集。以下是5折交叉验证的流程:
将模型在同一个训练数据的不同子集进行K次训练和测试,我们可以更准确地表示我们的模型在它以前没有见过的数据上的表现。在K-fold CV中,我们在每次迭代后对模型进行评分,并计算所有评分的平均值。这样就可以更好地表示该方法与只使用一个训练和验证集相比,模型的表现是怎样的。
Python中的K-fold交叉验证
因为Fitbit睡眠数据集相对较小,所以我将使用4折交叉验证,并将目前使用的多元线性回归、随机森林和xgboost回归这三种模型进行比较。
请注意,4折CV可以很好地与第2部分中分离出来的训练数据和验证数据进行比较,因为我们将数据分割为75%的训练数据和25%的验证数据。一个4折CV本质上也是如此,只是四次,每次使用不同的子集。我创建了一个函数,它将我们想要比较的模型列表,特征数据,目标变量数据以及我们想要创建的折叠数作为输入。该函数计算我们之前使用的性能度量并返回一个表格,其中包含所有模型的平均值以及每种度量类型的每一页的得分,以备我们进一步研究。函数如下:
#DefineafunctionthatcomparestheCVperfromanceofasetofpredetrminedmodelsdefcv_comparison(models, X, y, cv): #InitiateaDataFramefortheaveragesandalistforallmeasurescv_accuracies=pd.DataFrame() maes= [] mses= [] r2s= [] accs= [] #Loopthroughthemodels, runaCV, addtheaveragescorestotheDataFrameandthescoresof#allCVstothelistformodelinmodels: mae=-np.round(cross_val_score(model, X, y, scoring='neg_mean_absolute_error', cv=cv), 4) maes.append(mae) mae_avg=round(mae.mean(), 4) mse=-np.round(cross_val_score(model, X, y, scoring='neg_mean_squared_error', cv=cv), 4) mses.append(mse) mse_avg=round(mse.mean(), 4) r2=np.round(cross_val_score(model, X, y, scoring='r2', cv=cv), 4) r2s.append(r2) r2_avg=round(r2.mean(), 4) acc=np.round((100- (100* (mae*len(X))) /sum(y)), 4) accs.append(acc) acc_avg=round(acc.mean(), 4) cv_accuracies[str(model)] = [mae_avg, mse_avg, r2_avg, acc_avg] cv_accuracies.index= ['Mean Absolute Error', 'Mean Squared Error', 'R^2', 'Accuracy'] returncv_accuracies, maes, mses, r2s, accs
现在我们可以创建一个将要使用的模型列表,并通过4次交叉验证调用上面的函数:
#Createthemodelstobetestedmlr_reg=LinearRegression() rf_reg=RandomForestRegressor(random_state=42) xgb_reg=xgb_regressor=XGBRegressor(random_state=42) #PutthemodelsinalisttobeusedforCross-Validationmodels= [mlr_reg, rf_reg, xgb_reg] #RuntheCross-Validationcomparisonwiththemodelsusedinthisanalysiscomp, maes, mses, r2s, accs=cv_comparison(models, X_train_temp, y_train_temp, 4)
得到的结果对比表如下所示:
使用4折CV,随机森林回归模型在所有性能指标上都优于其他两个模型。但是在第2部分中,我们看到多元线性回归具有最好的性能指标,为什么会发生变化呢?
为了理解为什么交叉验证得到的分数与第2部分中简单的训练和验证不同,我们需要仔细看看模型在每个折叠上是如何执行的。上面的cv_compare()函数返回每个折叠中每个不同模型的所有分数的列表。让我们看看三种模型在每次折叠时的r平方是如何比较的。为了得到表格格式的结果,让我们也快速将其转换为数据帧:
#CreateDataFrameforallR^2sr2_comp=pd.DataFrame(r2s, index=comp.columns, columns=['1st Fold', '2nd Fold', '3rd Fold', '4th Fold']) #Addacolumnfortheaveragesr2_comp['Average'] =np.round(r2_comp.mean(axis=1),4)
上表说明了4折CV与训练集和验证集得分不同的原因。R-squared在不同的折叠中差异很大,特别是在xgboost和多元线性回归中。这也说明了为什么使用交叉验证如此重要,特别是对于小数据集,如果你只依赖于一个简单的训练集和验证集,你的结果可能会有很大的不同,这个结果就取决于你最终得到的数据分割是什么样子的。
现在我们知道了交叉验证是什么以及它为什么重要,让我们看看是否可以通过调优超参数从我们的模型中获得更多。
超参数调优
模型参数是在模型训练时学习的,不能任意设置。与模型参数不同,超参数是用户在训练机器学习模型前可以设置的参数。随机森林中超参数的例子有:森林中拥有的决策树的数量、每次分割时需要考虑的最大特征数量,或者树的最大深度。
正如我前面提到的,没有一种万能的方法可以找到最优超参数。一组超参数可能在一个机器学习问题上表现良好,但在另一个机器学习问题上可能表现不佳。那么我们怎么得到最优超参数呢?
一种可能的方法是使用有根据的猜测作为起点,手动调整优超参数,更改一些超参数,然后训练模型并评估该模型的性能。一直重复这些步骤,直到我们对性能满意为止。这听起来像是一个不必要的乏味的方法,但的确如此。
比较超参数调整和吉他调弦。你可以选择用你的耳朵来给吉他调音,这种方式需要大量的练习和耐心,而且你可能永远不会得到一个最佳的结果,特别是如果你是一个初学者。但幸运的是,有电子吉他调音器可以帮助你找到正确的音调,它可以解释你吉他弦的声波并显示它所读取的内容。虽然你仍然需要使用机器头来调音琴弦,但过程会快得多,电动调音器会确保你的调音接近最佳状态。那么机器学习和电吉他调音师有什么相同的地方呢?