コンペにおけるタスクの種類
- 何を予測するのか?
- どういう予測値を提出するのか?
回帰タスク
数値を予測する
- モノの値段
- 株価のリターン
- 店舗への来客数
評価指標:
- RMSE
- MAE
コンペ例
- House Prices - Advanced Regression Techniques
- Zillow Prize: Zillow’s Home Value Prediction (Zestimate)
分類タスク
二値分類
レコードがある属性に属しているかどうかを予測する
- 患者が病気にかかっているか否か
2つのケースがある
- 0 or 1 の2種類のラベルで予測を提出
- 0から1の間の確率を表す数値で予測を提出
評価指標:
- 1 の場合、F1-score
- 2 の場合、loglossやAUC
コンペ例
多クラス分類
マルチクラス分類
レコードが複数のクラスのうちどれか1つに属しているかどうかを予測する
評価指標:
- multi-class logloss
マルチラベル分類
同時に複数のクラスに属する場合がある
二値分類をクラスの数だけ繰り返すのが基本的な解法
評価指標:
- mean-F1
- macro-F1
それぞれ2つのケースがある
- 属していると予測されるクラスのラベルを提出
- 各クラスに属している確率を0から1の間の数値で提出
コンペ例
レコメンデーション
ユーザが購入しそうな商品や反応しそうな広告などを予測する
ユーザごとに商品や広告を複数個予測するケースが主になる
商品レコメンデーションの場合、2つのケースがある
- 購入可能性に応じた順位をつけて予測を提出する場合
- 順位をつけずに提出する場合(マルチラベル分類のように、複数の正解があり複数の予測値を提出するタスク)
いずれの場合でも、各ユーザが各商品を購入するかしないかという二値分類問題として解くのが一般的
二値分類によって予測した各ユーザの各商品の購入確率をもとに、上位の商品から予測値として提出する
評価指標:
- 1の場合、MAP@K
- 2の場合、マルチラベル分類と同様のもの
コンペ例
その他のタスク
物体検出
画像に含まれる物体のクラスとその存在する領域を矩形領域で推定する
コンペ例
セグメンテーション
画像に含まれる物体の存在領域を画像のピクセル単位で推定する
コンペ例
評価指標(evaluation metrics)
学習させたモデルの性能やその予測値の良し悪しを測る指標
回帰タスクにおける評価指標
下記の数式はこれらの表記に従う
- $N$: レコード数
- $i = 1, 2, ..., N$: 各レコードのインデックス
- $y_i$: $i$番目のレコードの真の値
- $\hat{y_i}$: $i$番目のレコードの予測値
RMSE(Root Mean Squared Error:平均平方二乗誤差)
計算方法:
各レコードの目的変数の真の値と予測値の差の二乗を取り、それらを平均した後に平方根をとる
RMSE=\sqrt{\frac{1}{N}\sum_{i=1}^{N}(y_i-\hat{y_i})^2}
ポイント
- RMSEを最小化した場合に求まる解が、誤差が正規分布に従うという前提のもとで求まる最尤解と同じになる
- 仮に1つの代表値で予測を行う場合、RMSEを最小化する予測値は平均値
- MAEに比べると外れ値の影響を受けやすいので、あらかじめ外れ値を除く処理などをしておいて、外れ値に過剰に適合したモデルを作成してしまう可能性を防ぐと良い
import numpy as np
from sklearn.metric import mean_squared_error
y_true = [1.0, 1.5, 2.0, 1.2, 1.8]
y_pred = [0.8, 1.5, 1.8, 1.3, 3.0]
rmse = np.sqrt(mean_squared_error(y_true, y_pred))
print(rmse)
# 0.5531726674375732
コンペ例
RMSLE(Root Mean Squared Logarithmic Error)
計算方法:
真の値と予測値の対数をそれぞれ取った後の差の二乗の平均の平方根
RMSLE=\sqrt{\frac{1}{N}\sum_{i=1}^{N}(\log(1+y_i)-\log(1+\hat{y_i}))^2}
- $\log x$は$x$の自然対数($\log_e x$)
ポイント
- 目的変数の対数をとって変換した値を新たな目的変数とした上でRMSEを最小化すれば、RMSLEを最小化することになる
- 目的変数が裾の重い分布を持ち、変換しないままだと大きな値の影響が強い場合や、真の値と予測値の比率に着目したい場合に用いられる。上式の二乗括弧内の式において、$\log(1+y_i)-\log(1+\hat{y_i})=\log\frac{1+y_i}{1+\hat{y_i}}$となることからも、この指標は比率に着目していることがわかる
- 対数を取るにあたり、真の値が0の時に値が負に発散するのを避けるため、通常は1を加えてから対数を取る。これには、numpyの
log1p
関数が使える - scikit-learnのmetricsモジュールにある
mean_squared_log_error
を用い計算できる。
コンペ例
MAE(Mean Absolute Error)
計算方法:
真の値と予測値の差の絶対値の平均
MAE=\frac{1}{N}\sum_{i=1}^{N}\left|y_i-\hat{y_i}\right|
ポイント
- 外れ値の影響を低減した形での評価に適した関数
- $\hat{y_i}$による微分が$\hat{y_i}=y_i$で不連続だったり、二次微分が常に0になってしまうという扱いづらい性質を持っている
- 仮に1つの代表値で予測を行うとき、MAEを最小化する予測値は中央値。
- scikit-learnのmetricsモジュールにある
mean_absolute_error
を用い計算できる。
コンペ例
決定係数
計算方法:
\begin{align}
R^2&=1-\frac{\sum_{i=1}^{N}(y_i-\hat{y_i})^2}{\sum_{i=1}^{N}(y_i-\bar{y})^2}\\
\bar{y}&=\frac{1}{N}\sum_{i=1}^{N}y_i
\end{align}
ポイント
- 回帰分析の当てはまりの良さを表す
- 分母は予測値に依らず、分子は二乗誤差を差し引いているため、決定係数を最大化することはRMSEを最小化するのと同義
- 決定係数は最大で1を取る、1に近づくほど精度の高い予測ができている事になる
- scikit-learnのmetricsモジュールの
r2_score
を用い計算できる
コンペ例
二値分類における評価指標:正例か負例かを予測値とする場合
混同行列(confusion matrix)
予測値と真の値の組み合わせには4つある
- TP(True Positive、真陽性):予測値が正例、予測が正しい
- TN(True Negative、真陰性):予測値が負例、予測が正しい
- FP(False Positive、偽陽性):予測値が正例、予測が誤り
- FN(False Negative、偽陰性):予測値が負例、予測が誤り
import numpy as np
from sklearn.metrics import confusion_matrix
# 正例が1、負例が0
y_true = [1, 0, 1, 1, 0, 1, 1, 0]
y_pred = [0, 0, 1, 1, 0, 0, 1, 1]
tp = np.sum((np.array(y_true) == 1) & (np.array(y_pred) == 1))
tn = np.sum((np.array(y_true) == 0) & (np.array(y_pred) == 0))
fp = np.sum((np.array(y_true) == 0) & (np.array(y_pred) == 1))
fn = np.sum((np.array(y_true) == 1) & (np.array(y_pred) == 0))
# numpy行列で混同行列を作る場合
confusion_matrix1 = np.array([[tp, fp], [fn, tn]])
print(confusion_matrix1)
# [[3 1]
# [2 2]]
# scikit-learnを使う場合
# 混同行列の要素の配置が違うのに注意
confusion_matrix2 = confusion_matrix(y_true, y_pred)
print(confusion_matrix2)
# [[2 1]
# [2 3]]
accuracy
(正答率)とerror rate
(誤答率)
計算方法:
正解のレコード数を全てのレコード数で割る
\begin{align}
accuracy&=\frac{TP+TN}{TP+TN+FP+FN}\\
error \, rate&=1-accuracy
\end{align}
ポイント
- 不均衡なデータの場合はモデルの性能を評価しづらく、分析コンペではあまり使用されない
from sklearn.metrics import accuracy_score
# 正例が1、負例が0
y_true = [1, 0, 1, 1, 0, 1, 1, 0]
y_pred = [0, 0, 1, 1, 0, 0, 1, 1]
accuracy = accuracy_score(y_true, y_pred)
print(accuracy)
# 0.625
コンペ例
precision
(適合率)とrecall
(再現率)
計算方法:
-
precision
は正例と予測したもののうち、真の値も正例の割合 -
recall
は真の値が正例のもののうち、どの程度を正例の予測として含められているかの割合
\begin{align}
accuracy&=\frac{TP}{TP+FP}\\
recall&=\frac{TP}{TP+FN}
\end{align}
- それぞれ0~1の値を取る。1に近づくほど良いスコア
-
precision
とrecall
は互いにトレードオフ。どちらかの値を高くしようとすると、もう一方の値は低くなる - 片方の指標を無視すれば、もう片方の指標を1に近づけられるので、単体として分析コンペの指標になることはない
- 誤検知を少なくしたい=>
precision
重視、正例の見逃しを避けたい=>recall
重視 - scikit-learnのmetricsモジュールの
precision_scrore
,recall_score
が使える
F1-score
とFβ-score
計算方法:
-
F1-score
はprecision
とrecall
の調和平均。バランスを取った指標なので、実務でもよく使われる。F値とも呼ばれる。 -
Fβ-score
はF1-score
からrecall
をどれだけ重視するかを表す係数β
により調整した指標。コンペではF2-score
の採用例がある。
\begin{align}
F_1&=\frac{2}{\frac{1}{recall}+\frac{1}{precision}}\\
&=\frac{2 \cdot recall \cdot precision}{recall+precision}\\
&=\frac{2TP}{2TP+FP+FN}\\
F_{\beta}&=\frac{(1+\beta^2)}{\frac{\beta^2}{recall}+\frac{1}{precision}}\\
&=\frac{(1+\beta^2) \cdot recall \cdot precision}{recall+\beta^2precision}
\end{align}
-
F1-score
は分子にTPのみが含まれることからわかるように、正例と負例について対称に扱っていない。真の値と予測値の正例と負例をともに入れ替えると、スコアや振る舞いが変わる。 - scikit-learnのmetricsモジュールの
f1_score
,fbeta_score
を用いて計算できる
コンペ例
MCC(Matthews Correlation Coefficient)
計算方法:
MCC=\frac{TP \times TN - FP \times FN}{\sqrt{(TP+FP)(TP+FN)(TN+FP)(TN+FN)}}
- 不均衡なデータに対してモデルの性能を適切に評価しやすい
- -1~+1の範囲の値を取り、+1の時に完璧な予測、0の時にランダムな予測、-1の時に完全に反対の予測を行なっている事になる
-
F1-score
と違い正例と負例を対称に扱っているため、真の値と予測値の正例と負例を入れ替えてもスコアは同じ - scikit-learnのmetricsモジュールの
matthews_corrcoef
を用いて計算できる
コンペ例
二値分類における評価指標:正例である確率を予測値とする場合
logloss
cross entropy
と呼ばれることもある
下記の数式は以下の表記に従う
- $N$: レコード数
- $i = 1, 2, ..., N$: 各レコードのインデックス
- $y_i$: $i$番目のレコードが正例かどうかを表すラベル(正例=1, 負例=0)
- $p_i$: $i$番目のレコードが正例である予測確率
- $p_i'$: 真の値を予測している確率(真の値が正例=$p_i$, 負例=$1-p_i$)
\begin{align}
logloss&=-\frac{1}{N}\sum_{i=1}^N(y_i \log p_i+(1-y_i)\log (1-p_i))\\
&=-\frac{1}{N}\sum_{i=1}^N\log p_i'
\end{align}
- 数値が低い方が良い指標
- そのレコードが正例である確率を低く予測したにもかかわらず正例である場合や、正例である確率を高く予測したにもかかわらず負例である場合、大きなペナルティが与えられる(数値が大きくなる)
- レコード$i$についてのスコア$L_i=-(y_i\log p_i+(1-y_i)\log(1-p_i))$を予測確率$p_i$で微分すると、$\frac{\partial L_i}{\partial p_i}=\frac{p_i-y_i}{p_i(1-p_i)}$となり、$p_i=y_i$の時に$L_i$が最小となる。つまり、確率を正しく予測できている時に
logloss
の値は最小。 - モデルを学習するときの目的関数としてもよく使われる
from sklearn.metrics import log_loss
# 正例が1、負例が0の真の値と予測確率
y_true = [1, 0, 1, 1, 0, 1]
y_prob = [0.1, 0.2, 0.8, 0.8, 0.1, 0.3]
logloss = log_loss(y_true, y_prob)
print(logloss)
# 0.7135581778200728
コンペ例
AUC(Area Under the ROC Curve)
計算方法:
ROC曲線(Receiver Operating Characteristic Curve)を元に算出される
ROC曲線は、予測値を正例とする閾値を1から0に動かし、そのときの偽陽性率/真陽性率を$(x, y)$としてプロットすることで描ける。
ここの真陽性率はrecall
(再現率)と同じ定義。
ROC曲線の下部の面積がAUCとなる。
- x軸=偽陽性率:誤って正例と予測した負例が、全体の負例のうちに占める割合(混同行列の要素では、$\frac{FP}{FP+TN}$)
- y軸=真陽性率:正しく正例と予測した正例が、全体の正例のうちに占める割合(混同行列の要素では、$\frac{TP}{TP+FN}$)
AUCの性質
- 完全な予測を行なった場合には、ROC曲線は左上の$(0.0, 1.0)$の点を通り、AUCは1となる
- ランダムな予測の場合には、ROC曲線は概ね対角線を通り、AUCは0.5程度となる
- 予測値を反対にした場合、AUCは$1.0-元のAUC$となる
- AUCは「正例と負例をそれぞれランダムに選んだ時に、正例の予測値が負例の予測値より大きい確率」としても定義できる。(2つの予測値が同率の場合は考慮せずに)式で表すと、
AUC=\frac{(y_i=1, y_j=0, \hat{y_i}>\hat{y_j})である(i, j)の組の個数}{(y_i=1, y_j=0)である(i, j)の組の個数}
- 上記の式を使うと、どの程度の予測を改善するとどの程度AUCが上がるかを考えやすい
- AUCは各レコードの予測値の大小関係のみが値に影響する。必ずしも予測値は確率でなくても構わない。各モデルの予測値をアンサンブルするときに、予測確率を順位に変換した平均が使われることがある
- 正例が非常に少ない不均衡データの場合、正例の予測値をどれだけ高確率の側に寄せるかがAUCに大きく影響する。負例の予測値の誤差の影響はあまり大きくない。
- Gini係数は$Gini=2 \cdot AUC - 1$で表され、AUCと線形関係にある。評価指標がGini係数の場合はAUCであるのとほぼ同義
- scikit-learnのmetricsモジュールの
roc_auc_score
で計算できる
コンペ例
多クラス分類における評価指標
multi-class accuracy
計算方法:
予測が正解であるレコード数を全てのレコード数で割ったもの
- 二値分類の
accuracy
を多クラスへ拡張したもの。予測が正しい割合を表す。 - 二値分類と同じく、scikit-learnのmetricsモジュールの
accuracy_score
を使い計算できる。
multi-class logloss
計算方法:
各クラスの予測確率を提出し、レコードが属するクラスの予測確率の対数をとり符号を反転させた値
下記の数式は以下の表記に従う
- $M$: クラス数
- $y_{i,m}$: レコード$i$がクラス$m$に属する場合は1、そうでない場合は0
- $p_{i,m}$: レコード$i$がクラス$m$に属する予測確率
multiclass\,logloss=-\frac{1}{N}\sum_{i=1}^N\sum_{m=1}^My_{i,m}\log p_{i,m}
-
logloss
をマルチクラス分類に拡張したもの - 予測値はレコード数×クラス数の行列で提出する
- レコードが属するクラスの確率を低く予測してしまうと大きなペナルティが与えられる
- 各レコードに対して予測確率の合計は1になる必要がある。そのようになっていない場合は評価指標の計算において自動的に調整される
- scikit-learnのmetricsモジュールの
log_loss
を使い計算できる。二値分類とはlog_loss
関数に与える予測値の配列の形が異なる
import numpy as np
from sklearn.metrics import log_loss
# M=3 のクラス分類の真の値と予測値
y_true = np.array([0, 2, 1, 2, 2])
y_pred = np.array([[0.68, 0.32, 0.00],
[0.00, 0.00, 1.00],
[0.60, 0.40, 0.00],
[0.00, 0.00, 1.00],
[0.28, 0.12, 0.60]])
logloss = log_loss(y_true, y_pred)
print(logloss)
# 0.3625557672904274
mean-F1
、macro-F1
、micro-F1
計算方法:
-
mean-F1
: レコード単位でF1-score
を計算し、その平均値が評価指標のスコア -
macro-F1
: 各クラスごとのF1-score
を計算し、それらの平均値が評価指標のスコア -
micro-F1
: レコードxクラスのペアのそれぞれに対しTP, TN, FP, FNのどれに当てはまるかカウントし、その混同行列に基づきF値を計算したものが評価指標のスコア
ポイント
-
F1-score
を多クラス分類に拡張したもの。主にマルチラベル分類で用いられる評価指標 -
macro-F1
はそれぞれのクラスで二値分類を行い、それらのF1-score
を平均しているのと同じ。マルチラベル分類ではそれぞれのクラスで独立に閾値を最適化できる - 各クラスのレコードが不均衡な場合など、これらの指標の振る舞いは異なる。どれが採用されるかはコンペ主催者がどんな観点から評価したいかによる
import numpy as np
from sklearn.metrics import f1_score
# 真の値 [[1, 2], [1], [1, 2, 3], [2, 3], [3]]
y_true = np.array([[1, 1, 0],
[1, 0, 0],
[1, 1, 1],
[0, 1, 1],
[0, 0, 1]])
# 予測値 [[1, 3], [2], [1, 3], [3], [3]]
y_pred = np.array([[1, 0, 1],
[0, 1, 0],
[1, 0, 1],
[0, 0, 1],
[0, 0, 1]])
mean_f1 = np.mean([f1_score(y_true[i, :], y_pred[i, :]) for i in range(len(y_true))])
n_class = 3
macro_f1 = np.mean([f1_score(y_true[:, c], y_pred[:, c]) for c in range(n_class)])
micro_f1 = f1_score(y_true.reshape(-1), y_pred.reshape(-1))
print(mean_f1, macro_f1, micro_f1)
# 0.5933333333333334 0.5523809523809523 0.6250000000000001
# scikit-learnのメソッドを使う場合
mean_f1 = f1_score(y_true, y_pred, average='samples')
macro_f1 = f1_score(y_true, y_pred, average='macro')
micro_f1 = f1_score(y_true, y_pred, average='micro')
print(mean_f1, macro_f1, micro_f1)
# 0.5933333333333334 0.5523809523809523 0.6250000000000001
コンペ例
quadratic weighted kappa
計算方法:
下記の数式は以下の表記に従う
- $O_{i, j}$: 真の値のクラスが$i$, 予測値のクラスが$j$のレコード数。これを行列の形に並べると多クラスでの混同行列となる
- $E_{i, j}$: 真の値のクラスと予測値のクラスの分布が互いに独立であるとした場合に、混同行列の各セル$(i,j)$に属するレコード数の期待値。「真の値が$i$である割合x予測値が$j$である割合xデータ全体のレコード数」
- $w_{i, j}$: 真の値と予測値の差の二乗$(i-j)^2$。真の値に対して大きく離れたクラスを予測してしまうと、この値は二乗で大きくなるので、大きく予測を外してしまう場合に大きなペナルティが課せられる。
\kappa=1-\frac{\sum_{i, j}w_{i, j}O_{i, j}}{\sum_{i, j}w_{i, j}E_{i, j}}
ポイント
- マルチクラス分類でクラス間に順序関係があるような場合に使用される(ex. 映画の評価。1~5のレーティング)
- 予測値として、各レコードがどのクラスに属しているかを提出
- 完全な予測で1、ランダムな予測で0、ランダムよりも悪い予測で負の値になる
- 予測値の各クラスの割合によって分母の値が変わるため、予測値を変えたときの動きが理解しづらい。予測値の各クラスの割合を固定したものとして考えると、二乗誤差に近い指標となり、少し理解しやすくなる
from sklearn.metrics import confusion_matrix, cohen_kappa_score
def quadratic_weighted_kappa(c_matrix):
numer = 0.0
denom = 0.0
for i in range(c_matrix.shape[0]):
for j in range(c_matrix.shape[1]):
n = c_matrix.shape[0]
wij = ((i - j) ** 2.0)
oij = c_matrix[i, j]
eij = c_matrix[i, :].sum() * c_matrix[:, j].sum() / c_matrix.sum()
numer += wij * oij
denom += wij * eij
return 1.0 - numer / denom
y_true = [1, 2, 3, 4, 3]
y_pred = [2, 2, 4, 4, 5]
c_matrix = confusion_matrix(y_true, y_pred, labels=[1, 2, 3, 4, 5])
kappa = quadratic_weighted_kappa(c_matrix)
print(kappa)
# 0.6153846153846154
# sklearnを使った例
kappa = cohen_kappa_score(y_true, y_pred, weights='quadratic')
print(kappa)
# 0.6153846153846154
コンペ例
レコメンデーションにおける評価指標
MAP@K(Mean Average Precision @ K)
計算方法:
下記の数式は以下の表記に従う
- $m_i$: レコード$i$の属しているクラスの数
- $P_i(k)$: レコード$i$について、$k(1 \le k \le K)$番目までの予測値で計算される
precision
。ただし、k番目の予測値が正解である場合のみ値を取り、それ以外は0になる
MAP@K=\frac{1}{N}\sum_{i=1}^N \left(\frac{1}{min(m_i,K)}\sum_{k=1}^KP_i(k) \right)
ポイント:
- 各レコードが一つまたは複数のクラスに属しているときに、属している可能性が高いと予測する順にK個のクラスを予測値とする
- Kの値は5や10など様々
- 各レコードについてK個未満の予測を行うことはできるが、そのようにすることでスコアが上がることはないので、通常はK個を予測値とする
- K個の中での正解数が同じでも、正解である予測値が後ろになってしまうとスコアが下がる。予測値の順序が重要
- 完全な予測で1、全く誤っている予測で0になる
import numpy as np
# K=3, N=5, クラスは4種類
K = 3
y_true = [[1, 2], [1, 2], [4], [1, 2, 3, 4], [3, 4]]
# K=3より、通常は3個まで順位をつけ予測
y_pred = [[1, 2, 4], [4, 1, 2], [1, 4, 3], [1, 2, 3], [1, 2, 4]]
# average precision @ k の関数
def apk(y_i_true, y_i_pred):
# y_predがK以下の長さで、要素が全て異なるならば処理。さもなければ例外を投げて止まる
assert (len(y_i_pred) <= K)
assert (len(np.unique(y_i_pred)) == len(y_i_pred))
sum_precision = 0.0
num_hits = 0.0
for i, p in enumerate(y_i_pred):
if p in y_i_true:
num_hits += 1
precision = num_hits / (i + 1)
sum_precision += precision
return sum_precision / min(len(y_i_true), K)
# MAP@Kの関数
def mapk(y_true, y_pred):
return np.mean([apk(y_i_true, y_i_pred) for y_i_true, y_i_pred in zip(y_true, y_pred)])
print(mapk(y_true, y_pred))
# 0.6499999999999999
# 正解数が同じでも順序が違うとスコアが変わる
print(apk(y_true[0], y_pred[0]))
print(apk(y_true[1], y_pred[1]))
# 1.0
# 0.5833333333333333
コンペ例
資料
豊富な解説がわかりやすく、ものすごく勉強になる資料です!