将“Softmax+交叉熵”推广到多标签分类问题
By 苏剑林 | 2020-04-25 | 328974位读者 |(注:本文的相关内容已整理成论文《ZLPR: A Novel Loss for Multi-label Classification》,如需引用可以直接引用英文论文,谢谢。)
一般来说,在处理常规的多分类问题时,我们会在模型的最后用一个全连接层输出每个类的分数,然后用softmax激活并用交叉熵作为损失函数。在这篇文章里,我们尝试将“Softmax+交叉熵”方案推广到多标签分类场景,希望能得到用于多标签分类任务的、不需要特别调整类权重和阈值的loss。
单标签到多标签 #
一般来说,多分类问题指的就是单标签分类问题,即从$n$个候选类别中选$1$个目标类别。假设各个类的得分分别为$s_1,s_2,
\dots,s_n$,目标类为$t\in\{1,2,\dots,n\}$,那么所用的loss为
\begin{equation}-\log \frac{e^{s_t}}{\sum\limits_{i=1}^n e^{s_i}}= - s_t + \log \sum\limits_{i=1}^n e^{s_i}\label{eq:log-softmax}\end{equation}
这个loss的优化方向是让目标类的得分$s_t$变为$s_1,s_2,\dots,s_t$中的最大值。关于softmax的相关内容,还可以参考《寻求一个光滑的最大值函数》、《函数光滑化杂谈:不可导函数的可导逼近》等文章。
现在我们转到多标签分类问题,即从$n$个候选类别中选$k$个目标类别。这种情况下我们一种朴素的做法是用sigmoid激活,然后变成$n$个二分类问题,用二分类的交叉熵之和作为loss。显然,当$n\gg k$时,这种做法会面临着严重的类别不均衡问题,这时候需要一些平衡策略,比如手动调整正负样本的权重、focal loss等。训练完成之后,还需要根据验证集来进一步确定最优的阈值。
这时候,一个很自然的困惑就是:为什么“$n$选$k$”要比“$n$选$1$”多做那么多工作?
笔者认为这是很不科学的事情,毕竟直觉上$n$选$k$应该只是$n$选$1$自然延伸,所以不应该要比$n$要多做那么多事情,就算$n$选$k$要复杂一些,难度也应该是慢慢过渡的,但如果变成多个二分类的话,$n$选$1$反而是最难的,因为这时候类别最不均衡。而从形式上来看,单标签分类比多标签分类要容易,就是因为单标签有“Softmax+交叉熵”可以用,它不会存在类别不平衡的问题,而多标签分类中的“sigmoid+交叉熵”就存在不平衡的问题。
所以,理想的解决办法应该就是将“Softmax+交叉熵”推广到多标签分类上去。
众里寻她千百度 #
为了考虑这个推广,笔者进行了多次尝试,也否定了很多结果,最后确定了一个相对来说比较优雅的方案:构建组合形式的softmax来作为单标签softmax的推广。在这部分内容中,我们会先假设$k$是一个固定的常数,然后再讨论一般情况下$k$的自动确定方案,最后确实能得到一种有效的推广形式。
组合softmax #
首先,我们考虑$k$是一个固定常数的情景,这意味着预测的时候,我们直接输出得分最高的$k$个类别即可。那训练的时候呢?作为softmax的自然推广,我们可以考虑用下式作为loss:
\begin{equation}-\log \frac{e^{s_{t_1}+s_{t_2}+\dots+s_{t_k}}}{\sum\limits_{1\leq i_1 < i_2 < \cdots < i_k\leq n}e^{s_{i_1}+s_{i_2}+\dots+s_{i_k}}}=\log Z_k - (s_{t_1}+s_{t_2}+\dots+s_{t_k})\end{equation}
其中$t_1,t_2,\dots,t_k$是$k$个目标标签,$Z_k = \sum\limits_{1\leq i_1 < i_2 < \cdots < i_k\leq n}e^{s_{i_1}+s_{i_2}+\dots+s_{i_k}}$是配分函数。很显然,上式是以任何$k$个类别总得分$s_{i_1}+s_{i_2}+\dots+s_{i_k}$为基本单位所构造的softmax,所以它算是单标签softmax的合理推广。又或者理解为还是一个单标签分类问题,只不过这是$C_n^k$选$1$问题。
在这个方案之中,比较困难的地方是$Z_k$的计算,它是$C_n^k$项总得分的指数和。不过,我们可以利用牛顿恒等式来帮助我们递归计算。设$S_k = \sum\limits_{i=1}^n e^{k s_i}$,那么
\begin{equation}\begin{aligned}
Z_1 =&\, S_1\\
2Z_2 =&\, Z_1 S_1 - S_2\\
3Z_3 = &\, Z_2 S_1 - Z_1 S_2 + S_3\\
\vdots\\
k Z_k = &\, Z_{k-1} S_1 - Z_{k-2} S_2 + \dots + (-1)^{k-2} Z_1 S_{k-1} + (-1)^{k-1} S_k
\end{aligned}\end{equation}
所以为了计算$Z_k$,我们只需要递归计算$k$步,这可以在合理的时间内计算出来。预测阶段,则直接输出分数最高的$k$个类就行。
自动确定阈值 #
上述讨论的是输出数目固定的多标签分类问题,但一般的多标签分类的目标标签数是不确定的。为此,我们确定一个最大目标标签数$K\geq k$,并添加一个$0$标签作为填充标签,此时loss变为
\begin{equation}\log \overline{Z}_K - (s_{t_1}+s_{t_2}+\dots+s_{t_k}+\underbrace{s_0+\dots+s_0}_{K-k\text{个}})\end{equation}
而
\begin{equation}\begin{aligned}
\overline{Z}_K =&\, \sum\limits_{1\leq i_1 < i_2 < \cdots < i_K\leq n}e^{s_{i_1}+s_{i_2}+\dots+s_{i_K}} + \sum\limits_{0 = i_1 = \dots = i_j < i_{j+1} < \cdots < i_K\leq n}e^{s_{i_1}+s_{i_2}+\dots+s_{i_K}}\\
=&\, Z_K + e^{s_0} \overline{Z}_{K-1}
\end{aligned}\end{equation}
看上去很复杂,其实很简单,还是以$K$个类别总得分为基本单位,但是允许且仅允许$0$类重复出现。预测的时候,仍然是输出分数最大的$K$个类,但允许重复输出$0$类,等价的效果是以$s_0$为阈值,只输出得分大于$s_0$的类。最后的式子显示$\overline{Z}_K$也可以通过递归来计算,所以实现上是没有困难的。
暮然回首阑珊处 #
看上去“众里寻她千百度”终究是有了结果:理论有了,实现也不困难,接下来似乎就应该做实验看效果了吧?效果好的话,甚至可以考虑发paper了吧?看似一片光明前景呢!然而~
幸运或者不幸,在验证了它的有效性的同时,笔者请教了一些前辈大神,在他们的提示下翻看了之前没细看的Circle Loss,看到了它里边统一的loss形式(原论文的公式(1)),然后意识到了这个统一形式蕴含了一个更简明的推广方案。
所以,不幸的地方在于,已经有这么一个现成的更简明的方案了,所以不管如何“众里寻她千百度”,都已经没有太大意义了;而幸运的地方在于,还好找到了这个更好的方案,要不然屁颠屁颠地把前述方案写成文章发出来,还不如现成的方案简单有效,那时候丢人就丢大发了~
统一的loss形式 #
让我们换一种形式看单标签分类的交叉熵$\eqref{eq:log-softmax}$:
\begin{equation}-\log \frac{e^{s_t}}{\sum\limits_{i=1}^n e^{s_i}}=-\log \frac{1}{\sum\limits_{i=1}^n e^{s_i-s_t}}=\log \sum\limits_{i=1}^n e^{s_i-s_t}=\log \left(1 + \sum\limits_{i=1,i\neq t}^n e^{s_i-s_t}\right)\end{equation}
为什么这个loss会有效呢?在文章《寻求一个光滑的最大值函数》、《函数光滑化杂谈:不可导函数的可导逼近》中我们都可以知道,$\text{logsumexp}$实际上就是$\max$的光滑近似,所以我们有:
\begin{equation}\log \left(1 + \sum\limits_{i=1,i\neq t}^n e^{s_i-s_t}\right)\approx \max\begin{pmatrix}0 \\ s_1 - s_t \\ \vdots \\ s_{t-1} - s_t \\ s_{t+1} - s_t \\ \vdots \\ s_n - s_t\end{pmatrix}\end{equation}
这个loss的特点是,所有的非目标类得分$\{s_1,\cdots,s_{t-1},s_{t+1},\cdots,s_n\}$跟目标类得分$\{s_t\}$两两作差比较,它们的差的最大值都要尽可能小于零,所以实现了“目标类得分都大于每个非目标类的得分”的效果。
所以,假如是有多个目标类的多标签分类场景,我们也希望“每个目标类得分都不小于每个非目标类的得分”,所以下述形式的loss就呼之欲出了:
\begin{equation}\log \left(1 + \sum\limits_{i\in\Omega_{neg},j\in\Omega_{pos}} e^{s_i-s_j}\right)=\log \left(1 + \sum\limits_{i\in\Omega_{neg}} e^{s_i}\sum\limits_{j\in\Omega_{pos}} e^{-s_j}\right)\label{eq:unified}\end{equation}
其中$\Omega_{pos},\Omega_{neg}$分别是样本的正负类别集合。这个loss的形式很容易理解,就是我们希望$s_i < s_j$,就往$\log$里边加入$e^{s_i - s_j}$这么一项。如果补上缩放因子$\gamma$和间隔$m$,就得到了Circle Loss论文里边的统一形式:
\begin{equation}\log \left(1 + \sum\limits_{i\in\Omega_{neg},j\in\Omega_{pos}} e^{\gamma(s_i-s_j + m)}\right)=\log \left(1 + \sum\limits_{i\in\Omega_{neg}} e^{\gamma (s_i + m)}\sum\limits_{j\in\Omega_{pos}} e^{-\gamma s_j}\right)\end{equation}
说个题外话,上式就是Circle Loss论文的公式(1),但原论文的公式(1)不叫Circle Loss,原论文的公式(4)才叫Circle Loss,所以不能把上式叫做Circle Loss。但笔者认为,整篇论文之中最有意思的部分还数公式(1)。
用于多标签分类 #
$\gamma$和$m$一般都是度量学习中才会考虑的,所以这里我们还是只关心式$\eqref{eq:unified}$。如果$n$选$k$的多标签分类中$k$是固定的话,那么直接用式$\eqref{eq:unified}$作为loss就行了,然后预测时候直接输出得分最大的$k$个类别。
对于$k$不固定的多标签分类来说,我们就需要一个阈值来确定输出哪些类。为此,我们同样引入一个额外的$0$类,希望目标类的分数都大于$s_0$,非目标类的分数都小于$s_0$,而前面已经已经提到过,“希望$s_i < s_j$就往$\log$里边加入$e^{s_i - s_j}$”,所以现在式$\eqref{eq:unified}$变成:
\begin{equation}\begin{aligned}
&\log \left(1 + \sum\limits_{i\in\Omega_{neg},j\in\Omega_{pos}} e^{s_i-s_j}+\sum\limits_{i\in\Omega_{neg}} e^{s_i-s_0}+\sum\limits_{j\in\Omega_{pos}} e^{s_0-s_j}\right)\\
=&\log \left(e^{s_0} + \sum\limits_{i\in\Omega_{neg}} e^{s_i}\right) + \log \left(e^{-s_0} + \sum\limits_{j\in\Omega_{pos}} e^{-s_j}\right)\\
\end{aligned}\end{equation}
如果指定阈值为0,那么就简化为
\begin{equation}\log \left(1 + \sum\limits_{i\in\Omega_{neg}} e^{s_i}\right) + \log \left(1 + \sum\limits_{j\in\Omega_{pos}} e^{-s_j}\right)\label{eq:final}\end{equation}
这便是我们最终得到的Loss形式了——“softmax + 交叉熵”在多标签分类任务中的自然、简明的推广,它没有类别不均衡现象,因为它不是将多标签分类变成多个二分类问题,而是变成目标类别得分与非目标类别得分的两两比较,并且借助于$\text{logsumexp}$的良好性质,自动平衡了每一项的权重。
这里给出Keras下的参考实现:
def multilabel_categorical_crossentropy(y_true, y_pred):
"""多标签分类的交叉熵
说明:y_true和y_pred的shape一致,y_true的元素非0即1,
1表示对应的类为目标类,0表示对应的类为非目标类。
警告:请保证y_pred的值域是全体实数,换言之一般情况下y_pred
不用加激活函数,尤其是不能加sigmoid或者softmax!预测
阶段则输出y_pred大于0的类。如有疑问,请仔细阅读并理解
本文。
"""
y_pred = (1 - 2 * y_true) * y_pred
y_pred_neg = y_pred - y_true * 1e12
y_pred_pos = y_pred - (1 - y_true) * 1e12
zeros = K.zeros_like(y_pred[..., :1])
y_pred_neg = K.concatenate([y_pred_neg, zeros], axis=-1)
y_pred_pos = K.concatenate([y_pred_pos, zeros], axis=-1)
neg_loss = K.logsumexp(y_pred_neg, axis=-1)
pos_loss = K.logsumexp(y_pred_pos, axis=-1)
return neg_loss + pos_loss
所以,结论就是 #
所以,最终结论就是式$\eqref{eq:final}$,它就是本文要寻求的多标签分类问题的统一loss,欢迎大家测试并报告效果。笔者也实验过几个多标签分类任务,均能媲美精调权重下的二分类方案。
要提示的是,除了标准的多标签分类问题外,还有一些常见的任务形式也可以认为是多标签分类,比如基于0/1标注的序列标注,典型的例子是笔者的“半指针-半标注”标注设计。因此,从这个角度看,能被视为多标签分类来测试式$\eqref{eq:final}$的任务就有很多了,笔者也确实在之前的三元组抽取例子task_relation_extraction.py中尝试了$\eqref{eq:final}$,最终能取得跟这里一致的效果。
当然,最后还是要说明一下,虽然理论上式$\eqref{eq:final}$作为多标签分类的损失函数能自动地解决很多问题,但终究是不存在绝对完美、保证有提升的方案,所以当你用它替换掉你原来多标签分类方案时,也不能保证一定会有提升,尤其是当你原来已经通过精调权重等方式处理好类别不平衡问题的情况下,式$\eqref{eq:final}$的收益是非常有限的。毕竟式$\eqref{eq:final}$的初衷,只是让我们在不需要过多调参的的情况下达到大部分的效果。
转载到请包括本文地址:https://www.spaces.ac.cn/archives/7359
更详细的转载事宜请参考:《科学空间FAQ》
如果您还有什么疑惑或建议,欢迎在下方评论区继续讨论。
如果您觉得本文还不错,欢迎分享/打赏本文。打赏并非要从中获得收益,而是希望知道科学空间获得了多少读者的真心关注。当然,如果你无视它,也不会影响你的阅读。再次表示欢迎和感谢!
如果您需要引用本文,请参考:
苏剑林. (Apr. 25, 2020). 《将“Softmax+交叉熵”推广到多标签分类问题 》[Blog post]. Retrieved from https://www.spaces.ac.cn/archives/7359
@online{kexuefm-7359,
title={将“Softmax+交叉熵”推广到多标签分类问题},
author={苏剑林},
year={2020},
month={Apr},
url={\url{https://www.spaces.ac.cn/archives/7359}},
}
June 23rd, 2021
from bert4keras.backend import keras,multilabel_categorical_crossentropy
from bert4keras.models import build_transformer_model
from bert4keras.tokenizers import Tokenizer
from bert4keras.optimizers import Adam
from bert4keras.snippets import sequence_padding, DataGenerator
from bert4keras.snippets import open
from keras.models import Model
from keras.layers import Dense
import tensorflow.compat.v1 as tf
# 基本信息
train = True # 训练的时候设置为True 预测的时候设置为Fals.
maxlen = 120
epochs = 10
batch_size = 4
learning_rate = 1e-5
num_labels = 15
model_save = 'train/best_model.weights'
# bert配置
# path = "data"
path = "/Users/sssdjj/bert_source"
config_path = path + '/chinese_L-12_H-768_A-12/bert_config.json'
checkpoint_path = path + '/chinese_L-12_H-768_A-12/bert_model.ckpt'
dict_path = path + '/chinese_L-12_H-768_A-12/vocab.txt'
def load_data(filename):
D = []
with open(filename) as f:
for i, l in enumerate(f):
if i == 0:
continue
content, labels = l.split(",", 1)
labels = [list(map(lambda x: int(x), labels.split(",")))]
D.append((content, labels))
return D
# 读取数据
train_data = load_data('data/train_onehot.csv')
valid_data = load_data('data/test_onehot.csv')
# 建立分词器
tokenizer = Tokenizer(dict_path, do_lower_case=True)
class data_generator(DataGenerator):
"""数据生成器
"""
def __iter__(self, random=False):
batch_token_ids, batch_segment_ids, batch_labels = [], [], []
for is_end, (text, labels) in self.sample(random):
token_ids, segment_ids = tokenizer.encode(text, maxlen=maxlen)
batch_token_ids.append(token_ids)
batch_segment_ids.append(segment_ids)
batch_labels.append(labels)
if len(batch_token_ids) == self.batch_size or is_end:
batch_token_ids = sequence_padding(batch_token_ids)
batch_segment_ids = sequence_padding(batch_segment_ids)
batch_labels = sequence_padding(batch_labels)
yield [batch_token_ids, batch_segment_ids], batch_labels
batch_token_ids, batch_segment_ids, batch_labels = [], [], []
model = build_transformer_model(
config_path,
checkpoint_path,
model='bert',
)
# lstm_output = Bidirectional(LSTM(num_labels//2, dropouit=0.2, return_sequences=True))(model.output)
output = Dense(num_labels,activation="sigmoid")(model.output)
model = Model(model.input, output)
model.summary()
opt = tf.keras.optimizers.Adam(learning_rate)
opt = tf.train.experimental.enable_mixed_precision_graph_rewrite(opt, loss_scale='dynamic')
model.compile(
# loss="binary_crossentropy",
loss=multilabel_categorical_crossentropy,
optimizer=Adam(learning_rate),
# metrics=["accuracy"]
)
def evaluate(data):
for text,label in data:
token_ids, segment_ids = tokenizer.encode(text, maxlen=maxlen)
result = model.predict([token_ids, segment_ids])
print(result)
class Evaluator(keras.callbacks.Callback):
"""评估和保存模型
"""
def __init__(self):
self.best_val_f1 = 0.
def on_epoch_end(self, epoch, logs=None):
evaluate(valid_data)
model.save_weights("train/best_model.weights")
if __name__ == '__main__':
if train == True:
train_generator = data_generator(train_data, batch_size)
evaluator = Evaluator()
model.fit_generator(
train_generator.forfit(),
steps_per_epoch=len(train_generator),
epochs=epochs,
callbacks=[evaluator]
)
苏神 我写了一个 多标签 二分类 ,输入 label 是 【【1,0,0,1,0,0】【0,0,0,1,0,0】】这种,但是loss 很大,是我哪里有问题吗;苏神如果觉得我代码太长影响评论,请删除
你这种就是要逼我骂人的。你不看文章不理解文章也就算了,你能不能给我好好看看multilabel_categorical_crossentropy的注释?“尤其是不能加sigmoid或者softmax!”那么明确的声明你都看不到?
June 24th, 2021
这个loss最后不是一个scalar,而是一个vector,是不是还要sum或mean一下?
August 5th, 2021
您好!我在项目中有使用到您提出的loss,然后打算发表到某期刊,但是老师说不能引用中文或博客类型的文献,我应该这么做呢?我可以直接说明这个是您提出的,而不附加上博客链接吗?
按我说,你老师要真的是那么硬气,就应该在你写论文之初就制止你,而不是现在要你来讨价还价...
言归正传,按照我个人的观点,我是坚决不会同意“不能引用中文或博客类型的文献”的说法的,也极其鄙视有这样想法的人,因为这代表着对学术成果的基本尊重都没有。不过,这其实也是我的一厢情愿,你要是提都不提直接用,我也不能拿你怎样。这种事情也不是第一次发生了,几乎纯copy的我都发现过,也没啥辙,毕竟我的影响力也没那么大,毕竟这也不是什么“英文的”、”专业的“专家认定过的东西。
苏神你好,我在看到ICJAI2021《Document-level Relation Extraction as Semantic Segmentation》论文第四页的公式(10)(11)跟本博客一模一样,没有看到引用,可以处理一下。(评论一直发送不成功)
好的,感谢反馈。
September 15th, 2021
[...]CircleLoss: 将“softmax+交叉熵”推广到多标签分类问题[...]
September 15th, 2021
[...]CircleLoss: 将“softmax+交叉熵”推广到多标签分类问题[...]
September 27th, 2021
苏神,俺这里有一个疑问,你说的n选k比n选1需要做很多工作,是因为前者用了多标签维度,后者只有了单标签维度;如果n选k也用单标签维度,进而直接使用soft+交叉熵,这样是不是就简单多了呢,苏神为什么没有这样做呢?而是把它改为了多标签的二分类问题,是因为单标签的n选k会出现某些问题吗?希望苏神能和俺讨论一下哈~
什么叫做“n选k也用单标签维度”?具体做法是怎样的?
对于softmax+交叉熵的多分类模型,输出层的神经元节点数为类别数n,也就是每一个神经元节点对应一个类别,普通的多分类只是每次获取输出值最大的神经元节点对应的类别;
对于多标签时,在预测阶段,就在上述网络结构上直接获取前k个神经元节点最大值对应的标签就可以是吧?
在多标签的训练阶段,与上述类似,每条样本对应的n个神经元节点中,将k个标签分别与输入的一条样本组合成k个单标签训练数据,然后利用上述网络结构进行训练。
不太清楚上述多标签的预测和训练的思路可不可以,如果可以的话,与当前苏神设计的多标签分类模型相比,缺点是什么呢?期待苏神回复~
如果你的$k$是固定的,那么这个应该勉强也可以,但是如果$k$不固定的情况下,感觉上比较悬。
September 28th, 2021
苏神,您在式(10)提到的"希望目标类的分数都大于$s0$,非目标类的分数都小于$s0$"这个限制应该可以包含"每个目标类得分都不小于每个非目标类的得分",所以多标签$loss$是不是可以变为:$$ log(1+\sum\limits_{i\in \Omega_{neg}}e^{s_i}+\sum\limits_{j\in \Omega_{pos}}e^{-s_j}) $$
指定阈值为$0$,我这也根据上式做了实验,发现效果稍微差些(上式:0.96904,式(11):0.96967),收敛速度差不多,是由于上式缺失的一项$$\sum\limits_{i\in \Omega_{neg},j\in \Omega_{pos}}e^{s_i-s_j}$$应该"每个目标类得分都不小于每个非目标类的得分"的同时暗含 目标类与非目标得分差距尽可能大?请教下苏神,根据上式设计的多标签$loss$会有其它问题吗?
我之前好像实验过你这个结果,结论是总类别数非常多的情况下,收敛会慢一些,结果也略差。
我也觉得这种形式更加统一
October 13th, 2021
苏神你好,我对这种方式如何解决了类别不平衡有点不解呢,为什么说单标签“softmax+交叉熵”它不会存在类别不平衡的问题呢。是从最大类内相似度与最小类间相似度这个角度吗?请苏神指教哈~
November 21st, 2021
[...]该解码器是苏神提出的一种可以处理嵌套与非嵌套NER的解码器。更多内容可以参见GlobalPointer:用统一的方式处理嵌套和非嵌套NER 然后,在其中加入相对位置编码,能大幅提升性能: 最后利用将“softmax+交叉熵”推广到多标签分类问题中提到的损失函数,得到其损失为: 其代码链接为: https://github.com/bojone/GlobalPointer[...]
December 3rd, 2021
苏神,请教下这样理解是不是哪里有问题哈
公式11相当于max(max(Si), 0) + max(max(-Sj), 0) = max(max(Si), 0) + max(-min(Sj), 0)
所以最后优化的是:及最小的目标最大的非目标分要小于0分数要大于0,通过lse按照softmax分配梯度
多个二分类优化的是:所有非目标分数都要小于0及所有的目标分数都要大于0,同样通过BCE按照softmax分配梯度
所以是不是引入阈值后,把我们最初想要的正负两两比较给整严格之后,导致退化了呢
但我这边确实观察到同样的参数下,这个loss前期收敛更快一点(尴尬,这证明我上面的理解应该有问题,但我又没想明白);当然最后两个loss表现就完全一样了
实在不好意思,看了下第三行搞错了,不知道怎么修改,应该是:
所以最后优化的是:最大的非目标分要小于0及最小的目标要大于0,通过lse按照softmax分配梯度
你要搞清楚,$(11)$式跟$(10)$式的第一个式子取$s_0=0$是完全等价的,而$(10)$式包含了你想要的两两比较。
喔喔多谢多谢,我应该就是这步没想清楚
式10前面说“而前面已经已经提到过,希望Si < Sj就往log里边加入exp(si−sj)”,我能理解前面只有一个目标时添加到log里,但是多个目标的时候直接加入到log里的推理我就没有捋得太清楚
理解的最原始的推理应该是:希望(Si < Sj) & (Si < S0) & (Sj > S0),那么loss应该是
loss = log(1+lse(Si-Sj)) + log(1+lse(Si-S0)) + log(1+lse(S0-Sj)),但后面怎么推过去,就蒙圈了
circle loss的原始形式,本身就不限于只有一个目标