拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Qwen 项目 recipes 单元测试指南:pytest 运行方式、测试矩阵与 Docker 验证实践

Qwen 项目 recipes 单元测试指南:pytest 运行方式、测试矩阵与 Docker 验证实践
  • 人工智能
  • 大模型
  • 微调
  • LoRA
  • 模型量化
  • 本地部署
  • 模型推理服务

【免费下载链接】Qwen

The official repo of Qwen (通义千问) chat & pretrained large language model proposed by Alibaba Cloud.

项目地址:https://gitcode.com/GitHub_Trending/qw/Qwen
点击查看免费下载

导读

本文以 recipes/tests/README.md 为骨架,系统讲解 Qwen 开源仓库中 recipes 层单元测试的三种标准运行方式(全量、按目录、重跑失败用例),并深入测试源码,剖析微调(DeepSpeed ZeRO 矩阵)与推理(OpenAI API、vLLM + FastChat)两大测试模块的实现细节、公共基础设施与已知边界条件。读完本文,你将掌握这套基于 pytest + Docker 的测试体系的完整脉络,能够独立运行、定位并扩展 Qwen 的回归测试。

一、测试体系概览:recipes/tests 的目录设计与定位

Qwen 仓库的recipes/目录承载了面向应用场景的配方式指南,而recipes/tests/则是支撑这些配方的回归测试集。其目录结构如下:

  • recipes/tests/init.py:空文件,标记测试包。
  • recipes/tests/ut_config.py:公共配置(模型标识、Docker 镜像版本、容器挂载路径等)。
  • recipes/tests/utils.py:通用工具函数(子进程命令执行、OpenAI API 探活、端口检测)。
  • recipes/tests/assets/test_sampled_qwen.json:微调测试所用的最小样本数据集。
  • recipes/tests/test_finetune/test_finetune_ds.py:微调链路测试(全参数 / LoRA / QLoRA,覆盖 DeepSpeed ZeRO-2/3)。
  • recipes/tests/test_inference/test_inference_api.py:基于openai_api.py的推理服务测试。
  • recipes/tests/test_inference/test_inference_vllm_fschat.py:基于 vLLM + FastChat 的推理服务测试。

从测试源码看,这套测试的设计意图是「在 Docker 容器内端到端验证真实链路」:微调测试在容器中通过torchrun拉起finetune.py训练 1 个 epoch,推理测试则在容器中启动模型服务并通过 OpenAI 兼容接口真实发请求验证。因此,运行测试的前提是准备好 GPU 环境与对应版本的 Qwen 官方 Docker 镜像(见下文公共配置)。

二、快速上手:三种标准 pytest 运行方式

原文档给出了三种运行方式,均在recipes/tests目录下执行(即先cd recipes/tests):

1. 运行全部单元测试

cd tests && pytest -s

-s关闭 pytest 的输出捕获(即--capture=no),让print语句直接打印到终端。这在上述测试中非常必要——测试源码里大量使用print(例如 test_inference_api.py 打印待执行的 Docker 命令、simple_openai_api打印模型回复、等待服务启动时打印进度),-s能让你实时观察容器启动、模型下载与服务探活的完整过程。

2. 运行指定子目录下的测试

cd tests && pytest -s {dir}

{dir}是recipes/tests下的子目录,例如:

cd tests && pytest -s test_finetune # 只跑微调测试 cd tests && pytest -s test_inference # 只跑推理测试

pytest 会自动发现test_*.py文件并收集其中的test_*函数。这一模式适合按功能模块定向回归,例如只改了finetune.py时,无需跑完整的推理测试。

3. 重跑上次失败的用例

cd tests && pytest -s --lf

--lf(--last-failed)是 pytest 内置的缓存机制:每次运行后,pytest 会把结果缓存在.pytest_cache中,--lf只重新执行上次失败的用例,适合在修复问题或环境就绪后快速验证。可结合--lf与{dir}进一步缩小重跑范围。

需要说明的是,原文档中的cd tests隐含「当前目录为recipes/」这一前提(测试文件通过sys.path.append(os.path.dirname(__file__) + "/..")向上导入同层的utils.py与ut_config.py,也印证了测试必须以其自身目录为工作根)。若从仓库根目录执行,等效命令为:

cd recipes/tests && pytest -s

三、公共基础设施:ut_config.py 与 utils.py

3.1 公共配置 ut_config.py

所有测试共享一套集中配置:

  • MODEL_TYPE = "Qwen/Qwen-1_8B":测试使用的默认模型,覆盖 Chat(对话微调)与 base(预训练底座)两种形态,QLoRA 场景额外使用-Int4量化版(由测试代码动态拼接,见 test_finetune_ds.py)。
  • 三个 Docker 镜像版本:qwenllm/qwen:cu114、cu117、cu121,对应不同 CUDA 版本,测试矩阵会逐个遍历。
  • DOCKER_MOUNT_DIR = "/qwen-recipes":仓库挂载进容器后的根路径。
  • DOCKER_TEST_DIR、DATA_DIR、DS_CONFIG_ZERO2_DIR、DS_CONFIG_ZERO3_DIR:分别指向容器内测试目录、样本数据与两份 DeepSpeed 配置。

从配置可以看出,测试采用「目录挂载」而非「镜像内置代码」的方式:宿主机上的 Qwen 仓库通过-v {仓库路径}:/qwen-recipes挂载进容器,因此代码改动无需重新构建镜像即可被测试覆盖。

3.2 通用工具 utils.py

三个函数支撑了整个测试体系的执行与验证:

  • run_in_subprocess(cmd):以shell=True启动子进程,实时读取 stdout/stderr 并写入日志;若返回码非零则抛出包含错误输出的RuntimeError,保证测试失败时能拿到完整诊断信息。这是所有 Docker 命令的统一执行入口。
  • simple_openai_api(model):通过openai库(openai.api_base = "http://localhost:8000/v1"、api_key="none")向本地模型服务发起一次非流式ChatCompletion.create请求(消息内容为「你好」),并打印模型回复,用于验证推理服务端到端可用。代码注释提示可在此添加自定义停止词,例如 ReAct 提示词所需的stop=["Observation:"]。
  • TelnetPort(server_ip, port):用带 1 秒超时的 TCP socket 探测指定端口是否可连接,作为「模型服务是否就绪」的探活手段,供测试在启动容器后轮询等待。

四、微调测试深入:test_finetune_ds.py 的测试矩阵

test_finetune_ds.py 是微调链路的回归测试,其核心是借助pytest.mark.parametrize构造的笛卡尔积测试矩阵。

4.1 测试矩阵的构成

矩阵由五个维度组合而成(源码 第 23-44 行):

  • GPU 数量:1 或 2(torchrun --nproc_per_node)。
  • 训练方式:full(全参数)、lora、qlora。
  • 模型形态:chat/base,分别对应Qwen/Qwen-1_8B-Chat与Qwen/Qwen-1_8B。
  • Docker 版本:cu114 / cu117 / cu121。
  • DeepSpeed 配置:None、ZeRO-2、ZeRO-3。

源码注释明确记录了兼容性边界:ZeRO-3 与 base 模型上的 LoRA 不兼容;FSDP 或 ZeRO-3 与 QLoRA 不兼容。因此 ZeRO-3 分支只对full全量开放,lora仅允许chat形态;QLoRA 分支则只在 1/2 卡下组合 None 或 ZeRO-2 配置。这份矩阵本身就是「什么组合可以跑」的可执行文档。

4.2 测试执行的关键细节

每个用例执行以下步骤(源码 第 50-99 行):

  1. 构造容器命令:docker run --gpus all --ipc=host --network=host --rm -v {仓库}:{DOCKER_MOUNT_DIR} {镜像} /bin/bash -c "..."。--network=host保证容器内torchrun的多卡通信与宿主机网络一致。
  2. 兼容性预处理:通过torch.cuda.get_device_capability()检测 GPU 算力,若 SM < 80(即不支持 Ampere 之后架构特性),先pip uninstall -y flash-attn;若lora+ ZeRO-2 + base 组合,追加--fp16 True。
  3. 运行微调:容器内执行torchrun拉起 finetune.py,关键训练参数包括--num_train_epochs 1、--per_device_train_batch_size 1、--gradient_accumulation_steps 2、--learning_rate 1e-5、--weight_decay 0.1、--adam_beta2 0.95、--warmup_ratio 0.01、--lr_scheduler_type "cosine"、--model_max_length 512、--save_strategy "steps"、--save_steps 1000、--save_total_limit 10,并根据train_type追加--use_lora/--q_lora、按需追加--deepspeed {配置路径}。
  4. 下载模型:snapshot_download(model_type, cache_dir=".", revision="master")从 ModelScope 拉取对应模型(QLoRA 场景为-Int4版)。
  5. 验证产物:全参数训练断言output_qwen/config.json存在,LoRA/QLoRA 断言output_qwen/adapter_config.json存在,随后shutil.rmtree清理输出,保证用例可重复执行。

这从源码层面印证了 finetune.py 中use_lora、q_lora、model_max_length等参数的真实语义,也与 finetune/deepspeed/ 下的配方文档形成闭环。

五、推理测试深入:OpenAI API 与 vLLM + FastChat 两条链路

推理测试的目标是「在容器内把模型服务真正跑起来,并发出真实请求验证返回」。

5.1 基于 openai_api.py 的服务测试

test_inference_api.py 按docker_version × use_int4参数化(共 6 组),每组执行:

  1. 启动容器:docker run --gpus all --ipc=host --network=host --rm --name="test_inference_api" -p 8000:8000 -v {仓库}:{DOCKER_MOUNT_DIR} {镜像} /bin/bash -c "python {DOCKER_MOUNT_DIR}/openai_api.py -c {模型路径}",其中模型为Qwen-1_8B-Chat或-Int4版本;SM < 80 时同样先卸载 flash-attn。
  2. 后台启动(nohup ... &)并将日志重定向到tmp.log。
  3. 用TelnetPort("localhost", 8000)轮询等待服务就绪;期间若容器已退出(docker inspect失败)则提前终止。
  4. 调用 utils.py 中的simple_openai_api发起真实对话请求(注意 Int4 场景下模型名按Qwen-1_8B-Chat传入),异常时读取tmp.log辅助定位。
  5. 用例结束强制docker rm -f清理容器。

源码注释记录了两条重要的已知边界:CPU-only 模式会因 Half 精度算子(addmm_impl_cpu_)未实现而报错;Int4 量化使用 Exllama/ExllamaV2 后端时要求全部模块位于 GPU,否则需要disable_exllama=True。这解释了为何该测试的use_cpu参数全部为False——当前测试矩阵仅覆盖 GPU 推理路径。

5.2 基于 vLLM + FastChat 的服务测试

test_inference_vllm_fschat.py 按num_gpus × use_int4参数化(1 卡/2 卡 × 普通/Int4,其中 2 卡 + Int4 因量化权重与张量并行尺寸不匹配的已知问题被注释禁用),执行 FastChat 三进程标准架构:

  • python -m fastchat.serve.controller:控制面,探活端口为 21002;
  • python -m fastchat.serve.openai_api_server --host localhost --port 8000:OpenAI 兼容网关;
  • python -m fastchat.serve.vllm_worker --model-path {模型} --tensor-parallel-size {num_gpus} --trust-remote-code:vLLM worker,--tensor-parallel-size决定跨卡并行度。

当 SM < 80 或使用 Int4 时追加--dtype half(对应源码 第 40-42 行 的兼容分支)。探活与验证流程与 API 测试一致:先等 21002 端口就绪,再通过simple_openai_api完成一次真实对话,最后清理容器。vLLM 推理路径与 recipes/inference/vllm/ 下的部署配方可互相印证。

六、测试数据与 DeepSpeed 配置

6.1 样本数据 test_sampled_qwen.json

该文件是微调测试的训练数据,采用 Qwen 多轮对话格式——每条约含id与conversations(user/assistant交替),例如{"conversations": [{"from": "user", "value": "你好"}, {"from": "assistant", "value": "你好!很高兴为你提供帮助。"}], "id": "identity_0"}。这与 finetune.py 中SupervisedDataset/LazySupervisedDataset对raw_data["conversations"]的解析逻辑(见 finetune.py)完全对应:数据被preprocess加工为<|im_start|>user/assistant的 ChatML 模板,用户侧 token 以IGNORE_TOKEN_ID屏蔽、助手侧参与损失计算。

6.2 DeepSpeed 配置

两份配置即 finetune/ds_config_zero2.json 与 finetune/ds_config_zero3.json,也是微调配方文档 recipes/finetune/deepspeed/ 的依赖项:

  • ZeRO-2:zero_optimization.stage = 2,开启overlap_comm、contiguous_gradients、reduce_scatter,allgather_bucket_size与reduce_bucket_size均为 2e8;优化器为 AdamW,调度器为 WarmupLR,学习率、权重衰减、梯度累积步数等均以"auto"交给训练脚本统一管理。
  • ZeRO-3:zero_optimization.stage = 3,在 ZeRO-2 基础上增加offload_param(device: "none")、stage3_gather_16bit_weights_on_model_save: true(保存时聚合 16bit 权重),并配置sub_group_size、stage3_max_live_parameters等分片参数。

测试中正是通过--deepspeed参数把这两份配置注入 finetune.py 的,验证了「配置即测试输入」的设计。

七、运行测试的注意事项与调试建议

综合上述源码细节,运行这套测试需要注意:

  1. 环境前提:需要 NVIDIA GPU 与 Docker(测试命令含--gpus all),并预先准备好qwenllm/qwen:cu114/cu117/cu121镜像;测试会从 ModelScope 下载Qwen/Qwen-1_8B系列模型,需要网络可达。
  2. 目录前提:在recipes/tests目录下执行 pytest;子目录运行、失败重跑均保持同样的工作目录。
  3. 算力兼容:SM < 80 的 GPU 上,测试会自动卸载 flash-attn 并追加--fp16 True;推理测试在 SM < 80 或 Int4 场景追加--dtype half。
  4. 已知禁用组合:ZeRO-3 + base 上的 LoRA、ZeRO-3/FSDP + QLoRA、2 卡 + Int4 的 vLLM 张量并行,均在源码中以注释形式记录了不兼容原因。
  5. 失败排查:推理测试把容器日志写入tmp.log,服务未就绪或请求异常时会读出日志内容拼入异常信息;微调测试的错误信息同样由run_in_subprocess汇总抛出,定位问题应先看这些输出,再用pytest -s --lf重跑失败用例。
  6. 只读仓库提示:本仓库为只读,运行测试不会修改仓库文件;微调测试的output_qwen产物会在用例内自动清理。

八、总结

Qwen 的 recipes 单元测试体系以 recipes/tests/README.md 的三种 pytest 命令为入口,背后是一套「Docker 容器 + 真实链路 + 参数化矩阵」的回归方案:公共配置集中在 ut_config.py,通用执行与验证逻辑沉淀在 utils.py,微调与推理两大模块分别由 test_finetune_ds.py 和 test_inference 目录承载。理解这套体系,既能快速跑通 Qwen 的回归测试,也能为扩展新的微调/推理场景测试提供可直接复用的范式。

  • 人工智能
  • 大模型
  • 微调
  • LoRA
  • 模型量化
  • 本地部署
  • 模型推理服务

【免费下载链接】Qwen

The official repo of Qwen (通义千问) chat & pretrained large language model proposed by Alibaba Cloud.

项目地址:https://gitcode.com/GitHub_Trending/qw/Qwen
点击查看免费下载

相关推荐

上一篇:Playnite 启动参数速查:3 步砍掉开机等待
下一篇:如何使用 lint-staged 优化 NestJS GraphQL API 开发流程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表