损失函数的基本用法:
criterion = LossCriterion() #构造函数有自己的参数
loss = criterion(x, y) #调用标准时也有参数
得到的loss结果已经对mini-batch数量取了平均值
1.BCELoss(二分类)
CLASS torch.nn.BCELoss(weight=None, size_average=None, reduce=None, reduction='mean')
创建一个衡量目标和输出之间二进制交叉熵的criterion
unreduced loss函数(即reduction参数设置为'none')为:
N表示batch size,xn为输出,yn为目标
如果reduction不为'none'(默认设为'mean'),则:
即默认情况下,loss会基于element求
平均值,如果size_average=False
的话,loss
会被累加。
这是用来测量误差error的重建,例如一个自动编码器
。注意 0<=target[i]<=1。
参数:
- weight (Tensor,可选) – 每批元素损失的手工重标权重。如果给定,则必须是一个大小为“nbatch”的张量。
- size_average (bool, 可选) –
弃用(见
reduction
参数)。默认情况下,设置为True,即对批处理中的每个损失元素进行平均。注意,对于某些损失,每个样本有多个元素。如果字段size_average设置为False,则对每个小批的损失求和。当reduce为False时,该参数被忽略。默认值:True - reduce (bool,可选) –
弃用(
见
)。默认情况下,设置为True,即根据size_average参数的值决定对每个小批的观察值是进行平均或求和。如果reduce为False,则返回每个批处理元素的损失,不进行平均和求和操作,即忽略size_average参数。默认值:Truereduction
参数 - reduction (string,可选) – 指定要应用于输出的
操作:' none ' | 'mean' | ' sum '。“none”:表示不进行任何reduction
,“mean”:输出的和除以输出中的元素数,即求平均值,“sum”:输出求和。注意:size_average和reduce正在被弃用,与此同时,指定这两个arg中的任何一个都将覆盖reduction参数。默认值:“mean”reduction
形状:
- 输入:(N,*), *代表任意数目附加维度
- 目标:(N,*),与输入拥有同样的形状
- 输出:标量scalar,即输出一个值。如果reduce为False,即不进行任何处理,则(N,*),形状与输入相同。
举例:
m = nn.Sigmoid()
loss = nn.BCELoss()
input = torch.randn(3,requires_grad=True)
target = torch.empty(3).random_(2) output = loss(m(input), target) output.backward()
input,target,output
返回:
(tensor([-0.8728, 0.3632, -0.0547], requires_grad=True),
tensor([1., 0., 0.]), tensor(0.9264, grad_fn=))
m(input)结果为:
tensor([0.2947, 0.5898, 0.4863])
计算output = (1 * ln 0.2947+(1-1)*ln(1-0.2947) + 0*ln0.5898 + (1-0)*ln(1-0.5898) + 0*ln0.4863 + (1-0)*ln(1-0.4863)) / 3 = 0.9264
input.grad
返回:
tensor([-0.2351, 0.1966, 0.1621])
当我们进行的是二分类时,即激活函数使用的是sigmoid函数时,常使用交叉熵作为损失函数。这样就能够解决因sigmoid函数导致的梯度消失问题
比如当我们使用的不是二进制交叉熵作为损失函数,而是使用的是平方差损失,即MSELoss作为损失函数,如:
那么假设进行的是二分类,损失函数为 ln= (xn - yn)2 / 2, n=1,2 , 激活函数为sigmoid函数,所以xn=σ(z),其中z = wx + b
那么当进行链式求导时,得:
- 对w求导: ∂L / ∂w = (xn - yn) * σ'(z) * z' = (xn - yn) * σ'(z) * x
- 对b求导: ∂L / ∂b = (xn - yn) * σ'(z)
从上面两个公式可知梯度计算都与sigmoid函数的梯度相关,而因为sigmoid函数左右两边梯度趋于0,这就会导致反向传播过程中计算得到的梯度会趋于0,即导致发生梯度消失的问题
而如果是以交叉熵作为损失函数,得到的梯度计算公式就会变为:
- 对w求导: ∂L / ∂w = 1/n * Σi xn * (σ(z)-yn)
- 对b求导: ∂L / ∂b =1/n * Σi (σ(z)-yn)
可见不会与sigmoid的梯度相关,这样就不会出现梯度消失的问题
2.BCEWithLogitsLoss
CLASS torch.nn.BCEWithLogitsLoss(weight=None, size_average=None, reduce=None, reduction='mean', pos_weight=None)
与BCELoss的不同:
将sigmoid函数和BCELoss方法结合到一个类中
这个版本在数值上比使用一个带着BCELoss损失函数的简单的Sigmoid函数更稳定,通过将操作合并到一层中,我们利用log-sum-exp技巧来实现数值稳定性。
损失函数(即reduction参数设置为'none')变为:
多出参数:
- pos_weight (Tensor,可选) –正值例子的权重,必须是有着与分类数目相同的长度的向量
该参数用处:
可以通过增加正值示例的权重来权衡召回率和准确性。在多标签分类的情况下,损失可以描述为:
c表示类的数量(c>1表明是多标签二进制分类,c=1表明是单标签二进制分类),n为一批中的例子数量,pc为类别c的正值的权重,解决正负例样本不均衡的情况
pc>1增加召回率,pc<1增加准确性
举例:例如,如果一个数据集包含一个类的100个正示例和300个负示例,那么该类的pos_weigh设为300/100=3。该损失函数将表现得像数据集包含了300个正示例
如果不考虑参数pos_weigh,其实BCEWithLogitsLoss就相当于比BCELoss多进行了一个sigmoid操作,所以上面的例子: