반응형
In [1]:
from IPython.core.display import display, HTML
display(HTML("<style>.container {width:90% !important;}</style>"))
Matrix Factorization을 통한 상품 추천¶
1. 개요¶
목적
- 상품 구매 데이터 탐색
- 상품 추천 모델을 만들어 고객 만족도 증대 및 매출 증대
작업 기간
- 2020.12.15. ~ 2020.12.29.
데이터 출처
- 쇼핑몰 주문 데이터
참고
- Collaborative Filtering for Implicit Feedback Datasets
- https://yeomko.tistory.com/5
- https://velog.io/@vvakki_/series/Recommendation-System
- https://medium.com/code-states/%EC%B6%94%EC%B2%9C-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-4e5044960bdd
분석 요약¶
- 상품 구매 패턴
- 800명의 고객 중 상위 10명이 전체 구매개수의 23%를 발생시킴 -> 핵심 고객층
- 155개의 상품 중 상위 10개가 전체 판매개수의 69% 차지 -> 핵심 상품
- 상품 추천 모델을 만들어 고객 만족도 증대 및 매출 증대
- 5개 이상 구매한 고객들에게 적용할 수 있는 모델 구성
- 향후 개선 방안
- 고객들을 세그멘테이션하여 고객 유형별로 적합한 추천 모델 생성 가능
- 5개 미만 구매 고객들에게는 판매순위별로 상품을 추천해주는 것도 좋을 것으로 생각
2. 탐색¶
In [1]:
import pandas as pd
import numpy as np
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
tbl_order = pd.read_csv("order_data.csv", encoding="cp949")
2.1. 데이터 개요¶
칼럼 정보¶
In [2]:
tbl_order.head()
Out[2]:
User: 유저 고유 번호
Product: 상품 고유 번호
cnt: 상품 구매 개수
-> cnt는 왓챠 별점 같은 Explicit Data가 아니라, Implicit Data임을 고려하여 모델 선정 필요
결측치 및 자료형¶
In [3]:
tbl_order.info()
-> 결측치 없음
-> User, Product 자료형을 Category로 변환 필요
2.2. 변수 탐색¶
User¶
In [4]:
len(tbl_order["User"].unique())
Out[4]:
-> 고유유저수 800
Product¶
In [5]:
len(tbl_order["Product"].unique())
Out[5]:
-> 고유상품수 155
cnt¶
In [6]:
tbl_order["cnt"].describe()
Out[6]:
-> cnt를 봤을 때, 상품은 최소 1개부터 최대 92개까지 판매됨
-> 평균 3.5개 판매되고 최소 75% 이상의 데이터는 cnt가 3미만
In [7]:
tbl_order["cnt"].value_counts().plot();
최다 구매자¶
In [8]:
def top10(data, index):
df = data.pivot_table(
index=index,
values="cnt",
aggfunc="sum"
)
df_pivot = df.sort_values(
by="cnt",
ascending=False
).head(10)
return df_pivot
In [9]:
top10(tbl_order, "User")
Out[9]:
In [10]:
tbl_order.pivot_table(
index="User",
values="cnt",
aggfunc="mean",
).mean()
Out[10]:
-> 1인당 평균구매개수는 약 3개이고, 최다 구매자는 311개, 구매개수 2~10위 고객은 약 100 ~ 200개의 상품 구매
In [11]:
print("%.2f"%(top10(tbl_order, "User")["cnt"].sum() / tbl_order["cnt"].sum()))
-> 800명의 고객 중 상위 10명이 전체 구매개수의 23%를 발생시킴
-> 판매 금액까지 살펴봐야겠지만, 이들을 핵심 고객층으로 분류할 수 있음
인기상품¶
In [12]:
top10(tbl_order, "Product")
Out[12]:
In [13]:
tbl_order.pivot_table(
index="Product",
values="cnt",
aggfunc="mean",
).mean()
Out[13]:
-> 개별 상품은 평균적으로 2개 정도 판매되고, 가장 많이 팔린 상품은 1037개, 2 ~ 10위는 100개 이상 판매됨
In [14]:
print("%.2f"%(top10(tbl_order, "Product")["cnt"].sum() / tbl_order["cnt"].sum()))
-> 155개의 상품 중 상위 10개가 전체 판매개수의 69% 차지
-> 상품 가격까지 살펴봐야겠지만, 이 상품들이 핵심 상품으로 보임
3. 전처리¶
In [15]:
# 타입 변환
tbl_order["User"] = tbl_order["User"].astype("category")
tbl_order["Product"] = tbl_order["Product"].astype("category")
In [16]:
# 기간 내 5개 미만 판매 상품, 구매 플레이어 제외 -> 모델 성능에 부정적인 영향
pro_cnt_pivot = tbl_order.pivot_table(
index="Product",
values="cnt",
aggfunc="sum"
)
product_over_5 = pro_cnt_pivot[pro_cnt_pivot["cnt"] > 5].index
user_cnt_pivot = tbl_order.pivot_table(
index="User",
values="cnt",
aggfunc="sum"
)
user_over_5 = user_cnt_pivot[user_cnt_pivot["cnt"] > 5].index
tbl_order = tbl_order.loc[tbl_order["User"].isin(user_over_5)]
tbl_order = tbl_order.loc[tbl_order["Product"].isin(product_over_5)]
# "user x product" matrix 생성
index = "User"
columns = "Product"
pl_pro_mt = pd.DataFrame(
index=tbl_order[index].unique(),
columns=tbl_order[columns].unique(),
)
pl_pro_mt = pl_pro_mt.fillna(0)
for user, product, cnt in tbl_order.to_numpy():
pl_pro_mt.loc[user][product] = cnt
4. 모델링¶
In [17]:
R = pl_pro_mt.values
# 하이퍼 파라미터 설정
r_lambda = 40
nf = 200
alpha = 40
# latent factor matrix 설정
nu = R.shape[0] # num of users
ni = R.shape[1] # num of items
X = np.random.rand(nu, nf) * 0.01
Y = np.random.rand(ni, nf) * 0.01
# 선호도 행렬 P 설정
# Pui = 1 if Rui > 0
# Pui = 0 if Rui = 0
P = np.copy(R)
P[P > 0] = 1
# 신뢰도 행렬 설정
# Cui = 1 + alpha * Rui
# Cui means confidence level of certain rating data
C = 1 + alpha * R
# loss function 설정
# xTy: predict matrix
# Total_loss = (confidence_level * predict loss) + regularization loss
def loss_function(C, P, xTy, X, Y, r_lambda):
predict_error = np.square(P - xTy)
confidence_error = np.sum(C * predict_error)
regularization = r_lambda * (np.sum(np.square(X)) + np.sum(np.square(Y)))
total_loss = confidence_error + regularization
return np.sum(predict_error), confidence_error, regularization, total_loss
# Optimization Function 설정
# X[u] = (yTCuy + lambda*I)^-1yTCuy
# Y[i] = (xTCix + lambda*I)^-1xTCix
# two formula is the same when it changes X to Y and u to i
def optimize_user(X, Y, C, P, nu, nf, r_lambda):
yT = np.transpose(Y)
for u in range(nu):
Cu = np.diag(C[u])
yT_Cu_y = np.matmul(np.matmul(yT, Cu), Y)
lI = np.dot(r_lambda, np.identity(nf))
yT_Cu_pu = np.matmul(np.matmul(yT, Cu), P[u])
X[u] = np.linalg.solve(yT_Cu_y + lI, yT_Cu_pu)
def optimize_item(X, Y, C, P, ni, nf, r_lambda):
xT = np.transpose(X)
for i in range(ni):
Ci = np.diag(C[:, i])
xT_Ci_x = np.matmul(np.matmul(xT, Ci), X)
lI = np.dot(r_lambda, np.identity(nf))
xT_Ci_pi = np.matmul(np.matmul(xT, Ci), P[:, i])
Y[i] = np.linalg.solve(xT_Ci_x + lI, xT_Ci_pi)
5. 학습¶
In [18]:
predict_errors = []
confidence_errors = []
regularization_list = []
total_losses = []
for i in range(11):
if i!=0:
optimize_user(X, Y, C, P, nu, nf, r_lambda)
optimize_item(X, Y, C, P, ni, nf, r_lambda)
predict = np.matmul(X, np.transpose(Y))
predict_error, confidence_error, regularization, total_loss = loss_function(C, P, predict, X, Y, r_lambda)
predict_errors.append(predict_error)
confidence_errors.append(confidence_error)
regularization_list.append(regularization)
total_losses.append(total_loss)
print('----------------step %d----------------' % i)
print("predict error: %f" % predict_error)
print("confidence error: %f" % confidence_error)
print("regularization: %f" % regularization)
print("total loss: %f" % total_loss)
predict = np.matmul(X, np.transpose(Y))
print('final predict')
print([predict])
In [19]:
# visualize training
from matplotlib import pyplot as plt
%matplotlib inline
plt.subplots_adjust(wspace=100.0, hspace=20.0)
fig = plt.figure()
fig.set_figheight(10)
fig.set_figwidth(10)
predict_error_line = fig.add_subplot(2, 2, 1)
confidence_error_line = fig.add_subplot(2, 2, 2)
regularization_error_line = fig.add_subplot(2, 2, 3)
total_loss_line = fig.add_subplot(2, 2, 4)
predict_error_line.set_title("Predict Error")
predict_error_line.plot(predict_errors)
confidence_error_line.set_title("Confidence Error")
confidence_error_line.plot(confidence_errors)
regularization_error_line.set_title("Regularization")
regularization_error_line.plot(regularization_list)
total_loss_line.set_title("Total Loss")
total_loss_line.plot(total_losses)
plt.show()
Out[19]:
Out[19]:
Out[19]:
Out[19]:
Out[19]:
Out[19]:
Out[19]:
Out[19]:
6. 결과 추출¶
In [20]:
# 행열 이름 매핑
output_matrix = pd.DataFrame(predict)
output_matrix.index = pl_pro_mt.index
output_matrix.columns = pl_pro_mt.columns
# df 스택
output_stack = output_matrix.stack().reset_index()
output_stack.columns = ["User", "Product", "preference"]
output_stack = output_stack.sort_values(by=["User", "preference"], ascending=(True, False))
In [21]:
output_stack
Out[21]:
반응형
'데이터 분석' 카테고리의 다른 글
[kaggle]유튜브 인기 동영상 데이터 분석(파이썬) (2) | 2020.02.26 |
---|---|
카톡 분석 : 파이썬 (0) | 2019.11.28 |
알바몬 분석: 알바몬 경기의 공고수는 몇개나 될까 (0) | 2019.08.18 |
공공데이터 활용 - 교통사고 통계 리포트 (0) | 2019.07.18 |