SQL도 안해보신 분한테 랜덤포레스트를 설명하라구요?

Robin_UPDATA·2023년 5월 7일
1

머신러닝

목록 보기
3/4
post-thumbnail

최근에 받은 피드백에 맞춰서 글쓰기 톤을 바꿔봤습니다 🥳

00. 아니 그게 무슨말이에요 팀장님

내가 어쩌다가 회사에서 갑자기 랜덤 포레스트를 사용하게 되었을까
지난 번 글에서는 결정트리에서 설명드렸습니다. 회사에서는 타겟마케팅을 진행하는 경우가 많고 이 때 브레이즈 등 다양한 마케팅 툴을 사용하게 됩니다. 대 AI 시대에 이제 곧 여러 툴에서 바로 머신러닝을 사용할 수 있게 되겠지만 아직 저희 회사는 그런 상황은 아닙니다. 이런 상황에서 한 가지 질문을 받게 되었습니다

그래서 이런 타겟마케팅을 하면 실제 증분은 얼마나 되는거야? 정말 효과 있는거야?

사실 별로 알고 싶지는 않았습니다. 그렇지만 또 어쩌겠습니까, 해봐야죠. 그래서 제가 확인해 본 여러 타겟 마케팅은 그렇게 효과적이진 않았습니다. 실적이야 어떻게 보느냐에 따라 다르게 볼 수 있겠지만 실험군과 대조군을 비교해봤을 때 큰 실적을 내고 있다고 보기는 어려웠거든요. 그런 이야기는 제가 하지 말았어야 했는데.

이런 문제제기를 한 저에게 새로운 지시가 내려왔습니다. 그럼 타겟마케팅을 더욱 효과가 좋게 만들어봐라. 사실 현업에서는 이런 지시를 해결하는 아주 좋은 해결책이 있습니다. 고객에게 주는 혜택을 늘리면 바로 해결됩니다. 그러나 또 그런 건 대답이 될 수 없습니다. 비용은 줄이고 실적은 늘리는 그런 희귀한 말을 잡아와야 하거든요. 거기다 당장 예산 측정하기도 쉽고 성과도 도출하기 쉬우며 이전에 안해봤고 거기다 멋진 용어도 들어가 있는 유니콘을 잡아와야 하는 상황에 놓였습니다.


위에서는 상당히 불만이 많은 어조로 글을 작성했지만 사실 데이터분석가로서 이런 업무를 하는 건 꽤나 즐거운 일이긴 합니다. 저도 그랬을 겁니다. 사수와 업무를 이해해주는 팀장님이 있었다면 말이죠

그래서 다음과 같은 문제를 해결하게 되었습니다. 타겟마케팅 대상인 고객의 증분효과를 늘리자. 혜택을 늘릴 수 없는 상황에서 더욱 정확하고 고도화된 타겟마케팅, 즉 고객 분류가 필요한 상황이 되었습니다.

01. 어떻게 하면 더욱 자세하게 고객 분류를 할 수 있을까?

지금 문제는 고객의 반응률, 유입률 등을 높이는 게 아닙니다. 저는 실험군과 비교군의 차이, 즉 타겟마케팅 대상의 고객이 정말 타겟마케팅의 결과로 행동이 변화하였는가? 를 보고 싶은 상황입니다. 물론 정말로 어떤 액션이 고객 행동의 변화를 이끌었는가를 보기 위해서는 더욱 자세한 분석방법론(인과관계분석, 더욱 고도화된 a/b test 등)을 응용하면 좋겠지만 일단 시작하는 과정에서 조금은 빠르게 진행할 수 있는 방법을 고민하게 되었습니다. 일단 고객의 구매가능성을 바탕으로 고객을 분류해보고자 했습니다. 해당 타겟마케팅이 없었다면 구매 행동이 없었을 고객인지, 아니면 타겟마케팅이 없었더라도 구매행동을 했을 고객인지를 구분하여 비효율적인 비용 집행을 줄이고자 했고 추가적으로 증분 효과를 더욱 키우고자 함입니다.

따라서 미구매 고객 예측을 하기 위해서 고객 분류를 진행하고자 하였고 이를 하기 위해서 랜덤포레스트 모델을 사용하기로 했습니다. 왜 결정트리를 사용하지 않았을까요? 사실 결정트리 모델이 훨씬 더 직관적이고 설명하기 쉬운 건 사실입니다. 그렇지만 저는 다음과 같은 이유로 랜덤포레스트를 선택했습니다.

  1. 모델성능이 훨씬 뛰어나다

현재 고객 분류를 하는 것은 증분효과를 더욱 극대화하기 위해 진행하는 것이기 때문에 주 목적이 고객 분류를 하는 것이 아니라 해당 분류를 통해서 성과를 늘리는 것이라고 판단했습니다. 즉, 분류는 도구일 뿐이지 목적이 아니기 때문에 일반적으로 모델 성능이 더욱 뛰어난 랜덤포레스트가 더 좋다고 생각했습니다.

  1. 다양한 유형의 데이터를 다뤄야 한다

고객 데이터는 범주형, 연속형 상관없이 정말 다양한 데이터가 존재하고 있습니다. 깔끔하게 정리된 데이터 뿐만 아니라 실제로 모델을 생성할 때는 데이터 전처리에 많은 시간을 들여야하는데 이런 과정에서 랜덤포레스트가 훨씬 모델 성능에 도움이 됩니다.

  1. 모델의 설명력이 별로 중요하지 않다 🤣

네.... 사실 제가 무슨 모델을 사용하든 크게 중요하지 않을 것이라고 생각했습니다. 물론 고객 분류가 도구이기 때문이기도 맞지만 제가 이 업무를 보고하고 설명해야 하는 대상자가 데이터 지식이 거의 없기 때문에 아무리 결정트리가 설명하기에 더 쉽고 직관적이라고 해도 유의미하게 쉽지는 않을 것이라고 생각했습니다. 따라서 설명력이 크게 중요하지 않을 것이라고 생각했기에 랜덤포레스트 모델을 선택하게 되었습니다.

위와 같은 이유들로 랜덤포레스트로 미구매 고객 예측(분류)를 진행하게 되었습니다. 제가 진행하는 업무 방향은 다음과 같습니다.

  1. 랜덤포레스트 모델에 사용할 수 있도록 다양한 형태의 고객 데이터를 수집 및 전처리 과정을 진행한다
  2. 과거 고객 데이터(약 3개월치)를 바탕으로 그 다음 달에 고객이 구매를 했는지 안했는지를 분류하는 랜덤포레스트 분류 모델을 생성한다
  3. 생성한 랜덤포레스트 분류 모델을 최근 3개월 (1월~3월)을 바탕으로 실제 4월에 고객이 구매를 할 것인지, 아닌지를 예측한다
  4. 고객 타겟마케팅을 진행할 때 미구매 고객으로 예측된 고객에게 진행을 하고 실제 증분효과를 확인한다
  5. 증분효과와 위 분류모델을 보고한다

그래서 실제로 어떻게 업무를 진행했는지 아래 자세하게 작성해보도록 하겠습니다.

02. 데이터 수집 및 전처리

위에서도 말했다시피 랜덤 포레스트는 상대적으로 데이터 종류가 다양해도 모델이 잘 수행되는 아주 좋은 모델입니다. 사용할 수 있는 데이터가 다양한 상황에서 랜덤포레스트는 그래도 저에게 위안을 주었습니다. 그렇지만 조금 업무가 줄었을 뿐이지 실제로 이 과정이 전체 과정에서 제일 오래걸리는 과정입니다.

먼저 실제로 어떤 특성을 사용하면 좋을까 하며 추출 가능한 특성들을 나열해나갔습니다. 특정 카테고리 구매 여부, 3개월 연속구매, 2개월 연속구매, 1개월 연속구매, 가입시점, 고객 등급, 수신동의여부, 특정 상품 구매여부 등 다양한 특성을 나열하고 추가적으로 가설을 통해서 생성할 수 있는 특성들을 나열했습니다. 아이 여부, 자동차 소유 여부, 취미, 예상 소득치 등 여러 특성들을 나열하고 이러한 특성에 대한 가설과 이를 실제로 수집할 수 있는지 담당자들에게 문의 및 확인을 해나가면서 해당 특성을 채워나갔습니다.

사실 이 부분을 쓰는게 약간 겁이 납니다. 회사 업무를 이렇게 작성해도 되나.. 싶어서요! 그래서 해당 과정은 블로그에는 대략적으로만 작성하고 포트폴리오에 구체적으로 작성할 예정입니다 😀

이렇게 생성한 고객 특성 테이블은 매달 업데이트를 해야하기 때문에 친절하게 주석을 작성하고 더 이쁘게 작성하여 유지보수하기 더 적합하게 생성했습니다. 혹시나 이 과정을 하고자 하는 분이 이 글을 읽고 계신다면 다시 한 번 더 강조하지만 이 파트가 전체에서 제일 중요한 파트입니다. 생각보다 분류에 도움이 안되는 특성이 많고 친절하게 답변해주지 않는 담당자분들이 더 많으니 이 과정에서 많은 시간을 소요하게 됩니다.

그러나 다행인 건 랜덤 포레스트 모델을 사용하고 있다는 점입니다. 랜덤 포레스트는 이상치에 대한 영향을 크게 받지 않습니다. 각각의 결정트리는 이상치를 제거하거나 대체하는 것이 아니라, 전체 데이터에서 무작위로 샘플링한 부분집합으로 학습하기 때문에, 이상치에 대한 영향력이 줄어들게 됩니다. 또한 랜덤 포레스트는 데이터 스케일링에 대한 영향을 크게 받지 않습니다. 따라서, 데이터를 정규화하거나 표준화하지 않아도 높은 예측 성능을 보일 수 있습니다. 그리고 랜덤 포레스트 모델은 모든 특성을 사용하는 것이 아니라 특성을 선별하여 모델을 학습하는 앙상블 모델이기 때문에 상대적으로 다양한 업무로부터 데이터 분석가를 자유롭게 합니다.


데이터 분석가는 이제 자유예요!
실제로 아래 과정은 위 과정에 비하면 큰 시간이 필요하진 않습니다. 운이 좋다면요.

03. 랜덤 포레스트 모델 생성 및 모델 평가

이제 학습에 사용할 데이터가 준비되었다면 실제 모델을 생성해보도록 하겠습니다. 사용 언어는 python이고 필요한 라이브러리는 다음과 같습니다.


import pyodbc
import pandas as pd
import numpy as np
import pickle
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score

물론 사용한 라이브러리에는 반드시 필요하지는 않은 것들도 많으니 취사선택하여 상황에 맞게 사용하시면 더욱 좋을 듯 합니다.

다음처럼 모델을 생성하는데 이 때 다양한 파라미터가 존재합니다. 간단하게만 해당 파라미터를 설명하자면


model = RandomForestClassifier(n_estimators=1000, max_depth=10, min_samples_split=3, min_samples_leaf=2, max_features='sqrt')

위 코드처럼 n_estimators, max_depth, min_samples_split, min_samples_leaf, max_features 등이 존재합니다. 이러한 파라미터 값들을 수정하면서 모델의 성능을 더욱 개선할 수 있습니다. 물론 모델의 성능을 개선하는데 가장 효과적인 방법은 데이터 수집과 전처리 과정에 더 많은 시간을 투자하는 것입니다.


언제나 진리입니다.

전체적인 코드를 보도록 하겠습니다.


# cat_cols: 범주형 변수들의 리스트입니다.

cat_cols = [여기 범주형 변수들을 넣습니다.]

# transformer: ColumnTransformer를 사용하여 범주형 변수들을 OneHotEncoder로 변환합니다.
# remainder='passthrough'는 나머지 변수들을 변환하지 않고 그대로 유지하도록 설정합니다.

transformer = ColumnTransformer(transformers=[('cat', OneHotEncoder(), cat_cols)], remainder='passthrough')

# data_transformed: ColumnTransformer를 적용하여 데이터를 변환합니다.

data_transformed = transformer.fit_transform(data)

X = data_transformed[:, :-1]
y = data_transformed[:, -1]

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

model = RandomForestClassifier(n_estimators=1000, max_depth=10, min_samples_split=3, min_samples_leaf=2, max_features='sqrt')
model.fit(X_train, y_train)
y_pred = model.predict(X_test)

이렇게 랜덤 포레스트 모델을 생성했다면 이제 모델의 성능을 평가해야 합니다. 다양한 평가방법이 존재하지만 저는 대표적인 아래와 같은 모델 성능 평가 지표를 사용했습니다.


accuracy = accuracy_score(y_test, y_pred)
print("정확도:", accuracy)

precision = precision_score(y_test, y_pred, pos_label='YES')
print("정밀도:", precision)

recall = recall_score(y_test, y_pred, pos_label='YES')
print("재현율:", recall)

f1 = f1_score(y_test, y_pred, pos_label='YES')
print("f1:", f1)

실제 결과는 아래와 같이 나왔습니다.

정확도: 0.9226388888888889
정밀도: 0.9410010983838066
재현율: 0.9707024927160893
f1: 0.955621066050514

이런식으로 오차행렬을 볼 수도 있습니다.

from sklearn.metrics import confusion_matrix

cm = confusion_matrix(y_test, y_pred)


labels = ['NO', 'YES']
fig, ax = plt.subplots()
im = ax.imshow(cm, interpolation='nearest', cmap=plt.cm.Blues)
ax.figure.colorbar(im, ax=ax)
ax.set(xticks=np.arange(cm.shape[1]),
       yticks=np.arange(cm.shape[0]),
       xticklabels=labels, yticklabels=labels,
       title='Confusion Matrix',
       ylabel='True label',
       xlabel='Predicted label')


thresh = cm.max() / 2.
for i in range(cm.shape[0]):
    for j in range(cm.shape[1]):
        ax.text(j, i, format(cm[i, j], 'd'),
                ha="center", va="center",
                color="white" if cm[i, j] > thresh else "black")

plt.show()

모델 성능이 나쁘지 않습니다. 아니 사실 매우 좋습니다. 운이 매우 좋은 상황입니다. 따라서 이제 이 모델을 실제 데이터에 적용해보도록 하겠습니다.

04. 실제 적용과 성과 확인

실제 데이터(1월~3월)를 바탕으로 4월에 고객들이 미구매할 것인지 아닌지를 예측해보도록 하겠습니다. 이 때, 고객을 더욱 세분화하기 위해서 이진분류가 아니라 예측 확률을 바탕으로 스코어를 계산해보도록 하겠습니다.

y_proba = model.predict_proba(new_data_transformed)

위 코드로 y_proba를 계산하면 이진분류가 아니라 해당 값이 레이블에 속할 확률이 나오게 됩니다. 이제 이러한 확률(스코어)를 실제 타겟 마케팅에 적용해보도록 하겠습니다. 이 부분은 자세하게 설명할 순 없지만 이전에 진행했던 타겟마케팅에 해당 미구매 고객 예측 조건만 추가하여 타겟 마케팅을 진행했습니다. 물론 여러 조건이 동시에 적용이 되었을 것이기 때문에 단순 비교는 어렵습니다. 그러나 성과를 대략적으로 비교해보도록 하겠습니다. 이전에는 타겟마케팅으로 인한 상승효과가 약 1%밖에 안되었지만 미구매 고객 예측을 적용 후에는 이러한 상승효과가 20% 까지 상승했습니다. 매우 기쁘네요! 이제 이 성과를 팀장님과 상무님께 보고만 하면 되겠습니다 😀


어쩐지 운수가 좋더라니..

05. 보고 및 개인적인 회고

랜덤 포레스트 모델 평가 지표도 매우 좋고 실제로 이를 타겟 마케팅에 적용해보니 성과도 개선이 되었습니다. 그러나 이러한 성과는 보고 대상자에게는 큰 의미가 없을 수 있습니다. 일단 이 전 과정을 설명하기에는 보고 대상자분들을 보통 시간이 없으며 관심도 없습니다. 따라서 이를 통해서 어떤 성과를 얻었는지만 간략하게 보고하게 됩니다. 그러나 미구매 고객을 분류 및 예측하여 이러한 고객에게만 타겟마케팅을 진행하는 것은 분명 증분, 상승효과의 개선에는 도움이 되지만 전체 반응률은 감소할 수 있습니다. 아니, 감소합니다. 왜냐하면 당연하게도 이전에는 타겟마케팅을 구분없이 진행했다면 이제는 타겟마케팅이 없이는 구매를 하지 않을 고객에게만 타겟 마케팅을 하는 거니까요. 하지만 보고대상자는 보통 여기까지는 고려하지 않습니다.

그럼 어떻게 보고를 해야 효과적으로 이 성과를 전달할 수 있을까요? 개인적으로는 아래와 같은 요건이 필요하다고 생각합니다.

  1. 지속적인 약식 보고

적어도 팀장님에게는 업무를 약식으로라도 보고를 해야한다고 생각합니다. 팀장님께서 커버하는 업무 범위는 매우 넓고 많을 것이므로 제가 하는 모든 업무를 알고 있을 것이라고 생각하면 안됩니다. 따라서 업무를 꾸준하게 보고 및 소통하여 해당 업무에 대한 팀장님의 이해도를 높여야 합니다.

  1. 두괄식, 그러나 논리구조 설명하기

저는 보통 업무를 보고할 때는 모두 두괄식으로 진행합니다. 그렇게 진행하는 것이 해당 보고를 이해하는 데 더욱 도움이 된다고 생각합니다. 그러나 일부 상황에서는 그렇지 않을 수도 있습니다. 이번 업무의 보고는 그 보고 대상자가 배경 지식이 부족할 수 있습니다. 따라서 바로 두괄식으로 설명하면 이해하기가 굉장히 어려울 수 있습니다. 물론 해당 보고에서 사용되는 지식을 설명하는 것도 좋겠지만 현실적으로 어려운 상황에서 왜 이러한 방식으로 이렇게 업무를 진행했는지에 대한 논리적인 흐름을 보고에 추가하는 것도 좋을 수 있습니다.

물론 현실적으로는 위와 같은 과정을 진행하더라도 어려움을 겪을 수 있습니다. 이번에 제가 겪은 가장 큰 어려움은 보고 대상자의 배경 지식 부족이었습니다. 해당 업무가 어느 정도의 리소스가 필요한지, 실제로 어느 정도 시간이 필요한지, 그래서 변화한 성과가 얼마나 좋은 것인지, 그게 그렇게 중요한 것인지에 대해서 설명 및 설득을 해야 하는 상황이 제일 큰 어려움으로 닥쳐왔습니다. 적어도 팀장님께서는 해당 업무에 대해서 같이 이해를 했으면 더욱 좋았겠네요.

결론적으로 저는 실제 모델을 생성하고 적용하는 것보다 보고를 하는 데 더 많은 어려움을 겪었습니다. 사실 이 전 과정을 업무위키에도 저장했지만 해당 위키를 읽으시는 일은 없으시더라구요. 이해없이 그냥 던지시는 피드백이 오히려 업무를 어렵게 만들기는 했습니다. 다음부터는 업무를 잘하는 것을 넘어서 어떻게 보고를 하고 설득을 할 것인지에 대해서도 고민을 하며 업무를 진행해야겠다는 반성을 하게 되었습니다.

실제로 해당 업무는 현재 진행 중에 있습니다. 이제 모델을 모두 생성했고 실제로 적용을 해봤으니 앞으로는 모델을 고도화하거나 해당 경험을 바탕으로 새로운 모델을 고민해보는 업무를 해볼 수 있겠네요. 그리고 벨로그에 이렇게 정리했던 경험을 바탕으로 팀장님에게 더욱 자세히 설명할 수 있는 계기를 마련할 수 있었음 좋겠습니다.


Reference.

파이썬 머신러닝 완벽 가이드 - 권철민 지음(위키북스)

0개의 댓글