저번 포스팅에 이어, 조금의 전처리를 수행하고 고객 이탈을 예측하는 분류 모델링을 해봅니다.
✔Table of Contents
Tech 44. 예측할 달의 재적 기간을 작성하자
앞선 포스팅에서 탈퇴회원과 지속회원의 데이터를 결합한 pred_data를 만들었습니다. 이 상태로 그대로 쓰기 전에 새로운 시계열 변수 '재적 기간'을 추가해봅니다.
재적기간(period) 변수는 [파이썬 데이터 분석 #4]에서 했던 것과 마찬가지로, 현재 연월에서 가입시기(start_date) 값을 빼서 만듭니다. period의 단위는 월(month) 입니다.
pred_data['period'] = 0 #초기화
pred_data['now_date'] = pd.to_datetime(pred_data['연월'], format = '%Y-%m-%d')
pred_data['start_date'] = pd.to_datetime(pred_data['start_date'])
#두 기간의 차이 계산(단위: month)
for i in range(len(pred_data)):
delta = relativedelta(pred_data['now_date'][i], pred_data['start_date'][i])
pred_data.loc[i, 'period'] = delta.years*12 + delta.months
pred_data.head()
Tech 45. 결측치 처리
다음으로는 결측치를 처리해봅니다. NaN이 있는 상태로는 머신러닝이 불가능하기 때문에, 결측치는 꼭 제거해주어야 합니다.
#결측치 파악
pred_data.isna().sum()
- 먼저, end_date와 exit_date에 있는 2842개의 결측치는 지속회원이기 때문에 있는 결측치입니다. (탈퇴한 적이 없어야 지속회원이므로)
- cnt_1 변수엔 결측치가 없어야 하므로 여기에 있는 252개의 결측치를 처리해줍시다. cnt_1 의 값이 NaN인 경우는 그 행 전체를 drop합니다.
pred_data = pred_data.dropna(subset = ['cnt_1'])
pred_data.isnull().sum()
Tech 46. 문자열 변수 처리하기
이제 pred_data가 거의 완성되었습니다. 남은 것은 문자열 변수인데, 문자열 변수는 무엇일까요?
가입 캠페인 구분, 회원 구분, 성별 등 숫자가 아닌 문자값을 담고 있는 변수를 '문자열 변수(카테고리 변수; Category Variable)' 이라고 합니다.
이러한 카테고리 변수를 머신러닝에 사용하기 위해서는 적절한 가공이 필요합니다. 머신러닝 모델은 숫자만 인식 가능하기 때문에, 문자를 숫자처럼 바꾸기 위해 0과 1의 값을 가진 숫자, 더미 변수(dummy)로 만들어줍니다.
먼저 pred_data 안에 담고 있는 변수가 너무 많아, 모델링에 사용할 변수들만 다음과 같이 추려보았습니다.
- 설명변수: cnt_1, campaign_name, class_name, gender, flag, period
- 종속변수(target): is_deleted
여기서 빨간색으로 색칠한 것이 우리가 전처리해줄 카테고리 변수입니다. 위의 변수들만 따로 뽑아봅니다.
먼저 campaign_name, class_name은 이름이 너무 길기 때문에, 앞에 숫자가 붙은 것을 떼어줍니다.
pred_data.iloc[:,1] = pred_data.iloc[:, 1].str[2:]
pred_data.iloc[:,2] = pred_data.iloc[:, 2].str[2:]
pred_data.head()
이제 빨간색으로 표시한 3개의 카테고리 변수들을 더미변수로 만들어줍니다. 이는 간단하게 pandas의 pd.get_dummies 를 사용하면 만들 수 있습니다.
#이제 이 카테고리 변수들을 더미변수화한다. 해당하는 값이먄 1, 아니면 0의 값을 갖는 변수로 쪼개짐.
pred_data = pd.get_dummies(pred_data)
pred_data.head()
얼핏 보면 잘 된 것처럼 보이지만, 문제점이 있습니다.
예를 들어 gender_F가 1이고, gender_M이 0이라면 이것은 무슨 뜻일까요? 맞습니다. 여성(F) 이란 뜻이죠.
그렇다면 굳이 이렇게 2개의 변수를 만들 필요가 있을까요? 그냥 gender_F 변수만 남기고, 이 변수값이 1이면 여성(F)이고, 0이면 남성(M)이라고 알아볼 수 있을 텐데요!
그래서 어떤 카테고리 변수가 갖고 있는 카테고리 값이 n개라면, 더미 변수는 n-1개만 만들면 됩니다. 위의 사례와 같이, '성별'은 카테고리가 2개 있으니 더미 변수는 1개만 있으면 되는 것처럼요.
만들어진 더미변수들을 하나씩 임의로 삭제해주었습니다.
#여기선 내가 임의로 삭제했음
del pred_data['campaign_name_일반']
del pred_data['class_name_야간']
del pred_data['gender_M']
pred_data.head()
좋아요! 이제 전처리가 모두 끝났습니다. 이제는 직접 고객 이탈 모델을 pred_data를 활용하여 만들어줍니다.
Tech 47. 의사결정나무를 활용해 탈퇴 예측 모델을 만들자.
여기서 활용할 머신러닝 모델은 의사 결정 나무(Decision Tree Classifier) 입니다.
굉장히 간단하면서 인과관계를 파악하기 용이하기 때문에 많이 사용되는 모델입니다. 사이킷런을 활용해 의사결정 나무 모델을 만들어보겠습니다.
다만, pred_data의 탈퇴회원과 지속회원의 비율은 차이가 났습니다. 지속회원의 수가 좀 더 많았었죠.
탈퇴회원과 지속회원의 숫자를 맞춰주기 위해, python sample 함수를 사용하여 지속회원 수를 탈퇴회원 수만큼 랜덤하게 뽑아서 모델링에 활용합니다.
from sklearn.tree import DecisionTreeClassifier
import sklearn.model_selection
exit = pred_data[pred_data['is_deleted']== 1] #탈퇴회원
conti = pred_data[pred_data['is_deleted']==0].sample(len(exit)) #지속회원-> 랜덤하게 탈퇴회원 개수만큼 뽑음
#설명, 종속 변수 설정
X = pd.concat([exit, conti], ignore_index = True) #인덱스 초기화
y = X['is_deleted']
del X['is_deleted']
X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y)
print(len(X_train), len(X_test))
학습셋은 1578개, 검증셋은 526개로 쪼개짐을 확인할 수 있습니다. 이제 모델을 만들어봅시다.
#모델 구축
model = DecisionTreeClassifier(random_state = 0)
model.fit(X_train, y_train)
#예측값
y_pred = model.predict(X_test)
print(y_pred)
기본적인 의사결정나무를 활용해 만든 모델은 model 이며,
검증셋인 X_test에 대해 예측한 것이 'y_pred' 입니다. 이 값을 실제값인 y_test와 얼마나 같은지를 비교해볼까요?
#예측된 값인 y_pred와 정답 데이터 y_test를 비교해 정확도 계산 (얼마나 맞췄을까?)
#두개를 갖고 데이터프레임 생성
result = pd.DataFrame({'actual': y_test, 'pred': y_pred})
result.head()
head만 확인해봤을 때는 두개의 차이가 없어 보이네요. 정확도를 계산해봅시다.
Tech 48. 예측 모델을 평가하고, 모델을 튜닝하자
정확도(accuracy rate)는 actual과 pred가 일치하는 개수가 전체 개수 대비 얼마인지 그 비율입니다.
print((len(result[result['actual'] == result['pred']])/len(result))*100)
계산을 해보니 89.9%로 나쁘지 않은 정확도입니다. 하지만 test data에 대해 예측해봤을 때에도 이 정확도를 유지할 수 있을지는 알 수 없습니다.
이렇게 정확도를 구하지 않고, model의 score 메서드를 사용해 정확도를 계산해도 됩니다.
#이번엔 함수 score를 사용해 정확도 계산
print(model.score(X_train, y_train))
print(model.score(X_test, y_test))
와우! 위의 것이 train set(학습셋)에 대한 정확도, 아래 것이 validatiaon set(검증셋)에 대한 정확도입니다.
그런데 train set에 대한 정확도는 무려 98%로 엄청난 과적합(overfitting)이 발생했네요. 검증셋과 거의 10% 차이가 납니다.
이렇게 과적합(오버피팅; overfitting)이 발생한 경우엔 어떻게 해야 할까요?
-> 데이터를 늘리거나, 변수를 재검토하거나, 모델의 파라미터를 변경하는 등의 방법을 활용해 과적합을 없앤 이상적인 모델을 만들 수 있습니다.
여기서는 모델의 파라미터를 변경해 모델을 수정해봅니다.
의사결정나무에 대해 공부해보신 분들이라면 아시겠지만, 이 모델엔 정말 다양한 파라미터가 있죠. (자세한 것은 사이킷런의 documentation를 확인해 보시는 것도 좋을거 같아요!)
저 또한 통계학 전공 시간에 이 모델을 배웠던 게 기억나는데, 의사결정나무의 과적합을 방지하기 위해선 pruning(가지치기)를 해야 합니다. 의사결정나무는 나무 형태로 조건에 맞춰 뻗어나가는데(그래서 의사결정 '나무' 인거죠!) 이 가지가 너무 많아지다 보면 조건으로 쪼개고 쪼개고 쪼개서 결국 과적합이 발생합니다. 그래서 가지치기를 해주면 좀 더 단순한 모델이 됩니다.
여기서는 의사결정 나무의 파라미터 중 하나인 'max_depth'(트리의 깊이)를 조정해보겠습니다. 이 값을 5로 낮춰 좀 더 단순한 모델로 만들어 볼게요.
#의사결정나무는 과적합을 방지하기 위해 트리 깊이를 얕게 하면 모델을 단순화할 수 있다.
#설명, 종속 변수 설정
X = pd.concat([exit, conti], ignore_index = True) #인덱스 초기화
y = X['is_deleted']
del X['is_deleted']
X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y)
#모델 구축
model = DecisionTreeClassifier(random_state = 0, max_depth = 5) #파라미터 변경
model.fit(X_train, y_train)
#예측값
print(model.score(X_train, y_train))
print(model.score(X_test, y_test))
결과를 보시면 학습셋(train set)에 대한 정확도는 약 92%로 낮아졌지만, 검증셋(validation set)에 대한 정확도가 90%로 높아진 것을 확인할 수 있습니다.
즉, 과적합을 잘 방지한 것으로 볼 수 있습니다. 이외에도 정확도를 더 높이기 위해 여러 튜닝을 거쳐, 더 정교한 모델을 만들 수 있습니다.
Tech 49. 모델에 기여하는 변수를 확인하자.
위에서 만들었던 모델에 대해, 가장 기여도가 높은 변수는 무엇인지를 확인해봅시다.
DecisionTreeClassifier의 feature_importances_ 옵션을 활용해 이를 확인할 수 있습니다. 좀 더 보기 쉽게 데이터프레임 형태로 만들고, 기여도(coefficient) 값이 높은 순서로 정렬해보았습니다.
imp = pd.DataFrame({'feature': X.columns, 'coefficient': model.feature_importances_})
imp = imp.sort_values(by = 'coefficient', ascending= False)
imp
좀 더 보기 쉽게 시각화해 볼게요.
import matplotlib.pyplot as plt
%matplotlib inline
plt.barh(imp['feature'], imp['coefficient'])
plt.show()
그래프에 나타난 ㅁㅁ는 한글 폰트가 깨져서 나타나는 현상입니다 T_T..
아무튼, 가장 영향력이 큰 변수는 재적기간(period) > cnt_1(바로 직전 달 이용횟수) > flag(정기적 이용 여부) 로 나타났으며, 나머지 카테고리 변수는 거의 0에 가까운 미미한 영향력을 나타내고 있습니다.
마지막으로 간단한 예제 데이터를 바탕으로, 이 회원이 탈퇴할 회원(1)인지, 아닌지(0)를 예측해봅시다. 책의 예제 데이터를 활용했습니다.
Tech 50. 회원 탈퇴를 예측하자.
#책의 예제 데이터 (임의 설정)
cnt_1 = 3
flag = 1
period = 10
campaign_name = '입회비무료'
class_name = '종일'
gender = 'M'
#예제 데이터가 들어왔을 때, 데이터를 가공하는 코드
if campaign_name == '입회비반값할인':
campaign_name_list = [1,0]
elif campaign_name == '입회비무료':
campaign_name_list = [0,1]
elif campaign_name == '일반':
campaign_name_list = [0,0]
if class_name == '종일':
class_name_list = [1,0]
elif class_name == '주간':
class_name_list = [0,1]
elif class_name == '야간':
class_name_list = [0,0]
if gender == 'F':
gender_list = [1]
elif gender == 'M':
gender_list = [0]
위의 예시처럼, 카테고리 변수의 경우 문자가 들어오면 모델이 인식을 못하기 때문에 위의 코드를 써서 꼭 더미변수로 변형해줘야 합니다.
- campaign_name 에 대해서는 campaign_name_list로,
- class_name은 class_name_list로,
- gender는 gender_list로 말이죠!
이제 예제 데이터를 input list로 만들어 모델에 적용해 예측해봅니다.
input_data = [cnt_1, flag, period]
input_data.extend(campaign_name_list)
input_data.extend(class_name_list)
input_data.extend(gender_list)
input_data
참고로 extend 함수는 list에 사용 가능한 함수로서, 리스트와 리스트를 연결해주는 역할을 합니다. (그냥 append 함수를 사용하면 리스트 내 리스트로 들어가기 때문에)
이제 예측을 해볼까요?
#input_data에 대한 예측 수행
print(model.predict([input_data]))
결과를 보면 1로 나옵니다. 이러한 예제 데이터에 대해선 1(탈퇴할 것이다) 라고 예측했네요.
참고로, 사이킷런의 DecisionTreeClassifier는 predict 외에 predict_proba 메서드도 제공합니다. 이는 예측 확률로서, 0이 나올 확률과 1이 나올 확률 2가지를 리턴해줍니다.
#확률로도 결과를 뽑을수 있다!
print(model.predict_proba([input_data]))
결과를 보면, 0이 나올 확률은 0.06, 1이 나올 확률은 0.94로 나옵니다.
그렇기 때문에 predict를 하면 1이 나오게 되는 것입니다. (이 구분기준(threshold, 임계값)는 0.5를 기준으로 합니다. 다른 옵션을 설정해서 threshold를 변경할 수도 있습니다)
Summary
이렇게 하여 의사결정나무를 활용한 '회원 이탈 예측 모델'을 구축해보았습니다. 책에서의 설명에 따르면, 실제 현장에서는 구축한 모델을 시스템화하여 이용하는 경우가 많다고 합니다.
이런 예측 모델을 만들면, 신속하게 탈퇴가능성이 높은 회원을 자동으로 찾아낼 수 있겠죠. 이렇게 머신러닝을 활용하면 비즈니스 의사 결정, data-driven한 의사 결정에 큰 도움을 줄 수 있습니다.
이번 [파이썬 데이터 분석 #4, #5장] 까지 스포츠 센터(헬스장) 데이터를 활용해 지도 학습 모델링을 해보았습니다. 이를 통해 4장에선 다음 달 이용 횟수를 예측(회귀)하고, 탈퇴할 회원인지의 여부를 예측(분류)해 보았죠!
4, 5장에서 다뤘던 데이터도 그렇지만 실제 실무에서는 모델링은 극히 일부이고, 많은 데이터 가공과 전처리를 하는 것이 선행되어야 합니다. (그리고 전처리하는 데 가장 큰 노력이 들죠,,,)
게다가 전처리를 한번 잘못하면 뒤의 모델링, 그리고 회사의 의사 결정에까지 치명적일 수 있기 때문에 전처리를 할 때는 검수, 또 검수가 필요합니다. (정말 꼼꼼해야 합니다.)
이제 다음 [파이썬 데이터 분석 #6장]부터는 좀 더 복잡한 상황의 데이터 분석을 해봅니다. 이제 머신러닝은 여기서 마치고, 최적화 문제(Optimization)를 다뤄볼 것입니다.
새로운 데이터인 물류 데이터를 활용해, 물류의 최적 경로를 탐색하고 최적 리소스 분배를 계산하는 등 재밌는 데이터 분석을 해봅니다. 그럼 다음 포스팅에서 만나요! :)
'Data Science > Analysis Study' 카테고리의 다른 글
파이썬 네트워크 시각화 분석하기(python networkx 예제) / 파이썬 데이터 분석 실무 테크닉 100 (0) | 2022.04.16 |
---|---|
최적화 문제란? (python optimization) / 파이썬 데이터 분석 실무 테크닉 100 (1) | 2022.04.16 |
머신러닝을 활용한 고객 이탈 예측 - 전처리 / 파이썬 데이터 분석 실무 테크닉 100 (6) | 2022.04.12 |
머신러닝을 활용한 고객 이용 횟수 예측 - 모델링 / 파이썬 데이터 분석 실무 테크닉 100 (6) | 2022.04.06 |
머신러닝을 활용한 고객 이용 횟수 예측 - 전처리 / 파이썬 데이터 분석 실무 테크닉 100 (4) | 2022.04.06 |