class_weight
scikit-learn의 Logistic Regression의 매개 변수가 어떻게 작동하는지 이해하는 데 많은 문제 가 있습니다.
그 상황
로지스틱 회귀를 사용하여 매우 불균형 한 데이터 세트에서 이진 분류를 수행하고 싶습니다. 등급은 0 (음성) 및 1 (양성)으로 표시되며 관찰 된 데이터의 비율은 약 19 : 1이며 대부분의 샘플은 음성 결과를 나타냅니다.
첫 번째 시도 : 수동으로 훈련 데이터 준비
훈련 및 테스트를 위해 보유한 데이터를 분리 된 세트로 분할했습니다 (약 80/20). 그런 다음 훈련 데이터를 무작위로 추출하여 19 : 1과 다른 비율로 훈련 데이터를 얻었습니다. 2 : 1-> 16 : 1.
그런 다음 이러한 다양한 훈련 데이터 하위 집합에 대한 로지스틱 회귀를 훈련하고 다른 훈련 비율의 함수로 재현율 (= TP / (TP + FN))을 플로팅했습니다. 물론 재현율은 관찰 된 비율이 19 : 1 인 분리 된 테스트 샘플에서 계산되었습니다. 다른 훈련 데이터에 대해 다른 모델을 훈련했지만 동일한 (분리 된) 테스트 데이터에서 모든 모델에 대한 재현율을 계산했습니다.
결과는 예상대로였습니다. 리콜은 2 : 1 훈련 비율에서 약 60 % 였고 16 : 1에 도달했을 때 다소 빠르게 감소했습니다. 리콜 률이 5 % 이상인 2 : 1-> 6 : 1 비율이 여러 개있었습니다.
두 번째 시도 : 그리드 검색
다음으로, 나는 다른 정규화 매개 변수를 테스트하고 싶어하고 그래서 GridSearchCV를 사용하여 여러 값의 그리드 만든 C
매개 변수뿐만 아니라 class_weight
매개 변수를. 네거티브 : 포지티브 훈련 샘플의 n : m 비율을 사전 언어로 번역하기 위해 class_weight
다음과 같이 여러 사전을 지정한다고 생각했습니다.
{ 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
나는 또한 포함 None
하고 auto
.
이번에는 결과가 완전히 엉망이었습니다. 내 모든 리콜은를 class_weight
제외한 모든 값에 대해 아주 작게 (<0.05) 나왔습니다 auto
. 따라서 class_weight
사전 설정 방법에 대한 나의 이해 가 잘못 되었다고 가정 할 수 있습니다 . 흥미롭게도 class_weight
그리드 검색에서 ‘auto’ 의 값은 모든의 값에 대해 약 59 % 였는데 C
1 : 1로 균형이 맞다고 생각 했나요?
내 질문
-
class_weight
훈련 데이터에서 실제로 제공하는 것과 다른 균형을 이루기 위해 어떻게 적절하게 사용 합니까? 특히class_weight
네거티브 : 포지티브 훈련 샘플의 n : m 비율을 사용하려면 어떤 사전을 전달해야 합니까? -
다양한
class_weight
사전을 GridSearchCV에 전달하는 경우 교차 검증 중에 사전에 따라 훈련 폴드 데이터의 균형을 재조정하지만 테스트 폴드에서 내 점수 함수를 계산하기 위해 실제 주어진 샘플 비율을 사용합니까? 모든 측정 항목이 관찰 된 비율의 데이터에서 나온 경우에만 유용하기 때문에 이것은 매우 중요합니다. -
비율만큼 의
auto
가치는 무엇입니까class_weight
? 나는 문서를 읽고 “데이터의 빈도에 반비례하여 균형을 잡는다”는 것은 단지 1 : 1로 만든다는 것을 의미한다고 가정합니다. 이 올바른지? 그렇지 않다면 누군가 명확히 할 수 있습니까?
답변
먼저, 리콜 만하는 것은 좋지 않을 수 있습니다. 모든 것을 포지티브 클래스로 분류하여 100 % 리콜을 달성 할 수 있습니다. 일반적으로 AUC를 사용하여 매개 변수를 선택한 다음 관심있는 작동 지점 (예 : 지정된 정밀도 수준)에 대한 임계 값을 찾는 것이 좋습니다.
class_weight
작동 방식 : 1 대신 class[i]
with 샘플의 실수에 페널티 를줍니다 class_weight[i]
. 따라서 클래스 가중치가 높을수록 클래스에 더 중점을두고 싶다는 의미입니다. 당신이 말한 바에 따르면 클래스 0은 클래스 1보다 19 배 더 자주 발생합니다. 따라서 class_weight
클래스 0에 비해 클래스 1 의 값 을 늘려야합니다 . 예를 들어 {0 : .1, 1 : .9}입니다. (가) 경우 class_weight
1의 합계가 1이되지 않습니다, 그것은 기본적으로 정규화 매개 변수를 변경합니다.
class_weight="auto"
작동 방식에 대해서는 이 토론을 참조하십시오 . 개발 버전에서는을 사용할 수 있습니다 class_weight="balanced"
. 이것은 이해하기 더 쉽습니다. 기본적으로 큰 샘플만큼 많은 샘플을 가질 때까지 작은 클래스를 복제하는 것을 의미하지만 암시적인 방식으로합니다.
답변
첫 번째 대답은 작동 방식을 이해하는 데 좋습니다. 하지만 실제로 어떻게 사용해야하는지 이해하고 싶었습니다.
요약
- 노이즈가없는 중간 정도의 불균형 데이터의 경우 클래스 가중치 적용에 큰 차이가 없습니다.
- 노이즈가 있고 불균형이 심한 데이터의 경우 클래스 가중치를 적용하는 것이 좋습니다.
- param
class_weight="balanced"
은 수동으로 최적화하지 않아도 괜찮습니다. - 함께
class_weight="balanced"
하면 더 진정한 이벤트 (높은 TRUE 리콜)을 캡처 할뿐 아니라 거짓 경고를받을 가능성이 더 높습니다 (TRUE 정밀도를 낮출)- 결과적으로 모든 거짓 긍정으로 인해 총 TRUE %가 실제보다 높을 수 있습니다.
- AUC는 잘못된 경보가 문제인 경우 여기에서 잘못 안내 할 수 있습니다.
- 불균형이 심한 경우에도 결정 임계 값을 불균형 %로 변경할 필요가 없습니다. 0.5를 유지하는 것이 좋습니다 (또는 필요에 따라 그 주변 어딘가).
NB
RF 또는 GBM을 사용할 때 결과가 다를 수 있습니다. sklearn에는 class_weight="balanced"
GBM이 없지만 lightgbm 에는LGBMClassifier(is_unbalance=False)
암호
# 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