![見出し画像](https://assets.st-note.com/production/uploads/images/133299529/rectangle_large_type_2_adcdd7c44e1a6ce1c84dfad6a8bf7627.png?width=1200)
【機械学習の初学者向け】不均衡データではRandomUnderSamplerを使ってみよう
機械学習の分類問題では、
陽性クラス・陰性クラスのデータ数に偏りがある
不均衡データによく直面します。
![](https://assets.st-note.com/img/1709427987422-BK06TxUXrN.png?width=1200)
このような不均衡データでは、
モデルが多数クラスの特徴を過剰に学習し、
少数クラスを適切に識別できなくなることで、
性能に悪影響を及ぼします。
特に少数クラスの予測は難しくなります。
この問題に対処するための方法はいくつかあります。
最も手っ取り早いのは、
陽性クラスと陰性クラスの数をなるべく
あわせることです。
そのために、
少数クラスを増やしたり=オーバーサンプリング、
多数クラスを減らしたり=ダウンサンプリング、
する方法があります。
この記事では、
機械学習用のPythonライブラリーの一つ
RandomUnderSamplerを活用した
ダウンサンプリングの方法についてご紹介します。
RandomUnderSamplerの原理
RandomUnderSamplerは、
多数クラスからランダムにデータを削除することで、
クラス間のバランスを調整するものです。
具体的には、
少数クラスと多数クラスのデータ数を
パラメータとして与えた割合に
することが可能です。
例えば、
もともとのデータが、
少数クラス:多数クラス =1:9の割合だったとして、
これを
少数クラス:多数クラス =1:3の割合にしたり
少数クラス:多数クラス =1:1の割合にしたり
することが可能です。
これにより、モデルがデータに適した学習をすることができます。
![](https://assets.st-note.com/img/1709428466404-jhnMTjuGdp.png?width=1200)
RandomUnderSamplerの実装
imbalanced-learnライブラリからインポートします。
インスタンス化し、
不均衡データ(X)と
陽性・陰性のターゲットデータ(y)を与えるだけです。
from imblearn.under_sampling import RandomUnderSampler
# RandomUnderSamplerのインスタンス化
rus = RandomUnderSampler(sampling_strategy=1.0 , random_state=42)
# データセットの再サンプリング
X_res, y_res = rus.fit_resample(X, y)
陽性・陰性の比率の調整は、sampling_strategyで行います。
少数クラス÷多数クラスの割合を指定します。
sampling_strategy=1.0とすると、
少数クラス:多数クラス=1:1になります。
sampling_strategy=0.5とすると、
少数クラス:多数クラス=1:2になります。
ダウンサンプリングの留意点
RandomUnderSamplerの主な利点は、
実装の容易さと、
クラス間のバランスを効果的に改善できることです。
しかしながら、
多数クラスからランダムにデータを削除することで、
重要な情報が失われる可能性があります。
これはダウンサンプリングの宿命と言っていいかもしれません。
乱数シードを活用した実装
ダウンサンプリングにおける
情報喪失の問題を少しでも和らげるため、
乱数シードを活用する方法があります。
ダウンサンプリングは
多数クラスからデータをランダムに削減し、
少数クラスとの均衡を図ります。
多数クラスの中からどのデータを削除するかは
乱数(正確には乱数シードの数)によって決まることになります。
この乱数(乱数シード)を複数発生させれば、
複数通りのサンプリングが可能となり、
情報喪失の問題が少しでも和らぐことになります。
以下では、具体的なPythonコードを示しています。
LightGBMの学習部分だけを取り出したものです。
rus_seedsで複数の乱数シードを設定し、
for文で回すことで
trainデータ(訓練データ)を
何通りもアンダーサンプリングします。
これにより情報喪失の問題が少し軽減できますね。
from imblearn.under_sampling import RandomUnderSampler
import lightgbm as lgb
import numpy as np
import pandas as pd
rus_seeds = [2, 12, 32, 42, 52, 62, 72]
num_rus = len(rus_seeds)
#lightgbm
def lightgbm_train(x_train, y_train, x_valid, y_valid , features, categorical_features):
valid_pred = np.zeros(len(x_valid))
models_seed =[]
for seed in rus_seeds:
# インスタンス化
rus = RandomUnderSampler(sampling_strategy=0.5, random_state=seed)
# クロスバリデーションの中のtrainデータだけをアンダーサンプリング
x_rus, y_rus = rus.fit_resample(x_train, y_train)
lgb_train = lgb.Dataset(x_rus, y_rus, categorical_feature=categorical_features)
lgb_valid = lgb.Dataset(x_valid, y_valid, categorical_feature=categorical_features)
model = lgb.train(
params = lgb_params,
train_set = lgb_train,
num_boost_round = num_boost_round,
valid_sets = [lgb_train, lgb_valid],
feval = lgb_metric,
callbacks=[lgb.early_stopping(stopping_rounds=early_stopping_round,verbose=verbose)])
valid_pred += model.predict(x_valid)/ num_rus
models_seed.append(model)
return models_seed, valid_pred
なお、乱数シードの発生にあたっては、
クロスバリデーション(K-分割交差検証)とセットで
行うのが良いでしょう。
上記もクロスバリデーションの中で
学習することを想定したコードになっています。
【クロスバリデーション(Kー分割交差検証)とは】
データをk個のグループに分割して,k個のうちひとつのグループをテストデータとして,残りのデータを訓練データとします。全てのグループがテストデータになるようk回繰り返します。これにより、モデルの汎化性能を高めようとするものです。
よろしければサポートお願いします! いただいたサポートはクリエイターとしての活動費に使わせていただきます!