经典CTR预估场景,主要对是否曝光/点击(0/1)进行二分类。最近遇到多分类(类别之间互斥)问题,踩了一些坑,总结和整理下,希望能够加深对多分类理解和思考。
区别
二分类 | 多分类 | |
标签 | 0/1 | c类,如c=10:[0,1,0,0,0,0,0,0,0,0] |
损失函数 | 交叉熵 | 多分类交叉熵 |
预估输出 | (0,1)值 | c维向量,如c=10:[0.1,0.1,0.2,0.2,0.1,0.1,0.1,0.2,0.2,0.1] |
auc评估 | 随机选取正样本和负样本,正样本预估概率>负样本概率 | c类中每个单类auc; macro:c类每个类别的F1均值(各类别F1的权重相同); micro:计算总体的TP,FN和FP的数量,再计算F1; |
标签
标签为(0/1)int,而多分类为c的one-hot向量,如下:
binary label:0/1
mult label:[0,1,0,0,0,0,0,0,0,0]
损失函数
二分类Cross Entropy
其中,y为样本标签:0/1;p为样本预测正样本的概率;其代码如下:
loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=y, labels=labels))
多分类Cross Entropy
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=y, labels=labels['one_hot']))
预估输出
二分类sigmod函数
其代码如下:
pred = tf.sigmoid(y)
多分类softmax函数
其中,对于多分类一定要考虑清楚:预估输出是c类中概率最大的类还是c类均值,其代码如下:
y = tf.nn.softmax(y_logits)
# 预估c类每类对应的概率
pred = y
# 预估c类平均期望
cls = tf.reshape(tf.cast(tf.range(0, 10), dtype=tf.float32), shape=(10, 1))
pred = tf.matmul(y, cls)
auc评估
auc(area under curve)描述二分类器分类能力,x轴=fpr,y轴=tpr构成的一条曲线下面积。描述随机选取正样本和负样本,正样本预估概率>负样本预估概率的概率。
对于多分类,有三种理解方法:
- 将每类单独看作0/1二分类器,分别描述每类auc;
- 将每类F1求均值,即为macro;
- 将c类预估正确为1,其他为0,则混淆矩阵c列展开为1列,计算总体的TP,FN和FP的数量,再计算F1;
for slotid in slot_dict:
# 计算每一类的ROC
fpr = dict()
tpr = dict()
roc_auc = dict()
y_label = np.array(slot_dict[slotid]['true_label'])
y_score = np.array(slot_dict[slotid]['preds'])
# print('slot_id:{}, y_label:{}, y_score:{}'.format(slotid, y_label, y_score))
# micro
fpr["micro"], tpr["micro"], _ = roc_curve(y_label.ravel(), y_score.ravel())
micro_auc = auc(fpr["micro"], tpr["micro"])
roc_auc["micro"] = micro_auc
print('slot_id: {}, micro_auc: {}'.format(slotid, micro_auc))
for i in range(n_classes):
if(y_label[:, i].size == y_score[:, i].size):
# print('i: {}, y_label: {}, y_score_i: {}'.format(i, y_label[:, i], y_score[:, i]))
fpr[i], tpr[i], _ = roc_curve(y_label[:, i], y_score[:, i])
test_auc = auc(fpr[i], tpr[i])
roc_auc[i] = test_auc
print('slot_id: {}, i: {}, auc: {}'.format(slotid, i, test_auc))
评估case
对于多分类效果如下,大家觉得哪个更好?