プログラミング
記事内に商品プロモーションを含む場合があります

SMOTE:不均衡データをオーバーサンプリングして数を揃える手法

SMOTEを用いた不均衡データオーバーサンプリング
tadanori

SMOTE(Synthetic Minority Over-sampling Technique)は、不均衡データの少数派データを増やすオーバサンプリング(Oversampling)手法です。この手法は、データがアンバランスなために予測がうまくいかない場合に利用します。この記事では、PythonでのSMOTEの使い方を説明します。

はじめに

データがクラスによって不均衡な場合、オーバーサンプリングする、ダウンサンプリングするなどがが考えられますが、ここで紹介するのはオーバーサンプリングする手法になります。

例えば、yesが1割、noが9割あるデータで予測モデルを作成した場合、すべてnoと答えたら90%正解するのですべてnoと答える予測モデルができるかもしれません。ここまで極端でなくても、なるべくnoと予測する方が有利なのに代わりはありません。

こういう場合、LightGBMなどでは重みつきのフィッティングをすることが可能ですが、前処理でデータ数を均等にしておくという方法もあります。SMOTEは後者の、データ数を均等にするための手法になります。

データが不均等というのは結構あります。こういう時に使う手法の1つとして覚えているおくと良いかも。

データの前処理・特徴量エンジニアリングについては以下の記事にまとめています

SMOTEとは

SMOTE(Synthetic Minority Over-sampling Technique)は、不均衡データセットのクラス間のサンプル数の差を解消するための一般的なオーバーサンプリング手法です。

不均衡なデータセットとは、あるクラスのサンプル数が他のクラスに比べて著しく少ないデータセットのことです。このような不均衡は、機械学習モデルの性能に悪影響を及ぼす可能性があり、SMOTEはこの問題に対処するために利用します。

簡単に言えば、データの水増し手法になります

SMOTEでは、少ない方のサンプルに対して合成したサンプルを作成することで、各クラスのデータ数のバランスを調整します。具体的には、少数派のサンプルとその近傍のサンプルを組み合わせて新しいサンプルを作成します。これにより、少ないサンプルのデータが増え、データセットのクラスバランスが改善します。

以下、SMOTEの処理の流れの概要です。

SMOTEの処理概要
  1. サンプリング:
    少数クラスのサンプルをランダムに選択
  2. k最近傍の決定
    選択したサンプルに最も近いk個の少数クラスのサンプルを見つける
  3. 合成サンプルの生成
    選択したサンプルとそのk最近傍サンプルの間に新しいサンプルを合成します。この合成は、選択したサンプルと最近傍サンプルの間の線上にランダムに点を生成
  4. プロセスの繰り返し
    必要な数のサンプルが生成されるまで①〜③をを繰り返す

SMOTEを利用する利点をまとめると以下になります

  • クラス間のバランスの改善
    少数クラスのサンプル数を増やすことで、データセットのクラス間のバランスを改善することができます。これにより、機械学習モデルが少数クラスをより良く学習できるようになります
  • 過学習のリスクの低減
    単純なオーバーサンプリング(少数クラスのサンプルを単に複製すること)と比較して、SMOTEは合成サンプルを生成するため、モデルが単に既存のサンプルを記憶するのではなく(過学習する可能性もある)、少数クラスの一般的な特性を学習するのに役立ちます
  • データの多様性の向上
    合成サンプルの生成により、データセット内の多様性が向上します。これにより、モデルがより複雑なパターンを捉える能力が向上する可能性があります。

ただ、SMOTEがすべての場合に有効なわけではないことに注意が必要です。

データセットの特性や問題の性質に応じて、他のオーバーサンプリング技術やアンダーサンプリング技術の組み合わせを検討することも重要です。ここは、トライ&エラーによる試行錯誤が必要な部分です。

PythonでSMOTEを使ってみる

SMOTEは、imbalanced-learnというライブラリに入っていますのでこれを利用します。インストールされていない場合は、以下の手順でインストールできます。

pip install -U imbalanced-learn

実際に不均等なデータを使って、使い方と動作を確認します。作成するデータセットは、クラス0が100個、クラス1が1000個のデータです。

X1 = np.random.normal(1,1,200).reshape(100, 2)
X2 = np.random.normal(-1,1,2000).reshape(1000, 2)
y1 = [0 for _ in range(100)]
y2 = [1 for _ in range(1000)]
X = np.concatenate([X1, X2])
y = np.concatenate([y1, y2])
print(f"counts : class0 = {sum(y == 0)}, class1 = {sum(y == 1)}")

クラス0のデータは(1,1)を中心とした標準偏差1の分布、クラス1は(-1,-1)を中心とした標準偏差1の分布で生成しています。生成した数を見ると、以下のように100:1000になっていることがわかります。

counts : class0 = 100, class1 = 1000

グラフにすると、こんな感じ。

圧倒的に赤点が多いです。これにSMOTEをかけてみます。コードは以下の通り。

sm = SMOTETomek(random_state=42)
X = X.reshape(1100,2)
X_res, y_res = sm.fit_resample(X, y)
print(f"resample result : class0 = {sum(y_res == 0)}, class1 = {sum(y_res == 1)}")

使い方は非常に簡単で、SMOTETomek()でオブジェクトを作り、fit_resample()を実行するとオーバサンプリングされたデータが戻ります。なお、入力のXは説明変数、yはクラス分類になります。例ではXは説明変数が2つあるデータ(2次元でプロットしたかったので、2つにしました)になります。

処理すると、各クラスのサイズは以下のようになり、バランスが調整されたことがわかります。

resample result : class0 = 965, class1 = 965

これをグラフにすると青点が増えていることがわかります。

「少数派のサンプルとその近傍のサンプルを組み合わせて新しいサンプルを作成」するため、なんとなく青の分布が気になりますが(直線で繋げてるように見える)、とりあえず、オーバーサンプリングされました。

一応、青の分布を調べてみると

XX = X_res[y_res == 0]
XX.mean(axis=0), XX.std(axis=0)
(array([0.95607874, 1.19124738]), array([0.93733671, 0.88194509]))

となって、中心(1,1)・標準偏差1から少しずれている感じです(まぁ、維持するような変換ではないのでしょうがないですが)。

一方、赤は、

(array([-1.0443179, -1.025522 ]), array([1.01322938, 0.978189  ]))

なので、こちらは中心(-1,-1)・標準偏差1に近い分布なっています。

ところで、PyCaretというライブラリでは、前処理でSMOTEを利用することができます。興味がある方は、以下の記事も参考にしてください。

あわせて読みたい
機械学習の初期検討を効率化|PyCaretを利用したモデル選択の自動化
機械学習の初期検討を効率化|PyCaretを利用したモデル選択の自動化

まとめ

データが不均衡であるというのは結構あります。特に異常検知などでは、異常に分類されるデータが不足することが多いです。このような場合、SMOTEを使ったオーバーサンプリングが有効かもしれません。ただし、万能な手法ではないため、精度が向上するかどうかはデータ次第です。

おすすめ書籍

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

記事URLをコピーしました