DAPR
论文解读
摘要
场景是从长文档(long document)中找到相关片段(passage),对SoTA分析发现错误主要源于错失了文档上下文内容,于是命名任务为DAPR——文档感知的片段检索(Document-Aware Passage Retrieval)。然后为此建立benchmark并进行实验。实验中对SoTA片段落检索器辅之以文档内容:(1)和BM25混合。(2)将文档信息掺和到段落信息里。实验结果:混合检索器法整体好,但是对于“需要文档级上下文信息”的问题完全失败;掺和法反之,整体较差但在上下文困难问题有进步。
介绍
IR有用。传统BM25通过统计方法。近期方法应用神经网络进行向量化表示实现复杂语义匹配。神经方法受限于“短片段”(short passage 如512tokens)输入。在现实中遇到长文本如学科论文时,超出限制而无法利用完整上下文。近期工作实现长文档encode并实现了文档级检索。然而返回长文档对于用户仍不便于获取有效信息,如在标注维基百科页面中与用户查询相关的段落时,35.8%的查询所对应相关段落平均位于文档第7.6段(标准差12.7)。
于是提出DAPR任务,就是在长文档中找到对应片段。举例解释“文档-感知” (Document-awared),如指代关系会导致Query的关键信息出现在不同片段里,可能导致关键信息所在片段遗漏。为深入了解,测试SoTA片段检索器并分析错误,发现53.5%的错误源于未结合文档上下文解析指代或主题,导致漏检正确答案段落。于是创建一个基准测试,其中包含来自不同领域的5个数据集,这些数据集提供了在关联文档中对相关段落的标注(就是问题有标出相关片段)。
实验中对SoTA片段落检索器辅之以文档内容:(1)和BM25混合。(2)将文档信息掺和到段落信息里。实验结果:混合检索器法整体好,但是对于“需要文档级上下文信息”的问题完全失败;掺和法反之,整体较差但在上下文困难问题有进步。这个基准测试对新的检索方法有意义。
相关工作
DocQA: 需要模型回答关于输入文档的一个问题,它的问题的相关文档是给定的,而DAPR需要从文档集里找相关信息(doc)。基于给定文档这个假设,DocQA的问题通常是和文档内容关联的,比如“哪个检索系统用于这个baseline”,这个问题在DocQA中因为给定了文档所以有默认关联,而转换到DAPR就不行了,“这个baseline"不知道是啥。
长文档检索:法1是MaxP,片段最高分作为文档分,法2是FirstP,第一片段得分作为文档分,法2不如法1。有学者比较了不同的“长距离注意力模型”的长文档检索。陈等人(2022年)提出了一种分层神经网络,并证明其优于基于MaxP。但这些工作相比DAPR,都没有研究 “考虑文档内容情况下的片段检索”。
混合检索:涉及多检索系统。其中排名混合将排名合并
实验复现代码
reproduce_bm25.sh
(.sh4种算法都类似,但passage_only.bm25大不相同)
export:将变量提升为 环境变量(全局变量),使得所有子进程(如其他脚本、Python程序等)都能继承这个变量。
直接设置:直接赋值定义的变量是 Shell变量(局部变量),仅在当前 Shell 会话中有效,子进程(如其他脚本、程序)无法访问。
| |
for X in ${datasets[@]}: 遍历,取出每个元素到X
$ 变量替换:获取变量的值 ${变量名}
| |
命令替换:执行命令,并将输出结果作为字符串返回
$(python -m dapr.exps.passage_only.args.bm25 $CLI_ARGS):传入 CLI_ARGS 给dapr.exps.passage_only.args.bm25.py 并执行,捕获输出结果(如print的内容),py脚本代码如下,传入 –data_dir=data/NaturalQuestions,即py里有个变量data_dir=“data/NaturalQuestions”,BM25PassageOnlyArguments里的初始化会生成各种属性,包括output_dir。
| |
export OUTPUT_DIR=$(…): … 即 bm25.py 里的输出被bash脚本捕获。
**mkdir -p $OUTPUT_DIR:**确保OUTPUT_DIR存在
**export LOG_PATH:**设置日志变量
| |
**torchrun … > $LOG_PATH:**PyTorch 的分布式训练启动器,自动设置多进程环境,将命令的 标准输出(stdout) 重定向到文件,错误输出(stderr)仍会打印到终端。
- –nproc_per_node=1:使用当前机器的1个GPU
- –master_port=295001:主进程的通信端口
- -m dapr.exps.passage_only.bm25:执行的脚本文件
- $CLI_ARGS:传入的参数
| |
dragon_plus (passage_only)
passage_only下的dragon_plus(类似上面的passage_only.bm25),即“仅段落检索使用dragon_plus检索器”,由以下 调用 实现。
reproduce_cp相当于main,其中主要执行了…passage_only.args.dragon_plus.py和…passage_only.dragon_plus_cp.py,前者用于配置一些参数,参数会传入后者,后者是实现功能和评估。
|——reproduce_cp.py
|——dapr.exps.passage_only.args.dragon_plus.py
| |——dapr.exps.passage_only.args.base.py
| |——clddp.args.base
|
|——dapr.exps.passage_only.dragon_plus_cp.py
|——dapr.dataloader.py
|——dapr.retrievers.dense.py
|——dapr.dataloader.py
|——clddp.dm
|——clddp.search
|——clddp.evaluation
|——clddp.evaluation
|——clddp.dm 后文中,以下简称,其他一致。
dapr.exps.passage_only.args.dragon_plus.py 简称为 …args.dragon_plus.py
dapr.exps.passage_only.dragon_plus_cp.py 简称为 …passage_only.dragon_plus_cp.py
reproduce_cp.py
|——reproduce_cp.py 替换原本的.sh,实现模拟命令行传参 reproduce_cp.py的功能为,替换原本的.sh,实现模拟命令行传参,而不用每次都在pycharm新建 运行/调试配置,大致流程和 reproduce_bm25.sh一样:
- 设置全局/局部变量
- 执行 dapr.exps.passage_only.args.dragon_plus,生成args
- 再构造一些变量
- 执行 dapr.exps.passage_only.dragon_plus_cp
| |
…args.dragon_plus.py
|——reproduce_cp.py 替换原本的.sh,实现模拟命令行传参
|——...args.dragon_plus.py 定义类 BM25PassageOnlyArguments,输出output_dir定义类 BM25PassageOnlyArguments 继承自 PassageOnlyArguments,重要属性如下:
run_name: str = XXX (自动生成的实验运行的唯一名称,见PassageOnlyArguments)data_dir: str = os.path.join("exps/passage_only/bm25", self.run_name) # 数据集路径split: Split = Split.test # 哪个数据部分(测试集 test 或训练集 train)topk: int = 1000 # 返回最相关的1000个结果per_device_eval_batch_size: int = 32 # 每个GPU一次处理32条数据fp16: bool = True # 用半精度浮点数(节省显存,加快计算)
不同点在于获取 parse_cli(BM25PassageOnlyArguments).output_dir 时使用的方法build_output_dir被重写为 os.path.join(“exps/passage_only/bm25”, self.run_name)
reproduce_cp执行该文件,进入if __name__ == “__main__",print(….output_dir),执行完reproduce_cp捕获该内容
| |
args.base.py
|——reproduce_cp.py 替换原本的.sh,实现模拟命令行传参
|——...args.dragon_plus.py 定义类 BM25PassageOnlyArguments,输出output_dir
| |——args.base.py 定义类 PassageOnlyArguments和一些重要参数定义类 PassageOnlyArguments,采用MixIn机制继承两个混入类(Mixin)(可插拔组件) ——AutoRunNameArgumentsMixIn, DumpableArgumentsMixIn,从clddp.args.base里import,继承前者能自动生成一次运行唯一名词(属性),继承后者获得输出目录和写入输出目录的方法。
设置了一些属性(见代码)
| |
clddp.args.base
定义两个类
- AutoRunNameArgumentsMixIn:自动生成实验运行的唯一名称
- DumpableArgumentsMixIn:设置output_dir和保存一些运行参数设置
|——reproduce_cp.py 替换原本的.sh,实现模拟命令行传参
|——...args.dragon_plus.py 定义类 BM25PassageOnlyArguments,输出output_dir
| |——...args.base.py 定义类 PassageOnlyArguments和一些重要参数
| |——clddp.args.base | |
…passage_only.dragon_plus_cp.py
|——reproduce_cp.py 替换原本的.sh,实现模拟命令行传参
|——...args.dragon_plus.py 定义类 BM25PassageOnlyArguments,输出output_dir
| |——...args.base.py 定义类 PassageOnlyArguments和一些重要参数
| |——clddp.args.base 定义两个MixIn
---------|----------------------------------------------------------后面省略上面内容
|——...passage_only.dragon_plus_cp.py其代码单步执行会顺序进出如下模块。
|——...args.dragon_plus 加载参数和保存参数到json文件
|——clddp.utils 初始化ddp
|——dapr.dataloader.py DAPRDataConfig, DAPRDataLoader实现数据加载
|——dapr.retrievers.dense.py 引入检索器
|——dapr.dataloader.py 获取标注好的问题
|——clddp.dm
|——clddp.search
|——clddp.evaluation
|——clddp.evaluation
|——clddp.dm代码
| |
clddp.utils
----|---------------------上面省略
|——...passage_only.dragon_plus_cp.py
|——...args.dragon_plus 加载参数和保存参数到json文件
|——clddp.utils 初始化ddpdapr.dataloader.py
----|---------------------上面省略
|——...passage_only.dragon_plus_cp.py
|——...args.dragon_plus 加载参数和保存参数到json文件
|——clddp.utils 初始化ddp
|——dapr.dataloader.py DAPRDataConfig, DAPRDataLoader实现数据加载dragon_plus_cp中语句:
| |
分两步:
DAPRDataConfig(args.data_dir)
DAPRDataLoader(…).load_data( progress_bar=is_device_zero() )
where is_device_zero()==True 单卡模式
| |
(DAPRDataLoader只列出用到的内容)
| |
dapr.datasets.dm
----|---------------------上面省略
|——...passage_only.dragon_plus_cp.py
|——...args.dragon_plus 加载参数和保存参数到json文件
|——clddp.utils 初始化ddp
|——dapr.dataloader.py DAPRDataConfig, DAPRDataLoader实现数据加载
|——dapr.datasets.dm | |
总结
屎一样的代码