五折交叉驗證/K折交叉驗證, python代碼到底怎么寫


圖片

五折交叉驗證: 把數據平均分成5等份,每次實驗拿一份做測試,其余用做訓練。實驗5次求平均值。如上圖,第一次實驗拿第一份做測試集,其余作為訓練集。第二次實驗拿第二份做測試集,其余做訓練集。依此類推~

但是,道理都挺簡單的,但是代碼我就不會寫,比如我怎么把數據平均分成5份?我怎么保證每次實驗的時候,數據都是這么划分的?本來一般的訓練時,把數據按6:2:2分成訓練集、驗證集和測試集,在訓練集上訓練圖像,驗證集上保存最佳模型,測試集用來最后的測試。現在交叉驗證沒有驗證集了,怎么保存模型?以下為大家一一解答。

1.把數據平均分成K等份

使用KFold類。
class sklearn.model_selection.KFold(n_splits=5, *, shuffle=False, random_state=None) sklearn提供的這個函數就是用來做K折交叉驗證的。 提供訓練集/測試集索引以分割數據。將數據集拆分為k折(默認情況下不打亂數據。

參數介紹

  • n_splits:int, 默認為5。表示拆分成5
  • shuffle:bool, 默認為False。切分數據集之前是否對數據進行洗牌。True洗牌,False不洗牌。
  • random_state:int, 默認為None。當shuffle為 True 時,如果random_state為None,則每次運行代碼,獲得的數據切分都不一樣,random_state指定的時候,則每次運行代碼,都能獲得同樣的切分數據,保證實驗可重復。random_state可按自己喜好設定成整數,如random_state=42較為常用。當設定好后,就不能再更改。

使用KFold類需要先初始化,然后再調用它的方法實現數據划分。它的兩個方法為:

  • get_n_splits(X=None, y=None, groups=None)

返回交叉驗證器中的拆分迭代次數

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

生成索引,將數據拆分為訓練集和測試集。
X: 數組,形狀為:(n_samples, n_features)
其中n_samples是樣本數,n_features是特征數。
y: 數組,形狀為(n_samples,)default=None。可要可不要
returntraintest的索引,注意返回的是每個集合的索引,而不是數據

舉例1:設置shuffle=False,每次運行結果都相同

from sklearn.model_selection import KFold import numpy as np X = np.arange(24).reshape(12,2) y = np.random.choice([1,2],12,p=[0.4,0.6]) kf = KFold(n_splits=5,shuffle=False) # 初始化KFold for train_index , test_index in kf.split(X): # 調用split方法切分數據 print('train_index:%s , test_index: %s ' %(train_index,test_index)) 復制代碼 

結果:5折數據的索引

train_index:[ 3  4  5  6  7  8  9 10 11] , test_index: [0 1 2] 
train_index:[ 0  1  2  6  7  8  9 10 11] , test_index: [3 4 5] 
train_index:[ 0  1  2  3  4  5  8  9 10 11] , test_index: [6 7] 
train_index:[ 0  1  2  3  4  5  6  7 10 11] , test_index: [8 9] 
train_index:[0 1 2 3 4 5 6 7 8 9] , test_index: [10 11] 
復制代碼

通過索引去獲取數據和對應的標簽可用:

fold1_train_data, fold1_train_label = X[train_index], y[train_index] 復制代碼 

圖片

舉例2:設置shuffle=True,每次運行結果都不相同

圖片

舉例3:設置shuffle=Truerandom_state=整數,每次運行結果相同

在這里插入圖片描述

因此,實際使用的時候建議采用案例3這種方式,即可保證實驗可重復,有增加了數據的隨機性。

舉例4: 真實案例數據划分

我有一些nii.gz的三維數據用來做分割,圖像和label分別放在不同的文件夾。如:

└── 根目錄
    └── image
    │       ├── 1.nii.gz
    │       │── 2.nii.gz
    │       └── 3.nii.gz
    │   
      ── label
    │       ├── 1.nii.gz
    │       │── 2.nii.gz
    │       └── 3.nii.gz   
復制代碼
 images1 = sorted(glob.glob(os.path.join(data_root, 'ImagePatch', 'l*.nii.gz'))) labels1 = sorted(glob.glob(os.path.join(data_root, 'Mask01Patch', 'l*.nii.gz'))) images2 = sorted(glob.glob(os.path.join(data_root, 'ImagePatch', 'r*.nii.gz'))) labels2 = sorted(glob.glob(os.path.join(data_root, 'Mask01Patch', 'r*.nii.gz'))) data_dicts1 = [{'image': image_name, 'label': label_name} for image_name, label_name in zip(images1, labels1)] data_dicts2 = [{'image': image_name, 'label': label_name} for image_name, label_name in zip(images2, labels2)] all_files = data_dicts1 + data_dicts2 # 把image和label創建成字典,統一放在列表里 復制代碼 

圖片

all_files是一個包含所有數據的列表,但列表里的每一個數據又是一個字典,分別當imagelabel的數據地址。
我們對all_files的數據進行五折交叉驗證:

    floder = KFold(n_splits=5, random_state=42, shuffle=True) train_files = [] # 存放5折的訓練集划分 test_files = [] # # 存放5折的測試集集划分 for k, (Trindex, Tsindex) in enumerate(floder.split(all_files)): train_files.append(np.array(all_files)[Trindex].tolist()) test_files.append(np.array(all_files)[Tsindex].tolist()) # 把划分寫入csv,檢驗每次是否相同 df = pd.DataFrame(data=train_files, index=['0', '1', '2', '3', '4']) df.to_csv('./data/Kfold/train_patch.csv') df1 = pd.DataFrame(data=test_files, index=['0', '1', '2', '3', '4']) df1.to_csv('./data/Kfold/test_patch.csv') 復制代碼 

我們把數據集的划分保存到csv里面,以防止代碼改動丟失了原本的划分方法。

數據集划分好了,就可以進行訓練和測試了。每一次拿划分好的一折數據就行。

    # 五折分開train, 每次拿一折train 和 test train(train_files[0], test_files[0]) test(test_files[0]) 復制代碼 

traintest方法里面,肯定要寫好對應的dataloder, 因為我們剛只是把數據的名字進行了划分,並沒有加載數據集。

通常的做法里,會循環5次,運行一次代碼,把五折的結果都做出來。但是我們這種寫法的好處在於,你想訓練第幾折,就把索引值改一下就是,不需要一下子全部訓練完。只要你不動代碼,你一年后再訓練,數據集的划分都不會變。變了也不怕,我們把划分已經保存成csv.

當然,這只是一種寫法,如果有更好的方案,歡迎留言探討~~

2.沒有驗證集了,怎么保存最佳模型

這是我之前一直好奇的問題。因為,如果不做交叉驗證,那么我會根據測試集上的指標保存最佳模型。比如以下代碼是在驗證集上完成的。

if metric > best_metric: best_metric = metric best_metric_epoch = epoch + 1 save_dir = 'checkpoints/checkpoint_04264/' if not os.path.exists(save_dir): os.makedirs(save_dir) save_path = save_dir + str(epoch + 1) + "best_metric_model.pth" torch.save(model.state_dict(), save_path) print('saved new best metric model') 復制代碼 

但是,現在,沒有驗證集了,我是根據訓練集上的指標保存模型呢,還是根據測試集上的指標?這個問題,沒有統一答案,兩者做法都有。正因為沒有統一答案,那我們可以選擇對自己最有利的答案啊😜。比如,寫論文的時候,根據測試集上的結果保存模型,那肯定得到的結果更好啊。

而且,還有一個小tips, 用交叉驗證的得到的結果通常比按6:2:2划分訓練集驗證集測試集上的結果要好。想想是為什么😉

作者:zh智慧


作者:九州編程
鏈接:http://www.imooc.com/article/317856
來源:慕課網


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM