1. 程式人生 > >Scikit-learn的K-fold交叉驗證類ShuffleSplit、GroupShuffleSplit用法介紹

Scikit-learn的K-fold交叉驗證類ShuffleSplit、GroupShuffleSplit用法介紹

當樣本資料量比較小時,K-fold交叉驗證是訓練、評價模型時的常用方法,該方法的作用如下

  • 交叉驗證用於評估模型的預測效能,尤其是訓練好的模型在新資料上的表現,可以在一定程度上減小過擬合
  • 交叉驗證可以從有限的資料中獲取儘可能多的有效資訊

Scikit-learn(以下簡稱sklearn)是基於Numpy、Scipy的開源的Python機器學習庫,提供了大量用於資料探勘和分析的工具,包括資料預處理、交叉驗證、演算法與視覺化演算法等一系列介面。
本文介紹sklearn的可用於K-fold交叉驗證的集合劃分類ShuffleSplit、GroupShuffleSplit的用法。

ShuffleSplit

sklearn.model_selection.ShuffleSplit類用於將樣本集合隨機“打散”後劃分為訓練集、測試集(可理解為驗證集,下同),類申明如下:

class sklearn.model_selection.ShuffleSplit(n_splits=10, test_size=’default’, train_size=None, random_state=None)

引數:

  • n_splits:int, 劃分訓練集、測試集的次數,預設為10
  • test_size:float, int, None, default=0.1; 測試集比例或樣本數量,該值為[0.0, 1.0]內的浮點數時,表示測試集佔總樣本的比例;該值為整型值時,表示具體的測試集樣本數量;train_size不設定具體數值時,該值取預設值0.1,train_size設定具體數值時,test_size取剩餘部分
  • train_size:float, int, None; 訓練集比例或樣本數量,該值為[0.0, 1.0]內的浮點數時,表示訓練集佔總樣本的比例;該值為整型值時,表示具體的訓練集樣本數量;該值為None(預設值)時,訓練集取總體樣本除去測試集的部分
  • random_state:int, RandomState instance or None;隨機種子值,預設為None

ShuffleSplit類方法包括get_n_splits、split,前者用於返回劃分訓練集、測試集的次數,後者申明如下:

split(X, y=None, groups=None)

引數:

  • X:array-like, shape (n_samples, n_features);樣本特徵集合
  • y:array-like, shape (n_samples,);樣本標記集合,該值設定時需與X的樣本數量(n_samples)一致
  • groups:該引數在此處不生效
  • 返回值:包含訓練集、測試集索引值的迭代器

ShuffleSplit應用舉例

上圖最左側為自動生成的索引,subject表示駕駛者編號,classname表示圖片分類,c0、c1、……、c9表示不同的圖片類別,img為圖片名稱。在實施訓練時,以img作為樣本特徵,以classname作為樣本標記。
對於上述樣本,使用ShuffleSplit劃分5-fold 5次交叉驗證的訓練集、測試集,程式碼如下:

import pandas as pd
import numpy as np
from sklearn.model_selection import ShuffleSplit
sample = pd.DataFrame({'subject':['p012', 'p012', 'p014', 'p014', 'p014', 'p024', 'p024', 'p024', 'p024', 'p081'],
                         'classname':['c5', 'c0', 'c1', 'c5', 'c0','c0','c1','c1','c2','c6'],
                         'img':['img_41179.jpg','img_50749.jpg', 'img_53609.jpg','img_52213.jpg','img_72495.jpg',
                                'img_66836.jpg','img_32639.jpg','img_31777.jpg','img_97535.jpg','img_1399.jpg']})
x_train_names_all = np.array(sample['img'])
y_train_labels_all = np.array(sample['classname'])

rs = ShuffleSplit(n_splits=5, test_size=0.2, random_state=0)
n_fold = 1
for train_indices, test_indices in rs.split(sample):
    print('fold {}/5......'.format(n_fold))
    print("train_indices:", train_indices)
    x_train = x_train_names_all[train_indices, ...]
    print("x_train_names:\n", x_train)
    y_train = y_train_labels_all[train_indices, ...]
    print("y_train:\n", y_train)

    print("test_indices:", test_indices)
    x_test = x_train_names_all[test_indices, ...]
    print("x_test:\n", x_test)
    y_test = y_train_labels_all[test_indices, ...]
    print("y_test:\n", y_test)
    n_fold += 1

輸出結果:
這裡寫圖片描述

從結果可以看出,依據n_splits值對樣本集合劃分5次,每次劃分時打亂樣本,依據比例重新劃分。
說明如下:

  • 劃分引數在ShuffleSplit類初始化時設定
  • 本例在例項化ShuffleSplit類時,test_size設定為0.2(5-fold),測試集樣本數量為2,訓練集樣本數量為8
  • split函式返回的是包含該訓練集、測試集索引的迭代器

GroupShuffleSplit

sklearn.model_selection.GroupShuffleSplit作用與ShuffleSplit相同,不同之處在於GroupShuffleSplit先將待劃分的樣本集分組,再按照分組劃分訓練集、測試集。
GroupShuffleSplit類的申明如下:

class sklearn.model_selection.GroupShuffleSplit(n_splits=5, test_size=’default’, train_size=None, random_state=None)

引數個數及含義同ShuffleSplit,只是預設值有所不同:

  • n_splits:int, 劃分訓練集、測試集的次數,預設為5
  • test_size:float, int, None, default=0.1; 測試集比例或樣本數量,該值為[0.0, 1.0]內的浮點數時,表示測試集佔總樣本的比例;該值為整型值時,表示具體的測試集樣本數量;train_size不設定具體數值時,該值取預設值0.2,train_size設定具體數值時,test_size取剩餘部分
  • train_size:float, int, None; 訓練集比例或樣本數量,該值為[0.0, 1.0]內的浮點數時,表示訓練集佔總樣本的比例;該值為整型值時,表示具體的訓練集樣本數量;該值為None(預設值)時,訓練集取總體樣本除去測試集的部分
  • random_state:int, RandomState instance or None;隨機種子值,預設為None

GroupShuffleSplit類的get_n_splits、split方法與ShuffleSplit類的同名方法類似,唯一的不同之處在於split方法的groups引數在此處生效,用於指定分組依據。

GroupShuffleSplit應用舉例

在State Farm Distracted Driver Detection資料集上進行訓練時,為減小駕駛者服飾、面貌等特徵對分類模型泛化能力的不利影響,需要按照駕駛者編號對樣本集進行劃分,此時用到GroupShuffleSplit類並在split方法中設定groups引數,對樣本進行4-fold 4次交叉驗證劃分的示例程式碼如下:

import pandas as pd
import numpy as np
from sklearn.model_selection import ShuffleSplit, GroupShuffleSplit
sample = pd.DataFrame({'subject':['p012', 'p012', 'p014', 'p014', 'p014', 'p024', 'p024', 'p024', 'p024', 'p081'],
                         'classname':['c5', 'c0', 'c1', 'c5', 'c0','c0','c1','c1','c2','c6'],
                         'img':['img_41179.jpg','img_50749.jpg', 'img_53609.jpg','img_52213.jpg','img_72495.jpg',
                                'img_66836.jpg','img_32639.jpg','img_31777.jpg','img_97535.jpg','img_1399.jpg']})
x_train_names_all = np.array(sample['img'])
y_train_labels_all = np.array(sample['classname'])
driver_ids = sample['subject']
_, driver_indices = np.unique(np.array(driver_ids), return_inverse=True)
n_fold = 1
rs = GroupShuffleSplit(n_splits=4, test_size=0.25, random_state=0)
for train_indices, test_indices in rs.split(x_train_names_all, y_train_labels_all, groups=driver_indices):
    print('fold {}/4......'.format(n_fold))
    print("train_indices:", train_indices)
    x_train = x_train_names_all[train_indices, ...]
    print("x_train_names:\n", x_train)
    y_train = y_train_labels_all[train_indices, ...]
    print("y_train:\n", y_train)

    print("test_indices:", test_indices)
    x_test = x_train_names_all[test_indices, ...]
    print("x_test:\n", x_test)
    y_test = y_train_labels_all[test_indices, ...]
    print("y_test:\n", y_test)
    n_fold += 1

輸出結果:
這裡寫圖片描述
回顧一下樣本,10個樣本來自4個駕駛者,其中p012有2個樣本、p014有3個樣本、p024有4個樣本、p081有1個樣本,駕駛者編號與索引的對應關係如下:

駕駛者編號 p012 p012 p014 p014 p014 p024 p024 p024 p024 p081
索引值 0 1 2 3 4 5 6 7 8 9

即p012包含索引0、1,p014包含索引2、3、4,以此類推。
在GroupShuffleSplit類初始化時設定test_size=0.25,在split方法中設定groups引數、指定分類依據為駕駛者編號,即對4個駕駛者進行劃分,測試集為1個駕駛者對應的資料,其餘駕駛者對應的資料作為訓練集。
從輸出結果可以看出,第1次劃分時,“p012、p014、p081”為訓練集,“p024”為測試集;第2次劃分時,“p014、p024、p081”為訓練集,“p012”為測試集;第3次劃分時,“p012、p014、p024”為訓練集,“p081”為測試集;第4次劃分時,“p012、p024、p081”為訓練集,“p014”為測試集。
GroupShuffleSplit用法與ShuffleSplit類似,需要說明的是,split方法的groups引數需設定為組標記,本例中先使用np.unique函式獲取舊陣列元素在去重後得到的新陣列中的位置,以此來作為組標記,其值為:[0 0 1 1 1 2 2 2 2 3],再將其設定為groups的實參。