Unbalanced-Classes

不平衡的數據集是否存在問題,以及(如何)過採樣(聲稱)有幫助?

  • July 16, 2018

TL; 博士

見標題。


動機

我希望有一個符合“(1)否,(2)不適用,因為(1)”的規範答案,我們可以用它來解決許多關於不平衡數據集和過採樣的錯誤問題。如果我的先入之見被證明是錯誤的,我會很高興。神話般的賞金等待著勇敢的回答者。


我的論點

我對我們收到的許多問題感到困惑不平衡類標籤。不平衡的班級似乎很糟糕。和過採樣不言而喻,少數群體被視為有助於解決不言而喻的問題。許多帶有兩個標籤的問題都會繼續詢問如何在某些特定情況下執行過採樣。

我既不了解不平衡類會帶來什麼問題,也不了解過採樣應該如何解決這些問題。

在我看來,不平衡的數據根本不會造成問題。應該對類成員概率進行建模,這些概率可能很小。只要它們是正確的,就沒有問題。當然,不應將準確率用作在分類問題中最大化的 KPI。或者計算分類閾值。相反,應該使用適當的方法評估整個預測分佈的質量計分規則. Tetlock 的Superforecasting是預測不平衡類的精彩且非常易讀的介紹,即使書中沒有明確提及這一點。


有關的

評論中的討論引發了許多相關的話題。

IcannotFixThis 的答案似乎假定 (1) 我們試圖最大化的 KPI 是準確度,以及 (2) 準確度是分類模型評估的合適 KPI。它不是。這可能是整個討論的關鍵之一。

AdamO 的回答側重於不平衡因素的估計精度低。這當然是一個有效的擔憂,可能是我名義問題的答案。但是過採樣在這裡沒有幫助,就像我們可以通過簡單地將每個觀察值複製十次來在任何普通回歸中獲得更精確的估計一樣。


概括

上面的線索顯然可以總結如下。

  • 稀有類(在結果和預測變量中)是一個問題,因為參數估計和預測具有高方差/低精度。這不能通過過採樣來解決。(從某種意義上說,獲得更多代表總體的數據總是更好的,選擇性抽樣會根據我和其他人的模擬產生偏差。)
  • 如果我們通過準確性評估我們的模型,稀有類是一個“問題”。但準確度並不是評估分類模型的好方法。(我確實考慮過在我的模擬中包括準確性,但是我需要設置一個分類閾值,這是一個密切相關的錯誤問題,而且問題已經足夠長了。)

一個例子

讓我們模擬一個插圖。具體來說,我們將模擬十個預測變量,其中只有一個實際上對罕見結果有影響。我們將研究兩種可用於概率分類的算法:邏輯回歸隨機森林.

在每種情況下,我們將模型應用於完整數據集或過採樣平衡數據集,其中包含稀有類的所有實例和來自多數類的相同數量的樣本(因此過採樣數據集小於完整數據集)。

對於邏輯回歸,我們將評估每個模型是否真正恢復了用於生成數據的原始係數。此外,對於這兩種方法,我們將計算概率類成員預測,並在使用與原始訓練數據相同的數據生成過程生成的保留數據上評估這些預測。預測是否真正匹配結果將使用最常見的正確評分規則之一Brier 評分進行評估。

我們將運行 100 次模擬。(加速這個只會使 beanplots 更加狹窄,並使模擬運行時間超過一杯咖啡。)每個模擬都包含 $ n=10,000 $ 樣品。預測器形成一個 $ 10,000\times 10 $ 具有條目均勻分佈的矩陣 $ [0,1] $ . 只有第一個預測變量實際上有影響;真正的 DGP 是

$$ \text{logit}(p_i) = -7+5x_{i1}. $$

這使得少數 TRUE 類的模擬發生率在 2% 到 3% 之間:

培訓_發病率

讓我們運行模擬。將完整的數據集輸入邏輯回歸,我們(不出所料)得到無偏的參數估計(真實的參數值由紅色菱形表示):

邏輯係數

但是,如果我們將過採樣數據集提供給邏輯回歸,則截距參數會出現嚴重偏差

logistic_coefficients_oversampled

對於邏輯回歸和隨機森林,讓我們比較適合“原始”數據集和過採樣數據集的模型之間的 Brier 分數。請記住,越小越好:

logistic_regression_Brier_scores

Random_Forest_Brier_score

在每種情況下,從完整數據集派生的預測分佈都比從過採樣數據集派生的預測分佈要好得多。

我的結論是,不平衡的類不是問題,過採樣並不能緩解這個非問題,而是無緣無故地引入了偏差和更糟糕的預測。

我的錯誤在哪裡?


一個警告

我很樂意承認過採樣有一個應用:如果

  1. 我們正在處理一個罕見的結果,並且
  2. 評估結果很容易或便宜,但是
  3. 評估預測變量是困難的或昂貴的

一個典型的例子是罕見疾病的全基因組關聯研究(GWAS)。檢測一個人是否患有某種特定疾病比對他們的血液進行基因分型要容易得多。(我參與了一些PTSD的 GWAS 。)如果預算有限,根據結果進行篩選並確保樣本中有“足夠”的罕見病例可能是有意義的。

然而,人們需要在節省金錢和上述損失之間取得平衡——我的觀點是,CV 中關於不平衡數據集的問題並沒有提到這種權衡,而是將不平衡類視為不言而喻的邪惡,完全不同於任何樣品採集費用


R代碼

   library(randomForest)
   library(beanplot)
   
   nn_train <- nn_test <- 1e4
   n_sims <- 1e2
   
   true_coefficients <- c(-7, 5, rep(0, 9))
   
   incidence_train <- rep(NA, n_sims)
   model_logistic_coefficients <- 
        model_logistic_oversampled_coefficients <- 
        matrix(NA, nrow=n_sims, ncol=length(true_coefficients))
   
   brier_score_logistic <- brier_score_logistic_oversampled <- 
     brier_score_randomForest <- 
   brier_score_randomForest_oversampled <- 
     rep(NA, n_sims)
   
   pb <- winProgressBar(max=n_sims)
   for ( ii in 1:n_sims ) {
       setWinProgressBar(pb,ii,paste(ii,"of",n_sims))
       set.seed(ii)
       while ( TRUE ) {    # make sure we even have the minority 
                           # class
           predictors_train <- matrix(
             runif(nn_train*(length(true_coefficients) - 1)), 
                 nrow=nn_train)
           logit_train <- 
            cbind(1, predictors_train)%*%true_coefficients
           probability_train <- 1/(1+exp(-logit_train))
           outcome_train <- factor(runif(nn_train) <= 
                    probability_train)
           if ( sum(incidence_train[ii] <- 
              sum(outcome_train==TRUE))>0 ) break
       }
       dataset_train <- data.frame(outcome=outcome_train, 
                         predictors_train)
       
       index <- c(which(outcome_train==TRUE),  
         sample(which(outcome_train==FALSE),   
               sum(outcome_train==TRUE)))
       
       model_logistic <- glm(outcome~., dataset_train, 
                   family="binomial")
       model_logistic_oversampled <- glm(outcome~., 
             dataset_train[index, ], family="binomial")
       
       model_logistic_coefficients[ii, ] <- 
              coefficients(model_logistic)
       model_logistic_oversampled_coefficients[ii, ] <- 
         coefficients(model_logistic_oversampled)
       
       model_randomForest <- randomForest(outcome~., dataset_train)
       model_randomForest_oversampled <- 
         randomForest(outcome~., dataset_train, subset=index)
       
       predictors_test <- matrix(runif(nn_test * 
           (length(true_coefficients) - 1)), nrow=nn_test)
       logit_test <- cbind(1, predictors_test)%*%true_coefficients
       probability_test <- 1/(1+exp(-logit_test))
       outcome_test <- factor(runif(nn_test)<=probability_test)
       dataset_test <- data.frame(outcome=outcome_test, 
                        predictors_test)
   
       prediction_logistic <- predict(model_logistic, dataset_test, 
                                       type="response")
       brier_score_logistic[ii] <- mean((prediction_logistic - 
              (outcome_test==TRUE))^2)
   
       prediction_logistic_oversampled <-      
              predict(model_logistic_oversampled, dataset_test, 
                       type="response")
       brier_score_logistic_oversampled[ii] <- 
         mean((prediction_logistic_oversampled - 
               (outcome_test==TRUE))^2)
       
       prediction_randomForest <- predict(model_randomForest, 
           dataset_test, type="prob")
       brier_score_randomForest[ii] <-
         mean((prediction_randomForest[,2]-(outcome_test==TRUE))^2)
   
       prediction_randomForest_oversampled <-   
                        predict(model_randomForest_oversampled, 
                                 dataset_test, type="prob")
       brier_score_randomForest_oversampled[ii] <- 
         mean((prediction_randomForest_oversampled[, 2] - 
               (outcome_test==TRUE))^2)
   }
   close(pb)
   
   hist(incidence_train, breaks=seq(min(incidence_train)-.5, 
           max(incidence_train) + .5),
     col="lightgray",
     main=paste("Minority class incidence out of", 
                   nn_train,"training samples"), xlab="")
   
   ylim <- range(c(model_logistic_coefficients, 
                  model_logistic_oversampled_coefficients))
   beanplot(data.frame(model_logistic_coefficients),
     what=c(0,1,0,0), col="lightgray", xaxt="n", ylim=ylim,
     main="Logistic regression: estimated coefficients")
   axis(1, at=seq_along(true_coefficients),
     c("Intercept", paste("Predictor", 1:(length(true_coefficients) 
            - 1))), las=3)
   points(true_coefficients, pch=23, bg="red")
   
   beanplot(data.frame(model_logistic_oversampled_coefficients),
     what=c(0, 1, 0, 0), col="lightgray", xaxt="n", ylim=ylim,
     main="Logistic regression (oversampled): estimated 
coefficients")
   axis(1, at=seq_along(true_coefficients),
     c("Intercept", paste("Predictor", 1:(length(true_coefficients) 
            - 1))), las=3)
   points(true_coefficients, pch=23, bg="red")
   
   beanplot(data.frame(Raw=brier_score_logistic, 
           Oversampled=brier_score_logistic_oversampled),
     what=c(0,1,0,0), col="lightgray", main="Logistic regression: 
Brier scores")
   beanplot(data.frame(Raw=brier_score_randomForest, 
     Oversampled=brier_score_randomForest_oversampled),
     what=c(0,1,0,0), col="lightgray", 
             main="Random Forest: Brier scores")

我想首先附議問題中的一個聲明:

…我的觀點是,CV 中關於不平衡數據集的問題沒有提到這種權衡,而是將不平衡類視為不言而喻的邪惡,完全不考慮樣本收集的任何成本。

我也有同樣的擔憂,我在這里這裡的問題旨在提出反證據,即這一個“不言而喻的邪惡”,缺乏答案(即使有賞金)表明它不是。許多博客文章和學術論文也沒有說明這一點。分類器可能會遇到數據集不平衡的問題,但僅限於數據集非常小的情況,因此我的回答與例外情況有關,通常不證明對數據集重新採樣是合理的。

一個類不平衡問題,但它不是由不平衡本身引起的,而是因為少數類的例子太少,無法充分描述它的統計分佈。如問題中所述,這意味著參數估計值可能具有高方差,這是真的,但這可能會導致偏向於多數類別(而不是平等地影響兩個類別)。在邏輯回歸的情況下,King 和 Zeng 對此進行了討論,

3加里·金和曾朗徹。2001. “罕見事件數據中的邏輯回歸”。政治分析,9,頁。137–163。https://j.mp/2oSEnmf

[在我的實驗中,我發現有時可能存在偏向少數類的偏見,但這是由於過度擬合造成的,其中類重疊由於隨機抽樣而消失,所以這並不算數和(貝葉斯)正則化應該解決這個問題]

好消息是 MLE 是漸近無偏見的,因此我們可以預期,隨著數據集整體規模的增加,這種針對少數類的偏見會消失,而不管不平衡如何。

由於這是一個估計問題,任何使估計更加困難的事情(例如高維)似乎都可能使類不平衡問題變得更糟。

請注意,概率分類器(例如邏輯回歸)和適當的評分規則不會解決這個問題,因為“流行的統計程序,例如邏輯回歸,可能會嚴重低估罕見事件的概率” 3。這意味著您的概率估計不會得到很好的校準,因此您將不得不調整閾值(相當於重新採樣或重新加權數據)之類的事情。

因此,如果我們查看具有 10,000 個樣本的邏輯回歸模型,我們不應期望看到不平衡問題,因為添加更多數據往往會解決大多數估計問題。

因此,如果您有一個極端的不平衡並且數據集很小(和/或高維等),那麼不平衡可能是有問題的,但是在這種情況下,可能很難做很多事情(因為您沒有足夠的數據估計需要對抽樣進行多大的修正才能糾正偏差)。如果您有大量數據,重新採樣的唯一原因是因為操作類頻率與訓練集中的頻率不同或錯誤分類成本不同等(如果未知或可變,您真的應該使用概率分類器)。

這主要是一個存根,我希望以後能夠添加更多。

引用自:https://stats.stackexchange.com/questions/357466

comments powered by Disqus