返回列表

5th place solution: Llama 2 70B meets Sparse & Dense Retrievals from Own parsed wikipedia dataset

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

开始: 2023-07-11 结束: 2023-10-10 自然语言处理 数据算法赛
第5名解决方案:Llama 2 70B结合稀疏与密集检索

第5名解决方案:Llama 2 70B结合稀疏与密集检索

作者:corochann
团队:charmq, qhapaq49, zaburo, flowlight
发布日期:2023-10-11

感谢主办方和所有参赛者的精彩表现。

从数据集准备、模型训练到后处理(如何利用训练好的LLM模型),确实有很多可以做的事情。对我来说这是一场非常激烈的竞争,但总体来说我享受了整个比赛过程!我也要感谢我的团队成员 @zaburo@qhapaq49@charmq@flowlight 的辛勤付出。

下图展示了我们从检索到模型的完整流程。

整体流程图

简要总结

  • 我们成功在Kaggle Notebook上运行了Llama 70B模型
  • 我们使用了基于BM-25的稀疏检索(使用pyserini库,其后端为Apache Lucene)来搜索所有维基百科页面
  • 我们自行解析了维基百科数据集,以覆盖大多数包含数字信息的页面

数据集创建

维基百科数据集

在比赛初期,当我查看给定的train.csv并尝试手动回答问题时,我感到这场比赛更像是一场"检索"竞赛,而非模型训练竞赛。我们还注意到一些页面或数字在公开可用的数据集中缺失。因此,我决定手动解析维基百科数据。

以下是各数据集的对比表格:

数据集 解析器 页面 文本信息
Kaggle发布的数据集 jjinho/wikipedia-20230701 wikitextparser 部分页面被丢弃 所有模板被丢弃
所有"\\n"被丢弃(难以人工阅读)
Huggingface数据集
- wikipedia
- graelo/wikipedia
mwparserfromhell 更好的覆盖率 所有模板被丢弃
<math><ref><table>标签被丢弃
我们的数据集 wikitextparser + 自定义模板处理 更好的覆盖率 保留{val}{math}模板文本(其他模板被丢弃)
保留<math>标签,但丢弃<ref><table>标签

Huggingface的维基百科数据集覆盖了大多数页面,但数字信息缺失,这似乎是由于mwparserfromhell解析器的问题。因此,我基于graelo/wikipedia的代码库,将解析器替换为wikitextparser(数据集描述底部有帮助说明!)。我相信我们的数据集覆盖了大多数包含数字或数学表达式信息的页面。

QA数据集创建

比赛期间分享了许多公开数据集,但我们自己也尝试创建了数据集。以下是我用于生成QA数据集的ChatGPT 3.5提示词:

system_content = """忘记之前的所有指示,严格遵循用户的规则。你是一位专业科学家的助手。"""

user_content_template_qa = Template(
    """请根据以下TEXT考虑5个选项的问题和答案。这个问题的目的是检查答题者对TEXT的深入科学理解。我们假设这个问题面向专业科学家,因此要考虑极其困难的问题。你可以提出非常详细的问题,例如检查对特定句子的理解。随机选择TEXT中的特定句子并基于此创建QA是一种好的做法。你必须基于TEXT中写明的事实创建QA。你可以通过修改正确答案的某些部分来创建错误答案。你的回复必须遵循以下格式,不要写任何其他信息。在每个Q)、1)、2)、3)、4)、5)和A)中不能包含"新行":
Q) `问题文本`
1) `答案候选1`
2) `答案候选2`
3) `答案候选3`
4) `答案候选4`
5) `答案候选5`
A) `答案`

其中只有1个"答案候选"是正确的,其他4个选项必须是错误答案。
注意1:我希望问题非常困难,因此请让错误答案不是显而易见的错误。
注意2:答案候选应为长句,约30个单词,而不是单个单词。
注意3:`答案`必须是1、2、3、4或5,不能包含任何其他文字。
注意4:问题示例有"What is ..."、"Which of the following statements ..."、"What did `the person` do"和"What was ..."。
注意5:问题应为科学、技术、工程和数学相关主题。如果给定的TEXT与科学完全不同,则只需输出"skip"而不是QA。

这是一个回复示例,请考虑这种难度级别:
Q) 以下哪个陈述准确描述了修正牛顿动力学(MOND)对星系团中观测到的"重子物质缺失"差异的影响?
1) MOND是一种通过假设存在一种称为"模糊暗物质"的新物质形式来减少星系团中观测到的重子物质缺失的理论。
2) MOND是一种将星系团中观测到的重子物质缺失与测量到的速度弥散之间的差异从约10倍增加到约20倍的理论。
3) MOND是一种通过证明质量以中微子和轴子的形式存在来解释星系团中被视为暗物质的缺失重子物质的理论。
4) MOND是一种将星系团中观测到的重子物质缺失与测量到的速度弥散之间的差异从约10倍减少到约2倍的理论。
5) MOND是一种通过施加不需要暗物质存在的新引力数学公式来消除星系团中观测到的重子物质缺失的理论。
A) 4

开始吧。这是TEXT:$title\n$text
"""
)

我仅向ChatGPT 3.5提供特定页面的摘要和1个段落作为$text

由于我们在比赛结束时能够对train.csv达到0.995这样的高分,因此我们将自己生成的1000个数据集用于本地验证。

检索

稀疏检索

我不是NLP领域的专业人士。我调研了现有研究,特别是与检索型LLM相关的研究。ORQA论文影响了我们的方法,该论文指出"在提问者已经知道答案的数据集上,传统的IR系统(如BM25)已经足够"。由于ChatGPT 3.5被给出了生成QA的句子,我们认为这在本次比赛中同样适用。

因此我们阅读了BERTserini论文,并决定使用pyserini库,这是anserini的Python封装,后端使用Apache Lucene。

这个notebook by @strifonov对在Kaggle Notebook上运行pyserini非常有帮助(谢谢!)。

Apache Lucene非常高效便捷。索引所有维基百科数据集并通过BM25分数搜索都很容易。我们将每个维基百科页面按段落分割(通过"\\n\\n")。索引总共7400万条记录仅需几个小时。在推理时,搜索通过多线程加速,200次搜索大约2分钟完成。

密集检索

我们创建了两种密集检索:

1. 所有维基百科内容按句子块分割

我们使用instructor-xl进行嵌入计算,使用faiss创建索引。原始索引太大(300GB),无法在内存中运行,因此我们对索引进行了量化(300GB → 10GB)。

2. STEM 270k内容按段落块分割

我们使用bge-large-en进行嵌入计算,并且只使用STEM270k数据集 by @mbanaei中发布的相同页面。

我们在每个块中添加了"title"信息,这样即使使用"it/he/she"来指代标题时,也能获得正确的搜索结果。

总共,我们使用了3种检索方法,并将它们合并为输入给LLM的上下文。

模型(由@zaburo撰写)

我们的团队使用了Llama-2 7B、13B、70B和Mistral 7B。由于Mistral 7B比同等规模的Llama-2取得了更好的分数,我们在最终提交中使用了Mistral 7B和Llama-70B。基于MMLU分数和我们初步实验的结果,我们使用了未经指令微调的模型,如Llama-2-70b-hf和Mistral-7B-v0.1。

对于每个模型,我们以以下格式输入问题,让模型输出在"▁A"、"▁B"、"▁C"、"▁D"、"▁E"对应标记的概率。即使在没有任何微调的情况下,这也产生了不错的性能。

{context_0}

Question: {prompt}
A. {A}
B. {B}
C. {C}
D. {D}
E. {E}
Answer:

使用QLoRA进行微调。我们将bitsandbytes的4位量化和PEFT的LoRAModel整合到训练流程中。QLoRA的超参数几乎与QLoRA论文中的完全一致,只是由于长输入和GPU内存限制而降低了批量大小。

我们预计在Kaggle Notebook上运行Llama 70B将具有挑战性,因此在比赛早期就开始着手解决这个问题。我们将每一层的量化权重(小于40GB)注册为数据集,并逐层执行推理。通过对注意力层应用xformers的memory_efficient_attention,我们能够在长上下文中保持内存消耗呈线性增长,在GPU内存中留出充足空间。我们仅使用了约6GB的GPU内存进行推理。

LLM的推理结果会因选项顺序的不同而有很大差异,因此我们采用了一种测试时增强策略,将选项顺序旋转5次并取平均值。这种TTA在无检索的预训练模型上最有效,虽然检索或QLoRA会大幅降低其效果,但直到最后仍然有效。

简单地执行TTA推理会将执行时间增加5倍,但我们没有将整个集合推理5次,而是将所有选项顺序模式如{context} {Q} {A B C D E} {B C D E A} … {E A B C D}连接起来,并适当地设置attention_mask,将长公共部分的推理减少到只有一次。通过这种技术,70B模型5倍TTA在完整测试集上的工作时间内运行,使用单个70B + BM25 Top2 + 5倍TTA获得了0.925的Private分数。

验证

我们注意到MAP@3是一个不太稳定的指标,会有些波动。以下是我们避免过拟合Public LB的策略:

  • 我们使用3个数据集进行本地验证:train.csv (200)、@yalickjdataset (300)和我们自己的数据集 (1000,如上所述)
    • 即使在最终提交中,我们也没有在训练中使用这些数据
  • 我们尝试使用本地数据集验证进行各种实验。对于在本地数据集中效果不好的方法,我们不予提交。我认为这能防止我们采用各种过拟合Public LB的方法
    • 例如,像这个自定义查询在我们的实验中效果不好,因此我们没有采用
    • 此外,我们尝试了嵌入模型微调,但在我们的本地数据集中效果不佳,因此我们也没有提交这个方法

最终流程 - 多阶段推理(由@charmq撰写)

我们注意到同时将多个上下文放入提示中可以提高模型性能。然而,由于同时输入上下文会增加推理时间,我们采用了一个多阶段推理流程,其中简单问题由小模型(mistral-7B)回答,困难问题由大模型(llama2-70B)回答。最终提交包含三个阶段:

第一阶段:mistral-7B模型集成
mistral-7B + BM25 top2上下文 + mistral-7B + instructor和bge上下文
对所有数据进行推理

第二阶段:llama2-70B
llama2-70B + BM25 top2、instructor和bge上下文
对第一阶段中置信度较低的后40%数据进行推理

第三阶段:llama2-70B
llama2-70B + bge上下文(比第一阶段和第二阶段使用的bge上下文长2-3倍)
对第一阶段和第二阶段中置信度较低的后5%数据进行推理

这个多阶段流程获得了Private: 0.926, Public: 0.928的成绩。

同比赛其他方案