训练集和测试集样本分布一致性的判断方法
(一)機器學習避坑指南:訓練集/測試集分布一致性檢查
https://blog.csdn.net/jpld/article/details/111659837
工業界有一個大家公認的看法,“數據和特征決定了機器學習項目的上限,而算法只是盡可能地逼近這個上限”。在實戰中,特征工程幾乎需要一半以上的時間,是很重要的一個部分。缺失值處理、異常值處理、數據標準化、不平衡等問題大家應該都已經手到擒來小菜一碟了,本文我們探討一個很容易被忽視的坑:數據一致性。
眾所周知,大部分機器學習算法都有一個前提假設:訓練數據樣本和位置的測試樣本來自同一分布。如果測試數據的分布跟訓練數據不一致,那么就會影響模型的效果。
在一些機器學習相關的競賽中,給定的訓練集和測試集中的部分特征本身很有可能就存在分布不一致的問題。實際應用中,隨著業務的發展,訓練樣本分布也會發生變化,最終導致模型泛化能力不足。
下面就向大家介紹幾個檢查訓練集和測試集特征分布一致性的方法:
KDE(核密度估計)分布圖
核密度估計(kernel density estimation)是在概率論中用來估計未知的密度函數,屬于非參數檢驗方法之一,通過核密度估計圖可以比較直觀的看出數據樣本本身的分布特征。
seaborn中的kdeplot可用于對單變量和雙變量進行核密度估計并可視化。
看一個小例子:
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
train_set=pd.read_csv(r'D:\...\train_set.csv')
test_set=pd.read_csv(r'D:\...\test_set.csv')
plt.figure(figsize=(12,9))
ax1 = sns.kdeplot(train_set.balance,label='train_set')
ax2 = sns.kdeplot(test_set.balance,label='test_set')
KS檢驗(Kolmogorov-Smirnov)
KS檢驗是基于累計分布函數,用于檢驗一個分布是否符合某種理論分布或比較兩個經驗分布是否有顯著差異。兩樣本K-S檢驗由于對兩樣本的經驗分布函數的位置和形狀參數的差異都敏感,所以成為比較兩樣本的最有用且最常用的非參數方法之一。
我們可以使用 scipy.stats 庫中的ks_2samp,進行KS檢驗:
from scipy.stats import ks_2samp
ks_2samp(train_set.balance,test_set.balance)
ks檢驗一般返回兩個值:第一個值表示兩個分布之間的最大距離,值越小即這兩個分布的差距越小,分布也就越一致。第二個值是p值,用來判定假設檢驗結果的一個參數,p值越大,越不能拒絕原假設(待檢驗的兩個分布式同分布),即兩個分布越是同分布。
Ks_2sampResult(statistic=0.005976590587342234, pvalue=0.9489915858135447)
最終返回的結果可以看出,balance這個特征在訓練集測試集中服從相同分布。
對抗驗證(Adversarial validation)
除了 KDE 和 KS檢驗,目前比較流行的是對抗驗證,它并不是一種評估模型效果的方法,而是一種用來確認訓練集和測試集的分布是否變化的方法。
具體做法:
1、將訓練集、測試集合并成一個數據集,新增一個標簽列,訓練集的樣本標記為 0 ,測試集的樣本標記為 1 。
2、重新劃分一個新的train_set和test_set(區別于原本的訓練集和測試集)。
3、用train_set訓練一個二分類模型,可以使用 LR、RF、XGBoost、 LightGBM等等,以AUC作為模型指標。
4、如果AUC在0.5左右,說明模型無法區分原訓練集和測試集,也即兩者分布一致。如果AUC比較大,說明原訓練集和測試集差異較大,分布不一致。
5、利用第 2 步中的分類器模型,對原始的訓練集進行打分預測,并將樣本按照模型分從大到小排序,模型分越大,說明與測試集越接近,那么取訓練集中的 TOP N 的樣本作為目標任務的驗證集,這樣即可將原始的樣本進行拆分得到訓練集,驗證集,測試集。
除了確定訓練集和測試集特征分布一致性,對抗驗證還可以用來做特征選擇。大家感興趣的話可以給個贊+在看,下一講《特征選擇》,我們用實例來看看對抗驗證的具體用法和效果。
————————————————
版權聲明:本文為CSDN博主「機器學習算法與Python實戰」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/jpld/article/details/111659837
(二)機器學習中的對抗驗證
https://blog.csdn.net/qq_39783265/article/details/104848263
對抗驗證
交叉驗證(Cross Validation)是常用的一種用來評估模型效果的方法。
當樣本分布發生變化時,交叉驗證無法準確評估模型在測試集上的效果,這導致模型在測試集上的效果遠低于訓練集。
通過本文,你將通過一個kaggle的比賽實例了解到,樣本分布變化如何影響建模,如何通過對抗驗證辨別樣本的分布變化,以及有哪些應對方法。
直接給鏈接:https://zhuanlan.zhihu.com/p/93842847
在此之前,不過不懂AUC,請學習下面鏈接:
https://www.zhihu.com/question/39840928/answer/241440370
貼上自己的代碼,以便于理解:
##df_train:用給的訓練集
##df_test:用測試機當驗證集
df_train = train_lables
df_test = test_lables
# 定義新的Y
df_train['Is_Test'] = 0
df_test['Is_Test'] = 1
# 將 Train 和 Test 合成一個數據集。
df_adv = pd.concat([df_train, df_test])
# features:訓練時所用到的特征
X = df_adv[features]
y = df_adv['Is_Test']
1
2
3
4
5
6
7
8
9
10
11
12
模型訓練
# 定義模型參數
params = {
? ? 'boosting_type': 'gbdt',
? ? 'colsample_bytree': 1,
? ? 'learning_rate': 0.1,
? ? 'max_depth': 5,
? ? 'min_child_samples': 100,
? ? 'min_child_weight': 1,
? ? 'min_split_gain': 0.0,
? ? 'num_leaves': 20,
? ? 'objective': 'binary',
? ? 'random_state': 50,
? ? 'subsample': 1.0,
? ? 'subsample_freq': 0,
? ? 'metric': 'auc',
? ? 'num_threads': 8
}
cv_pred = []
best_loss = []
test_prob = 0
n_splits = 5
skf = StratifiedKFold(n_splits=n_splits, shuffle=True, random_state=42)
for index, (train_idx, test_idx) in enumerate(fold.split(X, y)):
? ? lgb_model = lgb.LGBMClassifier(**params)
? ? train_x, test_x, train_y, test_y = X.loc[train_idx], X.loc[test_idx], y.loc[train_idx], y.loc[test_idx]
? ? eval_set = [(test_x, test_y)]
? ? lgb_model.fit(train_x, train_y, eval_set = eval_set, eval_metric='auc',early_stopping_rounds=100,verbose=None)
? ? best_loss.append(lgb_model.best_score_['valid_0']['auc'])
? ? print(best_loss, np.mean(best_loss))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
AUC結果:
[0.5548410714285714] 0.5548410714285714
[0.5548410714285714, 0.5788339285714286] 0.5668375
[0.5548410714285714, 0.5788339285714286, 0.5695142857142858] 0.5677297619047619
[0.5548410714285714, 0.5788339285714286, 0.5695142857142858, 0.5460357142857143] 0.56230625
[0.5548410714285714, 0.5788339285714286, 0.5695142857142858, 0.5460357142857143, 0.5811589285714286] 0.5660767857142857
此方法用來評價訓練集與測試集分布是否一致,以防止新的測試集出現,導致崩盤的現象
————————————————
版權聲明:本文為CSDN博主「貓愛吃魚the」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_39783265/article/details/104848263
(三)還在用交叉驗證?試試Kaggle大牛們常用的方法——對抗驗證
https://zhuanlan.zhihu.com/p/93842847
交叉驗證(Cross Validation)是常用的一種用來評估模型效果的方法。
當樣本分布發生變化時,交叉驗證無法準確評估模型在測試集上的效果,這導致模型在測試集上的效果遠低于訓練集。
通過本文,你將通過一個kaggle的比賽實例了解到,樣本分布變化如何影響建模,如何通過對抗驗證辨別樣本的分布變化,以及有哪些應對方法。
本篇文章完整代碼:?https://github.com/Qiuyan918/Adversarial_Validation_Case_Study/blob/master/Adversarial_Validation.ipynb
目錄
- 什么是樣本分布變化
- 為什么樣本分布變化的時候,交叉驗證不適用?
- 什么是對抗驗證?
- 分布變化時,有哪些優于交叉驗證的方法?
- Kaggle比賽實例(lightgbm模型;Python)
1 什么是「樣本分布變化」?
在真實的業務場景中,我們經常會遇到「樣本分布變化」的問題。
主要體現在訓練集和測試集的分布存在的差異。比如,在化妝品或者醫美市場,男性的比例越來越多。基于過去的數據構建的模型,漸漸不適用于現在。
2 為什么「樣本分布變化」的時候,交叉驗證不適用?
當我們要做一個模型,來預測人們在超市的消費習慣。
我們的訓練樣本主要是18歲至25歲的年輕人構成,而測試樣本主要是70歲以上的老人組成。這時樣本分布就發生了變化。
圖1 訓練樣本和測試樣本分布偏差
這種情況下,使用交叉驗證,無法準確評估模型的效果。原因是,交叉驗證的驗證集和測試集不夠相似。
交叉驗證中,每一折的驗證集都是從訓練集隨機抽取的。隨機抽取的驗證集的分布和整體的訓練集是相同的,也就意味著每一折的驗證集都和測試集的分布存在較大的差異。
所以在樣本分布變化時,通過交叉驗證的方式構建的模型,在測試集上的表現,相較于訓練集,通常會打折扣。稍后我們會通過一個實例來確認這一點。
3 什么是對抗驗證(Adversarial Validation)?
對抗驗證(Adversarial Validation),并不是一種評估模型效果的方法,而是一種用來確認訓練集和測試集的分布是否變化的方法。
它的本質是構造一個分類模型,來預測樣本是訓練集或測試集的概率。
如果這個模型的效果不錯(通常來說AUC在0.7以上),那么可以說明我們的訓練集和測試集存在較大的差異。
仍然以「預測人們在超市的消費習慣」為例。因為訓練集主要是18歲-25歲的年輕人,測試集主要是70歲以上的老人,那么通過「年齡」,我們就能夠很好的區分出訓練集和測試集。
圖2 分類器通過「年齡」可以輕松區分訓練集和測試集
具體步驟:
4 分布變化時,有哪些優于交叉驗證的方法?
4.1 人工劃分驗證集
人工劃分驗證集,需要我們對數據有充分的了解。
因為這次比賽的數據是根據時間劃分的,所以我的驗證集同樣可以根據時間劃分。
如果我們不清楚訓練集和測試集如何劃分,可以采用后面兩種方法。
# 將樣本根據時間排序 df_train = df_train.sort_values('Date').reset_index(drop=True) df_train.drop(['Date'], axis=1, inplace=True)# 前80%的樣本作為訓練集,后20%的樣本作為驗證集 df_validation_1 = df_train.iloc[int(0.8 * len(df_train)):, ] df_train_1 = df_train.iloc[:int(0.8 * len(df_train)), ]4.2 和測試集最相似的樣本作為驗證集
如果對數據沒有充分了解,如何找到訓練集中,和測試集分布最相似的樣本呢?
這就會用到我們做對抗驗證時,模型預測樣本是測試集的概率。概率越高,則說明和測試集越相似。
# 通過抗驗證中的模型,得到各個樣本屬于測試集的概率 model_adv.fit(df_adv.drop('Is_Test', axis=1), df_adv.loc[:, 'Is_Test']) preds_adv = model_adv.predict_proba(df_adv.drop('Is_Test', axis=1))[:, 1]# 只需要訓練樣本的概率 df_train_copy = df_train.copy() df_train_copy['is_test_prob'] = preds_adv[:len(df_train)]# 根據概率排序 df_train_copy = df_train_copy.sort_values('is_test_prob').reset_index(drop=True)# 將概率最大的20%作為驗證集 df_validation_2 = df_train_copy.iloc[int(0.8 * len(df_train)):, ] df_train_2 = df_train_copy.iloc[:int(0.8 * len(df_train)), ]4.3 有權重的交叉驗證
不僅可以用對抗驗證中,樣本是測試集的概率來劃分驗證集,也可以將這個概率作為樣本的權重。
概率越高,和測試集就越相似,權重就越高。這樣,我們就可以做有權重的交叉驗證。
# 生成lightgbm的數據格式,賦予各個樣本權重 train_set = lgb.Dataset(df_train.drop('HasDetections', axis=1),label=df_train.loc[:, 'HasDetections'], weight=preds_adv[:len(df_train)])5 實例
5.1 用到的數據
圖3 微軟惡意軟件比賽
這里用到的數據來自Kaggle上的微軟惡意軟件比賽。
因為這次比賽的 Train 和 Test 是根據時間劃分的,所以Train 和 Test 的分布非常不同,很具有代表性。
通過對該數據做對抗驗證,我們發現模型的AUC達到了0.99。說明本次比賽的訓練集和測試集的樣本分布存在較大的差異。
5.2 對比各種方法的效果
分別使用上述提到的總共4種方法,我們來對比一下四種方法的效果,如下表:
圖4 比較各類方法訓練集和測試集AUC
使用交叉驗證時,驗證集AUC和測試集AUC的差值是最大的,遠高于其他方式。說明在樣本分布發生變化時,交叉驗證不能夠準確評估模型在測試集上的效果。
5.3 為什么評價方式是差值,而不是測試集AUC?
有人可能會提到,哪種方法在測試集上的AUC最高,哪種方法就更好,不是嗎?
需要注意的是,本文討論的不是“提升”模型效果的方法,而是“評估”模型效果的方法。
具體來說,雖然目前看來,比如交叉驗證在測試集上的AUC,略高于有權重的交叉驗證。
但是,當前的模型只是一個很基礎的模型(Baseline Model),沒有做任何的變量篩選,特征工程,以及模型調參。
由于所有的優化模型的決定,都將基于驗證集,而交叉驗證無法準確評估模型在測試集上的效果,這將導致很多優化模型的決定是錯誤的。
只有在有一個可靠的驗證集的情況下,提升模型在驗證集上效果的方法,我們才有信心認為,它也可以提升在測試集上的表現。
另外,從本次比賽的結果,我們也可以發現,最終排名很好的參賽者,都沒有使用交叉驗證。
6 結論
在樣本分布發生變化時,交叉驗證不能夠準確評估模型在測試集上的效果。
這里建議采用其他方式:
- 人工劃分驗證集
- 和測試集最相似的樣本作為驗證集
- 有權重的交叉驗證
總結
以上是生活随笔為你收集整理的训练集和测试集样本分布一致性的判断方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ETL开发工具Kettle下载安装环境搭
- 下一篇: 信息论与编码_4G与5G分别采用什么信道