TensorFlow多正分类损失计算:标签编码与损失函数选择
一、创建对应的One-Hot编码
首先要明确:你给出的[0, 1, 0, 0, 1, 0]其实已经是多标签场景下的标准One-Hot编码了——它的长度等于总类别数(这里是6个类别,索引0到5),每个位置的1表示样本属于该类别,0表示不属于。
如果你的原始标签是类别集合(比如[1, 4],对应你例子里的类别),要转换成这种向量,在TensorFlow里可以这样实现:
import tensorflow as tf # 总类别数 num_classes = 6 # 原始类别列表(和你例子对应,类别编号是1-based) original_labels = [1, 4] # 初始化全0向量 one_hot_label = tf.zeros(num_classes, dtype=tf.float32) # 更新对应类别位置为1 one_hot_label = tf.tensor_scatter_nd_update( one_hot_label, indices=[[idx] for idx in original_labels], updates=[1.0]*len(original_labels) ) print(one_hot_label.numpy()) # 输出: [0. 1. 0. 0. 1. 0.]
如果是批量处理多个样本,也可以用更简洁的方式:
# 批量原始标签(每个样本对应一组类别) batch_original_labels = [[1,4], [0,5], [2,3]] # 生成批量多标签One-Hot向量 batch_one_hot = tf.reduce_max(tf.one_hot(batch_original_labels, num_classes), axis=1)
二、损失函数的选择
这种样本可同时属于多个类别的多标签场景,最适合的损失函数是Sigmoid Cross Entropy,原因和另外两种的对比如下:
Sigmoid Cross Entropy:对每个类别输出节点独立做二分类(判断“属于/不属于”该类别),用Sigmoid激活将每个节点输出映射到0-1区间,损失是所有类别节点交叉熵的总和。完美适配多标签的非互斥特性,在TensorFlow中可以用
tf.keras.losses.BinaryCrossentropy()(多标签本质是多个二分类任务,两者等价),或者底层的tf.nn.sigmoid_cross_entropy_with_logits()。Softmax Cross Entropy:会强制所有类别输出的和为1,隐含“样本只能属于一个类别”的互斥假设。用它的话,模型会被迫压制类别1和4的概率,完全不符合你的多标签需求。
Sparse Softmax Cross Entropy:只是Softmax Cross Entropy的简化版,针对整数形式的单类别标签(比如用
1代替[0,1,0,0,0,0]),同样是互斥分类场景,不适用。
举个实际使用的代码示例:
# 模型输出的logits(未经过激活的原始输出),shape为(批量大小, 类别数) logits = tf.random.normal((3, 6)) # 批量多标签One-Hot标签,shape为(批量大小, 类别数) labels = tf.constant([[0,1,0,0,1,0], [1,0,0,0,0,1], [0,0,1,1,0,0]], dtype=tf.float32) # 使用Sigmoid交叉熵损失(from_logits=True表示输入是未激活的logits) loss_fn = tf.keras.losses.BinaryCrossentropy(from_logits=True) loss = loss_fn(labels, logits) print(loss.numpy())
内容的提问来源于stack exchange,提问作者rodrigo-silveira

