【笔记】验证数据集与交叉验证

验证数据集与交叉验证

一些相关前提:

过拟合和欠拟合以及为什么要对分为训练数据集和测试数据集

可视化模型误差之学习曲线

验证数据集

严格意义上来说,使用训练数据集和测试数据集来进行是有不太好的地方,这样得出来的模型,可能导致一个问题,即这个最后得到的模型是过拟合了这个特定的测试数据集

不断的调参再去训练数据集重新得到模型在进行测试数据,最后找到一组参数使模型在测试数据集上效果最好,这样就使得这个模型始终围绕着这个测试数据,由于测试数据集是已知的,那么这就相当于针对这组测试数据集进行调参,这有可能出现过拟合的情况

那么要想解决这个情况,就可以将其分成三部分,即

训练数据集(训练模型)

验证数据集(测试好数据以后,将验证数据送入模型看一下效果是咋样的,效果不好的话就调参,重新训练模型,这就使得模型针对验证模型达到最优,即其为调整超参数使用的数据集)

测试数据集(在模型已经针对验证数据得到最优以后再传入模型,其作为衡量最终模型性能的数据集,其并没有参与模型的创建,其对于模型是完全不可知的)

这样得到的模型的结果是更加准确的,但是这样操作以后还有一个问题,即由于验证数据集是从原先的数据集中随机的切出来的,那么这个模型就有可能过拟合这个验证数据集,一旦这一份验证数据集里面的数据不好,比如存在极端数据的话,这就使得模型不准确,为了解决这个问题,就有了交叉验证

交叉验证

那么什么是交叉验证呢?

交叉验证,其是比较正规的在调参的时候使用的观察模型性能的方式

对于训练的数据来说,通常将其分为k份,那么就可以让这k份数据分别作为验证数据集,比如有三份,为abc,就可以分为以bc为训练数据集,a为验证数据集,以ac为训练数据集,b为验证数据集以及以ab为训练数据集,c为验证数据集,这样的对应的搭配就会产生三个模型,这三个模型每一个模型在验证数据集上都会求出来一个性能的指标,这几个指标的平均就作为当前算法的指标,即将这k个模型的均值作为结果调参

具体实现一下

(在notebook中)

使用书写识别数据

  import numpy as np
  import matplotlib.pyplot as plt
  from sklearn import datasets

  digits = datasets.load_digits()
  X = digits.data
  y = digits.target

这里还是将数据集分成训练数据集和测试数据集

  from sklearn.model_selection import train_test_split

  X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.4,random_state=666)

这里使用knn这个方法来对手写数据进行识别,在过程中要不断地进行调参,一共有两个参数,分别为k和p,对k来说,从2到10之间不断选择,对p来说,从1到5之间进行选择,每一次都传knn_clf调用KNeighborsClassifier这个构造函数,传入weights="distance",n_neighbors=k,p=p,然后计算得到score,将其存起来,如果这个score是大于历史上所找到的最好的score,这样就要将当前的score,k,p给记录下来,循环得出最佳的k和p,然后打印出来

  from sklearn.neighbors import KNeighborsClassifier

  best_score,best_p,best_k = 0,0,0
  for k in range(2,11):
      for p in range(1,6):
          knn_clf = KNeighborsClassifier(weights="distance",n_neighbors=k,p=p)
          knn_clf.fit(X_train,y_train)
          score = knn_clf.score(X_test,y_test)
          if score > best_score:
              best_score,best_p,best_k = score,p,k
        
  print("Best k =",best_k)
  print("Best p =",best_p)
  print("Best Score =",best_score)

结果如下

这里就是用交叉验证的方法来进行超参数的调整

使用sklearn中的cross_val_score,只要传入算法和相应的X_train,y_train,就会自动进行交叉验证的过程,同时返回每个模型对应的准确率

  from sklearn.model_selection import cross_val_score

  knn_clf = KNeighborsClassifier()
  cross_val_score(knn_clf,X_train,y_train)

结果如下(为啥我是五个值,就挺突然的,或许是默认为5?不过过程是对的,后续结果也会也能出现偏差)

调参的逻辑和上面的差不多,不同的是每次要调用cross_val_score的方法计算,scores = cross_val_score(knn_clf,X_train,y_train),这样一来就是用了交叉验证的结果

  best_score,best_p,best_k = 0,0,0
  for k in range(2,11):
      for p in range(1,6):
          knn_clf = KNeighborsClassifier(weights="distance",n_neighbors=k,p=p)
          scores = cross_val_score(knn_clf,X_train,y_train)
          score = np.mean(scores)
          if score > best_score:
              best_score,best_p,best_k = score,p,k
        
  print("Best k =",best_k)
  print("Best p =",best_p)
  print("Best Score =",best_score)

结果如下

这样我们拿到了最佳的参数,这是就是用找到的最佳的参数来进行分类,然后使用test来计算准确度

  best_knn_clf = KNeighborsClassifier(weights="distance",n_neighbors=2,p=2)

  best_knn_clf.fit(X_train,y_train)
  best_knn_clf.score(X_test,y_test)

结果如下

实际上这个过程是网格搜索中是有进行的,使用的sklearn中的GridSearchCV,GridSearchCV中的CV就是交叉验证的意思

详细过程:

  from sklearn.model_selection import GridSearchCV

  param_grid = [
      {
          'weights':['distance'],
          'n_neighbors':[i for i in range(2,11)],
          'p': [i for i in range(1,6)]
      }
  ]

  grid_search = GridSearchCV(knn_clf, param_grid,verbose=1)
  grid_search.fit(X_train,y_train)

结果如下

使用以下代码就可以发现对应的结果了,和上述的结果相同,不再赘述

  grid_search.best_score_

  grid_search.best_params_

  best_knn_clf = grid_search.best_estimator_
  best_knn_clf.score(X_test,y_test)

还可以对交叉验证设置cv为5,使其分成五份

  cross_val_score(knn_clf,X_train,y_train,cv=5)

结果为

同理网格搜索也是可以设置cv

  GridSearchCV(knn_clf,param_grid,verbose=1,cv=5)

结果为

我们一般称上面的为称为k-folds交叉验证,其也是有缺点的,由于分成了k份数据,每次训练k个模型,整体的性能肯定是会慢很多的

在极端情况下,k-folds交叉验证可以变成留一法LOO-CV的交叉验证方式,其就是训练数据集有m个样本,就把训练数据集分成m份,即每次都将m-1份样本去训练,然后去预测剩下的的一个样本,将这些结果平均,这样做将完全不受随机的影响,最接近模型真正的性能指标,但是很明显,计算量巨大

原文地址:https://www.cnblogs.com/jokingremarks/p/14310988.html