转自:http://blog.csdn.net/ybdesire/article/details/73695163
用sklearn,计算loglosss时,对多类别问题,在用这样的代码进行计算(如下),会报错。
y_true = [0,1,3]
y_pred = [1,2,1]
log_loss(y_true, y_pred)
ValueError: y_true and y_pred contain different number of classes 3, 2. Please provide the true labels explicitly through the labels argument. Classes found in y_true: [0 1 3]
这是怎么回事呢?
这个问题的产生,是因为不了解logloss的计算过程。logloss计算过程中,必须要求将输出用one-hot表示。将这个多类别问题的求解用OneHotEncoder改为如下,就能修复这个问题。
from sklearn.metrics import log_loss
from sklearn.preprocessing import OneHotEncoder
one_hot = OneHotEncoder(n_values=4, sparse=False)
y_true = one_hot.fit_transform([0,1,3])
y_pred = one_hot.fit_transform([1,2,1])
log_loss(y_true, y_pred)
那么,logloss的具体计算过程是怎么样的呢?下面详细解释。
首先,我们看logloss的计算公式:
这个公式中的各个字母含义为:
我们以下面这组数据来说明计算过程:
求解logloss
首先,可知N=3(3个样本),M=4(类别数为4(0,1,2,3))。
所以,y和p都是3x4的矩阵:
但是,如果对p矩阵做log,log(0)是无穷大。sklearn解决这个问题,是将p中的0转换为1e-15(1的-15次方)。
p = array([[ 1.00000000e-15, 1.00000000e+00, 1.00000000e-15,
1.00000000e-15, 1.00000000e-15, 1.00000000e-15,
1.00000000e+00, 1.00000000e-15, 1.00000000e-15,
1.00000000e+00, 1.00000000e-15, 1.00000000e-15]])
并且,经过调试(调试sklearn源码的方法参考这篇文章),还发现sklearn将logloss的计算公式做了点小修改,如下所示,将1/N移到了p项内。
这个改动对应的源代码是
y_pred /= y_pred.sum(axis=1)[:, np.newaxis]
所以,这两个矩阵会被转换为:
# 上面的p除以3后就是这个p
p=array([[ 3.33333333e-16, 3.33333333e-01, 3.33333333e-16,
3.33333333e-16, 3.33333333e-16, 3.33333333e-16,
3.33333333e-01, 3.33333333e-16, 3.33333333e-16,
3.33333333e-01, 3.33333333e-16, 3.33333333e-16]])
y = array([[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]])
得到y和p,用下面的点乘函数,就能计算得到logloss的值。
loss = -(y * np.log(p)).sum(axis=1)
最终求得的logloss是:106.91216605。