2019-10-04 公開
機械学習を行う際、正例(y = 1)と負例(y = 0)との割合が極端に偏っているケースがある。
割合が偏っていると、データの多い方を優先して予測してしまうことが多く、予測結果がすべてy = 0なんてことも。悲惨である。
そういう場合
- 多すぎるラベルのデータから一部を抽出して割合を近くする(Under Sampling)
- 少ないラベルのデータをかさましして割合を近くする(Over Sampling)
- 正例・負例の学習時に重み付けを行う
といった対策が考えられる*1。
Pythonであればimblearn
というライブラリを使用すると、手軽にUnder SamplingやOver Samplingができて便利。
[参考]
とても便利なimblearn
だが、Under Samplingを行う際の
- 抽出したデータだけでなく、抽出されなかったデータも使用したい
- 元のデータとの繋がり(インデックス)を失いたくない(pandasのデータフレームをnampyのndarrayに変換したくない)
といったささやかな願いを叶えるのが少しだけ面倒。
そこで、これらの願いを叶える方法を以下にメモする。
なお、以下のコード例の主目的は「UnderSamplingで抽出しなかったデータを取得」することだが、副産物として「元のindexを保持したままでの抽出」が可能になっている*2。
コード例
from imblearn.under_sampling import RandomUnderSampler # RandomUnderSampler resampler = RandomUnderSampler(sampling_strategy=1/1, random_state=64) _, _ = resampler.fit_sample(X, y) # Under Samplingを適用, .fit_sampleの返り値の型がnp.ndarrayなので捨てる # Under Samplingされたインデックスを取得 # Xのうち、どのデータが抽出されたかが分かる # インデックスそのものではなく、Xの何番目のデータが抽出されたかが記録されている点に注意 re_indices = resampler.sample_indices_ # こちらではUnder Sampling前のデータとre_indicesを使用して、 # Under Samplingされたデータのインデックスそのものを取得している re_idx = y.index[re_indices] # 元のデータとインデックスで結合するためのkeyになる) # Under Samplingされたデータを抽出 X_resampled = X.loc[re_idx] # re_idxが順番ではなくインデックスそのものなので、.ilocではなく.loc y_resampled = y.loc[re_idx] # Under Samplingされなかったデータのインデックスそのものを抽出 # Under Sampling前のデータのインデックスのre_indices番目のインデックスを削除 not_re_idx = y.index.delete(re_indices) # 元のデータとインデックスで結合するためのkeyになる) # Under Samplingされなかったデータを抽出 X_not_resampled = X.loc[not_re_idx] # not_re_idxが順番ではなくインデックスそのものなので、.ilocではなく.loc y_not_resampled = y.loc[not_re_idx]
案外お役に立つかもしれない。
[参考書籍]
pandasのインデックスでindex
からlist
を除外したindex
を取得するためのメソッド.delete()
を見つけるのが結構大変だった。
「どんなクエリで検索すればよいのやら…。」と困っていたところ、以下の本をパラッとめくれば速攻で見つかった。
手元にあって良かった。なんだかんだ、いつも助かっている。ありがとうございます。

Pythonによるデータ分析入門 第2版 ―NumPy、pandasを使ったデータ処理
- 作者: Wes McKinney,瀬戸山雅人,小林儀匡,滝口開資
- 出版社/メーカー: オライリージャパン
- 発売日: 2018/07/26
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
[関連ツイート]
不均衡データのクラス分類におけるunder samplingやSMOTE、imbalanced-learnってライブラリ使えば楽なのね。便利。
— こここ🍀 (@kokokocococo555) September 20, 2019
pandasのDataFrameやSeriesがnp.ndarrayに変換されて少し悲しいけど…。
使い方の参考:https://t.co/30wIFBJ3tShttps://t.co/FaZ32NPynN
いろんなSMOTE:https://t.co/Ud3X2h0Wwq
imbalanced-learnライブラリのRandomUnderSamplerやSMOTE、コンストラクタの引数でratioを使うのをやめてsampling_strategyを使え、とのこと。https://t.co/RrSxeMEb10
— こここ🍀 (@kokokocococo555) September 20, 2019
→
— こここ🍀 (@kokokocococo555) September 22, 2019
A, B, Cのどれか1つが1、それ以外は0のダミー変数では、SMOTEで作られたデータでは2つ以上が1のケースもある。
そして2つ以上に1が入っているデータは正例, それ以外は負例と学習する。
そうなると1つだけしか1が入っていないデータしかないテストデータでは全部負例と予測することになる、と。
正例 : 負例 = 1 : 1 にランダムに予測した場合
— こここ🍀 (@kokokocococo555) September 24, 2019
precision = 元の {正例, 負例} の割合
recall = 0.5
となるはず。
そのため、Under Sampling (以下US) 正例 : 負例 = 1 : 1 で学習した際に、US前に分離したtestdataでの結果が上の割合を上回っていれば、「ランダムな予測よりはまし」と言える?
Under Sampling、除外するデータを増やすほどprecisionが下がってrecallが上がる印象あるけどこれ正しいのかな。
— こここ🍀 (@kokokocococo555) October 3, 2019
サンプリングが偏ってるのかな…。
*1:Over Samplingは良い結果を生まない印象(SMOTEだけ?)。SMOTEを使用すると、valid段階ではそこそこ良い精度。しかし、元のデータから前もって分離しておいたデータ(元の比率を維持したデータ)を対象とした予測では、割合の大きい方のラベルばかり予測するという散々な結果に。原因はダミー変数か?(こここ🍀 on Twitter: "Over Samplingしたときに過学習になっている問題、ダミー変数が原因かもしれない。 →")ちなみに、Twitterでも2019-09の下旬頃「Over Samplingは微妙」という話で少し盛り上がりが見られた。
*2:pandasのDataFrame, Seriesを使用している場合