Boostingモデルを用いてクラス間のアンバランス問題を克服する


現在、保険会社の保険詐欺事件を摘発するモデリング作業が行われている.
Goal 1. 不審な上位プレイヤーへの摘発率を高める
Goal 2. 補償を最小化するモデリング
2種類あり、1日にある程度完成して、1日の過程で応用する方法がブログに残っています.
FDSを行う場合、Froudクラスは通常Normalクラスより非常に少ない.
この場合,Cross Engropy学習モデルではRecall値が低いという欠点がある.この現象をUnderEstimateと呼ぶと、ちょっと無理かな…、まず,Less Focusに集中する傾向が確認できた.
多くのクラスでC−E値を減少させる方向は,少数のクラスでC−E値を減少させる方向よりも全体的なロスプロトコルの観点に合致するからである.

(Fraud Classが小数クラスの場合、FPの数はFNよりかなり低く、TPは非常に少ない…)
これらの問題を解決するために、
1.ダウンサンプリング/オーバーサンプリングによるクラス間のアンバランスの低減または解消(最も一般的)
2.Focal Lossを適用するなどの方法があります.
他の方法(特にBoostingモデルにおいて)は不明である.(Soft Label-Puso Learningはこのフレームワークでは目標値が確率値をサポートしないため不可能であると考えている.)
実は私が使っている方法は次の2種類のWeighted Focal Lossを混ぜたものです.
△とても簡単です.
それ以外に、オーバーサンプリングは効果がなく、ダウンサンプリングは性能がわずかに向上したが、直感的に(統計的に)意味のない数字である.
  • Weighted Cross Entropy

  • 小数クラスに重み付け(通常定数,nと呼ばれる)を1つ増やし,勾配(罰)をn倍に増やした(罰が大きくなるにつれてモデル学習では小数クラスがフォーカス効果を持つ)

  • 限界:分類が難しいケースでは、モデルを学習する際にモデルに重点を置くのは難しい
  • Focal Loss

  • 詳細をスキップ
    (他のブログの良いコメント:https://gaussian37.github.io/dl-concept-focal_loss/)

  • 基本概念は分類しにくいケースを大幅に罰し,モデル学習時にこのケースに重点を置く.

  • どうしよう?勾配と罰に対して確率に関する重み付けを行い,答えクラスが1であれば確率pが1に近づくと小罰,0に近づくと大罰を付加する.

  • 具体的な実施方法
    grad:(y-p)、次のように変更します.
    if target == 1, (1-p) x (y-p) 
    elif target == 0, p x (y-p)
    もしそうであれば,ターゲットが1であれば出力確率が高く,良好な学習を経たため,小さなペナルティ(1−p)が付加される.

  • 次に,Hessian:Wegradはxを偏微分した.
    注意:https://stats.stackexchange.com/questions/231220/how-to-compute-the-gradient-and-hessian-of-logarithmic-loss-question-is-based
  • f = p x (y-p) = py - p^2
    df/dx=(df/dp)*(dp/dx).
    df/dp = y - 2p
    dp/dx = p(1-p)
    従ってdf/dx=(y−2 p)p(1−p)であった.(計算ミスが出るかも…)
    ここではy=1です.(target=1なのでgradは(1−p)x(y−p)である.)
    最終的に、target=1の場合、hessianは(1−2 p)p(1−p)である.
    次のようにコードで表現します.(CatBoostを適用)
    import math
    from six.moves import xrange
    
    
    class LoglossObjective(object):
        def calc_ders_range(self, approxes, targets, weights):
            assert len(approxes) == len(targets)
            if weights is not None:
                assert len(weights) == len(approxes)
            
            result = []
            for index in range(len(targets)):
                e = np.exp(approxes[index])
                p = e / (1 + e)
                der1 = targets[index] - p
                der2 = -p * (1 - p)
                
    
                if (targets[index] == 0.0) & (p<0.5):
                    # 타겟 값이 정상 유저인데, 정상이라고 판단한 경우 ( True Negative )
                    der1 = (p)*(targets[index] - p)
                    der2 = (1-2*p)*(-p * (1 - p))
                    
                elif (targets[index] == 0.0) & (p>=0.5):
                    # 타겟값이 노말 유저인데,fraud 라고 예측한 경우 ( False Positive )
                    der1 = 3*(p)*(targets[index] - p)
                    der2 = 3*(1-2*p)*(-p * (1 - p))
    
                elif (targets[index] > 0.0) :
                    # 타겟 값이 fraud 이라면 p값이 높을수록 (정답에 가까울수록) 페널티가 적도록 세팅. 추가로 3배 페널티
                    der1 = 3*(1-p)*(targets[index] - p)
                    der2 = 3*(2*p-1)*(-p * (1 - p))
                    
                if weights is not None:
                    der1 *= weights[index]
                    der2 *= weights[index]
    
                result.append((der1, der2))
            return result
    小数クラスの目的を達成するために、Tuningの手順を使用しました.
    上にpを乗じるか、p^2を乗じるか、p^1.5を乗じるか.
    △pの勝数が高ければ高いほど、推測しにくい状況に集中する.しかし、これは必ずしも私たちの望み通りではない.
    上のnは1か2か3か...ランプも試してみて、どの性能が一番いいかを知ることができます.
    このようにFocal Lossを使うのも欠点があります.
    1.失われたFunc内の分岐点が多くなり、(CatBoost)フレームワークは並列演算をサポートせず、学習速度が遅くなる.
    2.モデル出力は確率値ではありません.また、既存の出力に比べて過小評価されます.
    それでも、学生に目的に合ったモデルを学ぶことができます.
    (目的:Fraud不審者上位30%のユーザ内摘発率上昇)
    実際,上記の目標を基準として,既存モデルの65%から76%に上昇し,他の深さ学習SOTAモデルの組合せ結果よりも同一またはそれ以上の性能を示した.
    以降のCatBoost実行コードは以下の通りです.
    (実際の業務で適用されるpの乗数、n重み付け、焦点微分/偏微分値は、以下のコードとは異なる.)
    %%time
    
    from catboost import CatBoostClassifier, Pool
    
    
    for i in range(n_splits):
        print('='*20, str(i+1), '='*20) 
        
        cat_feat_ls = []
        for item in list(data[cat_cols].select_dtypes(include='category').columns):
            cat_feat_ls.append(item)
        
        cat_features = cat_feat_ls
    
        train_data = globals()[f'train_feat_{i+1}']
        train_labels = globals()[f'train_label_{i+1}']
        
        valid_data = globals()[f'valid_feat_{i+1}']
        valid_labels = globals()[f'valid_label_{i+1}']
    
    
        globals()[f'CatB_model_{i+1}'] = CatBoostClassifier(iterations=15000,
                                                            depth=12,
                                                            thread_count=12,
                                                            learning_rate=1e-3,
                                                            loss_function=LoglossObjective(),
                                                            eval_metric='PRAUC',
                                                            early_stopping_rounds=400,
                                                            
                                                            verbose=1000)
    
        # train the model
        globals()[f'CatB_model_{i+1}'].fit(train_data, train_labels, cat_features, eval_set = (valid_data,valid_labels))
    
        globals()[f'CatB_model_{i+1}'].save_model(f'CatB_C_Penalty_v6_fold_{i+1}')  
        print(str(i+1),'fold model saved')
    ここでEveal metricはPrecision Recall曲線の下面積であり,大量のクラスのアンバランスデータを学習する際に参照できる評価指標である.
    しかし,必ずしも高い性能を意味するものではなく,他の評価指標n個(ex.AUC,MC...,F 1,F 2)などn個のモデルで学習した後,最も性能のよいモデルを採用する.
  • に役立つ正式なファイルはH 2 Oフレームの正式なファイルです.(逆に詳細すぎる、https://docs.h2o.ai/h2o/latest-stable/h2o-docs/performance-and-prediction.html)
  • (私の場合)現在リリースされているデータは、通常のユーザーの約99%と詐欺ユーザーの1%であり、AUC評価指標は(会社が望んでいる)性能が最も優れていることを示しています.
    数日前、「データワイルド」の虚偽コメント発表で、LGBMはFocal Loss+(TomkLink+)OverSampling(具体的な適用方法を共有してほしい)を書いた.