ParlAI 众包问答数据采集任务(QA Data Collection)完整指南:用 Mephisto 批量构建"段落-问答对"训练数据
【免费下载链接】ParlAIA framework for training and evaluating AI models on a variety of openly available dialogue datasets.项目地址: https://gitcode.com/gh_mirrors/pa/ParlAI
本篇技术指南聚焦 ParlAI 仓库中的qa_data_collection众包任务:它基于 Mephisto 框架,让众包工人(crowdsource worker)阅读一段给定的文本段落,然后针对该段落写出一组"问题-答案"(question-and-answer pair),最终沉淀为可用于训练阅读理解 / 问答模型的高质量数据。通过本文,你将掌握该任务的完整运行流程、全部核心配置参数(包括mephisto.blueprint.task_description_file、mephisto.blueprint.world_file、mephisto.teacher.task、mephisto.teacher.datatype等)、底层的 ParlAI World 对话逻辑与 Teacher 数据流,以及如何自定义 World 类或替换数据源来扩展你自己的数据采集任务。
任务是什么:让工人"读段落、写问答"
qa_data_collection是一个 Mephisto 众包任务,其核心目标是:让众包工人阅读一段文字(passage),然后针对该段落编写一组问题与答案。任务定义位于 parlai/crowdsourcing/tasks/qa_data_collection/README.md,任务入口脚本为 run.py。
举例来说,如果给工人展示如下段落:
At the time, there were many varying opinions about Christian doctrine, and no centralized way of enforcing orthodoxy. Constantine called all the Christian bishops throughout the Roman Empire to a meeting, and some 318 bishops (very few from the Western Empire) attended the First Council of Nicaea.(passage continues)
工人可以据此写出问题"Who called the bishops to the First Council of Nicaea?",并给出答案"Constantine called the bishops"。这种"段落 + 问题 + 答案"的三元组,正是 SQuAD 这类抽取式阅读理解数据集的标准形态——因此该任务天然适合作为定制化 QA 数据采集流水线,为特定领域或特定风格的问答模型扩充训练语料。
从源码结构看,该任务目录下包含四个核心组成部分(目录结构):
run.py:任务启动脚本,负责解析 Hydra 配置、构造 Teacher 与 World 并交给 Mephisto Operator 运行;worlds.py:定义聊天 World 类QADataCollectionWorld,实现每轮对话的逻辑、任务结束条件与超时控制;util.py:提供get_teacher工具函数,负责根据配置从 ParlAI 数据集中构造 Teacher(数据提供方);hydra_configs/conf/example.yaml:任务的默认参数配置文件(含 blueprint、architect、provider 等 Mephisto 配置);task_config/task_description.html:展示给工人的任务说明 HTML,显示在聊天窗口左侧面板;webapp/:基于 React 的前端聊天界面,负责渲染段落文本、问答对话流。
任务如何运转:一条从数据集到众包工人的完整数据流
要真正理解该任务,先看清它背后"ParlAI Teacher → Mephisto World → Web 前端"的三层数据流。整个任务的装配过程都发生在 run.py 的main()函数中:
- 解析配置:
@hydra.main(config_path="hydra_configs", config_name="scriptconfig")加载 Hydra 配置(默认读取example.yaml),并通过load_db_and_process_config(cfg)加载 Mephisto 数据库、规范化配置; - 构造 Teacher:调用
util.py中的get_teacher(cfg)根据cfg.teacher.task与cfg.teacher.datatype构建数据提供方; - 组装 World 参数:
world_opt = {"turn_timeout": cfg.turn_timeout, "teacher": teacher}将超时时间与 Teacher 一起打包,通过SharedParlAITaskState(world_opt=world_opt, onboarding_world_opt=world_opt)传给 Mephisto Blueprint(parlai_chat); - 启动任务:创建
Operator(db),调用operator.validate_and_run_config(run_config=cfg.mephisto, shared_state=shared_state)校验并运行任务,最后以operator.wait_for_runs_then_shutdown(skip_input=True, log_rate=cfg.monitoring_log_rate)挂起监听,按monitoring_log_rate(默认 30 秒)的频率打印任务监控日志,直到所有任务运行完毕。
其中world_opt里的teacher就是 World 每轮对话中"出题"的数据源。get_teacher的实现(util.py)非常巧妙:它直接用 ParlAI 自身的ParlaiParser解析config.teacher中的键值对生成opt,然后实例化一个RepeatLabelAgent作为占位 agent,再通过create_task(opt, agent).get_task_agent()返回真正用于产出段落的 Teacher。这意味着该任务复用了 ParlAI 完整的数据集生态——任何 ParlAI 注册的 Teacher 都可以作为段落来源,而不仅仅是默认的 SQuAD。
在教师端,默认的段落来源是 SquadQATeacher,从源码可以看到它继承自AbstractWrapperTeacher,其注释明确写道:"Wrapper Teacher over SQuAD to get only the passage, and ignore the question"——即只取 SQuAD 的段落文本、忽略原有的问题,把"提问"的职责完全交给众包工人,从而避免工人受到原数据集问题的影响、鼓励产出多样化的问题。
核心配置参数详解
关联文档明确指出该任务有几个最值得关注的参数。下表整理了这些参数的完整语义、默认值与源码依据:
| 参数 | 作用 | 默认值 / 说明 | 源码依据 |
|---|---|---|---|
mephisto.blueprint.task_description_file | 指向描述数据采集任务的 HTML 文件,该描述会显示在聊天窗口的左侧面板中,向工人说明任务要求 | 默认指向task_config/task_description.html(见 example.yaml) | task_description.html |
mephisto.blueprint.world_file | Python 模块路径,包含聊天 World 的类定义,用于设定每轮对话的逻辑、任务何时结束、关闭时的动作等;如果存在 onboarding World,也定义在同一模块中 | 默认指向${task_dir}/worlds.py | worlds.py |
mephisto.teacher.task | 从中抽取段落的 ParlAI 数据集 | 默认squad:SquadQATeacher(只取段落、忽略原问题) | run.py |
mephisto.teacher.datatype | 上述数据集中抽取段落所使用的数据折(fold) | 默认train(训练集) | 同上 |
turn_timeout | 工人单轮回答的最大响应时间,超时将被踢出任务 | 默认 300 秒 | run.py |
monitoring_log_rate | 任务监控日志的输出频率(秒) | 默认 30 秒 | run.py |
特别需要说明的是mephisto.blueprint.world_file的扩展价值:文档指出——如果你希望编写自己的 World 类而不新建一个 Blueprint 类,只需修改这个参数指向你的模块即可。这极大降低了自定义任务的成本:沿用默认的parlai_chatBlueprint(负责消息收发、前端交互等通用逻辑),你只需要替换"对话逻辑"这一层。
完整配置示例解读
任务的默认配置位于 hydra_configs/conf/example.yaml,一份可运行的完整配置如下:
#@package _global_ defaults: - /mephisto/blueprint: parlai_chat - /mephisto/architect: local - /mephisto/provider: mock mephisto: blueprint: world_file: ${task_dir}/worlds.py task_description_file: ${task_dir}/task_config/task_description.html custom_source_bundle: ${task_dir}/webapp/build/bundle.js num_conversations: 1 task: allowed_concurrent: 1 assignment_duration_in_seconds: 600 max_num_concurrent_units: 0 # 0 means infinite; set this to a positive integer to limit concurrent HITs and prevent crashes maximum_units_per_worker: 3 task_name: parlai-qa-example task_title: "Test ParlAI QA Data Collection Task" task_description: > This is a ParlAI data collection task. task_reward: 0.3 task_tags: "dynamic,question answering,testing" teacher: task: squad:SquadQATeacher datatype: train逐段解读这份配置:
defaults段:声明了三层 Mephisto 组件的默认实现——blueprint: parlai_chat(通用聊天蓝本,提供聊天式任务的所有基础能力)、architect: local(本地沙箱架构,即任务在本地模拟运行)、provider: mock(模拟工人提供方,不需要真实 MTurk 账户即可测试)。这也是默认情况下任务以"本地沙箱 + 模拟工人"模式运行的原因,适合先完整验证任务流程。mephisto.blueprint段:world_file与task_description_file分别对应上文两个核心参数;custom_source_bundle指向前端打包产物webapp/build/bundle.js;num_conversations: 1表示每个工人只需完成 1 轮对话(即 1 个段落)。mephisto.task段:任务级参数——allowed_concurrent: 1限制并发任务数;assignment_duration_in_seconds: 600设定单次任务的总时限(600 秒);max_num_concurrent_units: 0表示并发单元数无限(注释明确提醒:改为正整数可以限制并发 HIT 数量、防止系统崩溃);maximum_units_per_worker: 3限制每个工人最多完成 3 个 HIT;task_name/task_title/task_description/task_tags用于在 MTurk 上展示任务信息(标题、描述、搜索标签);task_reward: 0.3设定每份任务的报酬(0.3 美元)。teacher段:对应mephisto.teacher.task与mephisto.teacher.datatype,默认从 SQuAD 训练集抽取段落。
此外,run.py中还有两个任务专属的脚本级参数值得注意:turn_timeout(工人每轮回答超时,默认 300 秒,超时即被踢出)与monitoring_log_rate(监控日志输出频率,默认 30 秒)。前者在world_opt中被传给 World,作为agent.act(timeout=...)的超时上限。
深入源码:World 的两轮对话如何完成一次数据采集
任务的"灵魂"在 worlds.py 中的QADataCollectionWorld类。它继承自 ParlAI 众包世界的通用基类CrowdTaskWorld(定义于 parlai/crowdsourcing/utils/worlds.py),类的 docstring 明确指出:"World for recording a turker's question and answer given a context"——即记录工人针对给定上下文提出的问题与答案,并假设上下文来自某个任务的随机段落(如 SQuAD、CBT 等)。
整个数据采集流程被建模为一次parley()(ParlAI 中"一轮对话"的术语)中的两个阶段:
阶段一:给出段落,索要问题。当self.question为空时,World 从 Teacher 取回一个段落(passage = self.teacher.act()),把段落原文放入消息的passage字段(act['passage'] = passage['text']),同时以act['text'] = 'Please provide a question given the passage.'提示工人针对段落提问,随后调用self.agent.act(timeout=self.opt["turn_timeout"])阻塞等待工人的问题输入(超时上限即配置中的turn_timeout,默认 300 秒)。
阶段二:收到问题,索要答案。当self.question已就绪而self.answer为空时,World 发送'Thanks. And what is the answer to your question?'提示工人为自己的问题作答,再次以同样的超时等待答案;答案到手后设置self.episodeDone = True,任务随即结束。
在阶段一中,消息被封装为:
act = {'episode_done': False} act['id'] = self.__class__.collector_agent_id其中collector_agent_id = 'QA Collector'是"采集方"在对话中的身份标识,而工人的agent_id则被设置为"QA Agent"(见__init__)。两个阶段的消息都经过validate()(来自 parlai/core/worlds.py)校验后再observe给工人,保证消息格式合法。
World 模块还提供了 Mephisto 所要求的两个工厂函数(worlds.py):
make_world(opt, agents):返回QADataCollectionWorld(opt, agents[0]),单工人任务取第一个 agent;get_world_params():返回{"agent_count": 1},声明每个任务世界只涉及 1 名工人。
基类赋予的能力:数据保存与任务审核
继承的CrowdTaskWorld(parlai/crowdsourcing/utils/worlds.py)为采集到的数据提供了两个重要钩子:
prep_save_data(workers):在任务收尾时准备待保存数据,默认结构为{'custom_data': ..., 'worker_data': {}},其中custom_data由get_custom_task_data()提供——子类可重写该方法,把采集到的 acts(如self.question、self.answer、self.context)以字典形式返回,用于后续质检与数据分析;review_work():支持程序化审核工人工作——源码注释展示了四种操作:approve_work()通过、reject_work()拒绝、pay_bonus(1000)发放奖金、block_worker()拉黑工人。若在任务运行期尽早执行审核,可以免去事后人工复核的大量工作。
这意味着你可以在自己的 World 子类中重写get_custom_task_data来按需定制保存格式,例如同时保存context、question、answer以及工人行为日志。
前端界面:段落如何展示给工人
任务的前端基于 React 构建,入口为 webapp/src/main.js,核心组件有两个:
Passage组件:把段落渲染为只读的ResizableTextArea(绿色背景、无边框、可缩放),并绑定logSelection事件——当工人在段落中拖选文本时,选中的文字会输出到控制台,为后续实现"选中段落片段作为答案锚点"等高级交互预留了能力;MainApp组件:通过ChatApp渲染聊天主界面,左侧面板(renderSidePane)由DefaultTaskDescription展示任务说明(taskConfig.task_description,即task_description_file指定的 HTML)与段落,右侧为问答对话流;onMessagesChange中检测到消息携带passage字段时,会自动把段落写入 React state 并渲染到左侧面板——这与 World 在消息中塞入act['passage']的设计前后呼应。
而任务说明 HTML(task_config/task_description.html)本身支持任意 HTML 内容,默认内容向工人说明"你将为给定的段落提供一个问题及对应的答案",并提醒:如果任务说明需要更复杂的交互,更稳妥的做法是复制 Mephisto 仓库中的mephisto/abstractions/blueprints/parlai_chat/目录、覆盖相关组件后重新构建,再用mephisto.blueprint.custom_source_bundle参数指定新构建产物。
运行任务:从本地沙箱到线上发布
该任务遵循 ParlAI 众包任务的通用运行范式,完整指引见 parlai/crowdsourcing/README.md 与 docs/source/tutorial_crowdsourcing.md。核心要点如下:
环境准备:需要先通过 Poetry 安装 Mephisto(避免与 ParlAI 产生依赖冲突),然后进入任务目录运行:
# 安装 Mephisto(使用 poetry,避免依赖冲突) curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python # 在项目根目录执行 poetry install启动任务:直接调用任务入口脚本即可,默认加载hydra_configs/conf/下的example.yaml:
python parlai/crowdsourcing/tasks/qa_data_collection/run.py查看全部可调参数:追加-c job标志可以列出所有可用参数,并按包名分组(mephisto.blueprint、mephisto.task、teacher等):
python parlai/crowdsourcing/tasks/qa_data_collection/run.py -c job指定自己的 YAML 配置文件:最简便的方式是在任务的hydra_configs/conf/子目录下新建一个文件(如my_params.yaml),然后运行:
python parlai/crowdsourcing/tasks/qa_data_collection/run.py conf=my_params如果 YAML 文件位于任务目录之外,需要将其存放在${CUSTOM_FOLDER}/conf/my_params.yaml路径下,并追加--config-dir ${CUSTOM_FOLDER}参数。
命令行直接覆盖参数:无需修改 YAML 即可临时调整参数。例如把报酬从 0.3 改成 0.6、或更换段落数据集:
python parlai/crowdsourcing/tasks/qa_data_collection/run.py \ mephisto.task.task_reward=0.6 \ teacher.task=cbt:CBTTask \ teacher.datatype=valid也可以使用mephisto/blueprint=my_blueprint_type直接指定 blueprint 类型。
发布线上任务(MTurk):默认在本地沙箱(architect: local+provider: mock)运行。要发布真实的 MTurk HIT,需追加 requester 名称并切换架构:
python parlai/crowdsourcing/tasks/qa_data_collection/run.py \ mephisto.provider.requester_name=${REQUESTER_NAME} \ mephisto/architect=heroku其中${REQUESTER_NAME}是你在配置 Mephisto 时指定的 MTurk requester 名称。
数据保存位置:默认情况下,Mephisto 会把数据保存在:
<mephisto_root_dir>/data/data/runs/NO_PROJECT/<project_id>/<task_run_id>/<assignment_id>/<agent_id>/data其中<mephisto_root_dir>默认为/scratch/${USER}/mephisto(后续版本可能改名NO_PROJECT与data子目录);<agent_id>可以通过 Mephisto SQLite3 数据库中的workers表映射回 MTurk 的worker_id,便于按工人维度追溯数据质量。
MTurk 任务信息参数:发布到 MTurk 时,以下参数直接影响 HIT 的展示与搜索(parlai/crowdsourcing/README.md):mephisto.task.task_title(HIT 标题,出现在搜索结果中)、mephisto.task.task_description(HIT 详情描述)、mephisto.task.task_tags(逗号分隔的搜索标签)、mturk.worker_blocklist_paths(软屏蔽工人 ID 列表文件路径,多个路径用逗号分隔,屏蔽逻辑见 parlai/crowdsourcing/utils/mturk.py)。
自定义与扩展:把"读段落写问答"改造成你的数据采集方案
基于源码分析,围绕该任务做定制主要有三条路径,成本从低到高:
更换段落数据源:只改
teacher.task与teacher.datatype。例如换用 CBT、CNN/Daily Mail 或其他任何 ParlAI 已注册的段落类数据集,即可让工人基于不同领域的文本产出问答对。得益于util.py中get_teacher对 ParlAIcreate_task的复用,任何 ParlAI Teacher 都可无缝接入。修改对话逻辑而不新建 Blueprint:复制
worlds.py中QADataCollectionWorld的模式,自定义parley()的轮次(例如要求工人先写问题、再写答案、最后自我评分),然后通过mephisto.blueprint.world_file指向你自己的模块。这是文档明确推荐的扩展方式——无需触碰 Mephisto 的 Blueprint 层。深度定制前端与保存格式:重写
webapp/src/main.js中的组件(例如基于已有的段落选词事件实现"高亮选中答案"),并重写get_custom_task_data()自定义保存的数据结构;更复杂的场景可复制并修改 Mephisto 的parlai_chatBlueprint 目录后用mephisto.blueprint.custom_source_bundle指定构建产物。
小结
qa_data_collection是 ParlAI 众包任务体系中"最小而完整"的示例之一:它以一段段落为输入、以工人的问答对为输出,通过parlai_chatBlueprint 承载聊天交互、以QADataCollectionWorld定义两轮对话状态机、用SquadQATeacher等 ParlAI Teacher 提供段落。理解了它的参数体系(task_description_file、world_file、teacher.task、teacher.datatype等)与"Teacher → World → 前端"的数据流,你就能以它为模板,快速搭建面向任意领域、任意交互形式的众包数据生产线。
【免费下载链接】ParlAIA framework for training and evaluating AI models on a variety of openly available dialogue datasets.项目地址: https://gitcode.com/gh_mirrors/pa/ParlAI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考