アセットマネージャーのためのファイナンス機械学習:特徴量の重要度分析 練習問題 確率荷重正解率を使ったMDA

予測確率と実際の結果の乖離を示す対数損失(log_loss)は、以下のように確率と外れたときのペナルティを大きく与える。

def logloss(y_pred, pred_proba, eps=1e-15):
    p=np.clip(pred_proba, eps,1-eps)
    if(y_pred==1):
        return np.log(p)
    else:
        return np.log(1-p)

p=list(np.arange(1e-10,1,0.01))
ll=lambda x:logloss(1,p)
plt.plot(p,-ll(p))
plt.xlabel('predicted probability')
plt.ylabel('log_loss')
y_pred=1 log_loss


$${y_{pred}=1}$$に対し、$${y_{pred}=1}$$の予測確率が小さいときに、ペナルティは指数で跳ね上がる。
 このペナルティの上がり方を穏やかにする確率荷重正解率、
$${PWA=\displaystyle{ \frac{\sum^{N-1}_{n=0}y_n(p_n-K^{-1})}{\sum^{N-1}_{n=0}(p_n-K^{-1})} }}$$をスコアリングにしてMDAを適用する。

def PWA(y_pred,y_true,predict_proba):
    
    n_samples=len(y_pred)
    invK=np.full((n_samples),1./float(predict_proba.shape[1]))
    indY=(y_true==y_pred)*1
    max_proba=np.amax(predict_proba,axis=1)
    s1=(max_proba-invK).sum()
    s0=np.dot(indY,max_proba-invK)
    
    return s0/s1

getFeatImpMDA(clf,X,y,cv,sample_weight,t1,pctEmbargo,scoring='neg_log_loss'):
    # features imporant based on OOS score reduction
    import FinCV as fcv
    if scoring not in ['neg_log_loss','accuracy','prob_wgt_acc']:
        raise ValueError('wrong scoring method.')
    from sklearn.metrics import log_loss, accuracy_score
    cvGen=fcv.PurgedKFold(n_splits=cv,t1=t1,pctEmbargo=pctEmbargo) # purged cv
    scr0,scr1=pd.Series(), pd.DataFrame(columns=X.columns)

    for i,(train,test) in enumerate(cvGen.split(X=X)):
        X0,y0,w0=X.iloc[train,:],y.iloc[train],sample_weight.iloc[train]
        X1,y1,w1=X.iloc[test,:],y.iloc[test],sample_weight.iloc[test]
        fit=clf.fit(X=X0,y=y0,sample_weight=w0.values)
        if scoring=='neg_log_loss':
            prob=fit.predict_proba(X1)
            scr0.loc[i]=-log_loss(y1,prob,sample_weight=w1.values,
                                  labels=clf.classes_)
        elif scoring=='accuracy':
            pred=fit.predict(X1)
            scr0.loc[i]=accuracy_score(y1,pred,sample_weight=w1.values)
        else:
            prob=fit.predict_proba(X1)
            pred=fit.predict(X1)
            scr0.loc[i]=PWA(pred,y1,prob)

    for j in X.columns:
        X1_=X1.copy(deep=True)
        np.random.shuffle(X1_[j].values) # permutation of a single column
        if scoring=='neg_log_loss':
            prob=fit.predict_proba(X1_)
            scr1.loc[i,j]=-log_loss(y1,prob,sample_weight=w1.values,
                                    labels=clf.classes_)
        else:
            pred=fit.predict(X1_)
            scr1.loc[i,j]=accuracy_score(y1,pred,sample_weight=w1.values)
    imp=(-scr1).add(scr0,axis=0)
    if scoring=='neg_log_loss':imp=imp/-scr1
    else: imp=imp/(1.-scr1)
    imp=(pd.concat({'mean':imp.mean(),
                    'std':imp.std()*imp.shape[0]**-0.5},
                   axis=1))
    return imp,scr0.mean()

X, y = getTestData(40, 5, 30, 10000,sigmaStd=.1)
imp,oos=getFeatImpMDA(clf,X=X,y=y['bin'],cv=10,
                           sample_weight=y['w'],t1=y['t1'],
                           pctEmbargo=0,scoring='prob_wgt_acc')
imp.sort_values('mean', inplace=True)
plt.figure(figsize=(10, imp.shape[0] / 5))
imp['mean'].plot(kind='barh', color='b', alpha=0.25, xerr=imp['std'], error_kw={'ecolor': 'r'})
plt.title('MDA results')
plt.show()
MDA with PWA
MDA with accuracy

ペナルティの与え方が違うため、MDAの値は違ってくるが、PWAではマイナスに振れる結果が出ないため、純粋な重要度の解釈がしやすくなっている。

いいなと思ったら応援しよう!