tkherox blog

データサイエンスおよびソフトウェア開発、たまに育児についての話を書いています

回帰における評価指標

回帰の評価指標はこれ

回帰の評価指標として扱うべき指標は主にMAERMSEです.
ただし,どちらが良いということではなく観測されるデータに応じて使い分けるということが非常に重要です. そのため,観測データの分布はしっかり可視化してどういった傾向があるのかを事前に把握しておきましょう.

さて,次からはそれぞれの指標の解説となぜMAEとRMSEで評価することが良いのかを話していこうと思います.

評価指標の種類

項目指標内容
1決定係数推定されたモデルの当てはまりの良さを表す
2平均絶対誤差予測結果と観測値の誤差の平均を表す
3平均二乗誤差予測結果と観測値の誤差の二乗平均を表す
4二乗平均平方根誤差予測結果と観測値の誤差の二乗平均にルートした値を表す
5平均絶対パーセント誤差予測結果と観測値の誤差の割合を表す

決定係数

決定係数は以下の式で表される.

\displaystyle{R^{2} = 1 - \frac{ \sum_{i=1}^{n} (y_{i} - \hat{y}_{i})^{2} }{  \sum_{i=1}^{n} (y_{i} - \bar{y})^{2} }}

この式の y _ {i} は観測値を \hat{y} _ {i} はモデルの予測結果, \bar{y}は観測値の平均を示す.
決定係数の算出する式からわかるように,モデルの予測結果が観測値と近しい値を推論することができれば分子が小さくなり決定係数が1に近づくことになる.逆にモデルの予測結果が観測値と大きく異なる場合は分子が大きくなるため決定係数は0への近づく.また,観測データのばらつきが大きい場合には分母が大きくなるため決定係数が1に近づくことになるため一概に決定係数が1に近い値をとっているからと言って良いモデルとは限らないことに注意が必要である.正解データの分布などは事前に確認することで間違った解釈を防ぐことができるため積極的に可視化で分布は確認することが良いと考えられる.

import numpy as np

y_small_scale = np.asarray([1, 1, 2, 2, 3, 3, 4, 4, 5, 5])
y_large_scale = np.asarray([1, 1, 5, 5, 10, 10, 100, 100,1000, 1000 ])

y_small_scale_mean = y_small_scale.mean()
y_large_scale_mean = y_large_scale.mean()

np.sum((y_small_scale - y_small_scale_mean)**2 ) # 20.0
np.sum((y_large_scale - y_large_scale_mean)**2 )  # 1522069.59999

一般論ではあるが以下に決定係数における判断基準をまとめておく.

  • 0.6以下: あまり良いモデルではない
  • 0.8以上: 高精度のモデルと考える
  • 0.9以上: 過学習している可能性がある

決定係数は各データの予測が直感的に理解できる点で有効ではあるが決定係数のみで回帰の評価指標を判断するのは得策ではないため他の指標と組みわせて使うことが良さそうである.

平均絶対誤差(MAE)

平均絶対誤差は以下の式で表される.

\displaystyle{ MAE = \frac{1}{n}\sum_{i=1}^{n} | y_i - \hat{y}_i | }

観測値と予測値の誤差の絶対値の合計を平均したものである. 単純に差を算出するだけのため外れ値のようなデータの影響を受けにくいため,外れ値のようなスパイクがあるようなデータに対して評価する場合にはMSEやRMSEよりMAEを用いた方が良さそうです.

平均二乗誤差(MSE)

平均二乗誤差は以下の式で表される.

\displaystyle{ MSE = \frac{1}{n}\sum_{i=1}^{n} (y_i - \hat{y}_i)^2 }

式を見ると観測値 y _ {i} と予測値  \hat{y} _ {i} の差を二乗した値の平均を算出した値となる.
これを見ると先ほどのMAEと比べて差があった場合に二乗の部分よりMSEの値が大きくなることが言えます. データセットが高いバイアスを含む公平な場合はMSEを利用することで適切な評価ができます.

平均二乗平方根誤差(RMSE)

平均二乗平方根誤差は以下の式で表される.

\displaystyle{ RMSE = \sqrt{\frac{1}{n}\sum_{i=1}^{n} (y_i - \hat{y}_i)^2}}

RMSEはMSEに対してルートをとった値になります.
RMSEはMSEと同様にデータセットが高いバイアスを含む公平なデータセットの時に利用することで適切な評価ができます. ところでRMSEとMSEの使い分けについてはMSEはRMSEより計算が早く操作が簡単なため多くのアルゴリズムで採用されておりますが,MSEは誤差が二乗されたままなので元の大きさにスケーリングされていないことから元の観測値などと関連づけることができないといった注意点があります.
MSEを利用しなければいけないという条件がないのであればRMSEを使うことが良いでしょう.

平均絶対パーセント誤差(MAPE)

平均絶対パーセント誤差は以下の式で表される.

\displaystyle{ MAPE = \frac{100}{n}\sum_{i=1}^{n} |\frac{ y_i - \hat{y}_i }{y_i} |}

MAEを相対誤差で表現したものになります.
MAPEは観測値 y _ {i} と予測値  \hat{y} _ {i} の誤差に対して観測値における割合を算出していることより,MAPEによって平均して予測がどのくらいの割合でずれているかを表現できます.しかし,観測値に0が含まれている場合は利用できないといった制約や観測値が非常に小さい値の場合には誤差が大きくなるため別途処理が必要になるといった対応が発生してくることを意識しておく必要があります.

なぜ「MAEとRMSE」なのか

サンプルのデータを使って各指標が状況に応じてどのような値になるのかを確認してみます.

整数値で均一な観測データを含む場合

import numpy as np
from sklearn.metrics import r2_score
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error

def culculate_error(y_test, y_pred):
    """
    Returns:
        y_test: 観測値のnumpy array
        y_pred: 予測値のnumpy array
    """
    print( "R^2: %s" % (r2_score(y_test, y_pred)) )
    print( "MAE: %s" % (mean_absolute_error(y_test, y_pred)) )
    print( "MSE: %s" % (mean_squared_error(y_test, y_pred)) )
    print( "RMSE: %s" % (np.sqrt(mean_squared_error(y_test, y_pred))) )
    print( "MAPE: %s" % (np.mean(np.abs((y_test - y_pred) / y_test)) * 100) )

y     = np.asarray([1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5])
y_hat = np.asarray([1, 1, 1, 1 ,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])

print("整数値で均一な観測データの誤差")
culculate_error(y, y_hat)

整数値で外れ値等を含まないある程度均一の観測データに対する誤差で注目する点はMSEとRMSEの値です.
平均絶対誤差であるMAEの値を基準にRMSEとMSEを見たときにMSEの値はより大きい値を指していることが分かります.これはMSEを算出する式からわかるように誤差を二乗している点が要因です.つまり観測値のスケールと合っていないことが分かります.そのため単純に誤差がいくつ離れているといった表現をすることができません.

整数値で均一な観測データの誤差
R^2: -2.0
MAE: 2.0
MSE: 6.0
RMSE: 2.449489742783178
MAPE: 54.333333333333336

外れ値の観測データを含む場合

import numpy as np
from sklearn.metrics import r2_score
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error

def culculate_error(y_test, y_pred):
    """
    Returns:
        y_test: 観測値のnumpy array
        y_pred: 予測値のnumpy array
    """
    print( "R^2: %s" % (r2_score(y_test, y_pred)) )
    print( "MAE: %s" % (mean_absolute_error(y_test, y_pred)) )
    print( "MSE: %s" % (mean_squared_error(y_test, y_pred)) )
    print( "RMSE: %s" % (np.sqrt(mean_squared_error(y_test, y_pred))) )
    print( "MAPE: %s" % (np.mean(np.abs((y_test - y_pred) / y_test)) * 100) )

y_1     = np.asarray([10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10])
y_hat_1 = np.asarray([1, 1, 1, 1 ,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
y_2     = np.asarray([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 136])
y_hat_2 = np.asarray([1, 1, 1, 1 ,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])

print("全て同じ観測値")
culculate_error(y_1, y_hat_1)
print("------------")
print("外れ値を含む観測値")
culculate_error(y_2, y_hat_2)

結果をみてわかるようにMAEの値は同じなのに対してRMSEとMSEの値には大きな誤差が生じています.
全体としての誤差が同じでも1つの外れ値の影響受けてRMSEとMSEの値は大きく変動してしまいます. 一方で,MAEとMAPEは外れ値の影響を受けにくいため全体の誤差を適切に表現することができていると言えます.

全て同じ観測値
R^2: 0.0
MAE: 9.0
MSE: 81.0
RMSE: 9.0
MAPE: 90.00000000000001
------------
外れ値を含む観測値
R^2: -0.0714285714285714
MAE: 9.0
MSE: 1215.0
RMSE: 34.85685011586675
MAPE: 6.61764705882353

予測値が小数点以下の小さい値を含む場合

import numpy as np
from sklearn.metrics import r2_score
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error

def culculate_error(y_test, y_pred):
    """
    Returns:
        y_test: 観測値のnumpy array
        y_pred: 予測値のnumpy array
    """
    print( "R^2: %s" % (r2_score(y_test, y_pred)) )
    print( "MAE: %s" % (mean_absolute_error(y_test, y_pred)) )
    print( "MSE: %s" % (mean_squared_error(y_test, y_pred)) )
    print( "RMSE: %s" % (np.sqrt(mean_squared_error(y_test, y_pred))) )
    print( "MAPE: %s" % (np.mean(np.abs((y_test - y_pred) / y_test)) * 100) )

y_1     = np.asarray([1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5])
y_hat_1 = np.asarray([1.01, 1, 1, 2.02, 2, 2, 3.03, 3, 3, 4.04, 4, 4, 5.05, 5, 5])
y_2     = np.asarray([0.1, 1, 1, 0.2, 2, 2, 0.3, 3, 3, 0.4, 4, 4, 0.5, 5, 5])
y_hat_2 = np.asarray([0.11, 1, 1, 0.22, 2, 2, 0.33, 3, 3, 0.44, 4, 4, 0.55, 5, 5])

print("1より大きい値の観測値")
culculate_error(y_1, y_hat_1)
print("------------")
print("1より小さい値を含む観測値")
culculate_error(y_2, y_hat_2)

観測値としてy_1では1より大きい値をだけの観測値とy_2では1より小さい値を含む観測値の場合で各指標を算出した結果は以下のようになりました.これより絶対的な誤差が同じであってもMAPEはより大きな値となっていることが分かります.
1より小さい値を予測する場合はMAPEの値が大きく算出されてしまうため,誤差の割合でどうしても評価したい場合を除き安易にMAPEで評価するのは得策ではないです.その場合は観測値に対して1より大きい値になるように処理を加えて評価することが必要となります.

1より大きい値の観測値
R^2: 0.9998166666666667
MAE: 0.00999999999999998
MSE: 0.000366666666666665
RMSE: 0.019148542155126718
MAPE: 0.3333333333333328
------------
1より小さい値を含む観測値
R^2: 0.9998761261261261
MAE: 0.010000000000000002
MSE: 0.0003666666666666669
RMSE: 0.019148542155126767
MAPE: 3.3333333333333335

これより外れ値の影響が受けにくい指標であるMAE,誤差のスケールが観測値と一致しているRMSEを利用することが良いと言えるのです.

実業務における評価の難しさ

ここまで各指標となぜMAEとRMSEが良いのかについて述べてきましたが,実務では精度以外にも追求する様々な観点があり,その状況に応じて最適な指標を選択する必要があります.例えば,結果の解釈を分かりやすくするために予測値の誤差がどれくらいのパーセントなのかを示すMAPEを使うといった場面も容易に想像できるかと思います.
データサイエンティストには一般的にサイエンスの領域だけではなくビジネスサイドの情報を踏まえて見せ方を変えていかなければいけないため幅広い指標について理解しておくことは非常に重要です.そして,それらを適切に使い分けることがポイントになってくると思います.
今回の内容は基礎的なものでしたが,これからも様々な観点で知識を吸収して最適な選択ができるように努力していきたいと思います.