SMOTE là viết tắt của Synthetic Minority Over-sampling Technique (Kỹ thuật Lấy Mẫu Quá Mức Tổng Hợp cho Lớp Thiểu Số).
Trong học máy, chúng ta thường gặp phải dữ liệu mất cân bằng (imbalanced data), tức là số lượng mẫu của một lớp (lớp đa số) nhiều hơn đáng kể so với lớp khác (lớp thiểu số). Ví dụ, trong bài toán phát hiện gian lận thẻ tín dụng, số lượng giao dịch gian lận (lớp thiểu số) thường rất ít so với các giao dịch hợp lệ (lớp đa số).
Nếu bạn huấn luyện một mô hình trên dữ liệu mất cân bằng như vậy, mô hình có xu hướng ưu tiên lớp đa số và bỏ qua lớp thiểu số. Điều này dẫn đến một mô hình có độ chính xác (accuracy) cao nhưng lại kém hiệu quả trong việc nhận diện các trường hợp quan trọng của lớp thiểu số (ví dụ: không phát hiện được gian lận).
SMOTE ra đời để giải quyết vấn đề này. Nó là một kỹ thuật lấy mẫu quá mức (oversampling), có nghĩa là nó tăng số lượng mẫu của lớp thiểu số để cân bằng lại phân bố dữ liệu.
SMOTE hoạt động như thế nào?
Thay vì chỉ sao chép ngẫu nhiên các mẫu hiện có của lớp thiểu số (điều này có thể dẫn đến quá khớp), SMOTE tạo ra các mẫu tổng hợp (synthetic samples) mới.
Cách SMOTE hoạt động cơ bản như sau:
- Chọn một mẫu từ lớp thiểu số: SMOTE chọn ngẫu nhiên một mẫu dữ liệu xi thuộc lớp thiểu số.
- Tìm các láng giềng gần nhất: Đối với mẫu xi đã chọn, SMOTE tìm k láng giềng gần nhất của nó (cũng thuộc lớp thiểu số) trong không gian đặc trưng. k là một tham số mà bạn có thể điều chỉnh (thường là 5 hoặc 10).
- Tạo mẫu tổng hợp mới:
- Nó chọn ngẫu nhiên một trong số k láng giềng này, gọi là xj.
- Một mẫu tổng hợp mới được tạo ra bằng cách nội suy giữa xi và xj. Về cơ bản, nó “vẽ” một đường thẳng trong không gian đặc trưng giữa xi và xj, sau đó tạo một điểm mới (mẫu tổng hợp) ở đâu đó trên đường thẳng đó. Công thức thường được sử dụng là:
Trong đó, rand(0,1) là một số ngẫu nhiên giữa 0 và 1.
Quá trình này được lặp lại cho đến khi số lượng mẫu của lớp thiểu số đạt đến mức mong muốn, giúp cân bằng phân bố dữ liệu.
Lợi ích của SMOTE:
- Giảm quá khớp: So với việc sao chép ngẫu nhiên, SMOTE tạo ra các mẫu mới có sự đa dạng hơn, giúp mô hình học được các ranh giới quyết định tổng quát hơn và giảm nguy cơ quá khớp.
- Cải thiện hiệu suất mô hình: Bằng cách cung cấp nhiều ví dụ hơn về lớp thiểu số, SMOTE giúp mô hình học các đặc điểm của lớp này tốt hơn, từ đó cải thiện các chỉ số đánh giá quan trọng như độ nhạy (recall), điểm F1 (F1-score) và AUC-PR trên lớp thiểu số.
- Dễ áp dụng: SMOTE là một kỹ thuật phổ biến và có sẵn trong nhiều thư viện học máy (ví dụ:
imbalanced-learn
trong Python), giúp dễ dàng tích hợp vào quy trình làm việc.
Python code
Bạn có thể triển khai SMOTE trong Python một cách dễ dàng bằng cách sử dụng thư viện imbalanced-learn (thường được gọi tắt là imblearn
). Đây là một thư viện được xây dựng dựa trên scikit-learn, chuyên xử lý các vấn đề về dữ liệu mất cân bằng.
Cài đặt thư viện imbalanced-learn
Nếu bạn chưa có, hãy cài đặt nó bằng pip:
pip install imbalanced-learn
Ví dụ mã nguồn SMOTE trong Python
Dưới đây là một ví dụ đầy đủ minh họa cách sử dụng SMOTE trên một tập dữ liệu giả định:
!pip install imbalanced-learn
import numpy as np
import pandas as pd
from collections import Counter
from sklearn.datasets import make_classification # Để tạo dữ liệu mẫu
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, confusion_matrix
# Imblearn cung cấp các phương pháp xử lý dữ liệu mất cân bằng
from imblearn.over_sampling import SMOTE
# --- 1. Tạo một tập dữ liệu mẫu mất cân bằng ---
X, y = make_classification(n_samples=300, n_features=20, n_informative=5,
n_redundant=0, n_repeated=0, n_classes=4,
n_clusters_per_class=1, weights=[0.4, 0.1, 0.2,0.3],
flip_y=0, random_state=42)
print(f"Phân bố lớp ban đầu: {Counter(y)}")
# Expected: Phân bố lớp ban đầu: Counter({0: 900, 1: 100})
# --- 2. Chia tập dữ liệu thành tập huấn luyện và tập kiểm tra ---
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)
print(f"Phân bố lớp trong tập huấn luyện (trước SMOTE): {Counter(y_train)}")
# Expected: Phân bố lớp trong tập huấn luyện (trước SMOTE): Counter({0: 630, 1: 70})
# --- 3. Áp dụng SMOTE lên tập huấn luyện ---
# Khởi tạo đối tượng SMOTE
# random_state để đảm bảo kết quả có thể tái lập
sm = SMOTE(random_state=42)
# Áp dụng SMOTE lên tập huấn luyện để tạo ra các mẫu tổng hợp cho lớp thiểu số
X_train_res, y_train_res = sm.fit_resample(X_train, y_train)
print(f"Phân bố lớp trong tập huấn luyện (SAU SMOTE): {Counter(y_train_res)}")
# Expected: Phân bố lớp trong tập huấn luyện (SAU SMOTE): Counter({0: 630, 1: 630})
# Lớp 1 (thiểu số) đã được tăng lên bằng với số lượng lớp 0 (đa số)
# --- 4. Huấn luyện mô hình Logistic Regression trên dữ liệu đã cân bằng ---
model = LogisticRegression(solver='liblinear', random_state=42)
model.fit(X_train_res, y_train_res)
# --- 5. Đánh giá mô hình trên tập kiểm tra (dữ liệu GỐC, chưa qua SMOTE) ---
y_pred = model.predict(X_test)
print("\n--- Báo cáo phân loại: mô hình dùng SMOTE ---")
print(classification_report(y_test, y_pred))
# So sánh với mô hình không dùng SMOTE
print("\n--- So sánh: Mô hình Logistic Regression KHÔNG dùng SMOTE ---")
model_no_smote = LogisticRegression(solver='liblinear', random_state=42)
model_no_smote.fit(X_train, y_train) # Huấn luyện trên dữ liệu huấn luyện GỐC
y_pred_no_smote = model_no_smote.predict(X_test)
print(classification_report(y_test, y_pred_no_smote))
Giải thích mã nguồn:
- Tạo dữ liệu mẫu:
make_classification
từ scikit-learn được dùng để tạo một tập dữ liệu phân loại nhân tạo.weights=[0.9, 0.1]
tạo ra sự mất cân bằng rõ rệt: 90% mẫu thuộc lớp 0, 10% mẫu thuộc lớp 1.Counter(y)
giúp bạn xem phân bố các lớp ban đầu.
- Chia tập dữ liệu:
- Đây là bước cực kỳ quan trọng! Bạn phải luôn chia dữ liệu thành tập huấn luyện (
X_train
,y_train
) và tập kiểm tra (X_test
,y_test
) trước khi áp dụng bất kỳ kỹ thuật lấy mẫu lại nào (bao gồm SMOTE). - Nếu bạn áp dụng SMOTE trước khi chia, các mẫu tổng hợp có thể “rò rỉ” từ tập huấn luyện sang tập kiểm tra, khiến mô hình của bạn trông tốt hơn thực tế (gây ra rò rỉ dữ liệu – data leakage).
stratify=y
đảm bảo rằng tỷ lệ các lớp được giữ nguyên trong cả tập huấn luyện và tập kiểm tra.
- Đây là bước cực kỳ quan trọng! Bạn phải luôn chia dữ liệu thành tập huấn luyện (
- Áp dụng SMOTE:
from imblearn.over_sampling import SMOTE
nhập lớp SMOTE.sm = SMOTE(random_state=42)
khởi tạo một đối tượng SMOTE.random_state
được sử dụng để đảm bảo rằng kết quả có thể tái lập được nếu bạn chạy lại mã.X_train_res, y_train_res = sm.fit_resample(X_train, y_train)
là dòng ma thuật! Nó áp dụng SMOTE lên tập huấn luyện:- Nó học các mẫu của lớp thiểu số từ
X_train
,y_train
. - Nó tạo ra các mẫu tổng hợp mới để cân bằng số lượng mẫu của lớp thiểu số với lớp đa số.
X_train_res
vày_train_res
là tập dữ liệu huấn luyện mới đã được cân bằng.
- Nó học các mẫu của lớp thiểu số từ
- Bạn sẽ thấy
Counter(y_train_res)
cho thấy lớp 1 (thiểu số) đã được tăng lên để có số lượng bằng với lớp 0 (đa số).
- Huấn luyện và đánh giá mô hình:
- Một mô hình
LogisticRegression
được huấn luyện trên dữ liệu đã được cân bằng (X_train_res
,y_train_res
). - Quan trọng là, mô hình được đánh giá trên tập kiểm tra gốc (
X_test
,y_test
), không phải tập đã qua SMOTE. Điều này cho chúng ta một ước tính thực tế về hiệu suất của mô hình trên dữ liệu mới, chưa thấy, vẫn còn mất cân bằng. classification_report
vàconfusion_matrix
là các công cụ đánh giá quan trọng cho dữ liệu mất cân bằng, cho bạn biết precision (độ chính xác), recall (độ nhạy), và f1-score cho từng lớp, thay vì chỉ dựa vào độ chính xác tổng thể.- Phần so sánh với mô hình không dùng SMOTE giúp bạn thấy rõ sự cải thiện về độ nhạy (recall) của lớp thiểu số. Thông thường, mô hình không dùng SMOTE sẽ có recall của lớp thiểu số rất thấp.
- Một mô hình
kết quả
Phân bố lớp ban đầu: Counter({np.int64(0): 120, np.int64(3): 90, np.int64(2): 60, np.int64(1): 30}) Phân bố lớp trong tập huấn luyện (trước SMOTE): Counter({np.int64(0): 84, np.int64(3): 63, np.int64(2): 42, np.int64(1): 21}) Phân bố lớp trong tập huấn luyện (SAU SMOTE): Counter({np.int64(2): 84, np.int64(3): 84, np.int64(0): 84, np.int64(1): 84}) --- Báo cáo phân loại: mô hình dùng SMOTE --- precision recall f1-score support 0 0.88 0.78 0.82 36 1 0.47 0.89 0.62 9 2 0.80 0.67 0.73 18 3 0.85 0.81 0.83 27 accuracy 0.78 90 macro avg 0.75 0.79 0.75 90 weighted avg 0.81 0.78 0.79 90 --- So sánh: Mô hình Logistic Regression KHÔNG dùng SMOTE --- precision recall f1-score support 0 0.79 0.83 0.81 36 1 0.46 0.67 0.55 9 2 0.92 0.61 0.73 18 3 0.81 0.81 0.81 27 accuracy 0.77 90 macro avg 0.75 0.73 0.73 90 weighted avg 0.79 0.77 0.77 90
Dưới đây là phần giải thích chi tiết về kết quả của mô hình, dựa trên việc sử dụng và không sử dụng kỹ thuật SMOTE (Synthetic Minority Over-sampling Technique):
📊 1. Phân bố lớp ban đầu và sau khi xử lý
Giai đoạn | Lớp 0 | Lớp 1 | Lớp 2 | Lớp 3 |
---|---|---|---|---|
Ban đầu | 120 | 30 | 60 | 90 |
Trước SMOTE (tập huấn luyện) | 84 | 21 | 42 | 63 |
Sau SMOTE (tập huấn luyện) | 84 | 84 | 84 | 84 |
🔹 SMOTE đã cân bằng lại số lượng mẫu giữa các lớp — điều này rất quan trọng khi xử lý bài toán mất cân bằng dữ liệu.
🤖 2. Hiệu suất mô hình có sử dụng SMOTE
- Accuracy: 78%
- Các lớp thiểu số như Lớp 1 cải thiện rõ rệt về recall (0.89), tức là mô hình đã học tốt hơn cách nhận diện các mẫu thuộc lớp này.
- Tuy nhiên, precision của lớp 1 lại khá thấp (0.47) → mô hình đánh giá nhiều mẫu là lớp 1, nhưng nhiều trong số đó là sai.
- Các lớp còn lại cũng có hiệu suất tốt và khá đều.
📉 3. So sánh với mô hình KHÔNG dùng SMOTE
- Accuracy: 77% (gần tương đương)
- Lớp 1 có recall thấp hơn (0.67) và f1-score thấp hơn (0.55) → chứng tỏ mô hình khó học với dữ liệu mất cân bằng.
- Precision của lớp 2 lên tới 0.92 nhưng recall thấp → mô hình cẩn trọng khi dự đoán lớp này nhưng lại bỏ sót nhiều mẫu.
⚖️ 4. Nhận xét tổng quan
- SMOTE giúp cân bằng tập huấn luyện, tăng khả năng phát hiện các lớp thiểu số.
- Tuy accuracy không tăng mạnh, nhưng recall của các lớp nhỏ được cải thiện đáng kể → tốt hơn nếu mục tiêu là phát hiện đầy đủ các mẫu trong từng lớp.
- Nếu sai sót trong dự đoán lớp thiểu số gây hậu quả nghiêm trọng (như trong y tế, gian lận…), dùng SMOTE là lựa chọn hợp lý.
Các tham số quan trọng của SMOTE
:
sampling_strategy
: Điều này kiểm soát cách bạn muốn cân bằng lại các lớp.'auto'
(mặc định): Cân bằng tất cả các lớp với số lượng của lớp đa số.'minority'
: Chỉ lấy mẫu quá mức lớp thiểu số.'not majority'
: Lấy mẫu quá mức tất cả các lớp không phải lớp đa số.'not minority'
: Lấy mẫu thiếu tất cả các lớp không phải lớp thiểu số.'all'
: Lấy mẫu quá mức và/hoặc lấy mẫu thiếu tất cả các lớp.float
: Tỷ lệ mong muốn của số lượng mẫu lớp thiểu số so với lớp đa số sau khi lấy mẫu quá mức (ví dụ:0.5
có nghĩa là lớp thiểu số sẽ có 50% số lượng của lớp đa số).dict
: Bạn có thể chỉ định chính xác số lượng mẫu mong muốn cho mỗi lớp (ví dụ:{0: 500, 1: 500}
để có 500 mẫu cho mỗi lớp).
k_neighbors
: Số lượng láng giềng gần nhất để xem xét khi tạo mẫu tổng hợp (mặc định là 5).
Bằng cách sử dụng mã này, bạn có thể dễ dàng tích hợp SMOTE vào quy trình xử lý dữ liệu của mình để cải thiện hiệu suất của mô hình trên các tập dữ liệu mất cân bằng!