アセットマネージャーのためのファイナンス機械学習:特徴量の重要度分析 練習問題 確率荷重正解率を使った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}$$に対し、$${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の値は違ってくるが、PWAではマイナスに振れる結果が出ないため、純粋な重要度の解釈がしやすくなっている。