Dự đoán lòng trung thành của khách hàng với Phân loại nhị phân XGBoost (XGBoost Binary Classification)
Trong ba phần trước, chúng ta đã khám phá ra việc theo dõi các chỉ số cần thiết, phân khúc khách hàng - RFM và dự đoán giá trị vòng đời của khách hàng.
Trong bài viết này, chúng ta sẽ tìm hiểu về tỷ lệ giữ chân người dùng hay còn gọi là Tỷ lệ duy trì (Retention Rate) là một trong những chỉ số quan trọng nhất.
Tỷ lệ giữ chân người dùng là một dấu hiệu cho thấy sản phẩm phù hợp với nhu cầu của thị trường (product market fit) như thế nào. Nếu PMF (product market fit) thấp, bạn sẽ thấy khách hàng đang bỏ mình đi dần.
Một trong những công cụ mạnh để cải thiện tỷ lệ giữ chân người dùng là Churn Prediction. Bằng cách sử dụng kỹ thuật này, bạn có thể tìm ra ai có khả năng rời đi trong khoảng thời gian nhất định.
Trong bài viết này, chúng ta sẽ sử dụng tập dữ liệu Telco và thực hiện các bước sau để phát triển mô hình Churn Prediction:
- Phân tích dữ liệu thăm dò (Exploratory data analysis)
- Feature engineering
- Tìm cách các tính năng ảnh hưởng đến tỷ lệ giữ chân người dùng bằng cách sử dụng Hồi quy logistic
- Xây dựng mô hình phân loại với XGBoost
Phân tích dữ liệu thăm dò (Exploratory data analysis)
Chúng ta bắt đầu kiểm tra dữ liệu của mình và trực quan hóa, cách nó tương tác với nhãn (có bị xáo trộn hay không?). Hãy bắt đầu với việc nhập dữ liệu và print mười hàng đầu tiên:
df_data = pd.read_csv('churn_data.csv')
df_data.head(10)
Output:
Cách tốt hơn để xem tất cả các cột và kiểu dữ liệu là sử dụng phương thức .info():
Có vẻ dữ liệu của chúng ta thuộc hai loại:
- Các đặc trưng phân loại: gender (giới tính), streaming tv (truyền hình trực tuyến), payment method (phương thức thanh toán)...
- Các đặc trưng số: tenure (thời gian chiếm hữu), monthly charges (phí hàng tháng), total charges (tổng phí)...
Bắt đầu từ những đặc trưng phân loại, chúng ta sẽ làm sáng tỏ và xem các đặc trưng này có hữu ích trong việc xác định khách hàng rời đi hay không.
Lưu ý: trong tập dữ liệu của chúng ta, cột Churn là chuỗi có các giá trị Có/Không. Chúng ta chuyển Có/Không thành số nguyên để dễ sử dụng hơn trong phân tích của mình.
df_data.loc[df_data.Churn=='No','Churn'] = 0
df_data.loc[df_data.Churn=='Yes','Churn'] = 1
Giới tính (Gender)
Bằng cách sử dụng khối lệnh bên dưới, chúng ta dễ dàng hình dung tỷ lệ Churn (tỷ lệ khách hàng rời bỏ) (1-Retention Rate) trông như thế nào đối với mỗi giá trị:
df_plot = df_data.groupby('gender').Churn.mean().reset_index()
plot_data = [
go.Bar(
x=df_plot['gender'],
y=df_plot['Churn'],
width = [0.5, 0.5],
marker=dict(
color=['green', 'blue'])
)
]
plot_layout = go.Layout(
xaxis={"type": "category"},
yaxis={"title": "Churn Rate"},
title='Gender',
plot_bgcolor = 'rgb(243,243,243)',
paper_bgcolor = 'rgb(243,243,243)',
)
fig = go.Figure(data=plot_data, layout=plot_layout)
pyoff.iplot(fig)
Output:
Phân tích giới tính đối với tỷ lệ churn:
Khách hàng nữ có khả năng rời đi cao hơn khách hàng nam, nhưng sự khác biệt này rất nhỏ (~ 0,8%).
Hãy lặp lại điều này đối với tất cả các cột phân loại. Để không lặp lại những gì đã làm với gender, bạn có thể tìm thấy code cho tất cả ở phía bên dưới:
Bây giờ chúng ta đi qua các đặc trưng có sự khác biệt đáng kể giữa các giá trị:
Dịch vụ Internet
Biểu đồ này cho thấy những khách hàng sử dụng dịch vụ Internet cáp quang (Fiber optic) có nhiều khả năng rời đi nhất.
Chúng ta vẫn thường hy vọng tỷ lệ rời đi của khách hàng khi sử dụng cáp quang sẽ thấp hơn. Bởi vì họ sử dụng dịch vụ cao cấp hơn. Nhưng điều này vẫn xảy ra do giá cao, cạnh tranh, dịch vụ chăm sóc khách hàng cùng những lý do khác.
Hợp đồng (Contract)
Như dự kiến, hợp đồng ngắn hạn hơn có nghĩa là tỷ lệ churn cao hơn.
Hỗ trợ ky thuật (Tech Support)
Khách hàng không được hỗ trợ kỹ thuật thường có khả năng rời đi cao hơn (chênh lệch ~ 25%).
Phương thức thanh toán (Payment Method)
Việc thanh toán tự động khiến khách hàng có nhiều khả năng ở lại hơn (chênh lệch ~ 30%).
Các đặc trưng khác
Dưới đây là những biểu đồ của các đặc trưng khác, bạn có thể tham khảo.
Chúng ta đã hoàn thành các đặc trưng phân loại. Tiếp theo, hãy xem các đặc trưng số trông như thế nào:
Tenure (thời gian chiếm hữu)
Để xem xu hướng giữa Tenure và Tỷ lệ Churn trung bình, hãy xây dựng một biểu đồ phân tán:
df_plot = df_data.groupby('tenure').Churn.mean().reset_index()
plot_data = [
go.Scatter(
x=df_plot['tenure'],
y=df_plot['Churn'],
mode='markers',
name='Low',
marker= dict(size= 7,
line= dict(width=1),
color= 'blue',
opacity= 0.8
),
)
]
plot_layout = go.Layout(
yaxis= {'title': "Churn Rate"},
xaxis= {'title': "Tenure"},
title='Tenure based Churn rate',
plot_bgcolor = "rgb(243,243,243)",
paper_bgcolor = "rgb(243,243,243)",
)
fig = go.Figure(data=plot_data, layout=plot_layout)
pyoff.iplot(fig)
Rõ ràng tenure cao hơn có nghĩa là Tỷ lệ Churn thấp hơn. Chúng ta sẽ áp dụng như vậy cho Monthly và Total Charges:
Output:
Thật không may, không có xu hướng giữa Tỷ lệ Churn và Monthly & Total Charges.
Feature Engineering
Trong phần này, chúng ta sẽ chuyển đổi các dữ liệu thô để trích xuất thêm thông tin từ chúng. Chiến lược của chúng ta như sau:
1- Nhóm các cột số bằng cách sử dụng kỹ thuật phân tổ (Clustering techniques)
2- Áp dụng Label Encoder cho các đặc trưng phân loại là nhị phân
3- Áp dụng get_dummies() cho các đặc trưng phân loại có nhiều giá trị
Cột số (Numerical Columns)
Như chúng ta đã biết từ phần EDA. Chúng ta có ba cột số:
- Tenure (Thời gian chiếm hữu)
- Monthly Charges (Phí hàng tháng)
- Total Charges (Tổng chi phí)
Chúng ta áp dụng các bước sau để tạo nhóm:
1. Sử dụng phương pháp Elbow để xác định số lượng cụm thích hợp
2. Áp dụng logic K-mean cho cột đã chọn và thay đổi cách đặt tên
3. Quan sát profile của các cụm
Hãy kiểm tra xem các bước này hoạt động như thế nào đối với Tenure trong thực tế:
Cluster profiles:
Chúng ta có 3 cụm với thời gian trung bình là 7,5, 33,9 và 63
Tỷ lệ Churn đối với mỗi cụm:
Đây là cách nó trông như thế nào sau khi áp dụng giống nhau cho Monthly & Total Charges:
Monthly Charge:
Total Charge:
Cột phân loại
Label Encoder chuyển đổi các cột phân loại thành số bằng cách gán các số nguyên cho các giá trị riêng biệt. Ví dụ: cột giới tính có hai giá trị: Nữ & Nam. Label encoder sẽ chuyển đổi nó thành 1 và 0.
Phương thức get_dummies() tạo các cột mới từ các cột phân loại bằng cách gán 0 & 1
Hãy xem cả hai trong đoạn code dưới đây:
#import Label Encoder
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
dummy_columns = [] #array for multiple value columns
for column in df_data.columns:
if df_data[column].dtype == object and column != 'customerID':
if df_data[column].nunique() == 2:
#apply Label Encoder for binary ones
df_data[column] = le.fit_transform(df_data[column])
else:
dummy_columns.append(column)
#apply get dummies for selected columns
df_data = pd.get_dummies(data = df_data,columns = dummy_columns)
Kiểm tra dữ liệu trông như thế nào đối với các cột đã chọn:
Như bạn thấy, các cột gender & Partner trở thành những cột số và chúng ta có ba cột mới của TenureCluster.
Đã đến lúc phù hợp với mô hình hồi quy logistic và trích xuất insights để đưa ra quyết định kinh doanh tốt hơn.
Hồi quy logistic (Logistic Regression)
Dự đoán churn là một bài toán phân loại nhị phân. Khách hàng rời bỏ hoặc ở lại trong một khoảng thời gian nhất định.
Hồi quy logistic là một mô hình mạnh mẽ và cung cấp các kết quả có thể hiểu được. Như chúng ta đã làm, hãy sắp xếp các bước cần thực hiện để xây dựng mô hình Hồi quy logistic:
1. Chuẩn bị dữ liệu (đầu vào cho mô hình)
2. Điều chỉnh và xem tóm tắt mô hình
Dưới đây là bản tóm tắt.
Chúng ta có hai kết quả quan trọng từ báo cáo này. Khi bạn chuẩn bị một mô hình Dự đoán Churn, bạn sẽ phải đối mặt với những câu hỏi dưới đây:
- Điểm nào khiến khách hàng rời đi và ở lại?
- Những điều quan trọng nhất là gì? Chúng ta nên tập trung vào điều gì?
Đối với câu hỏi đầu tiên, bạn nên nhìn vào cột thứ 4 (P>|z|). Nếu giá trị P tuyệt đối nhỏ hơn 0,05, nghĩa là tính năng đó ảnh hưởng đến Churn theo cách có ý nghĩa thống kê. Ví dụ như:
- SeniorCitizen
- InternetService_DSL
- OnlineSecurity_NO
Sau đó là câu hỏi thứ hai. Chúng ta muốn giảm Tỷ lệ Churn thì nên bắt đầu từ đâu? Câu hỏi này theo góc nhìn khoa học có nghĩa là:
Tính năng nào sẽ mang lại ROI tốt nhất nếu tôi tăng/giảm một đơn vị?
Câu hỏi này có thể được trả lời bằng cách nhìn vào cột coef. Hệ số mũ giúp chúng ta biết sự thay đổi dự kiến trong Tỷ lệ Churn nếu thay đổi một đơn vị. Áp dụng dòng code bên dưới, chúng ta sẽ thấy phiên bản chuyển đổi của tất cả các hệ số:
np.exp(res.params)
Ví dụ: thay đổi một đơn vị trong Monthly Charge nghĩa là cải thiện ~ 3,4% tỷ lệ chênh lệch nếu những thứ khác không đổi. Từ bảng trên, chúng ta có thể xác định được đặc trưng nào quan trọng hơn.
Hiện tại, mọi thứ đã sẵn sàng đối với việc xây dựng mô hình phân loại.
Mô hình phân loại nhị phân với XGBoost
Để XGBoost phù hợp với dữ liệu, chúng ta nên chuẩn bị các bộ đặc trưng (X) và nhãn (y) và thực hiện phân tách đào tạo & kiểm tra.
#create feature set and labels
X = df_data.drop(['Churn','customerID'],axis=1)
y = df_data.Churn
#train and test split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.05, random_state=56)
#building the model & printing the score
xgb_model = xgb.XGBClassifier(max_depth=5, learning_rate=0.08, objective= 'binary:logistic',n_jobs=-1).fit(X_train, y_train)
print('Accuracy of XGB classifier on training set: {:.2f}'
.format(xgb_model.score(X_train, y_train)))
print('Accuracy of XGB classifier on test set: {:.2f}'
.format(xgb_model.score(X_test[X_train.columns], y_test)))
Nhờ sử dụng mô hình đơn giản này, chúng ta đạt được độ chính xác 81%:
Tỷ lệ Churn thực tế trong tập dữ liệu là 26,5% (phản ánh 73,5% đối với hiệu suất mô hình). Điều này cho thấy mô hình của chúng ta là một mô hình hữu ích. Tốt hơn nên kiểm tra mô hình phân loại để biết mô hình bị lỗi ở đâu.
y_pred = xgb_model.predict(X_test)
print(classification_report(y_test, y_pred))
Chúng ta có thể giải thích báo cáo ở trên như thể mô hình cho chúng ta biết, 100 khách hàng sẽ ngừng hoạt động, 67 trong số đó sẽ ngừng hoạt động (0.67 precision).
Và trên thực tế, có khoảng 220 khách hàng sẽ bỏ cuộc (0.45 recall). Đặc biệt, recall là vấn đề chính và chúng ta có thể cải thiện hiệu suất tổng thể của mô hình bằng cách:
- Thêm nhiều dữ liệu hơn (chúng ta có khoảng 2 nghìn hàng cho ví dụ này)
- Thêm nhiều đặc trưng hơn
- Feature engineering khác
- Thử các mô hình khác
- Điều chỉnh siêu tham số (Hyper-parameter)
Trong tương lai, hãy xem mô hình của chúng ta hoạt động chi tiết như thế nào. Trước hết, chúng ta muốn biết mô hình sử dụng đặc trưng nào từ tập dữ liệu. Ngoài ra, cái nào là quan trọng nhất?
Để giải quyết câu hỏi này, chúng ta có thể sử dụng đoạn mã dưới đây:
from xgboost import plot_importance
fig, ax = plt.subplots(figsize=(10,8))
plot_importance(xgb_model, ax=ax)
Từ mô hình, chúng ta có thể thấy tầm quan trọng của TotalCharges và MonthlyCharges hơn so với các đặc trưng khác.
Cuối cùng, cách tốt nhất để sử dụng mô hình này là chỉ định Xác suất Churn cho từng khách hàng, tạo phân khúc và xây dựng chiến lược. Để có được xác suất churn từ mô hình, hãy sử dụng đoạn mã bên dưới:
df_data['proba'] = xgb_model.predict_proba(df_data[X_train.columns])[:,1]
Tập dữ liệu của chúng ta trông giống như bên dưới:
Bài viết được dịch từ đây.
Kieu Hoa
Khi mình yêu cuộc đời, cuộc đời cũng sẽ yêu mình đắm say
Bài viết liên quan
Database (Cơ sở dữ liệu) là gì? Những loại Database phổ biến nhất hiện nay
Sep 01, 2024 • 11 min read
Python là gì? Những đặc điểm nổi bật và Ứng dụng của Python
Aug 28, 2024 • 14 min read
Ứng dụng Hypothesis Testing - Kiểm định giả thuyết trong Y học
Jul 18, 2024 • 8 min read
Google Colab là gì? Hướng dẫn sử dụng Google Colab cho người mới
Jul 02, 2024 • 10 min read
Hướng dẫn cách lấy dữ liệu Facebook Ads Tự động Mỗi ngày Miễn phí - Phần 2
Jun 24, 2024 • 6 min read
Hướng dẫn cách lấy dữ liệu Facebook Ads Tự động Mỗi ngày Miễn phí- Phần 1
Jun 24, 2024 • 11 min read