PyTorch 的 Loss Function(损失函数)都在 torch.nn.functional
里,也提供了封装好的类在 torch.nn
里。PyTorch 里有关有 18 个损失函数,常用的有 5 个,分别是:
torch.nn.L1Loss
torch.nn.MSELoss
torch.nn.BCELoss
torch.nn.BCEWithLogitsLoss
torch.nn.CrossEntropyLoss
torch.nn.NLLLoss
损失函数是用来衡量模型的单个预测与真实值的差异的:
L o s s = f ( y ^ − y ) Loss=f(\hat{y}-y) Loss=f(y^−y)
还有额外的两个概念:Cost Function(代价函数)是 N 个预测值的损失函数平均值:
C o s t = 1 N ∑ i N f ( y i ^ − y i ) Cost=\frac{1}{N}\sum^N_if(\hat{y_i}-y_i) Cost=N1i∑Nf(yi^−yi)
而 Objective Function(目标函数)是最终需要优化的函数:
O b j = C o s t + R e g u l a r i z a t i o n Obj=Cost+Regularization Obj=Cost+Regularization
还有其它的损失函数,学识有限,暂时不理解。希望以后有缘能够接触。
回归模型有两种方法进行评估:MAE(mean absolute error) 和 MSE(mean squared error)。
torch.nn.L1Loss(reduction='mean')
这个类对应了 MAE 损失函数:
ℓ = L = { l 1 , . . . l n } , l n = ∣ y ^ − y ∣ \ell=L=\{l_1,...l_n\},\quad l_n=|\hat{y}-y| ℓ=L={l1,...ln},ln=∣y^−y∣
torch.nn.MSELoss(reduction='mean')
这个类对应了 MSE 损失函数:
ℓ = L = { l 1 , . . . l n } , l n = ( y ^ − y ) 2 \ell=L=\{l_1,...l_n\},\quad l_n=(\hat{y}-y)^2 ℓ=L={l1,...ln},ln=(y^−y)2
上面两个类中的 reduction
规定了获得 ℓ \ell ℓ 后的行为,有 none
、sum
和 mean
三个。none
表示不对 ℓ \ell ℓ 进行任何处理;sum
表示对 ℓ \ell ℓ 进行求和;mean
表示对 ℓ \ell ℓ 进行平均。默认为求平均。
>>> y = torch.tensor([1.1, 1.2, 1.3])
>>> y_hat = torch.tensor([1., 1., 1.])
>>> criterion_none = nn.L1Loss(reduction='none') # 什么都不做
>>> criterion_none(y_hat, y)
tensor([0.1000, 0.2000, 0.3000])
>>> criterion_mean = nn.L1Loss(reduction='mean') # 求平均
>>> criterion_mean(y_hat, y)
tensor(0.2000)
>>> criterion_sum = nn.L1Loss(reduction='sum') # 求和
>>> criterion_sum(y_hat, y)
tensor(0.6000)
自信息是一个事件发生的概率的负对数:
I ( x ) = − l o g [ p ( x ) ] I(x)=-log[p(x)] I(x)=−log[p(x)]
信息熵用来描述一个事件的不确定性公式为
H ( P ) = − ∑ i N P ( x i ) l o g P ( x i ) H(P)=-\sum^N_iP(x_i)logP(x_i) H(P)=−i∑NP(xi)logP(xi)
一个确定的事件的信息熵为 0,一个事件越不确定,信息熵就越大。
交叉熵,用来衡量在给定的真实分布下,使用非真实分布指定的策略消除系统的不确定性所需要付出努力的大小,表达式为
H ( P , Q ) = − ∑ i = 1 B P ( x i ) l o g Q ( x i ) H(P,Q)=-\sum^B_{i=1}P(x_i)logQ(x_i) H(P,Q)=−i=1∑BP(xi)logQ(xi)
相对熵又叫 “K-L 散度”,用来描述预测事件对真实事件的概率偏差。
D K L ( P , Q ) = E [ l o g P ( x ) Q ( x ) ] = E [ l o g P ( x ) − l o g Q ( x ) ] = ∑ i = 1 N P ( x i ) [ l o g P ( x i ) − l o g Q ( x i ) ] = ∑ i = 1 N P ( x i ) l o g P ( x i ) − ∑ i = 1 N P ( x i ) l o g Q ( x i ) = H ( P , Q ) − H ( P ) D_{KL}(P,Q)=E\bigg[log\frac{P(x)}{Q(x)}\bigg]\\ =E\bigg[logP(x)-logQ(x)\bigg]\\ =\sum^N_{i=1}P(x_i)[logP(x_i)-logQ(x_i)]\\ =\sum^N_{i=1}P(x_i)logP(x_i)-\sum^N_{i=1}P(x_i)logQ(x_i)\\ =H(P,Q)-H(P) DKL(P,Q)=E[logQ(x)P(x)]=E[logP(x)−logQ(x)]=i=1∑NP(xi)[logP(xi)−logQ(xi)]=i=1∑NP(xi)logP(xi)−i=1∑NP(xi)logQ(xi)=H(P,Q)−H(P)
而交叉熵的表达式为
H ( P , Q ) = − ∑ i = 1 N P ( x i ) l o g Q ( x i ) H(P,Q)=-\sum^N_{i=1}P(x_i)logQ(x_i) H(P,Q)=−i=1∑NP(xi)logQ(xi)
可见 H ( P , Q ) = H ( P ) + D K L ( P , Q ) H(P,Q)=H(P)+D_{KL}(P,Q) H(P,Q)=H(P)+DKL(P,Q),即交叉熵是信息熵和相对熵的和。上面的 P P P 是事件的真实分布, Q Q Q 是预测出来的分布。所以优化 H ( P , Q ) H(P,Q) H(P,Q) 等价于优化 H ( Q ) H(Q) H(Q),因为 H ( P ) H(P) H(P) 是已知不变的。
下面我们来了解最常用的四个分类损失函数。
torch.nn.BCELoss(weight=None, reduction='mean')
>>> inputs = torch.tensor([[1, 2], [2, 2], [3, 4], [4, 5]], dtype=torch.float)
>>> target = torch.tensor([[1, 0], [1, 0], [0, 1], [0, 1]], dtype=torch.float)
>>> criterion = nn.BCELoss()
>>> criterion(inputs, target)
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
...
RuntimeError: all elements of input should be between 0 and 1
通常可以先使用 F.sigmoid
处理一下数据。
torch.nn.BCEWithLogitsLoss(weight=None, reduction='mean', pos_weight=None)
torch.nn.BCELoss
相似,只是 x x x 先使用了 sigmoid 处理了一下,这样就不需要手动使用 sigmoid 的了。torch.nn.NLLLoss(weight=None, ignore_index=-100, reduction='mean')
torch.nn.CrossEntropyLoss(weight=None, ignore_index=-100, reduction='mean')
nn.LogSoftmax
和 nn.NLLLoss
。这个类的运算可以写成:torch.nn.KLDivLoss(reduction='mean')
reduction
,还有一个 weight
,就是每一个类别的权重。下面用例子来解释交叉熵和 weight
是如何运作的。我们先定义一组数据,使用 numpy 推演一下:inputs = torch.tensor([[1, 1], [1, 2], [3, 3]], dtype=torch.float)
target = torch.tensor([0, 0, 1],dtype=torch.long)
idx = target[0]
input_ = inputs.detach().numpy()[idx] # [1, 1]
target_ = target.numpy()[idx] # [0]
# 第一项
x_class = input_[target_]
# 第二项
sigma_exp_x = np.sum(list(map(np.exp, input_)))
log_sigma_exp_x = np.log(sigma_exp_x)
# 输出 loss
loss_1 = -x_class + log_sigma_exp_x
结果为
>>> print("第一个样本loss为: ", loss_1)
第一个样本loss为: 0.6931473
现在我们再使用 PyTorch 来计算:
>>> criterion_ce = nn.CrossEntropyLoss(reduction='none')
>>> criterion_ce(inputs, target)
tensor([0.6931, 1.3133, 0.6931])
可以看到,结果是一致的。现在我们再看看 weight
:
>>> weight = torch.tensor([0.1, 0.9], dtype=torch.float)
>>> criterion_ce = nn.CrossEntropyLoss(weight=weight, reduction='none')
>>> criterion_ce(inputs, target)
tensor([0.0693, 0.1313, 0.6238])
与没有权重的交叉熵进行比较后可以发现,每一个值都乘以了 p i ∑ p i \frac{p_i}{\sum{p_i}} ∑pipi。当 reduction
为 sum
和 mean
的时候,交叉熵的加权总和或者平均值再除以权重的和。
F.sigmoid
+ torch.nn.BCELoss
= torch.nn.BCEWithLogitsLoss
nn.LogSoftmax
+ nn.NLLLoss
= torch.nn.CrossEntropyLoss