Đánh Giá Mô Hình Học Máy Và Xữ Lý Dữ Liệu Mất Cân Bằng Với SMOTE Và Precision-Recall Bằng Python

lúc 15:27 2 tháng 9, 2026
8 views

Trong các bài toán học máy thực tế như phát hiện giao dịch gian lận tài chính (fraud detection) hay chẩn đoán bệnh hiểm nghèo, hiện tượng mất cân bằng dữ liệu (imbalanced data) xảy ra rất phổ biến. Khi số lượng mẫu thuộc lớp thiểu số (minority class) chiếm tỷ lệ cực nhỏ so với lớp đa số, các mô hình phân loại truyền thống thường dễ bị chệch hướng và thất bại hoàn toàn. Kết hợp thư viện imbalanced-learn và scikit-learn trong Python là giải pháp tối ưu giúp giải quyết triệt để bài toán này.

⚖️ 1. Thách Thức Của Dữ Liệu Mất Cân Bằng Và Sai Lầm Của Accuracy

Khi đối mặt với tập dữ liệu có tỷ lệ chênh lệch lớn (ví dụ 99% dữ liệu là giao dịch bình thường và 1% là gian lận), chỉ số Độ chính xác (Accuracy) trở nên hoàn toàn vô nghĩa. Một mô hình "lười biếng" chỉ cần dự đoán tất cả đều là lớp đa số cũng đạt độ chính xác 99%, nhưng lại bỏ sót hoàn toàn 1% mục tiêu quan trọng cần tìm kiếm.

Thay vì dựa vào Accuracy, các nhà phát triển bắt buộc phải sử dụng các chỉ số đo lường chuyên sâu hơn: Precision (Độ chính xác của các dự đoán dương tính), Recall (Tỷ lệ bao phủ mẫu thực tế), và F1-Score (Trung bình điều hòa giữa Precision và Recall). Đặc biệt, đường cong Precision-Recall Curve cung cấp góc nhìn toàn diện hơn so với ROC-AUC khi dữ liệu bị mất cân bằng nặng nề ở lớp mục tiêu.

🧪 2. Kỹ Thuật SMOTE (Synthetic Minority Over-sampling Technique)

Để cân bằng lại phân phối dữ liệu trước khi đưa vào huấn luyện mô hình, phương pháp SMOTE là một trong những tiêu chuẩn vàng. Thay vì lặp lại đơn thuần các bản ghi cũ của lớp thiểu số (oversampling dễ gây hiện tượng quá khớp - overfitting), SMOTE tạo ra các điểm dữ liệu tổng hợp hoàn toàn mới (synthetic samples) dựa trên không gian véc-tơ lân cận gần nhất (K-nearest neighbors).

Nhờ cơ chế này, mô hình học máy nhận được lượng thông tin phong phú và đa dạng hơn từ lớp thiểu số, giúp cải thiện đáng kể khả năng nhận diện các trường hợp thực tế quan trọng mà không làm mất đi tính tổng quát của toàn bộ hệ thống dữ liệu.

💻 3. Code Demo Python: Cân Bằng Dữ Liệu Với SMOTE Và Đánh Giá

Đoạn mã nguồn Python dưới đây minh họa cách áp dụng SMOTE trên tập huấn luyện để giải quyết mất cân bằng, sau đó đánh giá hiệu suất thông qua các chỉ số chuyên sâu bằng scikit-learn:

Code
import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, precision_recall_curve, auc
from imblearn.over_sampling import SMOTE

def evaluate_imbalanced_model():
    """
    Xử lý dữ liệu mất cân bằng bằng SMOTE và đánh giá mô hình với Precision-Recall.
    """
    # 1. Tạo tập dữ liệu giả lập bị mất cân bằng nghiêm trọng (95% lớp 0, 5% lớp 1)
    feature_matrix, target_vector = make_classification(
        n_samples=5000, 
        n_features=20, 
        weights=[0.95, 0.05], 
        random_state=42
    )
    
    # Chia dữ liệu thành tập huấn luyện (Train) và kiểm thử (Test)
    X_train, X_test, y_train, y_test = train_test_split(
        feature_matrix, target_vector, test_size=0.25, random_state=42, stratify=target_vector
    )
    
    # 2. Áp dụng kỹ thuật SMOTE CHỈ TRÊN TẬP HUẤN LUYỆN để tránh rò rỉ dữ liệu (Data Leakage)
    smote_sampler = SMOTE(random_state=42)
    X_train_resampled, y_train_resampled = smote_sampler.fit_resample(X_train, y_train)
    
    # 3. Khởi tạo và huấn luyện mô hình Random Forest trên dữ liệu đã cân bằng
    random_forest_model = RandomForestClassifier(n_estimators=100, random_state=42)
    random_forest_model.fit(X_train_resampled, y_train_resampled)
    
    # 4. Dự báo trên tập kiểm thử (Test Set gốc chưa qua xử lý SMOTE)
    predicted_probabilities = random_forest_model.predict_proba(X_test)[:, 1]
    predicted_labels = random_forest_model.predict(X_test)
    
    # 5. Đánh giá kết quả chi tiết
    performance_report = classification_report(y_test, predicted_labels)
    
    # Tính toán diện tích dưới đường cong Precision-Recall (PR-AUC)
    precision_values, recall_values, thresholds = precision_recall_curve(y_test, predicted_probabilities)
    pr_auc_score = auc(recall_values, precision_values)
    
    print(f"--- KẾT QUẢ ĐÁNH GIÁ MÔ HÌNH ---")
    print(f"Diện tích dưới đường cong PR-AUC: {pr_auc_score:.4f}\n")
    print("Báo cáo phân loại chi tiết (Classification Report):")
    print(performance_report)
    
    return pr_auc_score

# Chạy hàm thực thi đánh giá mô hình
evaluate_imbalanced_model()

📈 4. Lưu Ý Quan Trọng Khi Vận Hành Mô Hình Trên Dữ Liệu Mất Cân Bằng

Khi triển khai các mô hình học máy xử lý dữ liệu mất cân bằng vào hệ thống sản xuất (production), có một quy tắc vàng kỹ thuật không bao giờ được phép vi phạm: Chỉ áp dụng SMOTE trên tập huấn luyện (Train Set), tuyệt đối không áp dụng trên toàn bộ tập dữ liệu trước khi chia tách hoặc áp dụng lên tập kiểm thử (Test Set). Nếu bạn xử lý SMOTE trước khi chia train/test, thông tin từ tập kiểm thử sẽ bị rò rỉ sang tập huấn luyện, dẫn đến kết quả đánh giá độ chính xác bị sai lệch cực kỳ nghiêm trọng.

Bên cạnh đó, việc lựa chọn ngưỡng quyết định (decision threshold) mặc định là 0.5 cần được tinh chỉnh dựa trên biểu đồ Precision-Recall để cân bằng tối ưu giữa chi phí bỏ sót mục tiêu (False Negative) và chi phí báo động giả (False Positive), từ đó mang lại giá trị thực tiễn cao nhất cho chiến lược kinh doanh của doanh nghiệp.

📝 5. Bài tập thực hành xử lý dữ liệu mất cân bằng với Python

Để nâng cao kỹ năng xử lý dữ liệu mất cân bằng và tinh chỉnh mô hình học máy chuyên sâu, bạn có thể tự tay thực hiện 2 bài tập lập trình Python dưới đây.

Bài tập 1: Sử dụng trọng số lớp (Class Weights) thay thế cho SMOTE

Thay vì tạo ra dữ liệu tổng hợp bằng SMOTE, một phương pháp truyền thống và hiệu quả khác là điều chỉnh trọng số lớp (class_weight) để phạt nặng hơn các sai số ở lớp thiểu số. Hãy viết hàm Python cấu hình class_weight='balanced' trong mô hình Random Forest và đánh giá kết quả:

Code
import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report

def evaluate_class_weight_model():
    """
    Huấn luyện mô hình sử dụng class_weight để xử lý mất cân bằng dữ liệu.
    """
    # 1. Tạo tập dữ liệu giả lập mất cân bằng nghiêm trọng (95% lớp 0, 5% lớp 1)
    feature_matrix, target_vector = make_classification(
        n_samples=4000, 
        n_features=15, 
        weights=[0.95, 0.05], 
        random_state=42
    )
    
    # Chia tập dữ liệu huấn luyện và kiểm thử
    X_train, X_test, y_train, y_test = train_test_split(
        feature_matrix, target_vector, test_size=0.25, random_state=42, stratify=target_vector
    )
    
    # 2. Khởi tạo mô hình với tham số class_weight='balanced'
    weighted_rf_model = RandomForestClassifier(n_estimators=100, class_weight='balanced', random_state=42)
    
    # 3. Huấn luyện mô hình trực tiếp trên dữ liệu mất cân bằng mà không cần SMOTE
    weighted_rf_model.fit(X_train, y_train)
    
    # 4. Dự báo và đánh giá hiệu suất
    predictions = weighted_rf_model.predict(X_test)
    performance_report = classification_report(y_test, predictions)
    
    print(f"--- KẾT QUẢ ĐÁNH GIÁ MÔ HÌNH DÙNG CLASS WEIGHT ---")
    print(performance_report)
    
    return weighted_rf_model

# Chạy hàm đánh giá thực tế
evaluate_class_weight_model()

Bài tập 2: Tối ưu hóa ngưỡng quyết định (Decision Threshold Tuning) dựa trên Precision-Recall

Mặc định các mô hình phân loại sử dụng ngưỡng xác suất 0.5 để phân chia lớp. Tuy nhiên, với dữ liệu mất cân bằng, việc hạ thấp ngưỡng này giúp bắt được nhiều mẫu lớp thiểu số hơn. Hãy viết hàm tìm ngưỡng tối ưu dựa trên chỉ số F1-score từ đường cong Precision-Recall:

Code
import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import precision_recall_curve, f1_score

def optimize_decision_threshold():
    """
    Tìm kiếm ngưỡng xác suất tối ưu để tối đa hóa F1-score từ Precision-Recall.
    """
    # 1. Tạo và chia tập dữ liệu giả lập
    feature_matrix, target_vector = make_classification(
        n_samples=3000, n_features=10, weights=[0.92, 0.08], random_state=42
    )
    X_train, X_test, y_train, y_test = train_test_split(
        feature_matrix, target_vector, test_size=0.25, random_state=42, stratify=target_vector
    )
    
    # 2. Huấn luyện mô hình cơ bản
    model = RandomForestClassifier(n_estimators=100, random_state=42)
    model.fit(X_train, y_train)
    
    # Lấy xác suất dự báo của lớp dương tính (lớp 1)
    predicted_probabilities = model.predict_proba(X_test)[:, 1]
    
    # 3. Tính toán precision, recall và thresholds
    precision_values, recall_values, thresholds = precision_recall_curve(y_test, predicted_probabilities)
    
    # Tính F1-score cho từng ngưỡng (lưu ý: thresholds có độ dài ít hơn precision/recall 1 phần tử)
    f1_scores = 2 * (precision_values[:-1] * recall_values[:-1]) / (precision_values[:-1] + recall_values[:-1] + 1e-10)
    
    # Tìm ngưỡng cho F1-score cao nhất
    optimal_index = np.argmax(f1_scores)
    optimal_threshold = thresholds[optimal_index]
    best_f1_score = f1_scores[optimal_index]
    
    print(f"--- KẾT QUẢ TỐI ƯU NGƯỠNG QUYẾT ĐỊNH ---")
    print(f"Ngưỡng xác suất tối ưu (Optimal Threshold): {optimal_threshold:.4f}")
    print(f"F1-score đạt được tại ngưỡng tối ưu: {best_f1_score:.4f}")
    
    return optimal_threshold

# Chạy hàm tối ưu ngưỡng
optimize_decision_threshold()

Bình luận

Đăng nhập để để lại bình luận.
Chưa có bình luận nào cho bài viết này.

Bài viết liên quan