機器學習-變數篩選之IV值和WOE
1、IV值的用途
IV,即資訊價值(Information Value),也稱資訊量。
目前還只是在對LR建模時用到過這兩個關鍵指標,當我們使用決策樹時可以透過限制樹的深度間接篩選掉一些對於當前任務貢獻比較小的變數, 而LR則是給多少變數就使用多少變數(L2正則下)。通常我們為了保證模型的有效性以及資料的全面性,在特徵工程中會盡可能多的提供特徵變數,包括衍生變數,這些衍生變數不會全部進入模型進行訓練,否則模型會因為過多相關變數顯得不穩定,也會增加運算的複雜性。那麼應該如何選擇什麼變數可以進入模型呢?一般來說有主要根據5點:
1)變數的預測能力(貢獻值)
2)變數的魯棒性(穩定性)
3)變數的可解釋性
4)變數的可行性
5)變數的相關性
IV值就是用來
衡量變數預測能力
的,IV值越大,表示該變數的預測能力越強。類似的指標還有資訊增益、基尼係數等。
2、IV值的計算
為了介紹IV的計算方法,我們首先需要認識和理解另一個概念——WOE,因為IV的計算是以WOE為基礎的。
2.1 WOE的概念及計算
WOE,即證據權重(Weight of Evidence),WOE 是對原始自變數的一種編碼形式。要對一個變數進行 WOE 編碼,需要首先把這個變數進行分組處理(也叫離散化、分箱),分組後,對於第i組,WOE的計算公式如下:
是該組中響應客戶在該組中的比例;
是該組中未響應客戶在該組中的比例;
是該組中響應客戶資料量;
是該組中該組中未響應客戶資料量;
是該組中響應客戶總資料量;
是該組中未響應客戶總資料量。響應客戶指正樣本,未響應客戶指負樣本。
以上公式表示,WOE實際上是“當前分組中響應客戶佔所有響應客戶的比例”和“當前分組中沒有響應的客戶佔所有沒有響應的客戶的比例”的差異。
對這個公式做一個簡單變換,可以得到:
變換以後含義更明顯,可以理解為
當前組中正負樣本的比值,與所有樣本中正負樣本比值的差異
。這個差異是用這兩個比值的比值,再取對數來表示的。差異越大,WOE越大,這個分組裡的樣本響應的可能性就越大,差異越小,WOE越小,這個分組裡的樣本響應的可能性就越小。WOE可能為負,但其絕對值越大,對於分類貢獻越大。當分箱中正負的比例等於隨機(大盤)正負樣本的比值時,說明這個分箱沒有預測能力,即WOE=0。
2.2 IV值的計算
對於一個分組後的變數,第i組的WOE已經介紹過,同樣,對於分組i,也會有一個對應的IV值,計算公式如下:
IV值在WOE的基礎上保證了結果非負。
根據變數在各分組上的IV值,得到整個變數的IV值為:
為變數分組個數
WOE可能為負值,IV值不可能為負,根據IV值選擇變數後,用WOE替換變數各分組的值進入模型。
3、如何根據IV值評估變數預測能力
一般我們選擇 IV值大於0.02的那些變數進入模型。 如果IV值大於 0.5 ,改變數就是屬於過預測變數,通常被選座分群變數,將樣本拆分成多個群體,針對不同的群體分別開發模型。
4、關於IV和WOE的進一步思考
4.1 為什麼用IV而不是直接用WOE
從上面的內容來看,變數各分組的WOE和IV都隱含著這個分組對目標變數的預測能力這樣的意義。那我們為什麼不直接用WOE相加或者絕對值相加作為衡量一個變數整體預測能力的指標呢?
IV和WOE的差別在於IV在WOE基礎上乘以的那個(pyi-pni),我們暫且用pyn來代表這個值。
第一個原因,當我們衡量一個變數的預測能力時,我們所使用的指標值不應該是負數。從這個角度講,乘以pyn這個係數,保證了變數每個分組的結果都是非負數,可以驗證一下,當一個分組的WOE是正數時,pyn也是正數,當一個分組的WOE是負數時,pyn也是負數,而當一個分組的WOE=0時,pyn也是0。當然,上面的原因不是最主要的,因為其實WOE的絕對值也可以完全避免負數的出現。
更主要的原因,也就是第二個原因是,乘以pyn後,體現出了變數當前分組中個體的數量佔整體個體數量的比例,對變數預測能力的影響。即當各分組樣本分佈不均勻時對其預測能力的影響。
假設一個營銷響應模型中,有變數A,其取值只有兩個:0,1,資料如下:
從上表可以看出,當變數A取值1時,其響應比例達到了90%,非常的高,但是我們能否說變數A的預測能力非常強呢?不能。為什麼呢?原因就在於,A取1時,響應比例雖然很高,但這個分組的客戶數太少了,佔的比例太低了。雖然,如果一個客戶在A這個變數上取1,那他有90%的響應可能性,但是一個客戶變數A取1的可能性本身就非常的低。所以,對於樣本整體來說,變數的預測能力並沒有那麼強。我們分別看一下變數各分組和整體的WOE,IV。
從這個表我們可以看到,變數取1時,響應比達到90%,對應的WOE很高,但對應的IV卻很低,原因就在於IV在WOE的前面乘以了一個係數,而這個係數很好的考慮了這個分組中樣本佔整體樣本的比例,比例越低,這個分組對變數整體預測能力的貢獻越低。相反,如果直接用WOE的絕對值加和,會得到一個很高的指標,這是不合理的。
4.2 IV的其他情況以及處理方式
1)如果IV很小不一定是變數的預測能力很差,如4。1所述,如果某個變數飽和度很低,或者分組區分明顯的組別所佔比例很小,那麼 IV 也會響應較小
解決方法:嘗試重新對變數分組後檢視IV值
2)當樣本中為0正例或0負例時,時 IV 值是+∞,這兩個極端都是沒有意義的,不能與正常 iV進行比較;
解決方法:
1。如果可能,直接把這個分組做成一個規則,作為模型的前置條件或補充條件;2。重新對變數進行離散化或分組,使每個分組的響應比例都不為0且不為100%,尤其是當一個分組個體數很小時(比如小於100個),強烈建議這樣做,因為本身把一個分組個體數弄得很小就不是太合理。3。如果上面兩種方法都無法使用,建議人工把該分組的響應數和非響應的數量進行一定的調整。如果響應數原本為0,可以人工調整響應數為1,如果非響應數原本為0,可以人工調整非響應數為1。
3)每個變數分組內響應和未響應的比例與樣本整體的響應和未響應的比例差異越大,iV 值越大,當其相等時IV值為0;
4) 除非分組規則改變,組間的先後排序和目標變數的對調都不會影響IV 值;
5) WOE可以給變數的每個分組進行賦值,賦值過後的邏輯迴歸模型係數應該全部為正數,這是因為 WOE 值已經考慮了該組的正負貢獻。
5、python程式碼
def
bin_woe
(
tar
,
var
,
n
=
None
,
cat
=
None
):
“”“
連續自變數分箱,woe,iv變換
tar:target目標變數
var:進行woe,iv轉換的自變數
n:分組數量
”“”
total_bad
=
tar
。
sum
()
total_good
=
tar
。
count
()
-
total_bad
totalRate
=
total_good
/
total_bad
if
cat
==
‘s’
:
msheet
=
pd
。
DataFrame
({
tar
。
name
:
tar
,
var
。
name
:
var
,
‘var_bins’
:
pd
。
qcut
(
var
,
n
,
duplicates
=
‘drop’
)})
grouped
=
msheet
。
groupby
([
‘var_bins’
])
elif
(
cat
==
‘d’
)
and
(
n
is
None
):
msheet
=
pd
。
DataFrame
({
tar
。
name
:
tar
,
var
。
name
:
var
})
grouped
=
msheet
。
groupby
([
var
。
name
])
groupBad
=
grouped
。
sum
()[
tar
。
name
]
groupTotal
=
grouped
。
count
()[
tar
。
name
]
groupGood
=
groupTotal
-
groupBad
groupRate
=
groupGood
/
groupBad
groupBadRate
=
groupBad
/
groupTotal
groupGoodRate
=
groupGood
/
groupTotal
woe
=
np
。
log
(
groupRate
/
totalRate
)
iv
=
np
。
sum
((
groupGood
/
total_good
-
groupBad
/
total_bad
)
*
woe
)
if
cat
==
‘s’
:
new_var
,
cut
=
pd
。
qcut
(
var
,
n
,
duplicates
=
‘drop’
,
retbins
=
True
,
labels
=
woe
。
tolist
())
elif
cat
==
‘d’
:
dictmap
=
{}
for
x
in
woe
。
index
:
dictmap
[
x
]
=
woe
[
x
]
new_var
,
cut
=
var
。
map
(
dictmap
),
woe
。
index
return
woe
。
tolist
(),
iv
,
cut
,
new_var
# 確定變數型別,連續變數還是離散變數
dvar
=
[
‘Revol’
,
‘DebtRatio’
,
‘Num30-59late’
,
‘Num60-89late’
,
‘Num90late’
,
‘AllNumlate’
,
‘Withdepend’
,
‘Numestate’
,
‘Numdepend’
]
svar
=
[
‘MonthlyIncome’
,
‘age’
,
‘Monthlypayment’
,
‘Numopen’
]
# 視覺化woe得分和iv得分
def
woe_vs
(
data
):
cutdict
=
{}
ivdict
=
{}
woe_dict
=
{}
woe_var
=
pd
。
DataFrame
()
for
var
in
data
。
columns
:
if
var
in
dvar
:
woe
,
iv
,
cut
,
new
=
bin_woe
(
data
[
‘Isdlq’
],
data
[
var
],
cat
=
‘d’
)
woe_dict
[
var
]
=
woe
woe_var
[
var
]
=
new
ivdict
[
var
]
=
iv
cutdict
[
var
]
=
cut
elif
var
in
svar
:
woe
,
iv
,
cut
,
new
=
bin_woe
(
data
[
‘Isdlq’
],
data
[
var
],
n
=
5
,
cat
=
‘s’
)
woe_dict
[
var
]
=
woe
woe_var
[
var
]
=
new
ivdict
[
var
]
=
iv
cutdict
[
var
]
=
cut
ivdict
=
sorted
(
ivdict
。
items
(),
key
=
lambda
x
:
x
[
1
],
reverse
=
False
)
iv_vs
=
pd
。
DataFrame
([
x
[
1
]
for
x
in
ivdict
],
index
=
[
x
[
0
]
for
x
in
ivdict
],
columns
=
[
‘IV’
])
ax
=
iv_vs
。
plot
(
kind
=
‘barh’
,
figsize
=
(
12
,
12
),
title
=
‘Feature IV’
,
fontsize
=
10
,
width
=
0。8
,
color
=
‘#00688B’
)
ax
。
set_ylabel
(
‘Features’
)
ax
。
set_xlabel
(
‘IV of Features’
)
return
ivdict
,
woe_var
,
woe_dict
,
cutdict
# woe轉化
ivinfo
,
woe_data
,
woe_dict
,
cut_dict
=
woe_vs
(
data_train
)
from
sklearn。model_selection
import
train_test_split
IV_info
=
[
‘Num60-89late’
,
‘Num90late’
,
‘AllNumlate’
,
‘Revol’
,
‘age’
]
X
=
woe_data
[
IV_info
]
y
=
data_train
[
‘Isdlq’
]
X_train
,
X_test
,
y_train
,
y_test
=
train_test_split
(
X
,
y
,
random_state
=
42
)
#Logistic模型建立
from
sklearn。linear_model
import
LogisticRegression
model
=
LogisticRegression
(
random_state
=
0
,
solver
=
“sag”
,
penalty
=
“l2”
,
class_weight
=
“balanced”
,
C
=
1。0
,
max_iter
=
500
)
model
。
fit
(
X_train
,
y_train
)
model_proba
=
model
。
predict_proba
(
X_test
)
#predict_proba返回的結果是一個數組,包含兩個元素,第一個元素是標籤為0的機率值,第二個元素是標籤為1的機率值
model_score
=
model_proba
[:,
1
]