상세 컨텐츠

본문 제목

The RED : 우버처럼 하는 머신러닝 의사결정 by 정종빈 (ML 예측 및 인과관계)

일을 하고

by emje 2023. 4. 1. 00:45

본문

ML 예측과 인과관계 분석: 머신러닝 예측과 인과관계 분석
✔️ 3.1 예제 소개 및 강의 개요

 

[예제 소개]

'배송료 무료' 쿠폰 이벤트
*우와 많이 쓰는 거다...!

- 온라인 쇼핑몰 '구빵' DS (Data Scientist)로 취직한 당신의 첫 프로젝트
- 작년 대히트였던 '배송료 무료 쿠폰' 이벤트 시즌 2 진행

- 작년에는 비상장 기업이었어서 무작위로 쿠폰을 뿌렸던 데에 반해, 상장한 올해는 좀 더 스마트하게 쿠폰을 뿌리고자 하는 경영진
- 어떻게 쿠폰 지급을 하고 이벤트를 진행해야 할까?

**오 갑자기.. 손익과 관련된 진짜 커머스에서 쓸 법한 사례가...!

 

[2부 강의 목적 및 개요]

선수학습: 기본적인 ML 코딩 경험 (Phyton의 Sklearn Pipeline 활용 등)
목적
✅ 보편적인 'ML 모델을 활용한 의사결정 접근 방식' 소개

❌ 특정 ML 모델/알고리즘 소개

개요
예측 모형의 보편적인 고려사항 숙지
불가피한 '인가관계분석'의 난제, 접근법, 흔한 함정 탐구

 

✔️ 3.2 [데이터 소개]
- 귀찮아도, 데이터를 꼼꼼히 살펴보고 꼬치꼬치 캐묻는 것이 데이터사이언티스트 업무의 반(이상)
- 데이터는 어디서 왔는지? 로그면 어디서 로깅이 된건지, 설문조사면 질문이 무엇인지 등
- 각 항목의 정확한 정의 및 특이사항은?

*어디서 이 숫자와 문자를 기록했는지, 데이터의 출처, 어떤 식으로 수집되었는지, 타입이 무엇인지 (숫자인지 문자인지, continuous value인지 등) 이외 숫자/나이였을 때 유저가 숫자패드로 입력한 것인지 등 세부 사항 파고들기..!

 

[예제 상황]

- 데이터는 작년 이벤트 후 성과 분석을 위해 당시 DS가 수집

- 작년 이벤트 진행 관련 DS는 모두 상장 후 이직 (없음!)


구빵 영업 디테일
- 고객은 비회원(guest), 일반회원, 연간(유료)회원으로 구매 가능
- 연간 회원(subscriber)는 무조건 배송 무료
- 비회원/일반회원은 일정 금액 이상 구매 시 배송비 무료
- 배송비 무료 금액은 배송지역에 따라 다름 (도서산간/해외 등)

- 해외 배송비는 배송량에 따라 상이

- 배송비가 있고 배송비 무료 쿠폰을 보유한 경우, 자동 적용 후 소멸
*즉 1회만 적용 가능하고, 자동 적용되어 아껴놨다가 나중에 쓸 수는 없음

 

데이터 개요
- 파일명: gooppang.csv
- 당시 행사 대상이었던 일반회원 이상의 세션 정보만 포함
- 각 행(row)는 <한 유저, 한 세션>간 장바구니를 나타냄
- 행사 기간 내 한 유저가 여러 세션으로 기록 가능
- 유저를 구분할 수 있는 정보는 없음
- 유저 관련 개인 정보(나이, 성별 등) 정확하지 않을 수 있음
- (작년) 행사 기간 중 쿠폰은 한 번만 적용, 자동 적용!

 

[데이터 개요] (Column, 10개의 열/칼럼)

age:int
행사 당시 유저 나이
basket: float *소수점자리가 있는 숫자
장바구니 총액(천원)

checkout: boolean *tue/false로 기록
*결제(최종 구매) 여부

region: string
배송지역 코드
coupon: boolean
무료 쿠폰 보유 여부
gender: string
유저 성별 ("m":남성, "f":여성)
monthly_spend:float
행사 당시 월 평균 결제량
shipping_fee:float
배송비
subscriber: boolean
연간 회원 여부
tenure: int
행사 당신 유저 가입 기간 *가입 이후 얼마나 지속된 유저인지 (월)

 

 

 

 

[기본적인 의문점 탐구] (연습문제)
배송비 무료 쿠폰을 지급하는 이벤트

0. 가장 중요한 배송비 (shipping_fee)의 정확한 정의는?
(emje) 회원의 상태/장바구니 총액/배송지역/배송량에 따라 결정되는 금액
- 비회원vs기본회원vs유료회원: 유료회원은 무조건 무료
비회원vs기본회원은 장바구니 총액에 의해 무료
배송지역에 따라 배송비 상이
해외의 경우 배송량에 따라 배송비 상이/무료

1. 배송 지역별로 배송비 무료 금액 적용 전? 후? (emje) 적용 전으로 해야하지 않을까
2. 쿠폰 적용 전? 후? (emje) 쿠폰 적용 전을 기준으로 해야함

3. 연간 회원은 무조건 무료? 아니면 배송비 적용 후"할인"? (emje) 할인으로 처리해야 손해폭을 계산할 수 있을 듯.
4. 장바구니 총액(basket)은 배송비(shipping_fee)포함? (emje) 미포함.


그 외의 다른 의문점은?
앞으로의 분석을 위해 불가피한 가정은?

 

 

✔️ 3.3 연습문제 풀이

import pandas as pd

import seaborn as sns

1df=pd.read.csv('/content/gooppang.csv')

1dft.dtypes
df.describe()

6만개의 행이 있고 나이는 평균 36세, 결제금액은 32천원, 평균 월 소비량은 112 배송료는 16천원(?), 테니어는 7개월

 


1. 배송 지역별로 배송비 무료 금액 적용 전? 후? (emje) 적용 전으로 해야하지 않을까

> X축에는 결제금액을 그리고, Y축에 배송비를 그려서 지역마다 보고 (지역마다 무료되는 시점이 다르니까) 분산도로.

shipping_fee가 basket에 따른 배송비 무료가 적용되지 않는다면 basket이 아무리 커져도 shipping_fee가 0으로 가는 것이 안보임

shipping_fee가 배송료 무료 후의 정보를 담고 있다면, basket이 늘어나면서 쭉 있다가 0으로 훅 떨어질 것

shipping_fee에는 지역별 배송비 무료가 적용된 전일까/후일까

B라는 지역은 100천원을 넘어가면 무료, E라는 지역은 50천원, D지역은 해외로 추정되는데 상품의 무게 등에 따라서 배송료 범위가 다양하다가 200천원이상이 되었을 때 배송비가 무료가 됨

 

2. 쿠폰 적용 전? 후? (emje) 쿠폰 적용 전을 기준으로 해야함

쿠폰을 받고 배송비가 있으면 무조건 자동 적용되므로,

쿠폰 받은 사람 중에서 배송비가 0이  아닌 사람을 찾으면 됨 > 그럼 배송료가 쿠폰 적용 전일 것.
코딩해보면 쿠폰이 있으면서 shipping_fee>0인 사람이 있는지 없는지, 있으면 Ture했을 때
True가 나옴. 즉 이 shipping_gee는 배송비 무료 쿠폰이 적용되기 전의 값

 

 

3. 연간 회원은 무조건 무료? 아니면 배송비 적용 후"할인"? (emje) 할인으로 처리해야 손해폭을 계산할 수 있을 듯.

subscriber중에서 shipping_fee>0인 경우가 있음, 그말인 즉슨 
유료회원은 배송비가 무조건 무료인데,
배송비 무료가 적용되기 전에 원래 배송비가 무엇이었는지 기록되어있다 라는 것을 알 수 있음. 

 

 

 

4. 장바구니 총액(basket)은 배송비(shipping_fee)포함? (emje) 미포함.

basket이라는 변수가 배송비를 포함하는지, 안 하는지?
배송비가 포함이 된거라면 그리고 basket의 규모가 유사하다면
배송비가 포함이 된 경우에 basket의 크기가 평균적으로 더 클 것임
반면에 배송비가 basket에 포함 안되었으면
- 배송비를 실제로 낸 사람은 basket이 더 작을 것

(필터링) 무료쿠폰을 받은 사람과 연간 회원은 실제로 배송료를 내지 않음
그렇지만 데이터에서 shipping_fee의 경우 쿠폰을 가졌을 때도, 연간회원일 때도
할인 받기 전의 shipping_fee가 있다는 것을 알기 때문에 이를 제외하고 해야함 

 

 

df. 

.loc[labmda d:(~d.coupon)&(~d.subscriber)

쿠폰도 안 받고 유료회원도 아닌 사람들
.assign(paid_shipping=lambda d:9d.shipping_fee>0)

shipping을 낸 사람과 안 낸 사람의 평균 basket 크기
.groupy ('paid_shipping')
.agg({'basket':'mean'})


paid_shipping
shipping을 안 낸 사람 47.8
shipping을 낸 사람 31.4
> 아마도 basket에 shippin_fee가 안 들어있다라고 결론을 완전히 내기 어려운 이유도 있음
basket크기와 배송비 간의 상관관계가 잇음, basket이 클수록 shipping_fee를 안 내니까...
basket에 shipping이 들어있어도 위처럼 결과가 나올 수 있음 

 

 

 

✔️ 3.4 예측 모형 기초

 

[개요]

이론: Bias-variance (tradeoff?)
실무: Train, validation(, test)

 

 

[예측 모형의 목적]
- 관측한 정보(데이터)
를 기반으로
- 관측되지 않은 것(미래)
에 대한 '일반화'의 시도

 

[예측 모형의 목적]

- 정보 자체의 불확실성 (random noise)
아무리 많은 정보를 알고 있어도 예측하지 못하는 불확실성이 있음

➡️ 불가피한 오류임, 손쓸 수 없음 받아들여야 함.

 

- 예측 방법으로 인해 발생하는 오류
빈도주의적 '반복시행'의 관점에서 예측모형이 가지는 결점/오류가 있음

➡️ 예측 방법에 따라서 오류의 크기나 방향이 달라질 수 있음

✅ 편향(bias): 예측치가 실제값과 근본적으로 다른 정도
*모형 자체가 어느 쪽으로 비뚤어져서 있어 아무리 써도 실제값과는 다르게 됨 (빈도주의적 입장, 실제값이 존재한다는 가정)

✅ 분산(variance): 예측치가 데이터의 변화에 민감한 정도
*빈도주의적 입장에서 데이터가 랜덤한 것, 데이터가 어떻게 나오냐에 따라서 예측모델이 내뱉는 값이 달라짐.
예측 모형의 분산. 

 

bias, variance, tradeoff가 있다

 

[예측 모형의 오류: 극적인 예]
동전을 던져서 앞면나올 확률 예측
데이터: 같은 동전을 10번 던져 봄
- 예측방법1: 첫번째 던진게 앞면이면 100%, 뒷면이면 0%
*무조건 첫번째 동전만 보고...!
> Bias=0, 빈도주의적 관점에서 반복시행시 앞면이 나올 '확률'로 수렴
10,000개의 평행우주에 걸쳐서 100과 0의 평균을 구하면...
근본적으로 빈도주의 입장의 실제값과는 오차가 있지 않음.

> Variance 높음: 데이터에 따라 100% 혹은 0%
왔다 갔다할 것임, 데이터가 뭐가 나오느냐에 따라서 값의 차이가 큼
그렇지만 수없이 반복하다보면 그 예측치의 평균은 실제값에 수렴함. 

 

- 예측방법2: 무조건 50%
*동전이 앞면이 있고 뒷면이 있는데 무조건 50%지...

> Bias 높음: 실제 앞면이 나올 확률이 50%가 아닐 수 있음
> Variance=0 데이터와 무관하게 예측치 (50%)가 일정

 

 

예측치가 무한히 반복했을 떄 실제값이랑 가까워지냐/안 가까워지냐 Bias

데이터에 얼마나 민감한 예측치냐가 Variance

전통적 통계에서는 unbiased (bias=0) 예측치를 선호

수학적으로
- 다양한 unbiased 예측치를 찾고
- 그 중 Variance를 최소화

 

이제는 Bias, Variance보다 결과적인 예측 성능이 더 중요함
결과적인 예측 성능을 측정하는 실증적 방법의 중요성

실제로 bias, variance를 계산하지는 않더라도 이에 대한 이해가 직관적인 사고에 도움이 됨

 

 

 

 

 

실증적으로 어떻게 측정하나요?

training data test, validation data test, ... 

새로운 데이터에 대한 이야기를 하자.

그런데 새로운 데이터는 관측되지 않았다.

💥문제1: 새로운 데이터는 관측을 안했다

해결책1: 관측한 데이터 중 일부만 사용하고, 일부는 관측 못한 새로운 데이터 처럼 취급

💥문제2: 다양한 모형의 성과를 비교해서 선택했을 때, 측정된 모형의성과가 상향 편향

해결책2: 모델을 선택하기 위한 데이터와 모델의 실제 성과를 측정하기 위한 데이터를 분리 

하나의 예측모형으로 할 때는 괜찮은데 여러 개의 예측모형을 비교해서
그 중 성과가 제일 좋은 걸 고르려고 했을 때, 내가 고른 예측모형의 성과를 알기 어려움.

 

 

관측한'척' 관측한 데이터 + 새로운 '척' 관측한 데이터

성과 평가를 위한 데이터

Cross-validation 등으로 대체하기도 함

 

 

 

✔️ 3.5 예측 모형 실습 1

구글 코랩 사용: numpy, pandas, .... 

*예측 모형이 예측 성과를 높이는 것이 아니라
어떤 예측 모형을 가지고 어떻게 활용할 수 있느냐를 보기 위해서
기본적인, 복잡하지 않은 예측 모형들 사용


✅ 배송비 무료 쿠폰을 받았을 때 구매 확률이 가장 높은 유저를 예측해보기

from sklearn.compose import Columtransformer

from sklearn.pipeline import Pileline


살거냐/말거냐 성과 평가 지표로 roc_auc_score 사용
from sklearn.metric import roc_auc_score

데이터 모형 선택에는
from sklearn.model_selection import train_test_split,GridSearchCV
*데이터 셋을 따로 빼놓는 (train_set_split)
*GridSearch로 파라미터 설치하는 것

성별, 지역과 같은 category 
from sklearn.preprocessing import OneHotEncoder, StandardScaler


구매 가능성 판단
from sklearn.linear_model import LogisticRegression
from xgboost import XGBClassifier

필요한 pkg들을 미리 다 import하고
데이터 불러오기

 

1 traid_df, test_df = train_test_split(df, train_size=0.8, random_state=0)
*랜덤하게 나누는데 할 때마다 똑같이 나눠지게끔 random_state 정해두기

 

모델을 피팅하기 앞서 어떤 피쳐들이 있는지 살펴본다

age, basket, checkout...

먼저 3가지 종류의 피처가 있음

ColumTransformer를 쓸건데, 안에 list로 투플을 써서 변수의 이름,
transform하는 것, 실제 column네임을 쓰게 되어 있음

이심변수 (True/False)는 bin; 바이너리 변수, 있는 그대로 passthrough,
- 이심변수는 쿠폰을 줬을 때의 구매 확률이니까 쿠폰이 변수로 들어가야해서, 'coupon'
- 그리고 유료 회원도 영향을 줄 것 같으므로 'subscriber' 이진변수는 그대로 씀


checkout은 알고 싶은 타겟 변수

cat; categorical feature가 있음 지역, 성별

- OneHotEncoder를 쓰고 'region' 'gender'


나머지는 숫자 num;number, StandardScaler를 쓸거고
- 'age' 'basket' 'monthly_spend' 'tenure'
각 피쳐의 평균을 빼고 표준편차로 나누는 transformation
그렇게 하는 이유는 너무 크고 작은 값의 영향을 줄이고자. 

 

ct; ColumnTransformer 를 만들었음

 

lr_pipe = Pipeline 
*각 단계를 나타내고 마지막에는 estimator가 들어감

처음에는 column transformer가 들어가고 'trans',ct

 데이터가 들어가면 ct를 이용해서 coupon, subscriber는 passthrough 그대로 쓰고
region과 gender는 OneHotEncoder를쓰고 age, basket...는 StandardScaler를 쓰는 것이
파이프라인에서 ct단계

 

그 다음 classified에는 LogisticRegression을 쓰고

파이프라인을 피팅을 했음


roc_auc_score(y_true=train_df.checkout, y_score=lr_pipe.predict_proba(train_df)
두 개의 값이 나옴, 체크아웃이 0일 확률과 1일 확률
우리는 1일 확률이 궁금함
roc_auc_score(y_true=train_df.checkout, y_score=lr_pipe.predict_proba(train_df)[:,1]


 

이건 training data의 roc_auc이고 이제 test data를 통해서...

 

이건 하나의 모형으로 비교를 하는 것이고

보통 여러 개의 모형을 비교하기 위해서는 cross-validation test를 해야함.

 

 

e.g grid

1 lr_grid_params = {
2 'clf__C':np.logspace(-2,2,10)
3 }
리그레스의 단계 이름은 clf였고 언더스코어 두 개 변화시키고 싶은 파라미터는 capital C

 

4 lr_gs=GridSearchCV(lr_pipe, lr_gird_params, scoring='roc_auc, n_jobs=-1)

5 lr_gs=lr_gs.fit(train_df, y=train_df.checkout)

트레인 df를 5개로 나눈 다음에 5개를 돌아가면서 나머지 4개로 모델 fit을 하고 남은 1개에 성과 평가,
각 파라미터에 대해서 5번의 모델 fit을 한 다음 평균을 구하는 식으로 성과 평가를 하는 것

 

1 lr_gs.cv_results

mean_test_score 다섯개에 걸친 평균값이 거의 70불에 가깝게 나왓음
이 중 랭킹이 가장 높았던 것은 6번째 모델/파라미터를 쓴 게 테스트 스코어가 젤 높았음

이 그리드에서 저 모델을 빼오고 싶으면

 

lr_gs.best_estimator_

 

 

이 estimator를 사용해서 preict_prb=oba(test_df)

roc_auc_score(test.df.checkout, lr_gs.best_estimator_.predict_proba(test_df)[:,1])

lr_estimator=lr_gs.best_estimator

 

그래서 쿠폰을 줬을 때 구매자들의 구매 확률이 어케 되고

구매 확률이 높은 순으로 쿠폰을 주자!

 

 

test_with_coupon_df=(

test_df.assign(coupon=True)

쿠폰에 대한 변수를 True로 데이터셋을 바꿈

 

p_checkout_with_coupon = lr_estimator.predict_proba(test_df)[:,1]

 

test_df

.assign(p_checkout_with_coupon=p_checkout...)

 

 

이렇게 확률 높은 애들이 쫘르륵 나왔고 여기서 상위 20%만 뽑아서 쿠폰을 줄건지 등의 의사결정은 차후에..!

 

다른 모형 써서 확인해보기

XGBClassifier



 

✔️ 3.6 예측 모형 실습 2

Classfication 문제, 최종 결제 금액을 예측하는... 

구매율은 올라갈 수 있지만 실제로 더 관심이 있는 건 그래서 결제금액이 어떻게 되느냐 하는 것, 결제금액의 기대치!

두 가지 접근 방법.


✅ #1 데이터에서 관측한 구매금액의 분포가 미래에도 똑같을 거다 라는 가정

구매 확률 예측 모형을 그대로 사용할 수 있음

ColumnTrnasformer

bin, passthrough, ['coupon', 'subscriber']

cat, OneHotEncoder, ['region', 'gender']

num, StandardScaler ['age', 'monthly_spend', 'tenure']

 

결제금액이 어떻게 될까?

이미 관찰한 데이터의 결제금액이 앞으로의 결제금액과 똑같다면 > 기대 결제금액

basket * 쿠폰이 있을 때 결제할 확률

 

 

✅ #2 advanced. 결제 금액도 예측을 해보자
xgboost, XGBRegressor 활용하기

사람들이 쿠폰을 받은 후에 장바구니에 물건을 담기 때문에

쿠폰을 변수로 사용하면 안됨

 

cross-validation해야하지만 시간 관계상 pass

 

expected_basket 변수를 만들어서 이게 가장 큰 사람을 찾자

 

여기까지 예측모형을 코딩하는 실습...을 했읍니다 

예측 모형 코딩은,. 전 안되겠읍니다...

관련글 더보기

댓글 영역