最近在使用lightGBM做二分类的一些工作,用到了交叉熵,发现了使用sklearn当中的库和自己计算的结果并不相同的情况,分析记录如下
交叉熵公式
b c e = − 1 N ∑ i ( y i l o g p i + ( 1 − y i ) l o g ( 1 − p i ) ) bce = - \frac{1}{N} \sum_i (y_i log p_i + (1-y_i)log(1-p_i)) bce=−N1i∑(yilogpi+(1−yi)log(1−pi))
def logloss_z(trues, preds, sample_weight=1, eps=1e-7):
"""自定义交叉熵"""
preds = np.clip(preds, eps, 1-eps) # 防止log0出现
loss = np.where(trues, -np.log(preds), -np.log(1-preds))
return np.mean(loss * sample_weight)
sklearn.metrics.log_loss
from sklearn.metrics import log_loss
tf.losses.BinaryCrossentropy()
import tensorflow as tf
bce = tf.losses.BinaryCrossentropy()
bce(trues, preds)
在用到lightGBM的时候,想自定义交叉熵损失函数,对交叉熵损失函数做一些改动,就需要指明损失函数的一阶二阶导数
一阶导数:
p = s i g m o i d ( x ) = 1 1 + e − x ∂ p ∂ x = p ( 1 − p ) p = sigmoid(x) = \frac{1}{1 + e^{-x}} \\ \frac{\partial{p}}{\partial{x}} = p ( 1 - p ) p=sigmoid(x)=1+e−x1∂x∂p=p(1−p)
g r a d = ∂ l o s s ∂ x = ∂ [ − y l o g p − ( 1 − y ) l o g ( 1 − p ) ] ∂ x = ∂ l o s s ∂ p ∗ ∂ p ∂ x = p − y grad= \frac{\partial{loss}}{\partial{x}} = \frac{\partial{[-ylogp - (1-y)log(1-p)}]}{\partial{x}} \\ = \frac{\partial{loss}}{\partial{p}} * \frac{\partial{p}}{\partial{x}} = p - y grad=∂x∂loss=∂x∂[−ylogp−(1−y)log(1−p)]=∂p∂loss∗∂x∂p=p−y
二阶导数:
h e s s = ∂ 2 l o s s ∂ x 2 = p ( 1 − p ) hess = \frac{\partial^2loss}{\partial{x^2}} = p(1-p) hess=∂x2∂2loss=p(1−p)
FP(假阳性):无病 -> 有病
FN(假阴性):有病 -> 无病,这种情况通常是比较严重的,耽误治疗的时机
− y i l o g p i -y_ilogp_i −yilogpi:表示所有正样本的预测情况,正->负:FN, 正->正:TP,这一项的值越小说明 p i p_i pi的值越大,预测的FN越小,所以可以通过增加这一项的权重来减少FN
另外在lightGBM当中使用cross_entropy_lambda
计算所得的交叉熵和自定义的交叉熵结果相同
sklearn.metrics.log_loss
与 我自定义交叉熵的一点区别import numpy as np
from sklearn.metrics import log_loss
from scipy.special import softmax, expit
import tensorflow as tf
bce = tf.losses.BinaryCrossentropy()
def logloss_z(trues, preds, sample_weight=1, eps=1e-7):
"""自定义损失函数"""
#preds = np.clip(preds, eps, 1-eps)
loss = np.where(trues, -np.log(preds), -np.log(1-preds))
#return np.mean(loss * sample_weight)
return np.average(loss, weights=sample_weight)
trues = np.array([1, 0])
preds = np.array([0.8, 0.1])
w = np.array([0.1, 0.6])
print(
log_loss(trues, preds, normalize=True,sample_weight=w),
logloss_z(trues, preds, sample_weight=w),
bce(tf.expand_dims(trues, axis=-1), tf.expand_dims(preds, axis=-1), sample_weight=w)
)
sklearn.metrics.log_loss
使用的是np.average
加权平均tf.bce
使用的是np.mean(loss*sample_weight)