【数据竞赛】Kaggle竞赛如何保证线上线下一致性?
作者:?塵沙櫻落、杰少、新峰、謝嘉嘉、DOTA、有夕
驗證策略設計
這是一個系列篇,后續我們會按照我們第一章中的框架進行更新,因為大家平時都較忙,不會定期更新,如有興趣歡迎長期關注我們的公眾號,如有任何建議可以在評論區留言。因本章篇幅過長的原因,我們僅列出特征與標簽的交叉分析部分,我們會在后續給出特征之間的相關性等分析策略。其它內容可參考下面的篇章。
1. kaggle競賽寶典-競賽框架篇!
2.1?賽題理解,分析,規劃之賽題理解與分析!
2.2??kaggle競賽寶典-回歸相關指標優化!
2.3??kaggle競賽寶典-二分類相關指標優化!
2.4??kaggle競賽寶典-多分類相關指標優化!
2.5?數據競賽規劃!
3.1?數據探索分析-全局數據探索分析!
3.2?數據探索分析-單變量數據分析!
3.3 數據探索分析-交叉變量分析篇!
3.4?訓練集測試集分布不一致性探索!
4.1?kaggle競賽寶典-樣本篩選篇!
4.2?kaggle競賽寶典-樣本組織篇!
在數據集進行數據探索分析,做完樣本的初步篩選,并對樣本進行重新組織之后(如有必要)。接下來我們需要做的就是線下驗證策略的設定。驗證集設計的合理與否,對于整個競賽都會帶來非常大的影響,如果我們的模型線下驗證結果和線上的結果不一致,將會導致無法繼續進行后續的實驗,就像是在摸獎一樣,
注意:此處我們所說的不一致,指的是線下結果有一定幅度的提升,但線上卻下降了的情況;如果線下提升幅度不是非常大,但是線上下降了可能是因為波動的原因,可以認為是合理的。
此外,一個魯棒的驗證策略,還可以幫助我們更好地調整我們模型的參數,驗證各種框架以及特征的重要性等等。那么如何做好驗證策略的設計呢?下面我們一共介紹三大類一共十一種常見的驗證策略,并介紹這些對應策略常見的使用場景以及案例等。
1. 訓練集驗證集分割驗證
1. 隨機劃分
1.1 簡介
隨機的訓練集驗證集切分是最為簡單也是最為常見的驗證策略。它的步驟也很簡單:
將訓練集合按照一定比例隨機切分為新的訓練集和驗證集;
我們使用新的驗證集進行訓練并在驗證集上進行驗證;
它的示意圖如下:
1.2 常見使用場景
簡單的訓練集和驗證集劃分策略目前經常會出現在一些數據集較大的問題同時時間因素影響不大的情況下,因為數據集較大的原因,驗證一次的時間消耗較大,同時我們認為較大數據集的驗證結果是相對可靠的,所以簡單的訓練集和驗證集的劃分就可以滿足我們的需求。當然在早期一些小的數據集上有的朋友也會采用訓練集驗證集的劃分策略,但是這個時候,我們需要將驗證集的數據多劃分一些,以保證驗證結果相對置信。
如果我們的訓練集合非常大,比如有上億條記錄,采用80:20的比例進行劃分即可;當然70:30和90:10也都是可以的;
如果我們的訓練集合一般,比如只有20000條數據,那么采用70:30左右的比例進行劃分會較為合適點;盡可能往驗證集上多劃分一些數據,不過這個時候我們會更加傾向于使用接下來講的K折交叉驗證。
上面的比例只是一個參考,沒有明確的計算公式說哪個比例會更好,需要大家自己實踐來判斷。
1.3 使用案例
#?將數據集劃分為訓練集和驗證集合? from?sklearn.datasets?import?make_blobs from?sklearn.model_selection?import?train_test_split #?構建數據集 X,?y?=?make_blobs(n_samples=100000) #?數據集劃分 val_ratio?=?0.2 X_train,?X_test,?y_train,?y_test?=?train_test_split(X,?y,?test_size=val_ratio) print(X_train.shape,?X_test.shape,?y_train.shape,?y_test.shape)? (80000, 2) (20000, 2) (80000,) (20000,)2. 分層劃分
2.1 簡介
分層劃分主要常見于分類問題,有些分類問題的每個類標簽的示例數并不均衡。分層劃分的步驟和隨機的類似,最好將數據集拆分為訓練集和驗證集,以便在每個類中保留與在原始數據集中觀察到的差不多比例的樣本。
將訓練集合按照一定比例分層劃分為新的訓練集和驗證集;
我們使用新的驗證集進行訓練并在驗證集上進行驗證;
2.2 常見使用場景
分層劃分常見于類別標簽不平衡的分類問題中,采用分層劃分的策略,可以保證訓練集和驗證集合的樣本的標簽分布類似。
train_test_split(X,?y,?test_size=0.50,?random_state=1,?stratify=y)?2.3 使用案例
#?split?imbalanced?dataset?into?train?and?test?sets?without?stratification from?collections?import?Counter from?sklearn.datasets?import?make_classification from?sklearn.model_selection?import?train_test_split #?構建數據集 X,?y?=?make_classification(n_samples=1000,?weights=[0.95],?flip_y=0,?random_state=1) print(Counter(y)) #?訓練集驗證集劃分 X_train,?X_test,?y_train,?y_test?=?train_test_split(X,?y,?test_size=0.50,?random_state=1) print('label?distribution?in?train:?',Counter(y_train)) print('label?distribution?in?test:?',Counter(y_test))?? Counter({0: 950, 1: 50}) label distribution in train: Counter({0: 478, 1: 22}) label distribution in test: Counter({0: 472, 1: 28}) #?訓練集驗證集分層劃分 X_train,?X_test,?y_train,?y_test?=?train_test_split(X,?y,?test_size=0.50,?random_state=1,?stratify=y) print('label?distribution?in?train:?',Counter(y_train))? print('label?distribution?in?test:?',Counter(y_test))? Counter({0: 475, 1: 25}) Counter({0: 475, 1: 25})2. K折交叉驗證
1. 隨機K折交叉驗證
1.1 簡介
簡單的隨機劃分或者單次分層劃分在我們數據集非常大,驗證一次需要耗費較大量的計算代價和時間成本的時候較為常用。但是當我們的數據集并不是非常大時候,驗證一次的成本也沒有那么高的時候,為了保證模型的驗證是靠譜的,大家最為常見的就是K折交叉驗證。它的步驟如下:
對數據集進行shuffle打亂順序;
將數據分成K折。K=5或10適用于大多數情況;
保留一折用于驗證,使用剩下的其它折數據進行模型的訓練;
在訓練集合上訓練模型,在驗證集上評估模型,并記錄該Fold的結果;
現在對所有其它的Fold重復該過程,每次選擇單折作為驗證的數據集;
對于每一次迭代,我們的模型都會在不同的數據集上進行訓練和測試;
最后我們將每一次的分數相加,采用最終的平均分作為我們的驗證結果;
下面是一個五折交叉驗證的可視化圖例。
1.2 常見使用場景
K折交叉驗證在很多的數據競賽中都是非常常見的,當我們的數據量并不是非常大,驗證一次的時間代價也相對較小,同時數據集受時間等影響也非常小的時候,我們就考慮采用K折交叉驗證。從實踐經驗中,我們也發現:K折交叉驗證不僅可以給我們帶來一個更加靠譜的線下效果,與此同時,通過K折驗證我們可以得到K個訓練好的模型,采用K個模型分別對測試集進行預測并取均值或者中位數等作為最終預測結果帶來的預測效果往往也會更好更穩定。
1.3 使用案例
import?pandas?as?pd import?numpy?as?np from?sklearn.model_selection?import?KFold,?cross_val_score?? #?We?will?use?this?'kf'(KFold?splitting?stratergy)?object?as?input?to?cross_val_score()?methodK?=?5 kf?=KFold(n_splits=K,?shuffle=True,?random_state=42)? for?train_index,?test_index?in?kf.split(X,?y):print(f'Fold:{cnt},?Train?set:?{len(train_index)},?Val?set:{len(test_index)}')X_tr,?X_val,y_tr,y_val?=?X[train_index],X[test_index],?y[train_index],y[test_index]...2. 分層K折交叉驗證
2.1 簡介
簡單的K折驗證是最為常見的策略,但和隨機劃分處介紹的一樣,我們希望我們每折中都可以有準確的數據分布。
在回歸問題的情況下:我們選擇折,使每折中的平均值大致相等;
在分類問題的情況下,每折被選擇具有相同比例的分類標簽。
分層K折疊在分類問題中更有用,在分類問題中,每折中具有相同百分比的標簽非常重要。
2.2 常見使用場景&案例
分層K折驗證常見于類別標簽不平衡的分類問題中,在有些情況也會出現在一些回歸問題。使用案例和訓練集驗證集的分層劃分是類似的,此處不再闡述。
3.?分組K折交叉驗證
3.1 簡介
隨機K折交叉驗證以及基于分層的K折驗證已經適用于90%的時序影響較小的問題,但仍然存在一些問題。例如,如果我們的訓練集和測試集是不同組的內容,此處組我們指的是需要預測的問題的主體,例如我們的問題是:
我們的訓練集合是關于10萬用戶的歷史逾期貸款記錄(每個月產出一條記錄);我們需要預測另外1萬個未出現在訓練集合中的用戶對應的記錄是否會出現逾期貸款的問題。
此時我們的組就是用戶的ID列表。再比如:
我們從多個病人身上收集醫療相關的數據,從每個病人身上采集了多個樣本。我們的數據很可能取決于個別群體。在我們的案例中,每個樣本對應的患者id就是它的組識識符。我們希望知道,基于這些收集到的數據訓練得到的模型是否可以很好地推廣到不可見的另外一個群體。為了衡量這一點,我們需要確保驗證時每一折的所有樣本都來是訓練折中未出現的組。
此時我們的組就是患者的ID列表。關于分組K折驗證的步驟可以分為:
判定需要進行分組的ID;
基于分組的ID進行隨機K折驗證;
3.2 常見使用場景
分組的K折交叉驗證常常被用于判斷基于某個特定組的數據訓練得到的模型是否具有很好的泛化性,能夠在未見過的組上取得很好的效果。
如果測試集和訓練集合中的組存在較大的差異,這個時候對這些測試集數據采用分組訓練預測往往能帶來更加穩定的效果。
如果測試集和訓練集合中的組存在的差異較小,簡單的K折交叉驗證即可。
3.3 使用案例
kf?=?GroupKFold(5) grp_id?=?''? group?=?X[grp_id].copy() for?fold,?(trn_idx,?val_idx)?in?enumerate(kf.split(X,?y,?group)):print(f'Training?fold?{fold?+?1}')?X_tr,?X_val,y_tr,y_val?=?X[trn_idx],X[val_idx],?y[trn_idx],y[val_idx]4. 分層分組K折交叉驗證
4.1 簡介
從上面的介紹中,我們知道了分組和分層的使用場景。所以自然也就出現了分層分組的K折驗證,典型的兩個競賽案例如下:
PetFinder.my Adoption Prediction
SIIM-ISIC Melanoma Classification
4.2 分層分組K折驗證代碼
分層分組的K折驗證代碼目前還未嵌入在sklearn工具包中,但是已經有很多朋友寫過,下面摘取kaggle一個高贊的代碼,供參考。
'''摘自:https://www.kaggle.com/jakubwasikowski/stratified-group-k-fold-cross-validation '''import?random import?numpy?as?np import?pandas?as?pd from?collections?import?Counter,?defaultdict def?stratified_group_k_fold(X,?y,?groups,?k,?seed=None):labels_num?=?np.max(y)?+?1y_counts_per_group?=?defaultdict(lambda:?np.zeros(labels_num))y_distr?=?Counter()for?label,?g?in?zip(y,?groups):y_counts_per_group[g][label]?+=?1y_distr[label]?+=?1y_counts_per_fold?=?defaultdict(lambda:?np.zeros(labels_num))groups_per_fold?=?defaultdict(set)def?eval_y_counts_per_fold(y_counts,?fold):y_counts_per_fold[fold]?+=?y_countsstd_per_label?=?[]for?label?in?range(labels_num):label_std?=?np.std([y_counts_per_fold[i][label]?/?y_distr[label]?for?i?in?range(k)])std_per_label.append(label_std)y_counts_per_fold[fold]?-=?y_countsreturn?np.mean(std_per_label)groups_and_y_counts?=?list(y_counts_per_group.items())random.Random(seed).shuffle(groups_and_y_counts)for?g,?y_counts?in?sorted(groups_and_y_counts,?key=lambda?x:?-np.std(x[1])):best_fold?=?Nonemin_eval?=?Nonefor?i?in?range(k):fold_eval?=?eval_y_counts_per_fold(y_counts,?i)if?min_eval?is?None?or?fold_eval?<?min_eval:min_eval?=?fold_evalbest_fold?=?iy_counts_per_fold[best_fold]?+=?y_countsgroups_per_fold[best_fold].add(g)all_groups?=?set(groups)for?i?in?range(k):train_groups?=?all_groups?-?groups_per_fold[i]test_groups?=?groups_per_fold[i]train_indices?=?[i?for?i,?g?in?enumerate(groups)?if?g?in?train_groups]test_indices?=?[i?for?i,?g?in?enumerate(groups)?if?g?in?test_groups]yield?train_indices,?test_indices5. Repeated K折交叉驗證
5.1 簡介
K折交叉驗證將有限的訓練數據集合劃分為K個不重疊的折。K折中的每一折會被依次作為驗證集,而所有其他折則會被合并作為新的訓練數據集。在K個保持驗證集上對K個模型進行了擬合和評估,同時將最終的均值作為我們的評估結果。這在實踐中,其實我們大部分時候都是可以接受的,但也會發現下面的一個現象:
相同的特征,相同的K折驗證,不同的隨機種子,兩次的驗證結果分數相差的還挺多。
有些朋友會說K折交叉驗證的結果是有較大噪音的,有的時候為了方便比較,我們往往會固定住隨機種子,但是有的時候由于使用的機器和操作系統等的緣故,還會導致沒法比較。那么如何緩解此類問題呢?最簡單的:
那就再多做幾次驗證!
于是就有了Repeated K折交叉驗證。它的步驟也非常簡單:
設置重復的驗證的次數M;
對于每一次驗證,我們選用不同的隨機種子進行K折驗證;
將M次的驗證結果取均值作為最終的驗證結果。
注意:此處我們必須在同一數據集上執行K折交叉驗證,在每次的每次重復中,同一個數據集被拆分為不同折。
該驗證策略在《Applied Predictive Modeling》70頁也有提到。
… repeated k-fold cross-validation replicates the procedure […] multiple times. For example, if 10-fold cross-validation was repeated five times, 50 different held-out sets would be used to estimate model efficacy.
5.2 常見使用場景
Repeated K折交叉驗證可以很好地提升我們模型預估結果的置信度,一般常常使用在那些數據集相對不是非常大的情況下。因為這個時候,模型每次驗證消耗的時間相對較短,計算資源的消耗也相對較小。和K折交叉驗證類似,Repeated的K折交叉驗證很容易并行化,其中每折或每個重復交叉驗證過程可以在不同的內核或不同的機器上執行。
5.3 使用案例
案例1:5折驗證重復5次。
案例2:觀測每次K折效果的波動情況
6. Nested K折交叉驗證
6.1 簡介
傳統的K折交叉驗證已經被廣泛使用,大家在使用K折驗證方案的時候經常會基于某一折進行調參,比如尋找最優的停止輪數,也就是說,我們每一折的驗證結果都是在特定條件下相對最優的,但在實踐問題中,我們不可能基于測試集得到最好的停止輪數的,這就會導致我們對模型效果的評估過于樂觀。也就是說:
K折交叉驗證存在輕微的過擬合!
這在訓練集和驗證集隨機劃分的時候更加嚴重。那么我們該怎么做呢?Nested K折交叉驗證就是用來緩解該問題的。
In order to overcome the bias in performance evaluation, model selection should be viewed as an integral part of the model fitting procedure, and should be conducted independently in each trial in order to prevent selection bias and because it reflects best practice in operational use.
Nested K折交叉驗證將模型的超參調優作為模型的一部分,為了防止模型過擬合的問題,我們不再直接在驗證的那一折上進行調參等操作,我們按照下面的步驟進行:
基于特定的問題,我們將數據集進行特定的K折劃分(隨機/分層/分組...),;
在第L輪中,我們選用為驗證集,其它折的數據集進行拼接得到我們新的訓練集合;
基于新的訓練集合,我們采用折交叉進行超參數的調優,我們基于最優的參數重新訓練得到我們的模型;
使用重新訓練得到的模型對我們的驗證集進行預測,然后進行評估;
這么做,我們在整個流程中只在最后一次對其進行預測,所以得到的驗證結果會更加靠譜。我們也可以這么去理解,
內部的交叉驗證用來進行模型選擇以及參數調優;
外部的交叉驗證用來進行效果評估。
6.2 常見使用場景
適用于K折交叉驗證的問題在Repeated K折交叉的問題中都是適用的。
6.3 使用案例
案例1:Nested K折交叉驗證
案例2:使用現有的庫
3. 基于時間序列的驗證
上面的所講述的訓練集驗證集的劃分策略以及K折交叉驗證在時間影響較小的情況下是非常合適的,但是在很多時間影響較大的問題中,例如:
商家店鋪銷量預測;
用戶視頻觀看時長預測問題;
網頁流量預測;
...
這些問題如果直接使用傳統的驗證策略,往往會造成很嚴重的穿越問題,使得線下線上波動極大。因為時間序列相關的數據觀測值之間的相關性緊靠時間的(自相關的)。但是,經典的交叉驗證技術都是假設樣本是獨立同分布的,并且會導致時間序列數據上的訓練和測試實例之間不合理的相關性(產生較差的泛化誤差估計)。那么此類數據我們該如何做線下的驗證呢?
1. 單折時間劃分
1.1 簡介
在時間相關的問題中,最常見的驗證策略就是按照時間信息進行排序,然后選取某個時間點之后的數據集作為驗證集合,前面的數據作為訓練集合。
我們可以按照下面的策略進行:
按照時間信息對我們的數據集進行排序;
選取某個相對時間/絕對時間作為劃分點,之前的作為訓練結,之后的數據作為驗證集合;
1.2 常見使用場景
當我們時間相關的數據量較大的時候,我們可以直接使用簡單的按時間劃分策略進行模型的驗證。
1.3 使用案例
案例1:按照具體時間劃分
案例2:按照比例劃分
2. 基于時間的N折驗證
2.1 簡介
上面簡單的基于時間進行數據集劃分的驗證策略,在一些特殊的情況下,例如驗證集中含有一段奇異值數據的時候,模型的波動會非常大,那么如何構建靠譜的線下驗證策略呢?
使用Walk-Forward交叉驗證策略或者是基于時間的N折交叉驗證。
我們舉個推薦相關的實際案例,在很多公司的推薦系統中,當模型優化到后期的時候,就會出現線上線下不一致的情況,那么如何緩解這種情況呢?或者至少降低這種情況發生的概率呢?首先我們看看大家是如何驗證的......
90%公司的驗證策略:選用T+1或者T+2的數據作為驗證集,1,2,...,T的數據進行模型的訓練;
也就是最簡單的時間劃分策略,這么做就會有一個非常大的問題,因為很多公司會有很多的促銷之類的活動,所以模型只選擇一天進行驗證就會出現不穩定的情況,但是在我們的實踐中,發現,如果連續N天的驗證都是有提升的,那么大概率該模型上線之后也能帶來較為穩定的提升;其步驟也較為簡單:
選用T+1天的數據作為驗證集,1,2,...,T的數據進行模型的訓練;
選用T天的數據作為驗證集,1,2,...,T-1的數據進行模型的訓練;
選用T-1天的數據作為驗證集,1,2,...,T-2的數據進行模型的訓練;
以此類推。
一般驗證三天即可。
2.2 常見使用場景
適用于所有的中長時間序列建模的問題,即數據相對較多的情況。
2.3 使用案例
from?sklearn.model_selection?import?TimeSeriesSplit X?=?np.array([[1,?2],?[3,?4],?[1,?2],?[3,?4],?[1,?2],?[3,?4],[1,?2],?[3,?4],?[1,?2],?[3,?4]]) y?=?np.array([1,?2,?3,?4,?5,?6,?7,?8,?9,?10])for?train_index,?test_index?in?tscv.split(X):print("TRAIN:",?train_index,?"TEST:",?test_index)X_train,?X_test?=?X[train_index],?X[test_index]y_train,?y_test?=?y[train_index],?y[test_index]''' TRAIN:?[0?1?2?3?4]?TEST:?[5] TRAIN:?[0?1?2?3?4?5]?TEST:?[6] TRAIN:?[0?1?2?3?4?5?6]?TEST:?[7] TRAIN:?[0?1?2?3?4?5?6?7]?TEST:?[8] TRAIN:?[0?1?2?3?4?5?6?7?8]?TEST:?[9] '''3. 時間序列的Nested CV
該思路很簡單,為了得到模型更為靠譜的驗證效果,會采用Nested的驗證策略,對應的流程圖如下:
有興趣的朋友可以進行深入的研究。
參考文獻
Splitting a Dataset into Train and Test Sets
Train-Test Split for Evaluating Machine Learning Algorithms
Split Your Dataset With scikit-learn's train_test_split()
sklearn.model_selection.KFold
A Gentle Introduction to k-fold Cross-Validation
An Introduction to Statistical Learning
Repeated k-Fold Cross-Validation for Model Evaluation in Python
Nested Cross-Validation for Machine Learning with Python
Tutorial: K Fold Cross Validation
Stratified Group k-Fold Cross-Validation
Introduction to GroupKFold
SIIM Stratified GroupKFold 5-folds
Simple LGBM GroupKFold CV
Stratified Group k-Fold
Learn ML from Sklearn: Cross Validation
Applied Predictive Modeling
sklearn.model_selection.RepeatedKFold
Key Machine Learning Technique: Nested Cross-Validation, Why and How, with Python code
Nested-Cross-Validation
On Over-fitting in Model Selection and Subsequent Selection Bias in Performance Evaluation, 2010.
Tutorial: Time Series Analysis and Forecasting
Time Series Cross Validation
Correct time-aware cross-validation scheme
Nested cross validation for model selection
Cross validation on time series data
Train/Test Split and Cross Validation – A Python Tutorial
總結
以上是生活随笔為你收集整理的【数据竞赛】Kaggle竞赛如何保证线上线下一致性?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 360浏览器设置多标签操作步骤
- 下一篇: 腾讯视频app怎么允许腾讯视频访问位置信