[python]機械学習の回帰問題でMSE以外でモデルを評価する方法


はじめに


機械学習で回帰問題に取り組む際、モデルの評価には一般にmse(平均二乗誤差)が用いられます。しかし、実際にやっているとmseでは分からない場合もあるんですよね。そこで、今回はmse以外で回帰モデルを評価する方法を考えたので紹介しようと思います。



mseの弱点


mseの弱点は以下だと思います。


  1. データごとの傾向が分からない

  2. 少数の外れ値に引っ張られる


具体的に見ていきましょう。


1. データごとの傾向が分からない


mseはあくまで予測と実際のずれの「平均」を表しているので、個々のデータにおける予測の精度が分からないんですよね。下の図1を見てください。どちらもmseは同じなのですが、左側ではずれが均等なのに対して、右側では特定のデータだけにずれが偏っています。また別の例では、あるカテゴリのデータではそれなりの精度が出ているのに、別のカテゴリでは全然当たっていないかもしれません。このような実情もmseだけを見ていると「平均」に埋もれてしまいます。


図1 どちらもmseは同じ

2. 少数の外れ値に引っ張られる


これも1.に含まれるといえば含まれるのですが、mseを用いると細かいずれがたくさんあるのか、大きいずれが少数あるのか区別がつきません(下の図2は2つともmseは同じ値です)。kaggleなどではこれを避けるために外れ値は事前にはじくという対策がとられますが、ビジネスの場合にはそうはいかない場合もあるかもしれません。外れ値の除去処理を忘れているのに気づかないかもしれません。


図2 どちらもmseは同じ

どこが問題?


「で、それのなにが悪いねん」と思われた方もいるかもしれません。悪いというか、上に挙げたようなことが分かると、作ったモデルの現状が分かり、改善策が浮かびやすくなります。

例えば、

  • スケール変換や外れ値の処理は適切か?

  • 何か別の共通項があって、それを新たに特徴量に加えればいいのではないか?

  • ビジネス上の用途を考えれば、無視してもいいデータではないか?

などが考えられます。

3つ目について補足すると、特に精度を重視したいデータと、そこまででもないデータというのがあるかもしれません。(例えば、機械の寿命を予測するようなアプリケーションにおいては、十分余裕のあるところでは多少のずれは許容できますが、寿命が近い場合の予測は精度を良くする必要があります。)



解決策


1. 実データ - 予測値のグラフ


考えられる解決策の1つ目は、図1,2のようなx軸に実データ、y軸に予測値をとったグラフを描くことです。


以下y_realが実データ、y_predが予測値です。


plt.plot(y_real, y_pred, 'bo')
plt.xlabel('real', fontsize=12)
plt.ylabel('predeiction', fontsize=12)

ただ、モデルの精度を示す指標はないのであくまで全体を俯瞰することが目的です。


2. 予測誤差-分位数グラフ


1のグラフだと精度がどの程度かが分からないので、予測誤差の低い方から50%、75%、90%の値を求め、グラフ化します。


まずは各データにおける予測誤差を求めておきます


def get_pred_ratio(y_real, y_pred):

    ratios=[]    
    
    for real, pred in zip(y_real, y_pred):
    
      if real!=0:
       ratio=abs(real-pred)/real
         else:
             ratio=abs(real-pred)
                                                                          
         ratios.append(ratio)   
         
    return ratios          

numpyのpercentile関数で各分位数を求めます。

y_ratio=get_pred_ratio(y_real, y_pred)
percent=[50, 75, 90]
ratio_p=[]

for p in percent:
    ratio_p.append(np.percentile(y_ratio, p))
    
plt.figure()
plt.plot(percent, ratio_p, 'bo')
plt.xlim([0, 100])
plt.ylim([0, 1])
plt.xlabel('Percent (%)', fontsize=12)
plt.ylabel('Prediction ratio', fontsize=12)


自分は最終的には上2つを次のようにまとめて使っています。

from sklearn.metrics import mean_squared_error as mse
    
def eval_regression(y_pred, y_real):

    y_ratio=get_pred_ratio(y_real, y_pred)
    percent=[50, 75, 90]
    ratio_p=[]
      
    for p in percent:
       ratio_p.append(np.percentile(y_ratio, p))
       
    print('mse: ', mse(y_pred, y_real))
    for p, r in zip(percent, ratio_p):
       print('error {0}%: {1}'.format(p, r))
       
    plt.figure(figsize=(10, 5))
    plt.subplot(1, 2, 1)
    plt.plot(y_real, y_pred, 'bo')
    plt.plot(y_real, y_real, 'r')
    plt.xlabel('real', fontsize=12)
    plt.ylabel('predeiction', fontsize=12)
    
    plt.subplot(1, 2, 2)
    plt.plot(percent, ratio_p, 'bo')
    plt.xlim([0, 100])
    plt.ylim([0, 1])
    plt.xlabel('Percent (%)', fontsize=12)
    plt.ylabel('Prediction ratio', fontsize=12)
    plt.subplots_adjust(wspace=0.7)
    
eval_regression(y_pred, y_real)

mseと予測誤差の50%、75%、90%の値を出力し、実データ - 予測データのグラフと予測誤差 - 分位数のグラフを描きます。



まとめ


誤解のないよう言っておくと、mseを全否定しているわけではありません。上記のような手法をとった方がよい場合もありますよ、というだけです。以下にこのページの内容をまとめます。

  • mseだけを見ていると、個々のデータに対する予測の傾向が埋もれてしまう可能性がある。

  • 個々のデータに対する予測の傾向を見ることでデータ処理や特徴量作成に関するフィードバックが得られる可能性がある。

  • これは特に特徴量の多いデータや、目的変数の分布が幅広いデータに有効と考えられる。

  • これは特に訓練の初期段階において重要。ある程度精度が上がってきたらmseで良いと思う。

  • ビジネス利用の場合は、どの範囲のデータを対象にするのか、どこまでAIにやらせてどこまでは人間が判断するのか、などの施策を検討する際にも有効。


記事内で扱ったメソッドはgithub に上げてあります。

最新記事

すべて表示

概要 フィッティングを行いたい場合、pythonならばscipy.optimize.leastsqなどでできます。 しかし、フィッティングを行う場合、フィッティングパラメータに条件を付けたい場合も多々あります。 例えば、下記のようにパラメータa、bは共に正の範囲で最適な値を求める、という感じです。 f(x, a, b)=a*x^2+b (a>0 and b>0) 今回はそんな手法についてご紹介しま

靴を大切にしよう!靴管理アプリ SHOES_KEEP

納品:iPhone6.5①.png

靴の履いた回数、お手入れ回数を管理するアプリです。

google-play-badge.png
Download_on_the_App_Store_Badge_JP_RGB_blk_100317.png

テーマ日記:テーマを決めてジャンルごとに記録

訂正①2040×1152.jpg

ジャンルごとにテーマ、サブテーマをつけて投稿、記録できる日記アプリです。

google-play-badge.png