Skip to content

NMIFeatureSelector: Отбор признаков с помощью нормализованной взаимной информации

Обзор

Класс NMIFeatureSelector выполняет отбор признаков путем оценки нормализованной взаимной информации (NMI) между признаками и целевой переменной на основе статьи Local Bayesian Network Structure Learning for High-Dimensional Data. Этот метод выявляет признаки с сильными статистическими зависимостями от цели, одновременно уменьшая избыточность среди выбранных признаков. Он особенно эффективен для улавливания нелинейных взаимосвязей и совместим с API SelectorMixin из scikit-learn.


Математическая основа

Нормализованная взаимная информация (NMI)

NMI измеряет зависимость между двумя переменными, нормализованную в диапазоне [0, 1]. Для признаков \(a\) и \(b\) NMI вычисляется как:

\[ \text{NMI}(a, b) = \frac{H(a) + H(b) - H(a, b)}{\min(H(a), H(b))} \]

где:

  • \(H(a)\) и \(H(b)\) — это энтропии \(a\) и \(b\),
  • \(H(a, b)\) — их совместная энтропия.

Более высокие значения NMI указывают на более сильные зависимости.

Дискретизация данных

Алгоритм дискретизирует признаки для эффективного вычисления энтропии:

  • Целочисленные/строковые столбцы: сопоставляются с перечислимыми категориями.
  • Столбцы с плавающей точкой: дискретизируются с использованием равномерной дискретизации с заданным количеством бинов.

Процесс отбора признаков

  1. Первый этап:

    • Вычислить NMI между каждым признаком и целью.
    • Выбрать признаки с NMI > threshold.
  2. Второй этап:

    • Для каждой пары выбранных признаков вычислить попарное NMI.
    • Удалить признак \(f_j\), если:
      • Другой признак \(f_i\) имеет более высокое NMI с целью, и
      • NMI(\(f_i\), \(f_j\)) > NMI(\(f_j\), цель).

Это уменьшает избыточность, приоритизируя признаки, более релевантные для цели.


Пример

examples/feature_selection/nmi_fs_example.py
import numpy as np
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from applybn.feature_selection.bn_nmi_feature_selector import NMIFeatureSelector

# Load dataset
cancer = load_breast_cancer()
X = pd.DataFrame(cancer.data, columns=cancer.feature_names)
y = cancer.target

# Split data
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42
)

# Initialize and apply feature selector
selector = NMIFeatureSelector(threshold=0.5, n_bins=20)
selector.fit(X_train, y_train)  # Fixed version captures feature names before conversion

# Transform datasets
X_train_selected = selector.transform(X_train)
X_test_selected = selector.transform(X_test)

# Get selected feature names
selected_features = selector.feature_names_in_[selector.selected_features_]
print(f"Selected features ({len(selected_features)}):\n{selected_features}")

# Train classifier and compare results
clf_full = RandomForestClassifier(random_state=42)
clf_full.fit(X_train, y_train)
acc_full = clf_full.score(X_test, y_test)

clf_selected = RandomForestClassifier(random_state=42)
clf_selected.fit(X_train_selected, y_train)
acc_selected = clf_selected.score(X_test_selected, y_test)

print(f"\nOriginal features: {X_train.shape[1]}")
print(f"Selected features: {X_train_selected.shape[1]}")
print(f"Accuracy with all features: {acc_full:.4f}")
print(f"Accuracy with selected features: {acc_selected:.4f}")