对比学习与垂直领域微调
对比学习是优化向量化模型的常用训练方法,目的是:优化向量化模型,使其向量化后的文本,相似的在向量空间距离近,不相似的在向量空间距离远。文档召回场景下,做对比学习(有监督)需要三元组(问题,文档正例,文档负例)。文档正例是和问题密切相关的文档片段,文档负例是和问题不相关的文档片段,可以是精挑细选的(难例挖掘出来的,下一小节介绍),也可以是随机出来的。如果是随机出来的话,完全可以**用同一个batch里,其他问题的文档正例当作某一个问题的文档负例,如果想要效果好,还需要有比较大的batch size。**损失函数是基于批内负样本的交叉熵损失,如下公式所示,q、d分别表示问题和文档正例对应的向量, 𝜏 为温度系数,sim函数可以是cos相似度或者点积。向量化模型对比学习这块,其实非常值得完整的看看《SimCSE: Simple Contrastive Learning of Sentence Embeddings》这篇经典论文,实验充分,理论扎实。
ℓ𝑖=−log𝑒sim(𝑞𝑖,𝑑𝑖+)/𝜏∑𝑗=1𝑁𝑒sim(𝑞𝑖,𝑑𝑗+)/𝜏
具体代码实现也非常简单,如下所示。分别将B1个问题,和B2个文档片段通过向量化模型变成向量形式,然后通过矩阵乘积计算每个问题和文档的相似度,最后通过交叉熵损失进行优化。如果文档负例仅来自于同一个batch的其他样本的文档正例,那么B1=B2;如果人工的给每个样本陪k个文档负例(比如可以通过难例挖掘得到),那么B2 =(k+1)*B1。
解释q_reps = self.encode(query) # 问题矩阵 维度(B1, d)
d_reps = self.encode(doc) # 文档矩阵 维度(B2, d)
score = torch.matmul(q_reps, d_reps.transpose(0, 1)) #计算相似度矩阵 维度:(B1, B2)
scores = scores / self.temperature
target = torch.arange(scores.size(0), device=scores.device, dtype=torch.long) ## 得交叉熵损失函数的标签
# 考虑文档负例不仅来自于batch内其他样本的文档正例,也可能人工的给每个样本构造一些文档负例。
target = target * (p_reps.size(0) // d_reps.size(0))
loss = cross_entropy(scores, target) //交叉熵损失函数
bge2论文里,做基于批内负样本的对比学习时同时考虑了多任务问题。之前也介绍了,不同任务加的prompt是不同的,如果把不同任务的样本放到一个batch里,模型训练时候就容易出现偷懒的情况,有时候会根据pormpt的内容来区分正负例,降低任务难度,这是不利于对比学习效果的。因此,可以通过人为的规定,同一个batch里,只能出现同一种任务的样本缓解这个问题。(实际应用场景下,如果任务类别不是非常多的话,最好还是一个任务训练一个模型,毕竟向量化模型也不大,效果会好一些)
在垂直领域应用场景中,亲测构建几千条样本进行对比学习微调就能带来10%左右的召回率提升,性价比非常高。数据可以借助chatgpt进行构造,比如让他根据某段文本内容提出问题,bge项目就自带微调代码,在bge模型的基础上进行微调效果就比较不错了。
【大模型外挂知识库(RAG)优化】如何炼成强大的向量化召回模型 - 知乎 (zhihu.com)