特徴エンジニアリング:特徴選択の原理と実装(後編)

0x01 線形モデルと正則化を用いた特徴選択

1. 基本概念

すべての特徴量が同じスケール上にある場合、最も重要な特徴量はモデル内で最も高い係数を持つべきであり、出力変数と無関係な特徴量はゼロに近い係数値を持つべきです。シンプルな線形回帰モデルを使用する場合でも、データがノイズが少ない(または特徴量の数に対してデータ量が多い)場合、特徴量(相対的に)独立している場合、この方法はうまく機能します。

2. 正則化モデル

正則化とは、既存のモデル(損失関数)に追加の制約またはペナルティ項を加え、過学習を防ぎ汎化能力を向上させる手法です。損失関数は元のE(X,Y)からE(X,Y)+alpha||w||に変化します。ここでwはモデル係数からなるベクトル(パラメータや係数とも呼ばれます)、||·||は通常L1またはL2ノルム、alphaは正則化の強さを調整するパラメータです。線形モデルに適用される場合、L1正則化とL2正則化はそれぞれLassoとRidgeと呼ばれます。

1)L1正則化/Lasso回帰

L1正則化は、係数wのL1ノルムをペナルティ項として損失関数に加えます。正則化項が非零であるため、弱い特徴量に対応する係数をゼロに強制します。そのため、L1正則化は学習されたモデルをスパースにする傾向があります(係数wは頻繁にゼロになります)。この特性により、L1正則化は優れた特徴選択手法となります。

Lassoはいくつかの優れた特徴量を選び出し、他の特徴量の係数をゼロに近づけます。特徴量の数を減らす必要がある場合に有用ですが、データ理解にはあまり適していません。

2)L2正則化/Ridge回帰

L2正則化は、係数ベクトルのL2ノルムを損失関数に加えます。

  • L2ペナルティ項では係数が2乗されているため、L1と多くの違いがあります。最も明らかな違いは、L2正則化が係数の値を平均化することです。
  • 相関のある特徴量の場合、これはそれらがより近い係数を得られることを意味します。
  • Ridgeは回帰係数を関連変数に均等に配分します。

L2正則化は特徴選択において安定したモデルです。L1正則化のように、係数がデータの微小な変化で変動することはありません。したがって、L2正則化とL1正則化が提供する価値は異なり、L2正則化は特徴理解により有用です:表現能力の強い特徴量に対応する係数は非ゼロです。

3. 原理の詳細

多元線形回帰はn個の特徴量を持ち、予測式は以下のようになります。

多元線形回帰方程式はθを求める問題に変換されます。

各特徴量には対応する重み係数coefがあり、特徴量の重み係数の正負値は特徴量と目標値の間の正の相関または負の相関を示し、特徴量の重み係数の絶対値は重要性を表します。

scikit-learnのLinearRegressionのfit()メソッドは、訓練セットからθを求めます。LinearRegressionの2つの属性intercept_とcoef_はそれぞれθ0とθ1-θnに対応します。

4. コード実装

1)通常の線形モデル

# ボストンデータセットの読み込み
from sklearn.datasets import load_boston
from sklearn.linear_model import LinearRegression
import numpy as np

boston = load_boston()
x_data = boston.data
y_target = boston.target

# 外れ値を除外
x_filtered = x_data[y_target < 50]
y_filtered = y_target[y_target < 50]

# 線形回帰モデルの作成と学習
model = LinearRegression()
model.fit(x_filtered, y_filtered)

# 係数のソート
sorted_indices = np.argsort(model.coef_)
feature_names = boston.feature_names[sorted_indices]
sorted_coefs = model.coef_[sorted_indices]

print("特徴量の重要性順:", feature_names)
print("対応する係数:", sorted_coefs)

# 出力例:
# 特徴量の重要性順: ['NOX' 'DIS' 'PTRATIO' 'LSTAT' 'CRIM' 'INDUS' 'AGE' 'TAX' 'B' 'ZN' 'RAD' 'CHAS' 'RM']
# 対応する係数: [-1.24268073e+01 -1.21088069e+00 -8.38888137e-01 -3.50952134e-01
#  -1.05574295e-01 -4.35179251e-02 -2.36116881e-02 -1.37702943e-02 7.93577159e-03
#  3.52748549e-02  2.50740082e-01  4.55405227e-01 3.75411229e+00]

結果分析:

  • 正の相関で最も影響が大きい特徴量は"RM"(平均部屋数)で、係数値は3.75です。
  • 負の相関で最も影響が大きい特徴量は"NOX"(一酸化窒素濃度)で、係数値は-1.24です。

2)L1正則化線形モデル

# 線形モデルの美しい表示用ヘルパー関数
def format_linear_coefficients(coefs, names=None, sort=False):
    if names is None:
        names = ["X%s" % x for x in range(len(coefs))]
    coef_name_pairs = list(zip(coefs, names))
    if sort:
        coef_name_pairs = sorted(coef_name_pairs, key=lambda x: -np.abs(x[0]))
    return " + ".join("%s * %s" % (round(coef, 3), name) for coef, name in coef_name_pairs)

from sklearn.linear_model import Lasso
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_boston

# データの読み込みと前処理
boston = load_boston()
scaler = StandardScaler()
X_scaled = scaler.fit_transform(boston["data"])
Y_target = boston["target"]
feature_names = boston["feature_names"]

# Lassoモデルの学習
lasso_model = Lasso(alpha=0.3)
lasso_model.fit(X_scaled, Y_target)

# 結果の表示
print("Lassoモデル: {}".format(format_linear_coefficients(lasso_model.coef_, feature_names, sort=True)))

# 出力例:
# Lasso model: -3.707 * LSTAT + 2.992 * RM + -1.757 * PTRATIO
# + -1.081 * DIS + -0.7 * NOX + 0.631 * B + 0.54 * CHAS + -0.236 * CRIM
# + 0.081 * ZN + -0.0 * INDUS + -0.0 * AGE + 0.0 * RAD + -0.0 * TAX

多くの特徴量が係数0を持っています。L1正則化回帰の安定性は非正則化線形モデルと同様であり、これはデータに相関のある特徴量が存在する場合、係数(および特徴量のランク)が小さなデータ変化でも大幅に変化することを意味します。

3)L2正則化線形モデル

from sklearn.linear_model import Ridge
from sklearn.metrics import r2_score
import numpy as np

# データセットのサイズ
sample_size = 100

# 異なる乱数シードで10回実行
for i in range(10):
    print("乱数シード {}".format(i))
    np.random.seed(seed=i)
    X_seed = np.random.normal(0, 1, sample_size)
    X1 = X_seed + np.random.normal(0, 0.1, sample_size)
    X2 = X_seed + np.random.normal(0, 0.1, sample_size)
    X3 = X_seed + np.random.normal(0, 0.1, sample_size)
    Y = X1 + X2 + X3 + np.random.normal(0, 1, sample_size)
    X = np.array([X1, X2, X3]).T
    
    # 線形回帰モデル
    lr = LinearRegression()
    lr.fit(X, Y)
    print("線形モデル: {}".format(format_linear_coefficients(lr.coef_)))
    
    # Ridgeモデル
    ridge = Ridge(alpha=10)
    ridge.fit(X, Y)
    print("Ridgeモデル: {}".format(format_linear_coefficients(ridge.coef_)))

# 出力例:
# 乱数シード 0
# 線形モデル: 0.728 * X0 + 2.309 * X1 + -0.082 * X2
# Ridgeモデル: 0.938 * X0 + 1.059 * X1 + 0.877 * X2

# 乱数シード 1
# 線形モデル: 1.152 * X0 + 2.366 * X1 + -0.599 * X2
# Ridgeモデル: 0.984 * X0 + 1.068 * X1 + 0.759 * X2

例からわかるように、線形回帰の係数は生成されたデータに大きく依存して変化します。しかし、L2正則化モデルでは係数は非常に安定しており、データの生成方法(すべての係数が1に近い)を密接に反映しています。

0x02 ランダムフォレストを用いた特徴選択

ランダムフォレストは精度が高く、頑健性があり、使いやすいなどの利点を持つため、現在最も人気のある機械学習アルゴリズムの一つとなっています。ランダムフォレストは平均不純度減少(mean decrease impurity)と平均精度減少(mean decrease accuracy)という2つの特徴選択方法を提供します。

1. 平均不純度減少

1)原理の説明

  • ランダムフォレストは複数のCART決定木から構成されます。決定木の各ノードは特定の特徴量に関する条件であり、目的変数に基づいてデータセットを2つに分割するためのものです。
  • CARTは不純度を使用してノード(最適な条件)を決定します。分類問題では通常ジニ不純度が使用され、回帰問題では通常分散または最小二乗フィットが使用されます。
  • 決定木を学習する際に、各特徴量が木の不純度をどれだけ減少させたかを計算できます。決定木の森の場合、各特徴量が平均して不純度をどれだけ減少させたかを計算し、その平均減少量を特徴選択の基準として使用できます。
  • ランダムフォレストの不純度に基づくランキング結果は非常に明確であり、スコアが最も高いいくつかの特徴量の後の特徴量のスコアは急激に低下します。

2)コード実装

from sklearn.datasets import load_boston
from sklearn.ensemble import RandomForestRegressor
import numpy as np

# ボストン住宅価格データセットの読み込み
boston = load_boston()
X_data = boston["data"]
Y_target = boston["target"]
feature_names = boston["feature_names"]

# ランダムフォレストモデルの学習と特徴量の重要性の取得
rf = RandomForestRegressor()
rf.fit(X_data, Y_target)

print("スコア順の特徴量:")
print(sorted(zip(map(lambda x: round(x, 4), rf.feature_importances_), feature_names), reverse=True))

2. 平均精度減少

1)原理の説明

  • 各特徴量がモデルの精度に直接与える影響を測定することによる特徴選択。
  • 主な考え方は、各特徴量の値の順序をランダムにシャッフルし、順序の変更がモデルの精度に与える影響を測定することです。
  • 重要でない変数の場合、順序をシャッフルしてもモデルの精度への影響は大きくありません。
  • 重要な変数の場合、順序をシャッフルするとモデルの精度が低下します。

2)コード実装

from sklearn.model_selection import ShuffleSplit
from sklearn.metrics import r2_score
from collections import defaultdict

X_data = boston["data"]
Y_target = boston["target"]
rf = RandomForestRegressor()
feature_scores = defaultdict(list)

# データの異なるランダム分割に対してスコアを交差検証
for train_idx, test_idx in ShuffleSplit(len(X_data), 100, 0.3):
    X_train, X_test = X_data[train_idx], X_data[test_idx]
    Y_train, Y_test = Y_target[train_idx], Y_target[test_idx]
    
    # 元の特徴量を使用してモデルを学習し、精度を基準とする
    model = rf.fit(X_train, Y_train)
    base_accuracy = r2_score(Y_test, model.predict(X_test))
    
    # 各特徴量をシャッフルして精度への影響を測定
    for i in range(X_data.shape[1]):
        X_test_modified = X_test.copy()
        np.random.shuffle(X_test_modified[:, i])  # i番目の特徴量をシャッフル
        shuffled_accuracy = r2_score(Y_test, model.predict(X_test_modified))
        # 特徴量をシャッフルした後の平均精度減少を計算
        feature_scores[feature_names[i]].append((base_accuracy - shuffled_accuracy) / base_accuracy)

print("スコア順の特徴量:")
print(sorted([(round(np.mean(score), 4), feat) for feat, score in feature_scores.items()], reverse=True))

0x03 上位レベルの特徴選択

上位レベルの特徴選択は、線形回帰やSVMなどのモデルベースの特徴選択方法に基づいており、異なるサブセット上でモデルを構築し、最終的に特徴量のスコアを決定します。

1. 安定性選択(Stability selection)

1)原理の説明

  • 安定性選択は、二次サンプリングと選択アルゴリズムを組み合わせた比較的新しい方法であり、選択アルゴリズムは回帰、SVM、またはその他の類似の方法です。
  • 主な考え方は、異なるデータサブセットと特徴量サブセット上で特徴選択アルゴリズムを実行し、繰り返し最終的に特徴選択結果を要約することです。例えば、特定の特徴量が重要な特徴量と考えられる頻度(重要な特徴量として選択された回数が、それが含まれるサブセットでテストされた回数で割った値)を統計できます。
  • 理想的には、重要な特徴量のスコアは100%に近づきます。少し弱い特徴量のスコアはゼロではない値になり、最も役に立たない特徴量のスコアはゼロに近づきます。

2)コード実装

from sklearn.linear_model import RandomizedLasso
from sklearn.datasets import load_boston

boston = load_boston()
# ボストン住宅価格データセットを使用
# データはscikit-learnの実装によって自動的にスケーリングされます

X_data = boston["data"]
Y_target = boston["target"]
feature_names = boston["feature_names"]

# RandomizedLassoモデルの学習
rlasso = RandomizedLasso(alpha=0.025)
rlasso.fit(X_data, Y_target)

print("スコア順の特徴量:")
print(sorted(zip(map(lambda x: round(x, 4), rlasso.scores_), feature_names), reverse=True))

2. 再帰的特徴除去(Recursive feature elimination, RFE)

1)原理の説明

  • 再帰的特徴除去の主な考え方は、繰り返しモデル(SVMまたは回帰モデルなど)を構築し、最も良い(または最も悪い)特徴量を選び出すことです(係数に基づいて選択)、選ばれた特徴量を一時的に置き換え、残りの特徴量でこのプロセスを繰り返し、すべての特徴量が処理されるまで続けます。
  • この過程で特徴量が除去される順序が特徴量のランキングとなります。したがって、これは最適な特徴量サブセットを見つける貪欲アルゴリズムです。
  • RFEの安定性は、反復時にどのモデルを使用するかに大きく依存します。
  • もしRFEが通常の回帰を使用し、正則化されていない回帰が不安定である場合、RFEは不安定です。
  • もしRFEがRidgeを使用し、Ridge正則化された回帰が安定している場合、RFEは安定します。

2)コード実装

from sklearn.feature_selection import RFE
from sklearn.linear_model import LinearRegression
from sklearn.datasets import load_boston

boston = load_boston()
X_data = boston["data"]
Y_target = boston["target"]
feature_names = boston["feature_names"]

# 線形回帰をモデルとして使用
linear_model = LinearRegression()
# すべての特徴量をランク付け(最後の1つになるまで続行)
rfe = RFE(linear_model, n_features_to_select=1)
rfe.fit(X_data, Y_target)

print("ランク順の特徴量:")
print(sorted(zip(map(lambda x: round(x, 4), rfe.ranking_), feature_names)))

# 出力例:
# ランク順の特徴量:
# [(1, 'NOX'), (2, 'RM'), (3, 'CHAS'), (4, 'PTRATIO'), (5, 'DIS'),
# (6, 'LSTAT'), (7, 'RAD'), (8, 'CRIM'), (9, 'INDUS'), (10, 'ZN'),
# (11, 'TAX'), (12, 'B'), (13, 'AGE')]

0xFF まとめ

  1. 単変量特徴量選択はデータの理解、データ構造、特性の分析に使用でき、無関係な特徴量を除外するのにも役立ちますが、冗長な特徴量を発見することはできません。
  2. 正則化された線形モデルは特徴量の理解と選択に使用できます。L1正則化と比較して、L2正則化はより安定した性能を示し、データ理解に適しています。応答変数と特徴量の間にはしばしば非線形関係があるため、basis expansionを使用して特徴量をより適切な空間に変換し、その上でシンプルな線形モデルを適用することを検討できます。
  3. ランダムフォレストは非常に人気のある特徴量選択方法であり、使いやすいです。しかし、2つの主要な問題があります:
    • 重要な特徴量が低いスコアを取得する可能性がある(相関特徴量の問題)
    • この方法は、特徴量のカテゴリが多い特徴量ほど有利になります(バイアスの問題)

特徴量選択は多くの機械学習とデータマイニングのシナリオで非常に役立ちます。使用する際には、自分の目的を明確に理解し、どの方法が自分のタスクに適しているかを見つける必要があります。

  • モデルの性能を向上させるために最適な特徴量を選択する場合、交差検証を使用して特定の方法が他の方法よりも優れているかどうかを検証できます。
  • 特徴量選択の方法をデータ理解に使用する場合、特徴量選択モデルの安定性に注意を払う必要があります。安定性の低いモデルは簡単に誤った結論に導く可能性があります。
  • データを二次サンプリングし、サブセット上で特徴量選択アルゴリズムを実行することは役立ちます。各サブセットで結果が一貫している場合、そのデータセットで得られた結論は信頼できると考えられ、その特徴量選択モデルの結果を使用してデータを理解できます。

モデルの学習における特徴量選択の個人的な実施フロー:

  1. データ前処理後、まず値の変化が非常に小さい特徴量を除外します。計算リソースに余裕があり、可能な限りすべての情報を保持したい場合は、しきい値を高く設定するか、または離散型特徴量が1つの値しか持たないもののみをフィルタリングできます。
  2. データ量が大きすぎて計算リソースが不足する場合(すべてのデータを使用しての学習にメモリが不足、計算速度が遅すぎる)、単一特徴量選択法を使用して一部の特徴量を除外できます。これらの除外された特徴量は完全に使用されなくなるわけではなく、後続の特徴量構築時に元の特徴量として使用できます。
  3. この時点で依然として特徴量の数が非常に大きい場合、または特徴量が疎である場合、PCA(主成分分析)やLDA(線形判別分析)などの方法を使用して特徴量を次元削減できます。
  4. サンプルサンプリングと特徴量の事前選択後、訓練サンプルを使用してモデルを学習できます。しかし、特徴量の数が大きすぎて学習速度が遅い場合、またはさらに有効な特徴量を選択したり無効な特徴量(ノイズ)を除外したりしたい場合、正則化線形モデル選択法、ランダムフォレスト選択法、または上位レベル特徴量選択法を使用してさらに特徴量を選択できます。

最後に、特徴量選択はデータを理解するか、より良いモデルを学習するためのものです。目的に合わせて適切な方法を選択する必要があります。モデルをより良く/容易に学習させるための特徴量選択の場合、計算リソースに余裕がある場合、有用な情報を失う可能性があるため、過度な特徴量選択は避けるべきです。無効な特徴量の影響を減らし、過学習を避けるだけの場合、ランダムフォレストやXGBoostなどのアンサンブルモデルを使用して特徴量の過学習を避けることができます。

タグ: 特徴選択 正則化 ランダムフォレスト 線形モデル 安定性選択

7月17日 20:43 投稿