【Uplift Modeling】변수의 선택방법에 대하여
해당 포스트는 Zhenyu Zhao at el. "Feature Selection Methods for Uplift Modeling" (2020)의 내용을 기반으로, Uber사에서 개발한 파이썬 오픈소스 패키지 CausalML에 구현된 소스코드들을 다루고 있습다.
* Introduction
Feature Selection 방법은 각 feature에 대해 중요도 점수(importance score)를 계산한 뒤 점수를 바탕으로 랭크를 매긴다. Uplift model은 이렇듯 가장 중요하다고 판단된 변수들만을 가지고 만들어 질 수 있다. Uplift modeling에서 중요한 변수들에게만 집중하는 것은 몇가지의 이득을 가져다 준다.
- 훈련을 위한 빠른 계산처리
- overfitting 문제를 피함으로써 더욱 정확한 예측이 가능
- 데이터 파이프라인의 낮은 유지비용
- 더욱 쉬운 모델 해석과 진단
이렇듯 feature selection은 Uplift modeling에 있어서 중요한 문제임에도 불구하고 지금까지 관련 문헌에서 거의 논의되어오지 못했다. 전통적인 머신러닝에 있어서의 변수선택방법의 연구는 V. Bolon-Canedo et al.(2013), G. Chandrashekar & F. Sahin(2014), J. Tang(2014)과 같은 논문들에서 잘 논의되어있지만, 이것들은 Uplift modeling에서 최적의 변수선택 방법은 아니다.
이 논문에서는 방법론적이고 경험적인 평가 관점에서 변수선택을 다룬다.
* 일반적인 변수선택 방법과의 관계
일반적인 변수선택법의 종류
- filter methods
- wrapped methods
- embedded methods
일반적인 변수선택법이 Uplift modeling에서 최선이 아닌 이유
분류문제를 생각했을때 일반적 변수선택법의 목적은 feature에 기반하여 outcome이 각 클래스에 해당될 확률의 예측하는 것이다. 그러므로 feature의 중요도는 클래스 확률과 관계가 깊다.
반면에 Uplift model의 목적은 CATE를 예측하는 것이다. 그러므로 여기서 좋은 feature는 클래스 확률이 아니라 치료효과를 예측할 수 있게 해주는 것이어야 한다. 이 두가지 예측대상이 항상 일치할 필요는 없으므로 Uplift modeling에서 일반적 변수선택법은 최선이 아닐 수 있다.
* uplift modeling을 위한 변수선택 방법
예시로써 x1 , ... , x63 의 총 63개 feature 가 존재하는 가상의 data를 생각하자.
A. Filter Methods
이 방법은 각 feature에 대하여 치료효과와 feature간의 한계관계(marginal relationship)를 기반으로 중요도 점수(Importance score)를 계산한다. 이것은 한번에 하나의 feature에 대한 간단한 계산만 이루어지므로 빠른 전처리 단계이다.
1. F filter
- Causalml
package에서 implementation
1 | from causalml.feature_selection.filters import FilterSelect |
[output]
- 코드의 동작 원리를 자세하게 살펴보자.
치료여부변수(\(Z\))와 확인하고자하는 feature(\(x_i\)) 그리고 그들의 교호작용항(interaction term)을 사용하여 outcome변수를 예측하는 선형회귀모델이다.
\(y_{outcome} = \beta_0 + \beta_1Z + \beta_2x_i + \beta_3(Z\cdot x_i)\)
중요도 점수(importance score)는 교호작용항 \(\beta_3(Z\cdot x_i)\)의 계수\(\beta_3\)에 대한 F-통계값 으로 정의된다. 이 통계값이 크면 해당 feature는 강한 heterogeneous treatment effect와 상관이 있다는 것을 의미한다.
1 | method = 'F' |
[output]
각 feature 별로 F 통계량을 기준으로 scoring해서 점수가 높은 순으로 내림차순으로 반환한다.
2. LR filter (Likelihood ratio)
- Causalml
package에서 implementation
1 | method = 'LR' |
[output]
여기서는 score를 로지스틱 회귀모형의 교호작용항 계수에 대한 likelihood ratio 검정 통계량으로 정의한다. 각 feature 별 LR 통계량을 기준으로 scoring해서 점수가 높은 순으로 내림차순으로 반환한다.
3. Filter method with K bins
여기에는 Piotr Rzepakowski & Szymon Jaroszewicz (2012)에서 제안된 uplift tree의 분할기준으로 부터 세가지 방법이 존재한다.
3-1. Kullback-Leibler divergence
- Causalml
package에서 implementation
1 | method = 'KL' |
[output]
3-2. the squared Euclidean distance
- Causalml
package에서 implementation
1 | method = 'ED' |
[output]
3-3. the Chi-squared divergence
- Causalml
package에서 implementation
1 | method = 'Chi' |
[output]
- 코드의 동작 원리를 자세하게 살펴보자.
주어진 feature에 대해 이 방법은 먼저 샘플을 feature의 백분위를 기준으로 K개의 bin으로 나눈다. (여기서 K는 하이퍼파라미터) 중요도 점수는 이러한 K개의 bins에 대한 처리효과의 divergence measure로 정의된다.
구체적으로, outcome 변수에 C개의 클래스가 있다고 가정해보자.
\(P_k = (p_{k1},...,p_{kC})\) 와 \(Q_k = (q_{k1},...,q_{kC})\)가 각각 치료군과 대조군의 \(k\)번째(\(k=1,...,K\)) bin에서의 클래스별 sample의 비율이라고 했을 때, 중요도 점수는 이하와 같이 정의된다.
\(\Delta = \sum^K_{k=1}\cfrac{N_k}{N}D(P_k:Q_k)\)
\(N_k\) : \(k\)번째 bin의 샘플사이즈
\(N\) : 전체 샘플 사이즈
\(D\) : distribution divergence
- Kullback-Leibler divergence (denoted as KL )
- the squared Euclidean distance(denoted as ED )
- the chi-squared divergence (denoted as Chi )
- \(KL(P_k:Q_k)=\sum^n_{i=1}p_{ki}log\cfrac{p_{ki}}{q_{ki}}\)
- \(ED(P_k:Q_k)=\sum^n_{i=1}(p_{ki}-q_{ki})^2\)
- \(\chi^2(P_k:Q_k)=\sum^n_{i=1}\cfrac{(p_{ki}-q_{ki})^2}{q_{ki}}\)
[먼저 세개의 함수를 정의] 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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48def _GetNodeSummary(data,
experiment_group_column='treatment_group_key',
y_name='conversion'):
"""
To count the conversions and get the probabilities by treatment groups. This function comes from the uplift tree algorithm, that is used for tree node split evaluation.
Parameters
----------
data : DataFrame
The DataFrame that contains all the data (in the current "node").
Returns
-------
results : dict
Counts of conversions by treatment groups, of the form:
{'control': {0: 10, 1: 8}, 'treatment1': {0: 5, 1: 15}}
nodeSummary: dict
Probability of conversion and group size by treatment groups, of
the form:
{'control': [0.490, 500], 'treatment1': [0.584, 500]}
"""
# Note: results and nodeSummary are both dict with treatment_group_key
# as the key. So we can compute the treatment effect and/or
# divergence easily.
# Counts of conversions by treatment group
results_series = data.groupby([experiment_group_column, y_name]).size()
treatment_group_keys = results_series.index.levels[0].tolist()
y_name_keys = results_series.index.levels[1].tolist()
results = {}
for ti in treatment_group_keys:
results.update({ti: {}})
for ci in y_name_keys:
results[ti].update({ci: results_series[ti, ci]})
# Probability of conversion and group size by treatment group
nodeSummary = {}
for treatment_group_key in results:
n_1 = results[treatment_group_key][1]
n_total = (results[treatment_group_key][1]
+ results[treatment_group_key][0])
y_mean = 1.0 * n_1 / n_total
nodeSummary[treatment_group_key] = [y_mean, n_total]
return results, nodeSummary
1 | # Divergence-related functions, from upliftpy |
1 | def _evaluate_KL(nodeSummary, control_group='control'): |
[Kullback-Leibler divergence를 이용한 Scoring의 과정]
1 | method = 'KL' |
[output]
B. Embedded Methods
이 방법은 uplift model를 훈련시킬 때 나오는 부산물로 변수의 중요성을 얻는다. 이것은 meta-learner과 uplift tree 둘다에서 얻어질 수 있다.
- Meta-learner
feature 중요도는 base-learner로 부터 얻어진다.
예를 들어 Two Model approach 에서는 feature의 중요도점수는 두 base-learner가 산출한 embedding된 중요도 점수의 합으로 정의될 수 있다.
- Uplift tree
feature에 대한 중요도 점수는 Tree에서 Tree node가 분할되는 동안의 손실함수에 대한 누적기여로 정의할 수 있다. 이는 대상이 특별한 분할 기준이 있는 Uplift tree라는 점을 제외하면 일반적으로 잘 알려진 classification tree의 embedded feature 중요도와 유사하다. 각 분할에서 우리는 distribution divergence 의 증가분(gain) 을 계산한다.
\(\Delta = \sum_{k=left,\space right} D(P_k:Q_k)- D(P:Q)\)
(\(P,Q\)는 각각 치료군과 대조군의 Outcome distribution)
feature의 중요도 점수는 해당 feature가 사용된 노드 분할로 부터 발생하는 모든 \(\Delta\)를 더하는 것으로 계산할 수 있다.
이후 생략된 내용 ;
위에서 소개한 변수선택 방법들의 평가 (synthetic data & real-world data), 논문이 실제 적용에서 추천하는 방법 등