カラムがあったらとりあえず件数をカウントしておきたくなるのがデータサイエンティストの人情。以前にもデータが不均衡だったせいでクロスバリデーションが上手くいかずに痛い目を見た経験があります。※
そんなこんなもあって不均衡は見ておきたいので確認していきましょうというお話です。
まずカウント
何はともあれ不均衡であることを確認します。
import matplotlib.pyplot as plt df = clens_train.copy() # 適切なデータフレーム名に置き換えてください # 選択したカラムを指定 selected_columns = ['Gender'] # 適切なカラム名に置き換えてください # 各カラムの件数をプロット for column in selected_columns: plt.figure(figsize=(8, 6)) # カラムの値ごとの件数を取得 value_counts = df[column].value_counts() # 件数を棒グラフでプロット value_counts.plot(kind='bar') # グラフのタイトルとラベル設定 plt.title(f'{column}') plt.xlabel(column) plt.ylabel('records') # プロットの表示 plt.show()
手持ちデータだとこんな感じ。件数が偏ってそうなのが見て取れるかと思います。
不均衡データを補正するアイディアとその判断基準
で、じゃあどうするの?という部分ですが、不均衡データ対策にはいくつかの種類があるので有名どころを書いておきます。
1. (前提)欠損は埋めて質的データはエンコードしておく(データ処理でレコード数が変わるし質的データはサンプリングできないので)
2. **オーバーサンプリング**
- **概要**: 少数派クラスのデータを増やす。
- **判断基準**: データ量が少なく、少数派クラスの情報を保持したい場合。データの増加による計算負荷が許容できる場合。
3. **アンダーサンプリング**
- **概要**: 多数派クラスのデータを減らす。
- **判断基準**: データ量が多すぎて処理が重い場合や、少数派クラスのデータが十分にある場合。データ削減による情報損失が許容できる場合。
4. **合成的サンプリング(SMOTEなど)**
- **概要**: 擬似的なデータポイントを生成。
- **判断基準**: データの分布を考慮しつつ、少数派クラスのデータを増やしたい場合。データの合成がモデルの性能向上に寄与する場合。
5. **重み付け**
- **概要**: モデルの学習時にクラスに重みを付ける。
- **判断基準**: サンプリングによるデータの変更を避けたい場合や、モデルが重み付けをサポートしている場合。
6. **異常検知問題として扱う**
- **概要**: 少数派クラスを異常として検出。
- **判断基準**: 少数派クラスが非常に稀なイベントである場合。
yes/noで判断すると
1. **計算資源が少ない**:
- **アンダーサンプリング**を検討。計算資源が限られている場合、データ量を減らすことで処理を軽減できます。ただし、カラムが消えたりするので注意が必要です。
2. **計算資源が十分だけどデータ量が少ない**:
- **オーバーサンプリング**を検討。少数派クラスのデータを増やすことで、クラスバランスを改善し、モデルの性能を向上させます。
3. **計算資源もデータ量も十分で、少数クラスが重要**:
- **オーバーサンプリング**や**合成的サンプリング(SMOTEなど)**を検討。少数派クラスの精度を高めるために、これらの手法を用いてデータを増やします。
4. **計算資源もデータ量も十分で、少数クラスが重要でない**:
- **重み付け**や**合成的サンプリング**を検討。クラスの重要性が低い場合でも、全体のバランスを考慮して適切な手法を選択します。
このように計算資源やデータ量、少数クラスの重要性に応じて適切な手法を選択することで、モデルの性能を最大限に引き出すことができます。
データ量の多い少ないは賛否分かれますが、個人的には
件数→1万件未満かつ
カラム→50未満
がデータ量が少ない、と判断してます。今回の例では合わせても3000件程度なのでオーバーサンプリングを採用してみます。
import pandas as pd from imblearn.over_sampling import SMOTE def oversample_columns(df, columns_to_oversample, target_column): oversampled_df_list = [] for column in columns_to_oversample: # 特徴量とターゲットの分割 X = df[[column]] y = df[target_column] # SMOTEによるオーバーサンプリング smote = SMOTE(random_state=42) X_res, y_res = smote.fit_resample(X, y) # オーバーサンプリングされたデータをデータフレームに変換 df_resampled = pd.concat([X_res, y_res], axis=1) # オーバーサンプリングされたデータをリストに追加 oversampled_df_list.append(df_resampled) # すべてのオーバーサンプリングされたデータを結合 oversampled_df = pd.concat(oversampled_df_list).drop_duplicates() return oversampled_df # 使用例 # df: 対象のデータフレーム # columns_to_oversample: オーバーサンプリングを行いたいカラムのリスト # target_column: 予測ターゲットとなるカラム df = clens_train.copy() columns_to_oversample = ['column1', 'column2', 'column3'] oversampled_df = oversample_columns(df, columns_to_oversample, 'target')
結果はこちら。
(GenderからGneder_maleという名称に変わってしまっているのはご容赦ください。上のコードでは起こりません。)
他のサンプリングは、そのうち追記します笑笑。ご参考までにどうぞ!
※データが偏っていたためにデータをスプリットしたら複数のリーフが女性100%みたいになって精度がぐちゃぐちゃになった。
参考
Feature Engineering A-Z | Imbalanced Overview