在github看到一个开源项目,deepFM,即https://github.com/ChenglongChen/tensorflow-DeepFM。
代码中使用了Normalized Gini Coefficient评价指标。
这篇博客系统介绍了Normalized Gini Coefficient指标。https://blog.youkuaiyun.com/u010665216/article/details/78528261
我对文中一些公式和代码做一些补充说明。
1 基尼系数
文中
这里对公式做一下推导:
由基尼系数定义可知
由定积分与极限公式
基尼系数计算时将横纵坐标标准化为0到1
先以计算0到1区间y = x的面积为例。
这里可以知道实际对y的累加计算是,即
计算y = x的面积就是,将0到1分为n段,每段宽为1/n,高为yi,yi = i / n,因此用1/n乘以对y的累计求和来近似积分。
所以基尼系数可以如下计算:
这里除以是因为基尼系数横纵坐标要标准化为0到1。这里gini(x)积分就是B的面积。但是这里我有一个疑问,分子0到1的x的积分为什么不直接用1/2代替。
2 Normalized Gini Coefficient
文中的两个图,用第一个图橙色面积除以第二个图橙色面积,就是 Normalized Gini Coefficient。
但是找到的一些开源实现,却如下:
def gini(actual, pred):
assert (len(actual) == len(pred))
all = np.asarray(np.c_[actual, pred, np.arange(len(actual))], dtype=np.float)
all = all[np.lexsort((all[:, 2], -1 * all[:, 1]))]
totalLosses = all[:, 0].sum()
giniSum = all[:, 0].cumsum().sum() / totalLosses
giniSum -= (len(actual) + 1) / 2.
return giniSum / len(actual)
def gini_norm(actual, pred):
return gini(actual, pred) / gini(actual, actual)
可以发现,代码中对预测样本是从大到小排序,而文中是从小到大排序。
实际上,Normalized Gini Coefficient还有一(几)种理解方式,即从大到小排列时,下面第一个图橙色面积除以第二个橙色面积:
附上从大到小排列时公式,这里A是橙色加蓝色面积,B是蓝色面积
上述两个方法从小到大排列和从大到小排列,有什么区别吗,我认为几乎没有区别。
验证如下:
import numpy as np
def gini(actual, pred):
assert (len(actual) == len(pred))
# np.c_是按行连接两个矩阵,就是把两矩阵左右相加,要求行数相等。
all = np.asarray(np.c_[actual, pred, np.arange(len(actual))], dtype=np.float)
all = all[np.lexsort((all[:, 2], -1 * all[:, 1]))]
totalLosses = all[:, 0].sum() # 6.0 正样本个数
giniSum = all[:, 0].cumsum().sum() / totalLosses
giniSum -= (len(actual) + 1) / 2.
return giniSum / len(actual)
def gini1(actual, pred):
assert (len(actual) == len(pred))
# np.c_是按行连接两个矩阵,就是把两矩阵左右相加,要求行数相等。
all = np.asarray(np.c_[actual, pred, np.arange(len(actual))], dtype=np.float)
all = all[np.lexsort((all[:, 2], all[:, 1]))]
totalLosses = all[:, 0].sum() # 6.0 正样本个数
giniSum = all[:, 0].cumsum().sum() / totalLosses
giniSum = (len(actual) + 1) / 2. - giniSum
return giniSum / len(actual)
def gini_norm(actual, pred):
return gini(actual, pred) / gini(actual, actual)
def gini_norm1(actual, pred):
return gini1(actual, pred) / gini1(actual, actual)
# 针对main代码的注释
if __name__ == '__main__':
predictions = [0.9, 0.3, 0.8, 0.75, 0.65, 0.6, 0.78, 0.7, 0.05, 0.4, 0.4, 0.05, 0.5, 0.1, 0.1]
actual = [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
print(gini_norm(actual, predictions)) # 0.6296296296296299
print(gini_norm1(actual, predictions)) # 0.6296296296296295
3 参考文献&延伸阅读
https://www.kaggle.com/c/porto-seguro-safe-driver-prediction/overview/evaluation
https://blog.youkuaiyun.com/u010665216/article/details/78528261
https://www.kaggle.com/batzner/gini-coefficient-an-intuitive-explanation
https://www.kaggle.com/cppttz/gini-coefficient-an-explanation-with-math/