五折交叉驗證: 把數據平均分成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
。可要可不要return
:train
和test
的索引,注意返回的是每個集合的索引,而不是數據
舉例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=True
和random_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
是一個包含所有數據的列表,但列表里的每一個數據又是一個字典,分別當image
和label
的數據地址。
我們對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]) 復制代碼
在train
和test
方法里面,肯定要寫好對應的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
來源:慕課網