返回列表

[8th Public / 18th Private] - Full Wikipedia Passage-level Retrieval

573. Kaggle - LLM Science Exam | kaggle-llm-science-exam

开始: 2023-07-11 结束: 2023-10-10 自然语言处理 数据算法赛
Kaggle LLM Science Exam 解决方案 - Chan Kha Vu

[8th Public / 18th Private] - Full Wikipedia Passage-level Retrieval

作者:Chan Kha Vu (Kaggle Master)
比赛排名:18th Private LB
发布时间:2023年10月11日
得票数:38

祝贺所有顶尖队伍,特别祝贺@philippsinger@ilu000@ybabakhin!这是一场有趣的比赛,我学到了很多关于训练语言模型和在小型GPU上高效推理的知识。从零构建整个检索管道是一次非常有趣的练习。

关于这场比赛的一些思考

这场比赛对我来说就像坐过山车:加入仅一周就进入了前20名,随后STEM 270K数据集的发布彻底改变了排行榜(比赛结束前7天我掉到了约190名),工作繁忙导致我放弃了数周,最后在几天内冲回前20名既紧张又令人兴奋。更别提我的Azure账户在比赛结束前11小时被封了(忘记提高预算限制),就在我向Kaggle上传数据的时候,所以我最新的模型没能赶上最终提交 😅

最后,结果的波动让我非常失望——在这样 intense 的一周几乎没有睡眠之后,我本希望能有更好的成绩。不过我很高兴通过实践学到了很多 😁

我的解决方案

我的流程相当标准——首先,我从维基百科检索相关句子,添加到我的DeBERTa模型的上下文中。然而,与大多数公开notebook先检索文章再检索句子的方式不同,我执行的是基于全文维基百科的 passage-level 检索!这个决定来自于一个观察:在比赛的200条训练集中,有很多例子中问题的实际答案实际上可以在另一个不太相关的文章中找到。之后的重新排序和预测就相当标准了。

1. 完整 Passage-level 检索 - 5450万向量/ passages

我将整个维基百科分割成128个token的 passages(约每段100个单词),步长为96个token(例如,一篇文章在移除引用/分类/参考文献并经过tokenization后,会被分割成偏移量为{ [0, 127], [96, 224], ... }的 passages)。这产生了一个巨大的嵌入数据集,包含5440万个向量,每个维度768,在磁盘上占用154 GB。显然,这对Kaggle来说太大了,我们需要压缩这个索引以便在Kaggle上使用。

我尝试了不同的FAISS索引和量化技术组合——IVF、HNSW、带OPQ预处理的产品量化(PQ)、标量量化(SQ)等。我通过GPT-3.5生成问题来验证索引,保存用于生成问题的维基百科文章和 passage,并测量索引的召回率(如果检索到的 passage 包含在使用的 passage 内)。最佳最快的组合是IVF with 256 centroids,每个向量使用产品量化编码为96字节。显然,PQ的字节数越多越好,而96是GPU推理下PQ的最大可能值。索引被压缩到小于6 GB,对4000个问题进行完整搜索仅需约20秒(使用K=100nprobe=96)。

仅使用这个索引,我就用一个公开模型(来自Chris的notebook)达到了0.891 LB,这是在STEM 270K数据集发布之前!

2. 科学文章子索引 - 760万向量/ passages

我将STEM 270K文章分割成128个token的 passages,步长为64个token。因为向量比完整维基百科 passage-level 索引少,我们可以使用更精确的索引并去掉PQ量化(我认为这过于激进)。我最终使用IVF256并采用简单的FP16量化,实现更精确的检索。这使我的LB分数从0.891提升到0.895——这个提升比其他队伍小得多,我认为这表明我的完整 passage-level 检索本身已经相当强大。

比赛结束前12小时,我还从维基百科(不是通过API抓取,而是从Huggingface数据集添加)增加了30万篇与科学相关的文章。我通过让GPT-4生成科学领域的顶级主题(物理、化学、生物、工程等)并使用be-base嵌入进行聚类来获得这些文章。总计,科学子索引包含60万篇文章760万个向量(passages),这对我的公开和私有分数都带来了轻微提升。

3. 重新排序句子

这部分相当直接。我将 passages 分割成3个句子的块,步长为1个句子,并使用BAAI/bge-large-en-v1.5重新排序这些块。此外,我还为每个句子计算BM25检索分数(使用所有问题的检索句子作为语料库)。

4. 训练DeBERTa模型

我训练了6个DeBERTa模型,每个使用略有不同的训练数据和上下文长度。所有模型都在比赛最后3天使用1xA100和1x3090训练,由于时间不多,它们可能训练不足。一些有趣的观察:

  • 训练数据使用GPT-3.5生成,采用不同提示增加多样性。我还使用了Chris Deotte的6万数据集。我还提示模型尝试包含更多数字、公式等,使问题更难(从tokenization角度)。
  • 使用更差的上下文训练能产生更好的模型!所以我使用较弱版本的检索管道(使用bge-small)生成训练数据。在LB上的差异约为0.005。
  • 这场比赛是关于阅读理解和释放LM内部的知识。因此,更小的lr和更多的Q/A示例效果更好。

什么方法没有奏效

  • 我花了一些时间尝试使用LightGBM/XGBoost训练重新排序模型,使用LambdaRank目标和各种嵌入分数/BM25分数/TFIDF分数作为特征。可惜这比简单的嵌入分数更差。
同比赛其他方案