Scikit-learn'deki class_weight parametresi nasıl çalışır?


116

class_weightScikit-learn'ün Lojistik Regresyonundaki parametrenin nasıl çalıştığını anlamakta çok sorun yaşıyorum.

Durum

Çok dengesiz bir veri kümesi üzerinde ikili sınıflandırma yapmak için lojistik regresyon kullanmak istiyorum. Sınıflar, 0 (negatif) ve 1 (pozitif) olarak etiketlenmiştir ve gözlemlenen veriler, yaklaşık 19: 1 oranında olup, numunelerin çoğu negatif sonuca sahiptir.

İlk Deneme: Eğitim Verilerini Manuel Olarak Hazırlama

Sahip olduğum verileri eğitim ve test için ayrık kümelere ayırdım (yaklaşık 80/20). Daha sonra eğitim verilerini 19: 1'den farklı oranlarda almak için eğitim verilerini elle rastgele örnekledim; 2: 1 -> 16: 1 arasında.

Daha sonra bu farklı eğitim veri alt kümeleri üzerinde lojistik regresyon eğitimi aldım ve farklı eğitim oranlarının bir fonksiyonu olarak geri çağırmayı (= TP / (TP + FN)) çizdim. Elbette, geri çağırma, gözlenen 19: 1 oranlarına sahip ayrık TEST örneklerinde hesaplandı. Not, farklı modelleri farklı eğitim verileri üzerinde eğitmeme rağmen, geri çağırmayı hepsi için aynı (ayrık) test verileri üzerinde hesapladım.

Sonuçlar beklendiği gibiydi: 2: 1 eğitim oranlarında geri çağırma yaklaşık% 60 idi ve 16: 1'e ulaştığında oldukça hızlı düştü. Geri çağırmanın makul bir şekilde% 5'in üzerinde olduğu 2: 1 -> 6: 1 oranlarında birkaç oran vardı.

İkinci Deneme: Grid Search

Sonra, farklı düzenlileştirme parametrelerini test etmek istedim ve bu yüzden GridSearchCV'yi kullandım ve Cparametrenin yanı sıra parametrenin çeşitli değerlerinden bir ızgara yaptım class_weight. Negatif: pozitif eğitim örneklerinin n: m oranlarını sözlük diline çevirmek için, class_weightsadece aşağıdaki gibi birkaç sözlük belirttiğimi düşündüm:

{ 0:0.67, 1:0.33 } #expected 2:1
{ 0:0.75, 1:0.25 } #expected 3:1
{ 0:0.8, 1:0.2 }   #expected 4:1

ve ben de dahil Noneve auto.

Bu sefer sonuçlar tamamen yanlıştı. Tüm hatırlamalarım class_weighthariç her değer için küçük (<0.05) çıktı auto. Bu yüzden sadece class_weightsözlüğün nasıl kurulacağına dair anlayışımın yanlış olduğunu varsayabilirim . İlginç bir şekilde, class_weightızgara aramasında 'auto'nun değeri tüm değerleri için% 59 civarındaydı Cve 1: 1'e denk geldiğini tahmin ettim?

Sorularım

  1. class_weightEğitim verilerinde gerçekte verdiğinizden farklı dengeler elde etmek için doğru şekilde nasıl kullanıyorsunuz ? Spesifik olarak, class_weightnegatif: pozitif eğitim örneklerinin n: m oranlarını kullanmak için hangi sözlüğe geçmeliyim?

  2. class_weightGridSearchCV'ye çeşitli sözlükler iletirseniz, çapraz doğrulama sırasında eğitim katlama verilerini sözlüğe göre yeniden dengeleyecek, ancak test katındaki puanlama işlevimi hesaplamak için verilen gerçek örnek oranlarını kullanacak mı? Bu kritiktir, çünkü herhangi bir metrik sadece gözlemlenen oranlardaki verilerden geliyorsa benim için yararlıdır.

  3. Oranlara autogöre class_weightyapmanın değeri nedir ? Belgeleri okudum ve "veriyi frekansları ile ters orantılı olarak dengelediğini" varsayıyorum sadece 1: 1 anlamına geliyor. Bu doğru mu? Değilse, birisi netleştirebilir mi?


Sınıf_ağırlığı kullanıldığında, kayıp işlevi değiştirilir. Örneğin, çapraz entropi yerine, ağırlaştırılmış çapraz entropi haline gelir. warddatascience.com/…
prashanth

Yanıtlar:


123

Öncelikle, yalnızca hatırlayarak gitmek iyi olmayabilir. Her şeyi pozitif sınıf olarak sınıflandırarak% 100 geri çağırma elde edebilirsiniz. Genelde, parametreleri seçmek için AUC'yi kullanmanızı ve ardından ilgilendiğiniz çalışma noktası için bir eşik bulmanızı (verilen bir kesinlik seviyesi gibi) öneririm.

Nasıl class_weightçalıştığı için: 1 yerine class[i]ile örneklerindeki hataları cezalandırır class_weight[i]. Yani daha yüksek sınıf ağırlığı, bir sınıfa daha fazla önem vermek istediğiniz anlamına gelir. Söylediğinize göre, sınıf 0, sınıf 1'den 19 kat daha sık görünüyor. Dolayısıyla class_weight, sınıf 1'in sınıf 0'a göre artırılması gerekir , örneğin {0: .1, 1: .9}. Eğer class_weight1'e toplamı bir yapmıyor, temelde düzenlilestirme parametreyi değiştirecek.

Nasıl class_weight="auto"çalıştığını öğrenmek için bu tartışmaya bir göz atabilirsiniz . Dev sürümünde class_weight="balanced", anlaşılması daha kolay olanı kullanabilirsiniz : temelde, daha büyük olanla olduğu kadar çok sayıda örneğe sahip olana kadar küçük sınıfı çoğaltmak anlamına gelir, ancak örtük bir şekilde.


1
Teşekkürler! Hızlı soru: Açıklık için hatırlamadan bahsetmiştim ve aslında önlem olarak hangi AUC'yi kullanacağıma karar vermeye çalışıyorum. Anladığım kadarıyla, parametreleri bulmak için ya ROC eğrisi altındaki alanı ya da geri çağırma ve hassasiyet eğrisi altındaki alanı maksimize etmem gerekiyor. Parametreleri bu şekilde seçtikten sonra, eğri boyunca kaydırarak sınıflandırma için eşiği seçtiğime inanıyorum. Kastettiğin bu mu? Öyleyse, amacım mümkün olduğunca çok TP yakalamaksa, iki eğriden hangisine bakmak en mantıklı? Ayrıca, scikit-learn'deki çalışmalarınız ve katkılarınız için teşekkür ederiz !!!
kilgoretrout

1
Bence ROC kullanmanın daha standart bir yol olacağını düşünüyorum, ancak çok büyük bir fark olacağını düşünmüyorum. Yine de eğri üzerindeki noktayı seçmek için bazı kriterlere ihtiyacınız var.
Andreas Mueller

3
@MiNdFrEaK Andrew'un kastettiği, tahmin edenin azınlık sınıfındaki örnekleri kopyalaması, böylece farklı sınıfların örneklemlerinin dengelenmesi olduğunu düşünüyorum. Sadece örtük bir şekilde yüksek hızda örnekleme.
Shawn Tian

8
@MiNdFrEaK ve Shawn Tian: SV tabanlı sınıflandırıcılar yok sen 'dengeli' kullandığınızda daha küçük sınıfların daha fazla örnek üretirler. Küçük sınıflarda yapılan hataları kelimenin tam anlamıyla cezalandırır. Aksini söylemek bir hatadır ve yanıltıcıdır, özellikle daha fazla örnek oluşturmaya gücünüzün yetmediği büyük veri kümelerinde. Bu cevap düzenlenmelidir.
Pablo Rivas

4
scikit-learn.org/dev/glossary.html#term-class-weight Sınıf ağırlıkları, algoritmaya bağlı olarak farklı şekilde kullanılacaktır: doğrusal modeller için (doğrusal SVM veya lojistik regresyon gibi), sınıf ağırlıkları, kayıp fonksiyonunu şu şekilde değiştirecektir: her numunenin kaybını sınıf ağırlığına göre ağırlıklandırma. Ağaç tabanlı algoritmalar için, sınıf ağırlıkları, bölme kriterini yeniden ağırlıklandırmak için kullanılacaktır. Ancak bu yeniden dengelemenin her sınıftaki numunelerin ağırlığını hesaba katmadığını unutmayın.
prashanth

2

İlk cevap, nasıl çalıştığını anlamak için iyidir. Ama pratikte nasıl kullanmam gerektiğini anlamak istedim.

ÖZET

  • Gürültü OLMADAN orta derecede dengesiz veriler için, sınıf ağırlıklarının uygulanmasında çok fazla fark yoktur
  • Gürültülü ve oldukça dengesiz olan orta derecede dengesiz veriler için, sınıf ağırlıklarının uygulanması daha iyidir
  • class_weight="balanced"manuel olarak optimize etmek istemediğinizde param düzgün çalışır
  • ile class_weight="balanced"size daha doğru olayları (daha yüksek DOĞRU hatırlama) yakalamak aynı zamanda yanlış uyarılar için daha olasıdır (DOĞRU hassasiyet düşürmek)
    • sonuç olarak, tüm yanlış pozitifler nedeniyle toplam% DOĞRU gerçek olandan daha yüksek olabilir
    • Yanlış alarmlar bir sorunsa, AUC sizi burada yanlış yönlendirebilir
  • Karar eşiğini dengesizlik yüzdesine değiştirmeye gerek yok, güçlü dengesizlik için bile, 0,5'i (veya neye ihtiyacınız olduğuna bağlı olarak bunun etrafında bir yerde) tutmak için tamam

NB

RF veya GBM kullanıldığında sonuç değişebilir. sklearn yok class_weight="balanced" GBM için değil lightgbm vardırLGBMClassifier(is_unbalance=False)

KOD

# scikit-learn==0.21.3
from sklearn import datasets
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score, classification_report
import numpy as np
import pandas as pd

# case: moderate imbalance
X, y = datasets.make_classification(n_samples=50*15, n_features=5, n_informative=2, n_redundant=0, random_state=1, weights=[0.8]) #,flip_y=0.1,class_sep=0.5)
np.mean(y) # 0.2

LogisticRegression(C=1e9).fit(X,y).predict(X).mean() # 0.184
(LogisticRegression(C=1e9).fit(X,y).predict_proba(X)[:,1]>0.5).mean() # 0.184 => same as first
LogisticRegression(C=1e9,class_weight={0:0.5,1:0.5}).fit(X,y).predict(X).mean() # 0.184 => same as first
LogisticRegression(C=1e9,class_weight={0:2,1:8}).fit(X,y).predict(X).mean() # 0.296 => seems to make things worse?
LogisticRegression(C=1e9,class_weight="balanced").fit(X,y).predict(X).mean() # 0.292 => seems to make things worse?

roc_auc_score(y,LogisticRegression(C=1e9).fit(X,y).predict(X)) # 0.83
roc_auc_score(y,LogisticRegression(C=1e9,class_weight={0:2,1:8}).fit(X,y).predict(X)) # 0.86 => about the same
roc_auc_score(y,LogisticRegression(C=1e9,class_weight="balanced").fit(X,y).predict(X)) # 0.86 => about the same

# case: strong imbalance
X, y = datasets.make_classification(n_samples=50*15, n_features=5, n_informative=2, n_redundant=0, random_state=1, weights=[0.95])
np.mean(y) # 0.06

LogisticRegression(C=1e9).fit(X,y).predict(X).mean() # 0.02
(LogisticRegression(C=1e9).fit(X,y).predict_proba(X)[:,1]>0.5).mean() # 0.02 => same as first
LogisticRegression(C=1e9,class_weight={0:0.5,1:0.5}).fit(X,y).predict(X).mean() # 0.02 => same as first
LogisticRegression(C=1e9,class_weight={0:1,1:20}).fit(X,y).predict(X).mean() # 0.25 => huh??
LogisticRegression(C=1e9,class_weight="balanced").fit(X,y).predict(X).mean() # 0.22 => huh??
(LogisticRegression(C=1e9,class_weight="balanced").fit(X,y).predict_proba(X)[:,1]>0.5).mean() # same as last

roc_auc_score(y,LogisticRegression(C=1e9).fit(X,y).predict(X)) # 0.64
roc_auc_score(y,LogisticRegression(C=1e9,class_weight={0:1,1:20}).fit(X,y).predict(X)) # 0.84 => much better
roc_auc_score(y,LogisticRegression(C=1e9,class_weight="balanced").fit(X,y).predict(X)) # 0.85 => similar to manual
roc_auc_score(y,(LogisticRegression(C=1e9,class_weight="balanced").fit(X,y).predict_proba(X)[:,1]>0.5).astype(int)) # same as last

print(classification_report(y,LogisticRegression(C=1e9).fit(X,y).predict(X)))
pd.crosstab(y,LogisticRegression(C=1e9).fit(X,y).predict(X),margins=True)
pd.crosstab(y,LogisticRegression(C=1e9).fit(X,y).predict(X),margins=True,normalize='index') # few prediced TRUE with only 28% TRUE recall and 86% TRUE precision so 6%*28%~=2%

print(classification_report(y,LogisticRegression(C=1e9,class_weight="balanced").fit(X,y).predict(X)))
pd.crosstab(y,LogisticRegression(C=1e9,class_weight="balanced").fit(X,y).predict(X),margins=True)
pd.crosstab(y,LogisticRegression(C=1e9,class_weight="balanced").fit(X,y).predict(X),margins=True,normalize='index') # 88% TRUE recall but also lot of false positives with only 23% TRUE precision, making total predicted % TRUE > actual % TRUE
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.