【4日目】回帰の評価をやってみる【2021アドベントカレンダー】
2021年1人アドベントカレンダー(機械学習)、4日目の記事になります。
テーマは 回帰の精度評価 になります。
MAE, MSE, RMSE, R2スコアを取り扱います。
Colab のコードはこちら
MAE, MSE, RMSE, R2スコアの実装
LightGBM のメトリクスに RMSE を設定します。
(理論的な内容は後で触れます)
params = {
'task': 'train', # タスクを訓練に設定
'boosting_type': 'gbdt', # GBDTを指定
'objective': 'regression', # 回帰を指定
'metric': 'rmse', # 回帰の損失(誤差)
'learning_rate': 0.1, # 学習率
}
lgb_results = {} # 学習の履歴を入れる入物
model = lgb.train(
params=params, # ハイパーパラメータをセット
train_set=lgb_train, # 訓練データを訓練用にセット
valid_sets=[lgb_train, lgb_test], # 訓練データとテストデータをセット
valid_names=['Train', 'Test'], # データセットの名前をそれぞれ設定
num_boost_round=100, # 計算回数
early_stopping_rounds=50, # アーリーストッピング設定
evals_result=lgb_results,
verbose_eval=-1, # ログを最後の1つだけ表示
)
精度評価用のライブラリを使用すると便利ですね。
見た目を整えるために round を使っていますが使わなくてもよいです。
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error
from sklearn.metrics import r2_score
from sklearn.metrics import explained_variance_score
from sklearn.metrics import max_error
from sklearn.metrics import median_absolute_error
mae = mean_absolute_error(y_test, y_pred)
print("MAE:", round(mae, 4))
mse = mean_squared_error(y_test, y_pred)
print("MSE:", round(mse, 4))
rmse1 = np.sqrt(mean_squared_error(y_test, y_pred))
print("RMSE 1:", round(rmse1, 4))
rmse2 = mean_squared_error(y_test, y_pred, squared=False)
print("RMSE 2:", round(rmse2, 4))
r2score = r2_score(y_test, y_pred)
print("R2 score:", round(r2score, 4))
次にメトリクスに MAE を使ってみます。
(先述と同様、理論的な内容は後で触れます)
params = {
'task': 'train', # タスクを訓練に設定
'boosting_type': 'gbdt', # GBDTを指定
'objective': 'regression', # 回帰分類を指定
'metric': 'mae', # 回帰の損失(誤差)
'learning_rate': 0.1, # 学習率
}
出力は以下のとおりです。
微妙な差ですが、メトリクスに設定した指標のほうが精度は高くなっているのがわかりますね。
これはそれぞれの指標がどういったデータに影響を受けやすいか、ということを表していると思います。
出力:
指標 | metric:rmse | metric:mae |
---|---|---|
MAE: | 0.0183 | 0.0182 |
MSE: | 0.0601 | 0.0604 |
RMSE 1: | 0.2452 | 0.2457 |
RMSE 2: | 0.2452 | 0.2457 |
R2 score: | 0.9205 | 0.9202 |
理論
それではそれぞれの評価指標を見ていきましょう。
MAE 平均絶対誤差
予測値と正解値の差(=誤差)の絶対値の総和をデータ数で割った値
MSE 平均二乗誤差
「予測値と正解値の差(=誤差)」の二乗値を計算し、その総和をデータ数で割った値
RMSE 平均平方二乗誤差
予測値と正解値の差(=誤差)」の二乗値を計算した総和を、2乗平均したものの平方根。
$ R^{2} $ 決定係数
極端な例
理解しやすくするため、極端な例を使ってそれぞれの指標を見てみます。
(使用頻度が高いであろう MAE、MSE、RMSE R2スコア に絞っています。)
print("MAE:", mean_absolute_error(true, pred))
print("MSE:", mean_squared_error(true, pred))
print("RMSE:", mean_squared_error(true, pred, squared=False))
print("R2 score:", r2_score(true, pred))
パターン1 誤差大きいものと小さいものがある、予測は実績と相関がない
true = [100, 2]
pred = [1, 1]
実績 | 予測 | 誤差 |
---|---|---|
100 | 1 | 99 |
2 | 1 | 1 |
パターン2 誤差大きい、予測は実績と相関がない
true = [100, 99]
pred = [1, 1]
実績 | 予測 | 誤差 |
---|---|---|
100 | 1 | 99 |
99 | 1 | 98 |
指標 | 評価値1 | 評価値2 |
---|---|---|
MAE: | 50.0 | 98.5 |
MSE: | 4901.0 | 9702.5 |
RMSE: | 70.00714249274856 | 98.501.. |
R2 score: | -1.0412328196584757 | -38809.0 |
私見ですが、以下のように基本的に理解するとよいかと思います。
-
誤差がマイナスになった場合の影響を除去する手法として、(1) 二乗を使う場合と、(2) 絶対値を使う場合がある。
-
(1) 二乗を使って誤差がマイナスの影響を排除すると、指数的に数値が増加するため、誤差が大きい場合の影響を受けやすくなる。
-
ゆえに、大きな誤差の発生を抑えられるが、外れ値の影響を受けやすくなる。
-
(2) 絶対値を使う場合は、逆に誤差が大きい場合の影響が二乗を使う場合よりも受けにくくなる。
-
ゆえに、二乗を使う場合にくらべ、大きな誤差の発生を抑えにくくなる一方、外れ値の影響を受けにくくなる。
-
MAE は誤差がマイナスの影響を排除するために絶対値を使う。
-
MSE、RMSE は誤差がマイナスの影響を排除するために二乗を使う。
-
一方、R2スコア は相関関係の影響を受ける。
-
R2スコアは 1 から [差の二乗の総和の合計 ÷ 実績と実績の平均値との差の総和の合計] を引いたもの。
-
予測と実績の誤差と、個別の実績と実績の平均値の乖離が影響する。
-
個別の実績と実績の平均値の乖離がなく、分布が落ち着いていると、分母が小さくなる。予測も比較的しやすくなるはず?
-
にもかかわらず予測と実績の誤差がある程度発生していると、R2スコアがマイナスになることがある。
-
加えてその際、実績の推移と予測の推移に相関がなければないほど、R2スコアのマイナス幅が大きくなる。
追加で R2スコア補足
R2 スコアについてもう少し補足します。
パターン1 誤差大きいものと小さいものがある、予測は実績と相関がない
true = [100, 2]
pred = [1, 1]
print("実績: ", true)
print("予測: ", pred)
print("実績の平均: ", np.array([np.mean(true)] * len(true)).tolist() )
print()
a = np.sum(
(
# 実績
np.array(true) - \
# 予測
np.array(pred)
)
** 2)
print("実績と予測の差(誤差)の総和の二乗: ", a) # 分子
b = np.sum(
(
# 実績
np.array(true) \
# 実績の平均値
- np.array([np.mean(true)] * len(true))
)
** 2 )
print("実績と実績の平均値の差の総和の二乗: ", b) # 分母
score = 1 - a/b # R2 score
print("R2 Score: ", score)
R2スコアがマイナスとなりました。
出力:
実績: [100, 2]
予測: [1, 1]
実績の平均: [51.0, 51.0]
実績と予測の差(誤差)の総和の二乗: 9802
実績と実績の平均値の差の総和の二乗: 4802.0
R2 Score: -1.0412328196584757
パターン2 誤差大きい、予測は実績と相関がない
true = [100, 99]
pred = [1, 1]
### 以降、パターン1と同様
R2スコアのマイナス幅がとんでもないことになりました。
出力:
実績: [100, 99]
予測: [1, 1]
実績の平均: [99.5, 99.5]
実績と予測の差(誤差)の総和の二乗: 19405
実績と実績の平均値の差の総和の二乗: 0.5
R2 Score: -38809.0
パターン3 誤差小さい、予測と実績に相関ありそう
true = [1.5, 1.2]
pred = [1.4, 1.1]
### 以降、パターン1と同様
ようやくR2スコアがプラスになってよい精度がでました。
出力:
実績: [1.5, 1.2]
予測: [1.4, 1.1]
実績の平均: [1.35, 1.35]
実績と予測の差(誤差)の総和の二乗: 0.01999999999999999
実績と実績の平均値の差の総和の二乗: 0.04500000000000001
R2 Score: 0.5555555555555559
パターン4 誤差小さいものの予測と実績に相関ない
true = [1.5, 1.2]
pred = [1.0, 1.0]
### 以降、パターン1と同様
パターン3 と誤差の大きさはそれほどかわらないのに、予測の相関が失われたことでR2スコアはマイナスとなりました。
出力:
実績: [1.5, 1.2]
予測: [1.0, 1.0]
実績の平均: [1.35, 1.35]
実績と予測の差(誤差)の総和の二乗: 0.29
実績と実績の平均値の差の総和の二乗: 0.04500000000000001
R2 Score: -5.444444444444442
比較すると以下のとおりです。
パターン | 概要 | R2スコア |
---|---|---|
パターン1 | 誤差はまちまち、予測は実績と相関がない | -1.04 |
パターン2 | 誤差大きい、予測は実績と相関がない | -38809.0 |
パターン3 | 誤差も少なく、予測と実績に相関がある | 0.55 |
パターン4 | 誤差小さいものの予測と実績に相関ない | -5.44 |
4日目は以上になります、最後までお読みいただきありがとうございました。
参考サイト
Discussion