Bài viết đến từ Nguyễn Việt Cường – Quản lý Cao cấp Khoa học dữ liệu
Data Science team @Techcombank
Giới thiệu
Tỷ lệ khách hàng rời bỏ, hay tỷ lệ khách hàng dừng giao dịch với Techcombank, là một chỉ số quan trọng ảnh hưởng đến sự phát triển và lợi nhuận của ngành ngân hàng. Việc dự đoán chính xác khách hàng rời bỏ cho phép Techcombank xác định trước khách hàng có nguy cơ rời bỏ và áp dụng các biện pháp chủ động để giữ chân họ, từ đó tiết kiệm đáng kể chi phí thu hút khách hàng.
Trong bối cảnh này, việc sử dụng các mô hình học máy cho việc dự đoán khách hàng rời bỏ đã thu hút được sự chú ý đáng kể. Trong bài viết này, chúng tôi sẽ đi vào giải thích chi tiết về phương pháp phát triển một mô hình dự đoán khách hàng rời bỏ cho khách hàng doanh nghiệp. Quy trình này bao gồm việc thu thập dữ liệu, kỹ thuật chế biến đặc trưng, huấn luyện mô hình, phân tích đặc trưng, đánh giá hiệu suất mô hình và kết quả đầu ra.
1. Thu thập dữ liệu
Quá trình thu thập dữ liệu tạo nên nền tảng cho bất kỳ dự án học máy nào. Nó bao gồm việc thu thập dữ liệu cần thiết từ các nguồn khác nhau và chuẩn bị nó cho việc phân tích và xử lý tiếp theo. Trong trường hợp của chúng ta, chúng ta đang thu thập dữ liệu liên quan đến khách hàng doanh nghiệp để dự đoán khách hàng rời bỏ. Dưới đây là một cái nhìn chi tiết về từng bước trong quá trình thu thập dữ liệu.
Thu thập các mã khách hàng (Customer IDs) và trạng thái hoạt động (Active Status)
Bước đầu tiên trong quá trình thu thập dữ liệu là thu thập các định danh duy nhất (Customer ID) và trạng thái hoạt động cho mỗi khách hàng trong mỗi tháng. Trạng thái hoạt động là một trường nhị phân cho biết liệu một khách hàng đã hoạt động (1) hay không hoạt động (0) trong mỗi tháng. Trong bước này, chúng ta truy vấn dữ liệu từ lưu trữ dữ liệu của chúng ta (Data Lake, Data Warehouse).
Sau khi có Customer IDs và trạng thái hoạt động của họ, chúng ta cần tạo một nhãn cho mỗi Customer ID cho mỗi tháng. Nhãn thường đại diện cho việc khách hàng rời bỏ(1) hay không (0), và đây là điều mà mô hình của chúng ta sẽ học để dự đoán.
def filter_active_customer(path=raw_from_bb_path, label_time_col = 'LABEL_TIME', feature_ref_time_col = 'REF_TIME', cusid_col = 'CUSID', label_col = 'LABEL', label_open_date = 'OPEN_DATE'): # Read raw data and label processing label = pd.read_csv(raw_from_bb_path, dtype={cusid_col:'str', label_col:'str'}) label[label_open_date] = label[label_open_date].apply(lambda x: int(x.replace("-",''))) label[label_time_col] = label[label_time_col].apply(lambda x: int(x)) label = label[(label[label_time_col]>=DATE_LIMIT) & (label[label_open_date]<=OPEN_DATE_UPPER_LIMIT)] label[label_col] = label[label_col].astype('str') label[label_col] = label[label_col].apply(lambda x: '0' if x == '1' else '1') label[label_time_col] = label[label_time_col].astype('str') # Filter activate customer label_time_data_valid_list = [] for label_time in tqdm(label.LABEL_TIME.drop_duplicates()): checkback_time = str(timeop.ym_diff_inv(label_time, -INACTIVE_LENGTH)) label_time_data = label[label[label_time_col] == label_time] checkback_time_data = label[(label[label_time_col] == checkback_time) & (label[label_col] == '0')][cusid_col] label_time_data_valid = label_time_data.merge(checkback_time_data, how='inner', on=cusid_col) label_time_data_valid_list.append(label_time_data_valid) label_time_data_valid_full = pd.concat(label_time_data_valid_list, axis=0).reset_index(drop=True) label_time_data_valid_full[feature_ref_time_col] = label_time_data_valid_full[label_time_col].apply(lambda x: timeop.ym_diff_inv(int(x), -FEATURE_LABEL_LAG)) label_time_data_valid_full[feature_ref_time_col] = label_time_data_valid_full[feature_ref_time_col].apply(lambda x: (datetime(int(str(x)[:4]), int(str(x)[4:]), 1) + pd.offsets.MonthEnd(1)).strftime("%Y%m%d")) return label_time_data_valid_full
Thu thập các đặc trưng từ Feature Store
Bước tiếp theo trong quá trình thu thập dữ liệu là thu thập các đặc trưng sẽ được sử dụng để huấn luyện mô hình của chúng ta. Trong trường hợp này, những đặc trưng đó được lưu trữ trong một kho đặc trưng (feature store), một kho chứa tập trung để lưu trữ các đặc trưng đã được tạo. Những đặc trưng này được thu thập cho cả tập dữ liệu huấn luyện và tập dữ liệu dự đoán.
Train dataset
label_time_data_valid_full = filter_active_customer() label_time_data_valid_full['CUSID'] = label_time_data_valid_full['CUSID'].astype('str') label_time_data_valid_full['REF_TIME'] = label_time_data_valid_full['REF_TIME'].astype('str') label_time_data_valid_full = label_time_data_valid_full[label_time_data_valid_full['CUSID']!='10199999'] label_time_data_valid_full = split_dataframe(label_time_data_valid_full, 120000) label_time_data_valid_full.to_csv(clean_label, index=False) # Merge Feature with label spark = get_spark_session() loader = DataLoader(spark=spark) # Get features by chunks os.makedirs('data/raw/feature', exist_ok=True) n_group = pd.unique(label_time_data_valid_full['group']) for i in n_group: print("Merge feature with chunks", i) df_feat = loader.get_bb_features(label_time_data_valid_full[label_time_data_valid_full.group==i]) df_feat.to_parquet('data/raw/feature/bb_feature_re_{}'.format(i), index=False) print("save files to local") files_all = sorted(glob.iglob(os.path.join('data/raw/feature', '*')), key=os.path.getctime, reverse=True) label_feature_full = pd.concat(list(map(pd.read_parquet,files_all))) label_feature_full['REF_TIME'] = label_feature_full['REF_TIME'].str[0:6] # Save to csv label_feature_msme_sme = label_feature_full[label_feature_full.BUSINESS_LINE.isin(['MSME', 'SME'])] label_feature_msme_sme.to_csv(full_dataset_msme_sme) print('exported MSME/SME') label_feature_usme_mm = label_feature_full[label_feature_full.BUSINESS_LINE.isin(['USME', 'MM'])] label_feature_usme_mm.to_csv(full_dataset_usme_mm) print('exported USME/MM') spark.top()
Test dataset
label_pred = pd.read_csv( filepath_or_buffer=label_pred_path, dtype={'CUSID': str, 'LABEL_TIME': str, 'LABEL': int, 'REF_TIME': str, 'BBC_BRANCH':'str'}, ) label_pred.fillna('NONE',inplace=True) label_pred['CUSID'] = label_pred['CUSID'].astype('str') label_pred['REF_TIME'] = label_pred['REF_TIME'].astype('str') label_pred = label_pred[(label_pred['CUSID']!='10199999')] # Load features spark = get_spark_session() loader = DataLoader(spark=spark) os.makedirs('data/raw/feature', exist_ok=True) df_feat = loader.get_bb_features(label_pred) df_feat.to_parquet(pred_data_path, index=False) print("Get features") # Read Parquet and merge with GSM Data df_feat['REF_MONTH'] = df_feat['REF_TIME'].str[0:6] df_feat.to_csv(pred_data_csv_path, index=False) print("Save file to csv") spark.stop()
2. Xây dựng các đặc trưng
Sau khi thu thập được dữ liệu cần thiết, bước tiếp theo là xây dựng các đặc trưng (feature engineering). Xây dựng đặc trưng bao gồm việc chọn lọc những đặc trưng quan trọng nhất sẽ ảnh hưởng đến mô hình dự đoán của chúng ta. Trong mô hình này, chúng ta sẽ sử dụng 166 đặc trưng từ kho đặc trưng của chúng ta. Các đặc trưng này bao gồm nhiều danh mục khác nhau, tất cả đều cung cấp thông tin độc đáo về hành vi khách hàng và thói quen ngân hàng như: hồ sơ khách hàng, hoạt động, giao dịch, CASA (Tài khoản tiết kiệm tiền gửi có kỳ hạn), cho vay, lương, v.v.
Feature example:
'BB_STMT_CREDIT_OTHER_CCY_MEAN_1M', 'BB_STMT_CREDIT_VND_MEAN_1M', 'BB_STMT_DEBIT_OTHER_CCY_MEAN_1M', 'BB_STMT_DEBIT_VND_MEAN_1M', 'BB_STMT_YBUTTOAN_MEAN_1M', 'BB_STMT_REF_DATE_MEAN_1M', 'BB_STMT_ACCOUNTID_MEAN_1M', ……
3. Mô hình huấn luyện
Mô hình huấn luyện là quá trình cốt lõi mà trong đó mô hình học máy của chúng ta học từ dữ liệu. Giai đoạn này bao gồm một số bước quan trọng, bao gồm phân chia dữ liệu, huấn luyện mô hình, tối ưu hóa siêu tham số và đánh giá mô hình.
Phân đoạn dữ liệu
Bước đầu tiên trong quá trình huấn luyện là phân chia dữ liệu thành hai phân đoạn dựa trên kích thước khách hàng: Doanh nghiệp Nhỏ và Vừa (MSME/SME) và Doanh nghiệp Vừa và Lớn (USME/MM). Phân biệt này quan trọng vì các doanh nghiệp có kích thước khác nhau có thể thể hiện hành vi và mô hình churn khác nhau.
# model and lead for USME && MM print(full_dataset_usme_mm) model_usme_mm, oot_test_usme_mm = load_dataset_and_train_xgb(full_dataset_path=full_dataset_usme_mm, exclude=exclude_usme, xgb_params=param_usme_xgb) # model and lead for MSME && SME print(full_dataset_msme_sme) model_msme_sme, oot_test_msme_sme = load_dataset_and_train_xgb(full_dataset_path=full_dataset_msme_sme, exclude=exclude_msme, xgb_params=param_msme_xgb)
Chia dữ liệu thành Test and Training Sets
Tiếp theo, chúng ta chia dữ liệu thành tập huấn luyện (training set) và tập kiểm tra (test set). Tập huấn luyện được sử dụng để đào tạo mô hình của chúng ta, trong khi tập kiểm tra được sử dụng để đánh giá hiệu suất của mô hình trên dữ liệu chưa được nhìn thấy trước đó. Trong trường hợp này, chúng ta sử dụng ba tháng cuối cùng của dữ liệu làm tập kiểm tra, trong khi tất cả các tháng khác tạo thành tập huấn luyện.
# Validation oot_train, oot_test = split_oot_train_test(dataset=full_dataset, cutoff_time=OOT_CUTOFF, save=False, path_to_oot_train=path_to_oot_train, path_to_oot_test=path_to_oot_test) X = oot_train.loc[:, new_chosen_features_name + new_cat_features] y = oot_train[LABEL_COL]
Huấn luyện mô hình và tối ưu hóa siêu tham số (hyperparameter)
Sau đó, chúng ta huấn luyện mô hình sử dụng XGBoost, một thuật toán học máy phổ biến, đặc biệt phù hợp với dữ liệu có cấu trúc. XGBoost hoạt động tốt với một loạt các loại dữ liệu và nổi tiếng về tốc độ và hiệu suất của nó.
Ngoài ra, chúng ta sử dụng gói Optuna để tối ưu hóa siêu tham số. Siêu tham số là các thiết lập có thể điều chỉnh trước quá trình huấn luyện để thay đổi cách mô hình học. Optuna được lựa chọn vì hiệu suất của nó so với các phương pháp tối ưu hóa khác như GridSearch hoặc RandomSearch. Nó hiệu quả hơn vì nó sử dụng chiến lược khám phá thông minh tập trung vào các vùng hứa hẹn trong không gian siêu tham số, thay vì kiểm tra siêu tham số ngẫu nhiên hoặc liên tục tất cả các kết hợp.
param_usme_xgb = {'max_depth':10, 'learning_rate': 0.15431179955504462, 'booster':'gbtree', 'n_estimators':800, 'reg_lambda':0.000294530069940619, 'reg_alpha':3.123577541146153, 'grow_policy':'depthwise', 'sampling_method':'gradient_based', 'tree_method':'gpu_hist', # 'tree_method':'hist', 'max_leaves':987, 'max_bin':250, 'gamma':1.5, 'predictor':'gpu_predictor', # 'predictor':'cpu_predictor', 'eval_metric':'logloss'} param_msme_xgb = {'max_depth':10, 'learning_rate': 0.15431179955504462, 'booster':'gbtree', 'n_estimators':800, 'reg_lambda':0.0003, 'reg_alpha':3.123, 'grow_policy':'depthwise', 'sampling_method':'gradient_based', 'tree_method':'gpu_hist', # 'tree_method':'hist', 'max_leaves':987, 'max_bin':256, 'gamma':1.8, 'predictor':'gpu_predictor', # 'predictor':'cpu_predictor', 'eval_metric':'logloss'} optuna_params = { 'learning_rate': ['loguniform', 0.0001, 1], 'boosting': ['categorical', ['gbdt', 'dart']], 'scale_pos_weight': ['int', 1, 10], 'num_leaves': ['int', 10, 2**8], 'feature_fraction': ['loguniform', 0.1, 1], 'bagging_fraction': ['loguniform', 0.1, 1], 'lambda_l1': ['loguniform', 0.001, 10], 'lambda_l2': ['loguniform', 0.001, 10], 'min_data_in_leaf': ['int', 1, 100], 'num_iterations': ['int', 10, 1000], 'max_depth': ['int', 4, 15] }
Đánh giá mô hình
Sau khi mô hình đã được huấn luyện, chúng ta cần đánh giá xem mô hình đã học từ dữ liệu như thế nào. Điều này được thực hiện bằng cách đánh giá hiệu suất của mô hình trên tập dữ liệu kiểm tra, mà mô hình chưa từng nhìn thấy trong quá trình huấn luyện. Đánh giá này cung cấp một cái nhìn tốt về cách mô hình sẽ hoạt động khi gặp phải dữ liệu mới trong thực tế.
y_pred = model.predict_proba(oot_test.loc[:, new_chosen_features_name + new_cat_features])[:, 1] for thresh in [0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5]: print(f'nThreshold: {thresh}') print(f'Actual: {sum(oot_test[LABEL_COL])}; Predict: {sum(y_pred > thresh)}') model_metrics = dict() model_metrics['roc_auc_score'] = roc_auc_score(oot_test[LABEL_COL], y_pred) model_metrics['f1_score'] = f1_score(oot_test[LABEL_COL], y_pred > thresh) model_metrics['recall_score'] = recall_score(oot_test[LABEL_COL], y_pred > thresh) model_metrics['precision_score'] = precision_score(oot_test[LABEL_COL], y_pred > thresh) model_metrics['average_precision_score'] = average_precision_score(oot_test[LABEL_COL], y_pred) print(pd.Series(model_metrics).to_frame())
Quá trình huấn luyện mô hình là một giai đoạn quan trọng trong việc phát triển mô hình dự đoán churn. Đây là giai đoạn mà mô hình học được các mẫu trong dữ liệu, tối ưu hóa các thông số nội bộ và sẵn sàng để thực hiện dự đoán trên dữ liệu mới. Quá trình này bao gồm một số bước, mỗi bước đóng vai trò quan trọng trong hiệu suất tổng thể của mô hình.
4. Phân tích đặc trưng
Phân tích đặc trưng là một bước quan trọng trong học máy, trong đó chúng ta nhằm hiểu tầm quan trọng và tác động của các đặc trưng khác nhau đối với dự đoán của mô hình. Hiểu rõ những đặc trưng có tác động lớn nhất đến quyết định của mô hình có thể cung cấp thông tin quý giá về dữ liệu cơ bản và vấn đề đang được xử lý.
Trong mô hình dự đoán churn của chúng ta, chúng ta tập trung vào 10 đặc trưng hàng đầu góp phần nhiều nhất vào quyết định của mô hình. Những đặc trưng này có thể khác nhau tùy thuộc vào phân đoạn – USME/MM hoặc MSME/SME.
Đánh giá tầm quan trọng của đặc trưng trong phân đoạn USME/MM
Đối với mô hình USME/MM, 10 đặc trưng hàng đầu đóng góp khoảng 60% cho quyết định đầu ra của mô hình. Trong số đó, 5 đặc trưng hàng đầu (được biểu diễn bằng màu Xanh, Cam, Xanh lá cây, Đỏ và Tím trên biểu đồ) đóng góp một cách đáng kể hơn so với phần còn lại.
Hiểu được sự đóng góp của mỗi đặc trưng có thể giúp chúng ta diễn giải quyết định của mô hình một cách tốt hơn và có thể tiết lộ các mẫu và mối quan hệ có thể chưa rõ ràng trước đó.
Tầm quan trọng của đặc trưng trong phân đoạn MSME/SME
Tương tự, trong mô hình MSME/SME, 10 đặc trưng hàng đầu cũng đóng góp khoảng 60% cho quyết định đầu ra của mô hình. Tuy nhiên, 4 đặc trưng hàng đầu (được biểu diễn bằng màu Xanh, Cam, Xanh lá cây và Đỏ trên biểu đồ) có đóng góp quan trọng hơn so với phần còn lại.
Một điểm quan trọng cần lưu ý là đóng góp của mỗi đặc trưng đã giữ ổn định trong suốt 12 tháng. Điều này cho thấy rằng mô hình của chúng ta ổn định theo thời gian và tầm quan trọng của các đặc trưng không biến đổi đáng kể.
5. Hiệu suất mô hình
Phân tích và diễn giải hiệu suất của mô hình là một phần không thể thiếu trong quá trình học máy. Nó cung cấp thông tin về hiệu quả và độ chính xác của mô hình, giúp chúng ta hiểu được mức độ hoạt động của mô hình và xem xét những điều chỉnh cần thiết để cải thiện độ chính xác và hiệu suất của nó.
Đánh giá mô hình
Sau khi mô hình đã được huấn luyện và tối ưu hóa, chúng ta cần đánh giá hiệu suất của nó. Bước này liên quan đến việc sử dụng các chỉ số hiệu suất khác nhau để đo lường chất lượng dự đoán của mô hình. Đối với một mô hình dự đoán churn, các chỉ số phổ biến bao gồm độ chính xác (precision), độ phủ (recall), F1-score và diện tích dưới đường cong đặc trưng hoạt động của ngưỡng (ROC-AUC).
Performance of model USME/MM
Performance of model MSME/SME
Diễn giải kết quả
Diễn giải kết quả bao gồm hiểu ý nghĩa của các chỉ số này trong ngữ cảnh của mô hình và dữ liệu:
- ROC AUC (diện tích dưới đường cong ROC): là một độ đo hiệu suất cho các vấn đề phân loại ở các ngưỡng khác nhau.
- F1-score: là trung bình có trọng số của độ chính xác (precision) và độ phủ (recall).
- Độ phủ (recall, sensitivity): là tỷ lệ giữa số lượng quan sát dương được dự đoán chính xác và tổng số quan sát trong lớp thực tế.
- Độ chính xác (precision): là tỷ lệ giữa số lượng quan sát dương được dự đoán chính xác và tổng số quan sát dương được dự đoán.
Các giá trị lý tưởng cho các chỉ số này phụ thuộc vào đặc thù cụ thể của vấn đề, nhưng nói chung, giá trị cao hơn cho biểu đồ ROC AUC, F1-score, recall và precision đều chỉ ra hiệu suất tốt hơn của mô hình.
6. Kết quả đầu ra
Giai đoạn kết quả đầu ra là nơi chúng ta chuyển đổi các dự đoán từ mô hình thành những hiểu biết có thể thực hiện được. Ở giai đoạn này, chúng ta lọc và định dạng kết quả sao cho dễ hiểu và có thể thực hiện được.
Áp dụng bộ lọc kinh doanh
Trước khi trình bày kết quả, chúng ta điều chỉnh kết quả đầu ra của mô hình bằng cách áp dụng các bộ lọc theo yêu cầu của doanh nghiệp. Quá trình này giúp chúng ta đảm bảo rằng những khách hàng mà chúng ta tạo ra là có liên quan và có thể thực hiện được.
Một số bộ lọc mà chúng ta áp dụng bao gồm:
- Loại trừ khách hàng phá sản: Có khách hàng đã tuyên bố phá sản và do đó rất không có khả năng tiếp tục kinh doanh với ngân hàng.
- Loại trừ khách hàng chỉ hoạt động vì mục đích thuế: Một số doanh nghiệp chỉ duy trì mức hoạt động tối thiểu cho mục đích thuế và không phải là khách hàng thực sự tham gia.
- Loại trừ khách hàng có hoạt động mùa: Một số doanh nghiệp có hoạt động cao trong mùa nhất định và hoạt động rất thấp trong các mùa khác. Các giai đoạn hoạt động thấp này có thể gây ra các cảnh báo churn sai.
Loại trừ khách hàng có giao dịch hoạt động trong tháng trước: Nếu khách hàng có hoạt động trong tháng trước, có khả năng rằng họ chưa rời bỏ, ngay cả khi hiện tại họ có hoạt động thấp hơn.
Định dạng kết quả cuối cùng
Sau khi áp dụng những bộ lọc này, chúng tôi trình bày kết quả cuối cùng. Các khách hàng tiềm năng được trình bày theo định dạng cụ thể, với mỗi khách hàng được gán một giá trị “TO_BE_EXECUTED”. Khách hàng có giá trị “TO_BE_EXECUTED” bằng 1 được xem là có nguy cơ churn cao và nên được liên hệ để giữ chân.
Giai đoạn này quan trọng để đảm bảo những thông tin được tạo ra bởi mô hình thực tế hữu ích. Bằng cách lọc và định dạng kết quả một cách cẩn thận, chúng tôi có thể đảm bảo rằng đầu ra của mô hình áp dụng trực tiếp cho các chiến lược giữ chân khách hàng của doanh nghiệp.
Tóm lại, việc tạo ra kết quả đầu ra hữu ích từ mô hình churn của chúng tôi liên quan đến việc áp dụng kiến thức và quan điểm kinh doanh bổ sung vào đầu ra mô hình gốc. Điều này đảm bảo rằng kết quả có liên quan, dễ hiểu và có thể thực hiện được cho doanh nghiệp, nâng cao tính ứng dụng của mô hình và hỗ trợ doanh nghiệp trong việc đưa ra quyết định dựa trên dữ liệu để cải thiện việc giữ chân khách hàng.
7. Kết luận
Tóm lại, việc tạo ra một mô hình dự đoán khách hàng rời bỏ chính xác và đáng tin cậy liên quan đến một chuỗi các bước được lập kế hoạch và thực hiện cẩn thận, từ việc thu thập dữ liệu đến kết quả đầu ra. Với phương pháp tiếp cận hệ thống này, Techcombank có thể dự đoán khách hàng có nguy cơ churn tiềm năng một cách chính xác hơn, giúp chúng tôi thực hiện các biện pháp nhắm mục tiêu để cải thiện việc giữ chân khách hàng. Chiến lược dựa trên dữ liệu này không chỉ giúp giữ chân khách hàng có giá trị mà còn đóng góp đáng kể vào sự tăng trưởng bền vững và lợi nhuận của Techcombank. Do đó, mô hình học máy dự đoán churn trở thành một công cụ vô cùng quý giá cho ngân hàng kinh doanh trong kỷ nguyên số.