赞
踩
我的 torch 版本: 1.8.1+cu111
我的 paddle 版本: 2.4.1
torch API位置
torch.nn.functional.binary_cross_entropy_with_logits
paddle API位置
paddle.nn.functional.binary_cross_entropy_with_logits
二者除了 Deprecated 参数外,在大部分计算上基本都是对齐的
logit
是模型的输出,通过sigmoid激活函数 (
σ
\sigma
σ) 之后便可以转化为概率
该
l
o
s
s
loss
loss 是这样计算的:
O
u
t
=
−
L
a
b
e
l
s
∗
log
(
σ
(
L
o
g
i
t
)
)
−
(
1
−
L
a
b
e
l
s
)
∗
log
(
1
−
σ
(
L
o
g
i
t
)
)
Out = -Labels * \log(\sigma(Logit)) - (1 - Labels) * \log(1 - \sigma(Logit))
Out=−Labels∗log(σ(Logit))−(1−Labels)∗log(1−σ(Logit))
其实也就是,交叉熵
l
o
s
s
loss
loss 的最基本公式:
O
u
t
=
−
Y
∗
l
o
g
(
y
p
r
e
d
)
−
(
1
−
Y
)
∗
l
o
g
(
1
−
y
p
r
e
d
)
Out = -Y * log(y_{pred}) - (1 - Y) * log(1 - y_{pred})
Out=−Y∗log(ypred)−(1−Y)∗log(1−ypred)
将
σ
(
L
o
g
i
t
)
=
1
1
+
e
−
L
o
g
i
t
\sigma(Logit) = \frac{1}{1 + e^{-Logit}}
σ(Logit)=1+e−Logit1 带入可以简化计算,则:
O
u
t
=
L
o
g
i
t
−
L
o
g
i
t
∗
L
a
b
e
l
s
+
log
(
1
+
e
−
L
o
g
i
t
)
Out = Logit - Logit * Labels + \log(1 + e^{-Logit})
Out=Logit−Logit∗Labels+log(1+e−Logit)
文档上这样说:
该 OP 结合了 sigmoid 操作和 BCELoss 操作。同时,我们也可以认为该 OP 是sigmoid_cross_entrop_with_logits 和一些 reduce 操作的组合。
# -*- coding: utf-8 -*- """ Created on Wed Jan 4 22:36:50 2023 @author: zihao """ import numpy as np import torch import paddle # ----------- numpy 参数 ----------- np.random.seed(1107) # 假设 bs=4, 7种(多分类) np_logit = np.random.rand(4, 7).astype("float32") np_target = np.random.randint(2, size=(4, 7)).astype("float32") # 给每个类加权重 np_pos_weight = np.random.randint(2, 4, size=(7,)).astype("float32") # 给每个 batch 的元素 加权重 np_weight = np.random.randint(2, 4, size=(7,)).astype("float32") # ----------- torch ----------- t_logit = torch.tensor(np_logit) t_target = torch.tensor(np_target) t_pos_weight = torch.tensor(np_pos_weight) t_weight = torch.tensor(np_weight) t_out = torch.nn.functional.binary_cross_entropy_with_logits(t_logit, t_target, weight=t_weight, pos_weight=t_pos_weight, reduction='none') # torch 手动计算 t_out_hand = t_logit - t_logit * t_target + torch.log(1 + torch.exp(-t_logit)) t_pos_weight = t_target * t_pos_weight + (1 - t_target) t_out_hand = t_out_hand * t_pos_weight t_out_hand = t_out_hand * t_weight # ----------- paddle ----------- p_logit = paddle.to_tensor(np_logit) p_target = paddle.to_tensor(np_target) p_pos_weight = paddle.to_tensor(np_pos_weight) p_weight = paddle.to_tensor(np_weight) p_out = paddle.nn.functional.binary_cross_entropy_with_logits(p_logit, p_target, weight=p_weight, pos_weight=p_pos_weight, reduction='none') # paddle 手动计算 p_out_hand = p_logit - p_logit * p_target + paddle.log(1 + paddle.exp(-p_logit)) p_pos_weight = p_target * p_pos_weight + (1 - p_target) p_out_hand = p_out_hand * p_pos_weight p_out_hand = p_out_hand * p_weight
在以上代码中,t_out 和 t_out_hand 近乎相等,p_out 和 p_out_hand 近乎相等。前者是调用API计算的,后者是根据公式手动计算的
在 Paddle 源码新动态图部分是这样计算的:
if in_dygraph_mode(): one = _C_ops.full( [1], float(1.0), core.VarDesc.VarType.FP32, _current_expected_place(), ) # 此处按照公式进行计算 out = _C_ops.sigmoid_cross_entropy_with_logits( logit, label, False, -100 ) # 给每个正例乘以对应的权重 pos_weight if pos_weight is not None: log_weight = _C_ops.add( _C_ops.multiply(label, _C_ops.subtract(pos_weight, one)), one ) out = _C_ops.multiply(out, log_weight) # 给每个 batch 乘以对应权重 if weight is not None: out = _C_ops.multiply(out, weight) # 做 reduce 操作 if reduction == "sum": return _C_ops.sum(out, [], None, False) elif reduction == "mean": return _C_ops.mean_all(out) else: return out
关于 pos_weight 的计算,诸位需要稍微认真看一下
我是这样计算的:
p_pos_weight = p_target * p_pos_weight + (1 - p_target)
可以这样简化一下:
p_pos_weight = p_pos_weight * p_target - one * p_target + one
= (p_pos_weight - one) * p_target + one
= p_target * (p_pos_weight - one) + one
也就是源码中这样的计算
log_weight = _C_ops.add(
_C_ops.multiply(label, _C_ops.subtract(pos_weight, one)), one
)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。