NearMiss: lấy mẫu thiếu cho dữ liệu mất cân bằng + Python code

NearMiss là một nhóm các thuật toán lấy mẫu thiếu (undersampling) được thiết kế để xử lý dữ liệu mất cân bằng. Khác với SMOTE tạo ra các mẫu mới cho lớp thiểu số, NearMiss hoạt động bằng cách giảm số lượng mẫu của lớp đa số để cân bằng phân bố dữ liệu.

Điểm đặc biệt của NearMiss là nó không chỉ loại bỏ ngẫu nhiên các mẫu từ lớp đa số. Thay vào đó, nó chọn lọc các mẫu lớp đa số dựa trên khoảng cách của chúng đến các mẫu lớp thiểu số. Mục tiêu là giữ lại những mẫu lớp đa số có “thông tin” nhất, tức là những mẫu nằm gần ranh giới quyết định giữa hai lớp, giúp mô hình học ranh giới này hiệu quả hơn.

Có ba phiên bản chính của NearMiss, mỗi phiên bản có một chiến lược lựa chọn mẫu khác nhau:

  1. NearMiss-1: Chọn các mẫu từ lớp đa số có khoảng cách trung bình nhỏ nhất đến k láng giềng gần nhất của chúng trong lớp thiểu số. Ý tưởng là giữ lại các mẫu lớp đa số gần nhất với lớp thiểu số.
  2. NearMiss-2: Chọn các mẫu từ lớp đa số có khoảng cách trung bình nhỏ nhất đến k láng giềng xa nhất của chúng trong lớp thiểu số. Mục tiêu là giữ lại các mẫu lớp đa số nằm ở trung tâm của vùng chồng lấn giữa hai lớp.
  3. NearMiss-3: Chọn k láng giềng gần nhất từ lớp đa số cho mỗi mẫu trong lớp thiểu số. Sau đó, nó giữ lại các mẫu lớp đa số có khoảng cách trung bình lớn nhất đến các láng giềng của chúng trong lớp đa số. Phiên bản này nhằm mục đích giữ lại các mẫu đa số nằm gần ranh giới và loại bỏ các mẫu nằm sâu trong vùng của lớp đa số.

Tại sao lại dùng NearMiss?

  • Giảm kích thước tập dữ liệu: Khi tập dữ liệu quá lớn, việc lấy mẫu thiếu có thể giúp giảm kích thước và tăng tốc độ huấn luyện mô hình.
  • Tập trung vào ranh giới: Bằng cách chọn lọc các mẫu đa số gần với lớp thiểu số, NearMiss giúp mô hình tập trung vào việc học ranh giới phân loại, thay vì chỉ bị ảnh hưởng bởi số lượng lớn các mẫu đa số “dễ dàng”.
  • Hiệu quả khi dữ liệu có nhiều nhiễu: Trong một số trường hợp, SMOTE có thể tạo ra các mẫu tổng hợp gây nhiễu. NearMiss, bằng cách loại bỏ các mẫu đa số, có thể giúp làm sạch dữ liệu.

Hạn chế của NearMiss:

  • Mất thông tin: Nhược điểm lớn nhất của lấy mẫu thiếu nói chung là bạn có thể loại bỏ các mẫu quan trọng từ lớp đa số, dẫn đến mất thông tin và có thể làm giảm hiệu suất tổng thể của mô hình.
  • Có thể làm trầm trọng thêm vấn đề nếu dữ liệu chồng lấn nhiều: Nếu hai lớp chồng lấn quá nhiều, việc chỉ giữ lại các mẫu đa số gần lớp thiểu số có thể khiến mô hình khó phân biệt giữa hai lớp hơn.

Code NearMiss trong Python

Bạn có thể sử dụng lớp NearMiss từ thư viện imblearn.under_sampling.

Python

import numpy as np
import pandas as pd
from collections import Counter
from sklearn.datasets import make_classification
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.under_sampling import NearMiss

# --- 1. Tạo một tập dữ liệu mẫu mất cân bằng --- 
X, y = make_classification(n_samples=200, n_features=20, n_informative=5,
                           n_redundant=0, n_repeated=0, n_classes=3,
                           n_clusters_per_class=1, weights=[0.7,0.2, 0.1],
                           flip_y=0)

print(f"Phân bố lớp ban đầu: {Counter(y)}")

# --- 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 NearMiss): {Counter(y_train)}")

# --- 3. Áp dụng NearMiss lên tập huấn luyện ---
# Khởi tạo đối tượng NearMiss
# Bạn có thể chọn version=1, version=2, hoặc version=3
# random_state để đảm bảo kết quả có thể tái lập
nm = NearMiss(version=1, n_neighbors=3) # Sử dụng NearMiss-1

# Áp dụng NearMiss lên tập huấn luyện để giảm số lượng mẫu cho lớp đa số
X_train_res, y_train_res = nm.fit_resample(X_train, y_train)

print(f"Phân bố lớp trong tập huấn luyện (SAU NearMiss): {Counter(y_train_res)}")

# --- 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 NearMiss) ---
y_pred = model.predict(X_test)

print("\n--- Báo cáo phân loại với Near Miss---")
print(classification_report(y_test, y_pred))


# So sánh với mô hình không dùng NearMiss 
print("\n--- So sánh: Mô hình Logistic Regression KHÔNG dùng NearMiss ---")
model_no_sampling = LogisticRegression(solver='liblinear', random_state=42)
model_no_sampling.fit(X_train, y_train) # Huấn luyện trên dữ liệu huấn luyện GỐC

y_pred_no_sampling = model_no_sampling.predict(X_test)
print(classification_report(y_test, y_pred_no_sampling))


Chạy Code trên Colab

Giải thích mã nguồn:

  1. Tạo dữ liệu mẫu và chia tập dữ liệu: Tương tự như ví dụ SMOTE, chúng ta tạo một tập dữ liệu giả định và chia nó thành tập huấn luyện/kiểm tra. Việc chia dữ liệu trước khi lấy mẫu lại là rất quan trọng để tránh rò rỉ dữ liệu.
  2. Áp dụng NearMiss:
    • from imblearn.under_sampling import NearMiss nhập lớp NearMiss.
    • nm = NearMiss(version=1, n_neighbors=3, random_state=42) khởi tạo một đối tượng NearMiss.
      • version: Đây là tham số quan trọng nhất, bạn chọn 1, 2, hoặc 3 tùy thuộc vào chiến lược lấy mẫu thiếu mà bạn muốn.
      • n_neighbors: Số lượng láng giềng gần nhất được sử dụng trong các tính toán khoảng cách (mặc định là 3).
      • random_state: Để đảm bảo kết quả có thể tái lập.
    • X_train_res, y_train_res = nm.fit_resample(X_train, y_train) áp dụng NearMiss. Bạn sẽ thấy lớp đa số (0) đã được giảm số lượng xuống bằng lớp thiểu số (1).
  3. Huấn luyện và đánh giá mô hình:
    • Mô hình LogisticRegression được huấn luyện trên dữ liệu đã được cân bằng bởi NearMiss (X_train_res, y_train_res).
    • Đánh giá trên tập kiểm tra gốc (X_test, y_test).
    • Sử dụng classification_reportconfusion_matrix để có cái nhìn toàn diện về hiệu suất, đặc biệt là recall cho lớp thiểu số.

Khi so sánh kết quả của mô hình sử dụng NearMiss với mô hình không sử dụng kỹ thuật lấy mẫu lại, bạn có thể sẽ thấy sự cải thiện đáng kể về độ nhạy (recall) của lớp thiểu số, dù độ chính xác tổng thể có thể giảm một chút do việc loại bỏ mẫu từ lớp đa số.


kết quả

Phân bố lớp ban đầu: 
Counter({np.int64(0): 140, np.int64(1): 40, np.int64(2): 20})
Phân bố lớp trong tập huấn luyện (trước NearMiss): 
Counter({np.int64(0): 98, np.int64(1): 28, np.int64(2): 14})
Phân bố lớp trong tập huấn luyện (SAU NearMiss): 
Counter({np.int64(0): 14, np.int64(1): 14, np.int64(2): 14})

--- Báo cáo phân loại với Near Miss---
              precision    recall  f1-score   support

           0       0.98      0.95      0.96        42
           1       0.79      0.92      0.85        12
           2       0.40      0.33      0.36         6

    accuracy                           0.88        60
   macro avg       0.72      0.73      0.72        60
weighted avg       0.88      0.88      0.88        60


--- So sánh: Mô hình Logistic Regression KHÔNG dùng NearMiss ---
              precision    recall  f1-score   support

           0       0.95      0.95      0.95        42
           1       0.75      1.00      0.86        12
           2       0.50      0.17      0.25         6

    accuracy                           0.88        60
   macro avg       0.73      0.71      0.69        60
weighted avg       0.87      0.88      0.86        60

Dưới đây là phần phân tích kết quả mô hình khi dùng và không dùng kỹ thuật NearMiss — một phương pháp undersampling dữ liệu từ lớp chiếm đa số để cân bằng tập huấn luyện.

📊 1. Phân bố lớp

Giai đoạnLớp 0Lớp 1Lớp 2
Ban đầu1404020
Trước NearMiss (tập huấn luyện)982814
Sau NearMiss141414

🔍 NearMiss đã cắt bớt lớp 0 để tạo sự cân bằng tuyệt đối giữa các lớp. Điều này giúp mô hình tránh bị thiên vị khi học từ lớp chiếm ưu thế.


📈 2. Hiệu suất mô hình dùng NearMiss

LớpPrecisionRecallF1-scoreSupport
00.980.950.9642
10.790.920.8512
20.400.330.366
  • Lớp 0 vẫn được phân loại rất chính xác.
  • Lớp 1 có recall cao (0.92) → mô hình nhận diện tốt.
  • Lớp 2 thấp nhất về các chỉ số → có thể do đặc trưng lớp này khó nhận diện hơn hoặc dữ liệu chưa đủ đa dạng.

➡️ Accuracy tổng thể đạt 88%, tuy nhiên macro average thấp hơn do hiệu suất của lớp 2.


📉 3. So sánh với mô hình KHÔNG dùng NearMiss

LớpPrecisionRecallF1-scoreSupport
00.950.950.9542
10.751.000.8612
20.500.170.256
  • Accuracy tương đương 88%, nhưng lớp 2 có recall cực thấp (0.17) → mô hình không học được lớp này khi mất cân bằng dữ liệu.
  • Weighted avg thấp hơn mô hình NearMiss → mô hình học thiên lệch.

🧠 4. Nhận xét tổng quan

  • NearMiss giúp cân bằng dữ liệu bằng cách giảm bớt lớp chiếm đa số, cải thiện recall cho lớp thiểu số.
  • Dù accuracy giống nhau, nhưng NearMiss giúp tăng hiệu suất đều hơn giữa các lớp.
  • ⚠️ Tuy nhiên, undersampling sẽ loại bỏ nhiều dữ liệu có thể hữu ích, do đó cần cân nhắc giữa tính đại diện và sự cân bằng.

khi nào nên sử dụng NearMiss thay vì SMOTE (hoặc ngược lại)

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *

error: Content is protected !!