LoginSignup
0
0

機械学習で使ういろんな評価指標 (Metrics)

Posted at

機械学習で作成したモデルの良し悪しを比較するためにさまざまな評価指標があります。すぐ忘れるのでここにメモします。
今回の実験ではサンプルとして Boston Housing:ボストンの住宅価格 - @IT を使います。実はこのデータセットは人種差別の要素があるらしく最近非推奨になりましたが、データ量が少なく練習するのに楽なので勉強用として使わせていただきます。以下のライブラリを利用します。

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score, root_mean_squared_error, mean_absolute_percentage_error

昔は Scikit-Learn の load_boston() 関数でダウンロードできたましたが、非推奨になったので手動でデータセットを作ります。

url = "https://archive.ics.uci.edu/ml/machine-learning-databases/housing/housing.data"
column_names = [
    "CRIM", "ZN", "INDUS", "CHAS", "NOX", "RM", "AGE", "DIS", "RAD", "TAX", 
    "PTRATIO", "B", "LSTAT", "PRICE"
]
data = pd.read_csv(url, sep=r'\s+', names=column_names)

print("データの概要:")
data.head()
データの概要:
CRIM ZN INDUS CHAS NOX RM AGE DIS RAD TAX PTRATIO B LSTAT PRICE
0 0.00632 18.0 2.31 0 0.538 6.575 65.2 4.0900 1 296.0 15.3 396.90 4.98 24.0
1 0.02731 0.0 7.07 0 0.469 6.421 78.9 4.9671 2 242.0 17.8 396.90 9.14 21.6
2 0.02729 0.0 7.07 0 0.469 7.185 61.1 4.9671 2 242.0 17.8 392.83 4.03 34.7
3 0.03237 0.0 2.18 0 0.458 6.998 45.8 6.0622 3 222.0 18.7 394.63 2.94 33.4
4 0.06905 0.0 2.18 0 0.458 7.147 54.2 6.0622 3 222.0 18.7 396.90 5.33 36.2

それぞれの列の詳しい説明は先ほどの Boston Housing:ボストンの住宅価格 - @IT を参照ください。CRIM(犯罪率)や ZN(25,000平方フィートを超える広い家の割合) といった入力から、最後の列の PRICE(価格 x 1000ドル) を当てるというのが目的です。

本題ではないのでモデルの作成方法はさらっと書いて、正解の価格 (y_test) と予想した価格 (y_pred) の違いを散布図にして感じを見てみます。

# 説明変数と目的変数の設定
X = data.drop('PRICE', axis=1)
y = data['PRICE']

# 訓練データとテストデータの分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 線形回帰モデルの構築
model = LinearRegression()
model.fit(X_train, y_train)

# 予測
y_pred = model.predict(X_test)

# 実測値でソートして後の評価を見やすくする。
results = pd.DataFrame({'y_test': y_test, 'y_pred': y_pred})
results.sort_values('y_test', inplace=True)
results.reset_index(inplace=True)

# 結果の表示
print("予測値と正解値:")
results.plot.scatter(x='y_test', xlabel='y_test(Actual)', y='y_pred', ylabel='y_pred(Predicted)')
予測値と正解値:

output_5_2.png

かりに予測が完璧だと y_pred(予測値) == y_test(正解値) なのでナナメ 45 度の直線になるはずです。まあまあ予測できていると思います。
この予測できてる具合を測るために評価指標 (metrics) があります。sklearn では関数一つで評価指標を計算できるようになっています。
ただ、このままだと数値の意味がよくわからないので自分で実装しながらいろいろな評価指標の意味を確認します。

# モデルの評価
mae = mean_absolute_error(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
rmse = root_mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
mape = mean_absolute_percentage_error(y_test, y_pred) * 100

print("\nモデルの評価:")
print(f"残差の絶対値の平均 (MAE): {mae:.2f}")
print(f"MAPE 平均絶対パーセント誤差 (%): {mape:.2f}")

print(f"平均二乗誤差 (MSE): {mse:.2f}")
print(f"ルート平均二乗誤差 (RMSE): {rmse:.2f}")
print(f"決定係数 (R^2): {r2:.2f}")
モデルの評価:
残差の絶対値の平均 (MAE): 3.19
MAPE 平均絶対パーセント誤差 (%): 16.87
平均二乗誤差 (MSE): 24.29
ルート平均二乗誤差 (RMSE): 4.93
決定係数 (R^2): 0.67

MAE 平均絶対誤差 とは?

一番簡単な評価指標は MAE (平均絶対誤差)です。これは単に予測値と正解値の差分を平均したものです。予測値と正解値のどちらが大きくても良いように絶対値を平均します。

$$
MAE = \frac{1}{n}\sum_{i=1}^{n}|予測値_i - 正解値_i|
$$

差分は小さい方がよいので、値が小さい方が正確です。この例だと、平均して 3.19 (x1000 ドル) の誤差がある事になります。実際の値と評価指標の関係を掴むためにグラフにしてみました。

  • y_test: 正解値。この値でソート済み。一番上に表示されます。
  • -y_pred: 見やすいように予測値をマイナスにして一番下に上下反転してに表示します。
  • err: y_test - y_pred: 正解値と予測値の差分です。ゼロ近辺を上下に細かく動きます。
  • abs_err: 差分の絶対値です。ゼロの上をギザギザしています。
  • mean_abs_err: 差分の絶対値を平均したものです。abs_err の真ん中あたりの水平な線です。
err = results.y_test - results.y_pred
abs_err = err.abs()
mean_abs_err = abs_err.sum() / len(abs_err)
print(f"残差の絶対値の平均 (MAE): {mean_abs_err:.2f}")
pd.DataFrame({"MAE": mean_abs_err, "err": err, "abs_err": abs_err, "y_test": results.y_test, "-y_pred": -results.y_pred}).plot.line(grid=True)
残差の絶対値の平均 (MAE): 3.19

output_9_2.png

MAPE 平均絶対パーセント誤差 とは?

平均絶対誤差では単に予測値と正解値の差分を平均しましたが、値が大きいほど誤差も大きくなるような場合(普通そうなると思いますが)、誤差ではなく正解値に対する誤差の割合(絶対パーセント誤差)を指標にしたくなります。割合を求めるためには誤差の絶対値(abs_err)を正解値(y_test)で割れば良いです。これを平均化したものを絶対平均パーセント誤差 (MAPE) と呼びます。この例だと、絶対平均パーセント誤差は 0.1687 なので、正解値に比べて平均で 17% 程度の誤差がある事になります。これも小さい方が良い成績です。

$$
MAPE = \frac{1}{n}\sum_{i=1}^{n}|\frac{予測値_i - 正解値_i}{正解値_i}|
$$

絶対パーセント誤差は割合なので、正解値を同じグラフに入れると収まりが悪いので絶パーセント誤差とその平均だけをグラフに描きました。正解値が大きくなるほど誤差の影響が少なくなっている事がわかります。

abs_per_err = abs_err / results.y_test
pd.DataFrame({"MAPE": abs_per_err.mean(), "abs_per_err": abs_per_err}).plot.line()
print(f"MAPE 平均絶対パーセント誤差 (%): {abs_per_err.mean() * 100:.2f}")
MAPE 平均絶対パーセント誤差 (%): 16.87

output_11_1.png

MSE 平均二乗誤差 / RMSE 平均二乗誤差の平方根 とは?

平均二乗誤差(MSE) は平均絶対誤差と同じく誤差の平均を使った評価指標で、機械学習のモデル作成によく使われます。学習時にできるだけこの値が小さくなるように調整するのです。平均二乗誤差では、絶対値の代わりに二乗を使います。二乗を使う理由は以下です。

  • 差分を正の数値にしたい。 (絶対値を使う理由と同じ)
  • 大きい誤差に対してペナルティを課したい。機械学習で素早くモデルを調整するために、大きい誤差を素早く調整できる。
  • 絶対値と違って二乗は微分可能。微分というのはグラフの傾きの事ですが、誤差の傾きよってモデルをどっち向きにどれくらい調整したら良いか決めます。

$$
MSE = \frac{1}{n}\sum_{i=1}^{n}(予測値_i - 正解値_i)^2
$$

評価指標の感触を掴むために、この値の平方根を取った RMSE (Root MSE) というのもよく使われます。平方根を取ると、この場合のモデルは平均して 4.93 (x1000 ドル)の誤差がある事がわかります。

これも縦軸が正解値や予測値と離れていてグラフにしずらいですが、無理やりプロットすると誤差の大きさが誇張されて指標に影響されている事がわかります。

sq_err = (results.y_pred - results.y_test)**2
mean_sq_err = sq_err.sum() / len(sq_err)

print(f"MSE 平均二乗誤差 (%): {mean_sq_err:.2f}")
print(f"RMSE 平均二乗誤差の平方根 (%): {np.sqrt(mean_sq_err):.2f}")
pd.DataFrame({
    "MSE": mean_sq_err,
    "RMSE": np.sqrt(mean_sq_err),
    "sq_err": sq_err,
    "y_test": results.y_test,
    "-y_pred": -results.y_pred
}).plot.line()
MSE 平均二乗誤差 (%): 24.29
RMSE 平均二乗誤差の平方根 (%): 4.93

output_13_2.png

R2 決定係数 とは?

まだ良くわからないので後で調べます。少々お待ちください。

決定係数R2を超わかりやすく解説【統計学入門16】 とか分かりやすそう。

sum_sq = (results.y_test - results.y_test.mean())**2
r2 = 1 - sq_err.sum() / sum_sq.sum()
print(f"決定係数 (R^2): {r2:.2f}")
pd.DataFrame({"R^2": r2, "sum_sq": sum_sq, "sq_err": sq_err, "y_test": results.y_test, "-y_pred": -results.y_pred}).plot.line()
決定係数 (R^2): 0.67

output_15_2.png

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0