SimCSE是有大神陈丹琦发表的《Simple Contrastive Learning of Sentence Embeddings》,确实简单高效。
SimCSE包含无监督(图左部分)和有监督(图右部分)两种方法。实线箭头代表正例,虚线代表负例。
创新点在于使用Dropout对文本增加噪音。
- 正例构造:利用Bert的随机Dropout,同一文本经过两次Bert enconder得到不同的句向量构成相似文本。
- 负例构造:同一个Batch中的其他样本作为负例被随机采样。
- 损失函数: l i = − l o g e s i m ( h i z i , h i z i ′ ) / τ ∑ j = 1 N e s i m ( h i z i , h j z j ′ ) / τ l_i=-log\;\frac{e^{sim(h_i^{z_i},h_i^{z'_i})/\tau}}{\sum_{j=1}^Ne^{sim(h_i^{z_i},h_j^{z'_j})/\tau}} li=−log∑j=1Nesim(hizi,hjzj′)/τesim(hizi,hizi′)/τ
- 实验表明该种方式比其他数据增强方式要好:
可能的原因是,在源头上对输入的原始词,做增删改查,离最后的输出太远,而Dropout基本是在最后阶段,效果会更直接一些。
- 正例:标注数据
- 负例:同Batch内的其他样本
- 损失函数: − l o g e s i m ( h i , h i + ) / τ ∑ j = 1 N ( e s i m ( h i , h j + ) / τ + e s i m ( h i , h j − ) / τ ) -log\;\frac{e^{sim(h_i,h_i^+)/\tau}}{\sum_{j=1}^N(e^{sim(h_i,h_j^+)/\tau}+e^{sim(h_i,h_j^-)/\tau})} −log∑j=1N(esim(hi,hj+)/τ+esim(hi,hj−)/τ)esim(hi,hi+)/τ
Dropout虽然可以防止模型训练中的过拟合并增强鲁棒性,但是其操作在一定程度上会使训练后的模型成为一种多个子模型的组合约束。SimCSE就是希望Dropout对模型结果不会有太大影响,也就是模型输出对Dropout是鲁棒的。所以,“Dropout两次”这种思想是可以推广到一般任务的,这就是R-Drop(Regularized Dropout),由微软亚洲研究院和苏州大学提出的更加简单有效的正则方法。
- R-Drop与传统作用于神经元或模型参数的约束方法不同,而是作用于输出层,弥补了Dropout在训练和测试时的不一致性。在每个mini-batch中,每个数据样本过两次带有Dropout的同一个模型,R-Drop再使用KL-divergence(KL散度)约束两次的输出一致。所以,R-Drop约束了由于Dropout带来的两个随机子模型的输出一致性。
- R- Drop只是简单增加了一个KL-散度损失函数项,并没有其他任何改动。虽然该方法看起来很简单,但在NLP和CV的任务中,都取得了非常不错的SOTA结果。
- 同样的输入,同样的模型,经过两个 Dropout 得到的将是两个不同的分布,近似将这两个路径网络看作两个不同的模型网络。基于此,这两个不同的模型产生的不同分布分别为 P 1 ( y ∣ x ) P_1(y|x) P1(y∣x)和 P 2 ( y ∣ x ) P_2(y|x) P2(y∣x)。而这篇文章的主要贡献就是在训练过程中不断拉低这两个分布之间的KL 散度。由于KL 散度本身具有不对称性,作者通过交换这两种分布的位置以间接使用整体对称的KL 散度,称之为双向KL 散度。
- 损失函数:
普通分类问题,训练集为 D = { ( x i , y i ) } i = 1 n D=\{(x_i,y_i)\}^n_{i=1} D={(xi,yi)}i=1n,模型为 P ω ( y i ∣ x i ) P^\omega(y_i|x_i) Pω(yi∣xi),则loss一般为最小化交叉熵: L n l l = 1 n ∑ i = 1 n − l o g P ω ( y i ∣ x i ) L_{nll}=\frac{1}{n}\sum^n_{i=1}-log\;P^\omega(y_i|x_i) Lnll=n1i=1∑n−logPω(yi∣xi)
Dropout两次,样本已经有了两个略微不同的模型,这时候的loss分为两部分,一是常规的交叉熵: L n l l i = − l o g P 1 ω ( y i ∣ x i ) − l o g P 2 ω ( y i ∣ x i ) L_{nll}^i=-log\;P^\omega_1(y_i|x_i)-log\;P^\omega_2(y_i|x_i) Lnlli=−logP1ω(yi∣xi)−logP2ω(yi∣xi)
另一部分为两个模型之间的对称KL散度,希望不同Dropout的模型输出尽可能一致: L K L i = 1 2 ( D K L ( P 1 ω ( y i ∣ x i ) ∣ ∣ P 2 ω ( y i ∣ x i ) ) + D K L ( P 2 ω ( y i ∣ x i ) ∣ ∣ P 1 ω ( y i ∣ x i ) ) ) L^i_{KL}=\frac{1}{2}(D_{KL}(P^\omega_1(y_i|x_i)||P^\omega_2(y_i|x_i))+D_{KL}(P^\omega_2(y_i|x_i)||P^\omega_1(y_i|x_i))) LKLi=21(DKL(P1ω(yi∣xi)∣∣P2ω(yi∣xi))+DKL(P2ω(yi∣xi)∣∣P1ω(yi∣xi)))
则最终的loss为: L i = L n l l i + α ⋅ L K L I L^i=L^i_{nll}+\alpha\cdot L^I_{KL} Li=Lnlli+α⋅LKLI也就是说在常规交叉熵的基础上,加了一项强化模型鲁棒性正则项。
- SimCSE构建正负例时存在两个两个缺点:
①构造正例长度相等,导致模型预测时存在偏差,长度相等的文本会倾向预测相似度高。
②对比学习理论上负例越多,对之间模型学习的越好,但增大Batch会受到性能的限制。- ESimCSE针对以上问题做了相应的改进:
- 正例构造:通过引入噪声较小的“单词重复”方式改变正例的长度,设置重复率dup_rate,确定dup_len后利用均匀分布随机选取dup_len子词进行重复。
d u p _ l e n ∈ [ 0 , m a x ( 2 , i n t ( d u p _ l e n ∗ N ) ) ] dup\_len\in[0,max(2,int(dup\_len*N))] dup_len∈[0,max(2,int(dup_len∗N))] d u p _ s e t = u n i f o r m ( r a n g e = [ 1 , N ] , n u m = d u p _ l e n ) dup\_set=uniform(range=[1,N],num=dup\_len) dup_set=uniform(range=[1,N],num=dup_len)- 负例构造:为了更有效的扩展负对,同时不降低性能,通过维护一个队列,重用前面紧接的mini-batch的编码嵌入来扩展负对:
①将当前mini-batch的句嵌入放入队列,同时将“最老的”句子踢出队列。由于排队句子嵌入来自前面的mini-batch,通过取其参数的移动平均来保持动量更新模型,并利用动量模型生成排队句子嵌入。
②在使用动量编码器时,关闭了dropout,这可以缩小训练和预测之间的差距。 θ ← λ θ m + ( 1 − λ ) θ e , θ m 为动量更新编码器参数, θ e 为编码器参数, λ ∈ [ 0 , 1 ) 为动量系数参数,只有编码器参数通过反向传播 更新 , 由于动量更新,尽管两个编码器编码不同步,差异很小 \theta\leftarrow\lambda\theta_m+(1-\lambda)\theta_e,\\\scriptsize \theta_m为动量更新编码器参数,\theta_e为编码器参数, \lambda\in[0,1)为动量系数参数,只有编码器参数通过反向传播\\更新, 由于动量更新,尽管两个编码器编码不同步,差异很小 θ←λθm+(1−λ)θe,θm为动量更新编码器参数,θe为编码器参数,λ∈[0,1)为动量系数参数,只有编码器参数通过反向传播更新,由于动量更新,尽管两个编码器编码不同步,差异很小- 损失函数: l i = − l o g e s i m ( h i , h i + ) / τ ∑ j = 1 N e s i m ( h i , h j + ) / τ + ∑ m = 1 M e s i m ( h i , h m + ) / τ l_i=-log\frac{e^{sim(h_i,h_i^+)/\tau}}{\sum^N_{j=1}e^{sim(h_i,h_j^+)/\tau}+\sum^M_{m=1}e^{sim(h_i,h_m^+)/\tau}} li=−log∑j=1Nesim(hi,hj+)/τ+∑m=1Mesim(hi,hm+)/τesim(hi,hi+)/τ
- 效果对比:
Prompt Learning比较火热,号称NLP的第四范式,今天我们先从文本表征切入来认识Prompt+对比学习——PromptBERT
- 作者发现BERT在语义相似度方面表现不好,主要由:static token embeddings biases和ineffective layers,而不是high cosine similarity of the sentence embedding。static token embedding是在bert结构中,输入进block前,通过embedding layer产生的结果,这里强调是静态的embedding,就是embedding metrics中每个token都唯一对应的embedding,是不随句子环境而变化的。至于ineffective layers就很好理解了,就是bert中堆叠的block结构,比如bert-base中的12层。作者认为这些结构,对语义相似度的表征这个方面是无效的。
- Anisotropy(各向异性):上篇我们已经提到,词向量是有维度的,每个维度上基向量单位向量长度不一样,就是各向异性的。这会造成计算向量相似度的时候产生偏差。如何度量Anisotropy: 1 n 2 − n ∣ ∑ i ∑ j ≠ i c o s ( M ( s i ) , M ( s j ) ) ∣ , s i 为所有句子的集合, M 表示 s e n t e n c e e m b e d d i n g 方法 \frac{1}{n^2-n}|\sum_i\sum_{j\neq i}cos(M(s_i),M(s_j))|,\\\small s_i为所有句子的集合,M表示sentence embedding方法 n2−n1∣i∑j=i∑cos(M(si),M(sj))∣,si为所有句子的集合,M表示sentenceembedding方法任意两两计算cos similarity,越接近1,则越more anisotropy。
- 作者分析了造成embedding bias的原因,除了token frequency是造成bias的原因,作者又提出了:subwords,case sentitive。
图中不同词频token的分布情况,颜色越深代表词频越高,我们可以看出词频高的token,分布比较紧凑,词频低的token,分布较分散。作者输出这个图像还有一个目的是他提出各向异性(anisotropy)和偏差(bias)是不相关的,各向异性不是导致偏差的原因。
Embedding bias意思是映射分布被一些不相关的信息所干扰,是可以用降维的方式去可视化的。
各向异性意思是整体向量都被映射在了一个“狭窄”的高维空间,这一点是无法用降维的方式去可视化的。
如果按照之前其他论文作者的结论,roberta是不具备各向异性的,所以不应该会产生bias,但结合下面的表格来看,三种模型的embedding都有bias,所以这样也可以证明 anisotropy ≠ bias \textbf {anisotropy} \neq \textbf{bias} anisotropy=bias。
- 为了消除bias对模型performance的影响,作者用了四种人工方法移除bias,分别为:①分别除去一定量高频词,②subwords,③uppercase tokens和④标点。结果是效果都有提升,说明这些确实是产生bias的原因。不过这种人工去除的方法也有一定局限性,做长文本任务时可以使用,短文本时无法使用,因为文本本身就过短,移除的话会损失大量meaningful的信息。
- Prompt+contrastive
将prompt和constrasive应用到表征句向量中,主要从两个问题入手:①如何用prompt表征句子 ②怎样选择合适的prompt表征句向量。
第一种方法是用[MASK]对应位置的hidden state去代表sentence embedding: h = h [ M A S K ] 输入为 [ t e x t ] m e a n [ M A S K ] , 输出 [ M A S K ] 对应最后一层的 h i d d e n s t a t e h=h_{[MASK]}\\\small 输入为[text]\;mean\;[MASK],输出[MASK]对应最后一层的hidden\;state h=h[MASK]输入为[text]mean[MASK],输出[MASK]对应最后一层的hiddenstate
第二种方法和其他prompt task一样,选取由h[mask]输出的top-k tokens,将第一种方法的输出映射到词表维度,选概率最大的k个结果,取其概率和对应的static token embedding,相乘进行加权平均: h = ∑ v ∈ V t o p − k W v P ( [ M A S K ] = v ∣ h [ M A S K ] ) ∑ v ∈ V t o p − k P ( [ M A S K ] = v ∣ h [ M A S K ] ) h=\frac{\sum_{v\in V_{top-k}}W_vP([MASK]=v|h_{[MASK]})}{\sum_{v\in V_{top-k}}P([MASK]=v|h_{[MASK]})} h=∑v∈Vtop−kP([MASK]=v∣h[MASK])∑v∈Vtop−kWvP([MASK]=v∣h[MASK])两种方法比较,第二种是较传统的做法,但是缺点也很明显:
①我们利用到了static token embedding,就会受到bias的影响;②加权平均会让bert在下游任务上很难去fine-tune。作者选用第一种方式来表征句向量。- Prompt search
论文介绍了三种构建prompt的方法:①人工构建,②用T5生成,③OptiPrompt
人工构建就是人工尝试不同的prompt templates,不同的prompt templates对结果的影响还是很大的,其中prefix tokens的整体效果是优于relationship tokens的。
另一种方式就是利用T5去生成prompt templates,这种方法生成的效果是比人工构建的效果要好的。
最后的一种方法是OptiPrompt,将离散的template替换成连续的template,然后在连续的空间内去优化prompt。- 正例构造:
用不同的templates去表征同一个句子,就像从不同的view去提取句子embedding,得到的结果互为正例。但这样做问题是,得到的句向量中是包含了template的信息的,所以要去做一个denoise的操作。
具体做法:
对一个句子,先用两个template去表征这个句向量,可以得到 h i h_i hi 和 h i ′ h'_i hi′ ,然后为了denoise掉template的信息,将这两个template单独的输入进bert,但是输入时需要调整position id,加上句子的长度,确保使用和原来一样的position id,然后可以得到 h ^ i \hat{h}_i h^i和 h ^ i ′ \hat{h}'_i h^i′,再用刚才得到的结果减去这个结果,得到的 h i − h ^ i h_i-\hat{h}_i hi−h^i 和 h i ′ − h ^ ’ i h'_i-\hat{h}’_i hi′−h^’i 互为正例。- 损失函数: l i = − l o g e c o s ( h i − h ^ i , h i ′ − h ^ i ′ ) / τ ∑ j = 1 N e c o s ( h i − h ^ i , h j ′ − h ^ j ′ ) / τ l_i=-log\frac{e^{cos(h_i-\hat{h}_i,h'_i-\hat{h}'_i)/\tau}}{\sum^N_{j=1}e^{cos(h_i-\hat{h}_i,h'_j-\hat{h}'_j)/\tau}} li=−log∑j=1Necos(hi−h^i,hj′−h^j′)/τecos(hi−h^i,hi′−h^i′)/τ
- SNCSE同样是由微软团队提出,主要是针对以上方法存在的问题:当前对比学习的数据增强方式,获取的正样本都极为相似,导致模型存在特征抑制,即模型不能区分文本相似度和语义相似度,并更偏向具有相似文本,而不考虑它们之间的实际语义差异。
为了减轻特征抑制,该论文提出了通过软负样本结合双向边际损失的无监督句子嵌入对比学习方法。其中,软负样本,即具有高度相似,但与原始样本在语义上存在明显的差异的样本。双向边际损失,即通过扩大原始样本与正例和原始样本与软负例之间的距离,使模型更好地学习到句子之间的语义差别。- 软负样本构造:为原文本添加显示的否定词。
在获取句子表征时,受PromptBERT启发,通过三种模板表示原样本、正样本和软负样本: T h e s e n t e n c e : “ X ” m e a n s [ M A S K ] . T h e s e n t e n c e o f “ X + ” m e a n s [ M A S K ] . T h e s e n t e n c e o f “ X # ” m e a n s [ M A S K ] . [ m a s k ] 的隐藏状态作为输出向量 The\;sentence:“X”\;means\;[MASK].\\The\;sentence\;of\; “X^+”\;means\;[MASK].\\The\;sentence\;of\;“X^\#”\;means[MASK].\\\small [mask]的隐藏状态作为输出向量 Thesentence:“X”means[MASK].Thesentenceof“X+”means[MASK].Thesentenceof“X#”means[MASK].[mask]的隐藏状态作为输出向量- 损失函数:
利用InfoNCE损失来区分正负例 L I n f o N C E = − l o g e c o s _ s i m ( h i , h i + ) / τ ∑ j = 1 N e c o s _ s i m ( h i , h j + ) / τ L_{InfoNCE}=-log\frac{e^{cos\_sim(h_i,h_i^+)/\tau}}{\sum^N_{j=1}e^{cos\_sim(h_i,h_j^+)/\tau}} LInfoNCE=−log∑j=1Necos_sim(hi,hj+)/τecos_sim(hi,hi+)/τ计算原样本和正样本、原样本和软负样本的余弦相似度之差 Δ = c o s _ s i m ( h i , h i # ) − c o s _ s i m ( h i , h i + ) \Delta=cos\_sim(h_i,h_i^\#)-cos\_sim(h_i,h_i^+) Δ=cos_sim(hi,hi#)−cos_sim(hi,hi+)定义双向边际损失(bidirectional margin loss,BML)来模拟语义相似度差异 L B M L = R e L U ( Δ + α ) + R e L U ( − Δ − β ) L_{BML}=ReLU(\Delta+\alpha)+ReLU(-\Delta-\beta) LBML=ReLU(Δ+α)+ReLU(−Δ−β)BML损失的目标是将 Δ \Delta Δ限制在 [ − β , − α ] [-\beta,-\alpha] [−β,−α]内, α \alpha α和 β \beta β分别表示正例和软负例语义相似度的上、下差异,总损失为InfoNCE损失和双向边际损失的加权求和: L S N C S E = L I n f o N C E + λ L B M L L_{SNCSE}=L_{InfoNCE}+\lambda L_{BML} LSNCSE=LInfoNCE+λLBML
结合句子间差异的无监督句子嵌入对比学习方法——DiffCSE主要还是在SimCSE上进行优化(可见SimCSE的重要性),通过ELECTRA模型的生成伪造样本和RTD(Replaced Token Detection)任务,来学习原始句子与伪造句子之间的差异,以提高句向量表征模型的效果。
- 其思想同样来自于CV领域(采用不变对比学习和可变对比学习相结合的方法可以提高图像表征的效果)。作者提出使用基于dropout masks机制的增强作为不敏感转换学习对比学习损失和基于MLM语言模型进行词语替换的方法作为敏感转换学习「原始句子与编辑句子」之间的差异,共同优化句向量表征。
- 图左为标准的SimCSE模型,给定句子 x x x,SimCSE通过Dropout构造正例 x + x^+ x+,使用BERT编码器 f f f获得句向量 h = f ( x ) h=f(x) h=f(x)。
图右为带条件的句子差异预测模型,即ELECTRA,包含生成器和判别器。给定长度T的句子 x = [ x 1 , x 2 , . . . , x T ] x=[x_1,x_2,...,x_T] x=[x1,x2,...,xT],生成的随机掩码序列 m = [ m 1 , m 2 , . . . , m T ] , m t ∈ [ 0 , 1 ] m=[m_1,m_2,...,m_T],m_t\in[0,1] m=[m1,m2,...,mT],mt∈[0,1]。使用MLM预训练语言模型作为生成器G,通过掩码序列 x ′ \textbf{x}' x′来生成句子中被掩掉的token,获取生成序列 x ′ ′ \textbf{x}'' x′′。然后使用判别器D进行替换token检测,也就是预测哪些token是被替换的。- 损失函数: L R T D x = ∑ t = 1 T ( − 1 ( x t ′ ′ = x t ) l o g D ( x ′ ′ , h , t ) − 1 ( x t ′ ′ ≠ x t ) l o g ( 1 − D ( x ′ ′ , h , t ) ) ) L_{RTD}^x=\sum^T_{t=1}(-1(x''_t=x_t)logD(x'',h,t)-1(x''_t\neq x_t)log(1-D(x'',h,t))) LRTDx=t=1∑T(−1(xt′′=xt)logD(x′′,h,t)−1(xt′′=xt)log(1−D(x′′,h,t)))同一个batch的训练目标为 L R T D = ∑ i = 1 N L R T D x i L_{RTD}=\sum^N_{i=1}L_{RTD}^{x_i} LRTD=∑i=1NLRTDxi。
最终将SimCSE的Contrastive Loss和RTD Loss结合: L = L c o n t r a s t + λ L R T D L=L_{contrast}+\lambda L_{RTD} L=Lcontrast+λLRTD为了使判别器D的损失可以传播的编码器f中,将句向量拼接到判别器D的输入中,辅助进行RTD任务,这样做可以鼓励编码器f使信息量足够大,从而使判别器D能够区分和之间的微小差别。
当训练DiffCSE模型时,固定生成器G参数,只有句子编码器f和判别器D得到优化。训练结束后,丢弃鉴别器D,只使用句子编码器f提取句子嵌入对下游任务进行评价。- 在SimCSE模型中,采用pooler层(一个带有tanh激活函数的全连接层)作为句子向量输出。该论文发现,采用带有BN的两层pooler效果更为突出,BN在SimCSE模型上依然有效。
①对于掩码概率,经实验发现,在掩码概率为30%时,模型效果最优。
②针对两个损失之间的权重值,经实验发现,对比学习损失为RTD损失200倍时,模型效果最优。
本篇主要拆析了SimCSE以来几种比较重要的文本增强式的对比学习算法,按时间顺序,理论上应该是距离越近的算法效果越好,但使用时,还是要结合具体的业务场景,算法没有好坏,还是用看怎么用。对于有些内容,可能叙述的不是很细致或是需要一定的知识铺垫,感兴趣的同学可以针对性的研读论文和辅助其他资料。当然,算法层出不穷,更新很快,后续出现比较重要的对比学习算法,我也会更新,也欢迎各位交流讨论。
参考文献: