分级利器:序数回归

之前面试的时候遇到过好几个候选人在做”评级“相关的项目,例如针对图片、视频的色情程度,评论的粗鲁程度分级等等。绝大部分的人在面对这种问题时通常想到的都是用回归或分类来解决。这两种方法都是有效的,但都有一些问题: 常规的分类无法很好地建模类别间的关系。例如你要对评论的不文明程度分5档,但对于分类常用的交叉熵损失函数来说,把一个最高档的评论分成了最低档还是中间档对它来说损失是一样的。但对于实际的业务,前者显然比后者是更难接受的。 回归算法需要比较多的超参调试。在之前的文章里聊过,回归对标签的尺度是敏感的,把细粒度,例如100档(标签为1-100)的评级问题直接交给MSE Loss往往得不到好的结果。回归对标签中的最大值和最小值也天然会有一些抗拒。 在Pawpularity比赛结束后CPMP说他使用了一种叫Ordinal Regression(中文名没找到,姑且称它为序数回归)的方法,我在一些评级问题上尝试之后发现这个方法确实行之有效,而且非常简单优美。 数学解释 说是序数“回归”,但我它认为本质上是一个考虑了类别间关系的分类算法。 大家一定都很熟悉sigmoid函数$σ$,它的定义域是(-∞,+∞),而值域是(0,1),且是单调增函数,连续可导。我们可以把$σ(x)$看做是随机变量小于x的概率,即某个(-∞,+∞)上随机变量的累积分布函数(CDF)。 假设我要处理一个5档的分类问题,而上面说的随机变量就是模型的输出,那么问题可以转化为找到四个切分点$\theta_1, \theta_2, \theta_3, \theta_4$,并用$P(x<\theta_1)$, $P(\theta_1< x<\theta_2)$, $P(\theta_2< x<\theta_3)$, $P(\theta_3< x<\theta_4)$, $P(\theta_4< x<+\infty)$这五个概率来表示$x$分别属于五个等级的概率。进一步结合前面的sigmoid函数做CDF的方法,可以把五个概率转化为$σ(\theta_1-x)$, $σ(\theta_2-x)-σ(\theta_1-x)$, $σ(\theta_3-x)-σ(\theta_2-x)$, $σ(\theta_4-x)-σ(\theta_3-x)$, $1-σ(\theta_4-x)$。 这样我们就把一个模型输出的实数logit转化成了属于五个等级的概率,进而可以使用负对数似然损失函数来优化这个分类问题。在这样的设定下既可以使用一组固定的切分点来优化模型,又可以把切分点也作为可学习的权重和模型一起优化。 代码 一开始我在网上找到了一个pytorch的Ordinal Regression实现spacecutter,但经过一番实验之后我发现它写的并不完美,于是自己又修改了一下,在这里分享给大家 class OrdinalRegressionLoss(nn.Module): def __init__(self, num_class, train_cutpoints=False, scale=20.0): super().__init__() self.num_classes = num_class num_cutpoints = self.num_classes - 1 self.cutpoints = torch.arange(num_cutpoints).float()*scale/(num_class-2) - scale / 2 self.cutpoints = nn.Parameter(self.cutpoints) if not train_cutpoints: self.cutpoints.requires_grad_(False) def forward(self, pred, label): sigmoids = torch.sigmoid(self.cutpoints - pred) link_mat = sigmoids[:, 1:] - sigmoids[:, :-1] link_mat = torch....

March 2, 2022 · 1 min · Yuanhao