在模型中,除了问题的表示方法要尽可能与最终目标挂钩外,其次,loss函数与一些其他的小的操作技巧也是十分重要的
比如:
最近看到的focal loss,这是一个焦点损失函数,目标就是为了解决正类多而负类少的问题,(最直接的想法,就是在BCE loss中加权重,对于正类的损失权重大一些,负例则小一些),但是,不同的类别,数量多少也是不一致的(这里在NLP中是否可以理解为长尾分布,就是不同类别下出现的概率是不同的),因此,在损失函数中考虑了不同类别下的出现概率,形成了最终的焦点损失函数。
上边的公式展开的话:先说明的是,当y=1(正类),p_{t}=p,others情况下,p_{t}=(1-p)
−
α
(
1
−
p
)
γ
l
o
g
(
p
)
−
(
1
−
α
)
p
γ
l
o
g
(
1
−
p
)
-\alpha(1-p)^\gamma log(p)-(1-\alpha)p^\gamma log(1-p)
−α(1−p)γlog(p)−(1−α)pγlog(1−p)
但是,在针对长尾分布时,2022年的文章提到了适应性焦点损失函数(源码还没有给出),具体是在《Document-Level Relation Extraction
with Adaptive Focal Loss and Knowledge Distillation》
具体来说,它添加了一类,TH作为可调整的适应性阈值。
loss如下:P(ri)为正例概率,P(rTH)为负例概率,仔细观察的话,这是focal loss的前面一部分,(1-P(ri)是为了适应类别不均衡的情况)
这个,源码公开后,需要在详细看看
focal loss的实现(https://zhuanlan.zhihu.com/p/308290543)
#--------------二分类
class BCEFocalLoss(torch.nn.Module):
def __init__(self, gamma=2, alpha=0.25, reduction='mean'):
super(BCEFocalLoss, self).__init__()
self.gamma = gamma
self.alpha = alpha
self.reduction = reduction
def forward(self, predict, target):
pt = torch.sigmoid(predict) # sigmoide获取概率
#在原始ce上增加动态权重因子,注意alpha的写法,下面多类时不能这样使用
loss = - self.alpha * (1 - pt) ** self.gamma * target * torch.log(pt)
- (1 - self.alpha) * pt ** self.gamma * (1 - target) * torch.log(1 - pt)
if self.reduction == 'mean':
loss = torch.mean(loss)
elif self.reduction == 'sum':
loss = torch.sum(loss)
return loss
#------------多分类
class MultiCEFocalLoss(torch.nn.Module):
def __init__(self, class_num, gamma=2, alpha=None, reduction='mean'):
super(MultiCEFocalLoss, self).__init__()
if alpha is None:
self.alpha = Variable(torch.ones(class_num, 1))
else:
self.alpha = alpha
self.gamma = gamma
self.reduction = reduction
self.class_num = class_num
def forward(self, predict, target):
pt = F.softmax(predict, dim=1) # softmmax获取预测概率
class_mask = F.one_hot(target, self.class_num) #获取target的one hot编码
ids = target.view(-1, 1)
alpha = self.alpha[ids.data.view(-1)] # 注意,这里的alpha是给定的一个list(tensor
#),里面的元素分别是每一个类的权重因子
probs = (pt * class_mask).sum(1).view(-1, 1) # 利用onehot作为mask,提取对应的pt
log_p = probs.log()
# 同样,原始ce上增加一个动态权重衰减因子
loss = -alpha * (torch.pow((1 - probs), self.gamma)) * log_p
if self.reduction == 'mean':
loss = loss.mean()
elif self.reduction == 'sum':
loss = loss.sum()
return loss
另外,最近比较关注的是:CNN相关的操作,我本来是认为CNN卷积核能够包络邻近信息,这对于长实体识别也许是一定优势(毕竟,现在的方法多是只考虑[head_information,tail_information]的cat),但发现,这里的好多改进,在于pool,出现了max_pool,average_pool,动态最小、最大池化、softpool,还有对角线那种的pool等等。https://blog.csdn.net/weixin_42764932/article/details/112515715、https://blog.csdn.net/qq_41487299/article/details/105815782。
这种从左向右,从上到下的pool方式,这符合了langauge的习惯,我觉得,是可以借鉴的,无奈水平有限,没有实现。(源码是可以找到的,C++编译的,我环境还没有配置好,所以,搁浅了)