为何含sigmoid的神经网络代码与softmax_cross_entropy_with_logits差异显著?
为什么用Sigmoid的神经网络代码和用
softmax_cross_entropy_with_logits的代码差异这么大? 其实这俩工具从设计初衷到实际使用逻辑都有着本质区别,哪怕是在二分类场景下,也不是简单的“换个函数就行”的关系,我来拆解下具体差异:
1. 输出节点数和语义完全不一样
- 用
softmax_cross_entropy_with_logits的时候,不管你是二分类还是多分类,输出层必须设置和类别数一致的节点数——比如二分类就要2个节点。softmax会把这两个节点的输出归一化成和为1的概率,分别代表样本属于两类的概率,本质是从互斥的类别里选一个。 - 而用Sigmoid的话,二分类只需要1个输出节点就行,它直接输出样本属于正类的概率(0到1之间),负类概率就是
1 - 这个值,完全砍掉了冗余的输出,逻辑是判断样本是否属于某个类别。
举两个TensorFlow的代码片段对比下,你就能直观感受到:
Softmax二分类写法
# 假设输入是[batch_size, feature_dim]的特征张量 logits = tf.layers.dense(input_features, units=2) # 必须输出2个节点 # 标签要转成one-hot编码的2维向量 one_hot_labels = tf.one_hot(true_labels, depth=2) cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits( logits=logits, labels=one_hot_labels ))
Sigmoid二分类写法
logits = tf.layers.dense(input_features, units=1) # 只需要1个输出节点 # 标签直接用0/1的单值张量,不需要转one-hot float_labels = tf.cast(true_labels, tf.float32) cost = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits( logits=logits, labels=float_labels ))
2. 损失计算的底层逻辑有差异
softmax_cross_entropy_with_logits是先对logits做softmax归一化,再计算交叉熵,而且它默认类别是互斥的——一个样本只能属于一个类别,这也是为什么它需要one-hot标签。sigmoid_cross_entropy_with_logits则是对每个输出节点单独计算sigmoid和交叉熵,它不要求类别互斥,甚至支持多标签分类(比如一张图里同时有猫和狗)。哪怕是二分类场景,它的逻辑也是“判断是否属于正类”,而不是“在两个类里二选一”。
3. 数值稳定性的优化方式不同
TensorFlow的这两个API都做了数值稳定性优化,避免计算时出现溢出或NaN,但优化路径不一样:
softmax_cross_entropy_with_logits把softmax和交叉熵的计算合并成了一个更稳定的公式,避免单独计算exp(logits)时可能出现的数值溢出(比如logits很大的时候,exp值会炸掉)。sigmoid_cross_entropy_with_logits同样合并了sigmoid和交叉熵的计算,用max(x, 0) - x * z + log(1 + exp(-abs(x)))这个公式来避免数值问题,比手动先算sigmoid再算交叉熵稳定得多。
4. 标签格式要求天差地别
这是代码里最直观的差异之一:
- 用softmax交叉熵时,标签必须是one-hot编码的向量,二分类下就是
[1,0]或[0,1]这种形式。 - 用sigmoid交叉熵时,标签直接是0或1的单值(甚至可以是代表概率的浮点值),完全不需要转one-hot。
最后补充一句:虽然理论上二分类场景下两者的最终效果是等价的,但代码写法差异大正是因为这些底层设计的不同。如果是严格互斥的二分类,两者都能用;如果是多标签分类,那只能用Sigmoid那一套。
内容的提问来源于stack exchange,提问作者rwallace
相关产品推荐
相关产品推荐

