假设我们有一个0-1的分类问题,有两个feature,那么我们将如何判断这个分类模型的优劣呢?
最简单的方法是将我们的数据集划分为训练集和测试集。如上图所示,我们将75%的数据集划分为训练集,并使用训练集训练模型,然后将训练好的模型在剩余的25%的测试集上进行测试。测试集上的正确率的结果相当于在未来那些没有标签的数据上预测的无偏结果。
在sklearn
包中提供了train_test_split
方法对数据集进行快速划分。
from sklearn.model_selection import train_test_splitX_train, X_test, y_train, y_test = train_test_split(X, y)from sklearn.neighbors import KNeighborsClassifierknn = KNeighborsClassifier(n_neighbors=1)knn.fit(X_train, y_train)print("accuracy: {:.2f}".format(knn.score(X_test, y_test)))y_pred = knn.predict(X_test)```
在上面的代码中我们使用了最简单的KNN模型,并指定参数K=1。那么如何选择更合适的参数K呢?在训练集上把不同的K值都试一遍,然后看哪个K值在测试集上表现更好(正确率更高)?
这样做可以选出合适的K值,但是会对正确率有一个过于乐观的估计,也就是说在测试集上过拟合了。什么是测试集过拟合?简单地说,就是在测试集上尝试的次数太多,模型学习了测试集上的噪声,导致模型不具有可推广性。
from sklearn.datasets import load_breast_cancerfrom sklearn.preprocessing import scaledata = load_breast_cancer()X, y = data.data, data.targetX = scale(X)X_trainval, X_test, y_trainval, y_test = train_test_split(X, y)X_train, X_val, y_train, y_val = train_test_split(X_trainval, y_trainval)knn = KNeighborsClassifier(n_neighbors=5).fit(X_train, y_train)print("Validation: {:.3f}".format(knn.score(X_val, y_val)))print("Test: {:.3f}".format(knn.score(X_test, y_test)))
这里我们的做法是将数据集划分两次:
第一次是将数据集划分为训练集和测试集第二次将训练集再划分为真正的训练集和验证集(Validatio Set)
通过这种方法,我们用训练集训练模型,用验证集选择合适的参数,最后应用到测试集上最终评判模型好坏。
在这个过程中,会有多少个模型应用在测试集上?只有一个!
这种方法的缺点在于
只使用了很小一部分数据,我们丢失了很多信息结果基于某种特定的划分,如果改变划分,结果可能很不一样(High Variance)
交叉检验可以很好地解决这个问题,上图中我们可以取5次结果的平均值来得到一个更稳定的结果。
当需要调参时,可以用上图所示的方法。(Cross-Val + test-set)
from sklearn.model_selection import cross_val_scoreX_train, X_test, y_train, y_test = train_test_split(X, y)cross_val_scores = []for i in neighbors:knn = KNeighborsClassifier(n_neighbors=i)scores = cross_val_score(knn, X_train, y_train, cv=10)cross_val_scores.append(np.mean(scores))print("best cross-validation score: {:.3f}".format(np.max(cross_val_scores)))best_n_neighbors = neighbors[np.argmax(cross_val_scores)]print("best n_neighbors:", best_n_neighbors)knn = KNeighborsClassifier(n_neighbors=best_n_neighbors)knn.fit(X_train, y_train)print("test-set score: {:.3f}".format(knn.score(X_test, y_test)))
我们对7个K值进行调参,并进行10折交叉检验。总共训练7*10+1
个模型。最后一个模型是在挑选好最优参数以后,在所有训练集上训练得到的最终模型。
也可以用GridSearchCV
来实现上述功能
from sklearn.model_selection import GridSearchCVX_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y)param_grid = {'n_neighbors': np.arange(1, 15, 2)}grid = GridSearchCV(KNeighborsClassifier(), param_grid=param_grid,cv=10, return_train_score=True)grid.fit(X_train, y_train)print("best mean cross-validation score: {:.3f}".format(grid.best_score_))print("best parameters: {}".format(grid.best_params_))print("test-set score: {:.3f}".format(grid.score(X_test, y_test)))
其它衍生方法
Nested Cross-validation
把最外面的那层划分也采用K-fold的方法,最终可能会生成多个不同的模型。因为该方法耗时过长,实际操作中并不常见。