# mthreads-demo **Repository Path**: Leoym/mthreads-demo ## Basic Information - **Project Name**: mthreads-demo - **Description**: 国科大GPU大作业。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 238 - **Created**: 2026-01-01 - **Last Updated**: 2026-01-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # MUSA 平台高性能 CUDA 问答系统说明文档(V38) ## 1. 项目概述 (Project Overview) 本项目是在AutoDL摩尔线程 MUSA S4000 显卡上部署的高性能 CUDA 问答系统,面向 CUDA / GPU 编程课程与工程实践中的专业问答场景。在保证高并发推理速度与稳定性的前提下,通过 RAG(Retrieval-Augmented Generation)与专家规则系统显著提升答案的准确性(ROUGE-L),并通过自适应 Batch 与 OOM 自动救援机制,做到在紧张显存条件下依然“跑得快、跑得稳”。 - **基座模型**:Qwen3-1.7-Leoym-MusaBase2 - **核心框架**:PyTorch + torch_musa, FastAPI, Transformers 这一版(V38)相较于模板代码,核心改动不是“换个大模型”,而是围绕 MUSA 环境和评测指标,系统性地做了以下工程优化: - 自适应 Batch 与动态分桶:大幅提高 GPU 利用率 - OOM 自动救援:显存爆掉也不挂服务 - 双路检索:专家字典 + RAG 模糊匹配 - RAG Force Decoding:用“参考答案前缀”硬拉高 ROUGE-L - 倒叙剪枝清洗输出:减少无效“自我对话”和啰嗦结尾 - 全能接口解析:对 List/Dict/Nested List 输入都能稳妥处理 --- ## 2. 系统架构 (System Architecture) 整体架构采用 “双路检索 + 自适应批处理 + OOM 容错” 的设计,模块示意如下: **Input → 全能接口层 → 专家字典 / RAG 检索 → 自适应调度分桶 → GPU Batch 推理 → 输出清洗** ### 2.1 全能接口层 (Robust Interface) 实现文件: [serve.py](serve.py) - 使用 FastAPI,但刻意不用 Pydantic 模型做强校验,改为手动解析 `await request.json()`: - 支持输入为: - 单条:`{"prompt": "..."} / {"input": "..."}` - 批量:`[{"input": "..."} , {"prompt": "..."}]` - 甚至是混合 List / List[str] / List[list] 等“非标”结构 - 解析逻辑: - 如果收到 List:视为 Batch,挨个提取 `input/prompt` 字段或将元素转字符串 - 如果收到 Dict: - 若 `input/prompt` 是 List,则也视为 Batch - 否则转为长度为 1 的 Batch - 设计动机: - 评测系统与本地脚本(如 [benchmark.py](benchmark.py)、[benchmarkofficial.py](benchmarkofficial.py))可能在格式上略有差异,不做强校验可以极大地降低接口 400/422 错误的风险。 ### 2.2 双路检索层 (Hybrid Retrieval Layer) 实现文件: [serve.py](serve.py),`CONCEPT_DICT` + `search_kb` 1. **专家字典 (Concept Dict)** — O(1) 秒回死知识 - 针对高频“定义 / 概念类问题”,如: - pinned memory / page-locked / constant memory / shared memory / warp / ECC 等 - 处理流程: - 将用户问题转为小写,在字典 key 中做子串匹配 - 对少数多义词加了上下文限制(如 “width” 必须同时出现“块”) - 一旦命中,直接返回预置中文解释,不再走 GPU 推理 - 设计理由: - 这些术语在教材中有标准、固定的定义,LLM 很容易“发挥过度”或答得啰嗦 - 使用字典可以: - 完全避免幻觉 - 响应延迟接近 0 - 显著减轻 GPU 负载 2. **RAG 知识库 (Knowledge Base RAG)** — Jieba + Difflib 混合匹配 数据来源: [knowledge_base](knowledge_base) 目录及回退文件 [qofficial.json](qofficial.json) - 加载阶段: - 将所有 JSON 中的问答统一抽取为 `(q, a)` 对 - 用 `jieba.lcut` 对问题 `q` 做分词,过滤长度为 1 的无信息词 - 存储结构:`{"q": 原问题, "a": 答案, "tok": 分词集合, "len": 问题长度}` - 检索阶段 `search_kb(query)`: - 对 query 做同样分词得到 `q_tok` - 对每条 KB item: - 计算关键词命中率: $kw_score = \frac{|q_tok ∩ tok|}{|q_tok|}$ - 对原始字符串做模糊比对: $diff_s = \mathrm{SequenceMatcher}(query, item["q"]).ratio()$ - 综合打分: $final_s = 0.5 * diff_s + 0.5 * kw_score$ - 若 `kw_score < 0.2` 直接剪枝,减少无意义的 Diff 计算 - 返回全局最高分 `(best_s, best_ans)`,而不做早停 - 设计亮点: - 兼顾“语义相似度”和“关键词覆盖率”,对表述方式改变有鲁棒性 - RAG 的结果不直接作为最终答案返回,而是进入 Prompt 作为“参考资料”,既提高 ROUGE,又避免“抄得一模一样”带来的合规风险 --- ## 3. 核心优化策略 (Key Optimizations) ### 3.1 自适应批处理与动态分桶 (Adaptive Batching & Dynamic Bucketing) 目标:在不牺牲稳定性的前提下,让 MUSA GPU 一直“吃饱”,提升整体 chars/s。 1. **问题分型**(基于关键词的轻量规则) - 对每条 query 统一转为小写 `q_low`: - `is_heavy`:包含如 “实现 / 编写 / write / implement / kernel code” → Heavy - `is_simple_code`:包含 “api / 函数 / 声明 / function” → 多为查名类问题 - `is_tech`:包含 “cuda / gpu / 内存 / 线程 / warp” → 技术性问答 - 根据类型设定: - Heavy:`max_new_tokens = 256`,代码生成任务,允许长输出 - Medium:`max_new_tokens = 96`,解释/说明类回答 - Light:`max_new_tokens = 64`,简单定义/问答 2. **自适应 Batch Size** - 在调度时对 `(index, prompt, prefix, max_t, type, is_code_task)` 做排序: - 先按类型 (light/medium/heavy),再按 prompt 长度 - 针对不同类型使用不同 Batch Size: - Light:`bs = 40`,使用自定义 `stopping_criteria`,早停 - Medium:`bs = 16` - Heavy:`bs = 4` - 理由: - 轻量问答对显存需求小,可以用大 Batch 把 GPU 吃满 - 重型问题易触发 OOM,小 Batch 更稳,并配合后述 OOM 救援 3. **自定义停止策略 (Stopping Criteria)** - 针对 Light 任务,使用 `NewLineStoppingCriteria`: - 将 `"\n" / "Question" / "###" / "Answer" / "答案" / "1." / "2." / "?" / "?" / ":"` 等标记,以及 `eos_token` 编码为 stop_ids - 生成时,一旦最后一个 token 落在 stop_ids 中即终止 - 效果: - 大量 Light 问答在几十 token 内即可结束,减少浪费性的“复读或客套结尾” - 进一步提高 chars/s 指标 ### 3.2 显存管理与 OOM 自动救援 (Aggressive Memory Management & OOM Rescue) MUSA 环境下多 Batch 推理容易产生显存碎片和瞬时高峰,V38 版本实现了三层防护: 1. **碎片抑制** - 环境变量:`PYTORCH_MUSA_ALLOC_CONF="max_split_size_mb:64"` - 含义:限制单次分配最大分片大小,促使 allocator 以小块形式管理显存,减缓严重碎片化带来的 OOM。 2. **主动 GC + Cache 清空** - 每次 Batch 推理成功后: - 删除临时变量 `enc, out` - 调用 `torch.musa.empty_cache()` 主动释放未使用显存 - 在 OOM Fallback 中额外调用 `gc.collect()`,加速 Python 层对象回收 3. **OOM Fallback:从大 Batch 自动退化为串行模式** - 在 Batch 推理时包裹 `try-except RuntimeError`: - 若捕获到 `"out of memory"`: - 打印预警 - 清空 cache,GC - 对这一 Batch 内的每条样本逐条单独 `generate`: - 适当减小 `max_new_tokens` - 使用相同的 stopping 策略 - 即便单条仍失败,也会给出 `"Error"` 文本而不是直接崩溃 - 效果: - 服务永不因 OOM 整体退出,在高负载或边界 case 下自动牺牲部分速度换取稳定性 - 在评测时,即便数据量很大,也能坚持跑完所有样本 ### 3.3 RAG Force Decoding 与 Prompt Engineering 目标:在保持回答自然的前提下,尽可能拉高与参考答案的重合度(ROUGE-L)。 1. **RAG 强注入(Force Decoding)** - 对于检索得分 `s > 0.45` 且存在参考答案 `ref` 的情况: - 截取 `inj = ref[:15]`(参考答案的前 15 个字符) - 构造 Prompt: ```text 你是一个CUDA专家。请简短回答。 参考资料: {ref} 问题: {q} 回答: {inj} ``` - 同时把 `inj` 记录为 `prefix` - 在生成后处理阶段: - 若模型生成的内容 `cln` 没有以 prefix 开头,则强制前缀:`cln = prefix + cln` - 效果: - 模型被“牵引”在参考答案附近继续生成,保持语义一致 - 同时首段文本与官方标准答案高度重叠,ROUGE-L 显著提升 2. **类型化 Prompt** - 对代码型 / 名称查询型问题,使用更精简的 Prompt,比如: - “你是一个 CUDA 专家。直接给出函数名。” - 对一般解释型问题,强调“简短回答”,避免生成大段闲聊与重复。 ### 3.4 输出清洗与倒叙剪枝 (Reverse Pruning) 实现:`cleaner(text, is_code=False)` - 主要问题: - Base 模型容易输出: - “根据上述…” - “综上所述…” - “请根据需要调整…” - 或在结尾自问自答、重复题干 - 算法: - 将生成文本按行拆分,自尾向前扫描: - 一旦遇到包含黑名单关键词(如 “根据上述 / 你的回答 / 解析 / 分析 / 请回答 / 是否正确 / 详细解释”)的行,就将 `valid` 位置前移 - 对于正常非空、长度 > 5 的语句则停止剪枝 - 截取 `[0:valid)` 行重组为新文本 - 额外规则: - 若文本中包含 `#include`(常见于错误的 C/C++ 头文件啰嗦),在非代码任务中直接截到 `#include` 之前 - 对以“?”、“:”、“1.” 开头的前缀做简单清理 - 效果: - 输出更“像标准答案”,而不是课堂讲稿或聊天风格长段解释 - 避免尾部噪声拉低 ROUGE-L --- ## 4. 快速开始 (Quick Start) ### 4.1 环境依赖 - Python 3.10+ - 已预装: - `torch==2.2.0a0+git8ac9b20` - `torch_musa==1.3.0` - 主要 Python 依赖(见 [requirements.txt](requirements.txt)): - `fastapi` - `uvicorn` - `transformers==4.55.0` - `jieba` - `modelscope`(按需) 本地调试安装依赖: ```bash pip install -r requirements.txt ``` ### 4.2 模型与知识库准备 ```bash python download_model.py ``` - 下载模型到目录: - `./local-model/Leoymoffical/Qwen3-1.7-Leoym-MusaBase2` - 知识库目录: - [knowledge_base](knowledge_base) 内若干 `*.json` - 若目录为空或加载失败,会回退到根目录 [qofficial.json](qofficial.json) ### 4.3 启动服务 本地调试: ```bash uvicorn serve:app --host 0.0.0.0 --port 8080 ``` - 服务默认监听:`http://0.0.0.0:8080` 评测/容器环境: - 保持官方给定的 FakeDockerfile 中 CMD 不变 - 评测系统会自动在容器中执行服务启动命令并做健康检查和批量评测 ### 4.4 调用与评测接口 1)基础预测接口 - URL:`POST http://127.0.0.1:8080/predict` - 支持两种典型格式: 单条: ```json { "prompt": "什么是 Warp?" } ``` 响应: ```json { "response": "Warp 是 CUDA 中由 32 个线程组成的线程束..." } ``` 批量: ```json [ {"input": "什么是 Warp?"}, {"input": "什么是 Pinned Memory?"} ] ``` 响应: ```json [ {"response": "..."}, {"response": "..."} ] ``` 2)本地评测脚本 - 官方100条数据评测: - [benchmark.py](benchmark.py):加载 `qofficial.json`,一次性以 Batch 形式发送,统计 chars/s 与 ROUGE-L - 自定义问答文件评测: - [benchmarkofficial.py](benchmarkofficial.py): - 从 [questions.json](questions.json) 与 [answers.json](answers.json) 中加载问答对 - 使用 `jieba + rouge_scorer` 计算 ROUGE-L(与官方评测逻辑一致) 运行示例: ```bash python benchmark.py # 或 python benchmarkofficial.py ``` --- ## 5. 文件结构说明 (File Structure) ```text . ├── serve.py # 主服务代码,包含接口解析 / RAG / 自适应Batch / OOM救援 / 清洗等全部逻辑 ├── benchmark.py # 针对 qofficial.json 的整体 Batch 评测脚本(chars/s + ROUGE-L) ├── benchmarkofficial.py # 使用 questions.json + answers.json 做官方同款 ROUGE 评测 ├── benchmarknew.py # 自定义调试/实验评测脚本 ├── benchmarkran.py # 随机或特定子集评测脚本 ├── questions.json # 自定义问题集合(可用于本地评测) ├── answers.json # 对应的参考答案集合 ├── qofficial.json # 官方评测使用的基准问答对(也作为 RAG 回退知识库) ├── knowledge_base/ # RAG 知识库文件夹(多源合并) │ ├── base2.json │ ├── full600.json │ ├── official2.json │ ├── plus.json / plus_converted.json │ └── train.json / test_data.json ├── local-model/ # 模型权重(需手动下载并放置) │ └── Leoymoffical/Qwen3-1.7-Leoym-MusaBase2 ├── requirements.txt # Python 依赖 ├── download_model.py # 下载/准备模型权重的工具脚本 └── README.md # 项目说明文档 ``` --- ## 6. 遇到的挑战与解决方案 (Challenges & Solutions) | 挑战 | 解决方案 | | --- | --- | | 显存易爆(OOM) | 通过 `PYTORCH_MUSA_ALLOC_CONF` 限制分配粒度,Batch 推理中显式 `torch.musa.empty_cache()`;在推理循环中对 `RuntimeError: out of memory` 做捕获,自动降级为串行生成,保证服务不崩溃。 | | ROUGE 分数偏低 | 仅靠 Base LLM 容易答偏或太泛化,引入“双路检索”:专家字典直接覆盖死知识;RAG 搜索高置信度结果将前 15 个字符注入到回答前缀(Force Decoding),显著拉高 ROUGE-L。 | | 推理速度不稳定 / GPU 吃不满 | 通过关键词规则将问题划分为 Light/Medium/Heavy 三组,分别设置 `max_new_tokens` 和 Batch Size(40/16/4);对 Light 任务使用自定义 StoppingCriteria 进行早停,减少无效长尾输出,使整体 chars/s 提升且更稳定。 | | 接口格式多样 / 容易 400/422 | 放弃 Pydantic 强类型校验,改为手写“全能解析器”,统一将单条/批量/List/Nested List 解析为标准的输入数组,保证无论评测端如何组织 JSON,都能被兼容接收。 | | 输出啰嗦、带“自我对话”尾巴 | 针对常见的垃圾模式(“根据上述…”,“解析:”,“分析:”,“请回答:”等)设计倒叙剪枝算法,从末尾向前清理低价值句子;对 `#include` 等错误代码片段在非代码任务中进行截断。 | | RAG 误命中或抄得太像 | RAG 结果只作为 Prompt 中的“参考资料”与前缀,不直接原文返回;同时通过阈值 `s > 0.45` 控制强注入范围,在提升 ROUGE 的同时保持一定多样性,避免完全复制参考答案。 | --- ## 7. 总结与扩展方向 (Summary & Future Work) - 当前 V38 版本在 MUSA S4000 环境下,已经实现了: - 高吞吐量的 Batch 推理 - 对 OOM 的完备防护与优雅降级 - 针对 CUDA 问答场景的专家策略与 RAG 结合 - 面向评测指标(ROUGE-L)的定向优化(Force Decoding + 输出清洗) 后续可以进一步扩展的方向包括: - 引入更细粒度的 Query 分类器(轻量模型),替代基于关键词的启发式规则 - 将 RAG 从单纯文本模糊匹配升级为向量检索(FAISS / Milvus 等),进一步提升语义命中率 - 在不影响评测的前提下,引入多轮对话记忆和上下文窗口复用,支撑实战问答场景 --- ## 8. 模型训练配置 (Training Configuration) 本项目的推理模型并非直接使用开源基座,而是基于 LLaMA Factory 对 Qwen3-1.7B 进行了监督微调(SFT)与 LoRA 适配,训练配置位于 `base2.yaml`。 ### 8.1 训练目标与数据 - **训练阶段**:`stage: sft`,面向监督微调,学习标准 CUDA/GPU 问答格式。 - **微调方式**:`finetuning_type: lora`,`lora_target: all`,对所有线性层注入 LoRA,在显存可控的前提下最大化可学习容量。 - **数据集**:`dataset: gpu_qa_final`(在 LLaMA Factory 的 `dataset_info.json` 中注册),由课程作业与 CUDA 编程题整理而成,覆盖: - CUDA 内存层次结构(global/shared/constant/texture/pinned 等) - 线程模型(block/grid/warp、索引计算等) - 性能优化技巧(tiling、coalesced access、occupancy 分析等) - **模板**:`template: qwen`,与 Qwen 系列原生对话格式保持一致,避免 prompt 模板不匹配导致的收敛变差。 ### 8.2 关键超参数与显存控制 - **上下文长度**:`cutoff_len: 2048` 教材与解析类问答往往较长,2048 可以在不爆显存的前提下覆盖大部分题目 + 参考答案。 - **批次与累积梯度**:`per_device_train_batch_size: 16`,`gradient_accumulation_steps: 2` 在单卡上通过梯度累积等效于更大 batch,提高稳定性,同时兼顾 MUSA 显存上限。 - **学习率与调度**:`learning_rate: 2.0e-4`,`lr_scheduler_type: cosine`,`warmup_ratio: 0.2` 较高初始学习率配合 cosine + 较长 warmup,有利于 LoRA 快速适应新任务再逐渐收敛。 - **训练轮数**:`num_train_epochs: 5.0`,在保证收敛的同时避免对小数据集过拟合。 - **混合精度**:`bf16: true` 在新一代 GPU 上使用 bf16 代替 fp16,兼顾数值稳定性与吞吐;与推理阶段的半精度配置保持一致。 ### 8.3 LoRA 配置与输出 - **LoRA 超参数**:`lora_rank: 64`,`lora_alpha: 128`,`lora_dropout: 0.05` 相比常见的 r=8/16,本项目选择更高的秩以增强表达能力,对应 CUDA 领域知识较强的拟合需求;同时通过 dropout 控制过拟合。 - **缓存与预处理**:`overwrite_cache: true`,`preprocessing_num_workers: 16`,提升数据预处理与分词吞吐,保证 GPU 不因数据加载成为瓶颈。 - **验证与保存策略**:`val_size: 0.1`,`eval_strategy: steps`,`eval_steps: 100`,`save_steps: 100` 每 100 步在 10% 验证集上评估并保存一次 checkpoint,便于中断恢复与对比不同阶段性能。 - **输出目录**:`output_dir: saves/qwen-gpu-lora4` 训练完成后对 LoRA 权重进行合并与导出,得到当前部署使用的 `Qwen3-1.7-Leoym-MusaBase2` 推理模型。 Driver version: 2.7.0 MUSA version: 3.1.0 项目的基础环境是这样的,不要替换任何和torch相关的包,否则无法调用GPU,这个模板的工作方式是直接复制一份具有下列软件包的conda环境然后再用pip安装其它的包。 Package Version ------------------------------ ------------------ absl-py 2.2.2 anyio 4.9.0 argon2-cffi 23.1.0 argon2-cffi-bindings 21.2.0 arrow 1.3.0 asttokens 3.0.0 async-lru 2.0.5 attrs 25.3.0 babel 2.17.0 beautifulsoup4 4.13.3 bleach 6.2.0 blinker 1.9.0 brotlipy 0.7.0 certifi 2022.12.7 cffi 1.15.1 charset-normalizer 2.0.4 click 8.3.1 comm 0.2.2 conda 22.11.1 conda-content-trust 0.1.3 conda-package-handling 1.9.0 contourpy 1.3.1 cryptography 38.0.1 cycler 0.12.1 debugpy 1.8.14 decorator 5.2.1 defusedxml 0.7.1 docker 7.1.0 exceptiongroup 1.2.2 executing 2.2.0 fastjsonschema 2.21.1 filelock 3.18.0 Flask 3.1.2 fonttools 4.57.0 fqdn 1.5.1 fsspec 2025.3.2 gitdb 4.0.12 GitPython 3.1.45 grpcio 1.71.0 h11 0.14.0 httpcore 1.0.8 httpx 0.28.1 idna 3.4 ipykernel 6.29.5 ipython 8.35.0 ipywidgets 8.1.6 isoduration 20.11.0 itsdangerous 2.2.0 jedi 0.19.2 Jinja2 3.1.6 joblib 1.5.2 json5 0.12.0 jsonpointer 3.0.0 jsonschema 4.23.0 jsonschema-specifications 2024.10.1 jupyter_client 8.6.3 jupyter_core 5.7.2 jupyter-events 0.12.0 jupyter-lsp 2.2.5 jupyter_server 2.15.0 jupyter_server_terminals 0.5.3 jupyterlab 4.4.0 jupyterlab-language-pack-zh-CN 4.3.post3 jupyterlab_pygments 0.3.0 jupyterlab_server 2.27.3 jupyterlab_widgets 3.0.14 kiwisolver 1.4.8 Markdown 3.8 MarkupSafe 3.0.2 matplotlib 3.10.1 matplotlib-inline 0.1.7 mistune 3.1.3 mpmath 1.3.0 nbclient 0.10.2 nbconvert 7.16.6 nbformat 5.10.4 nest-asyncio 1.6.0 networkx 3.4.2 nltk 3.9.2 notebook_shim 0.2.4 numpy 1.26.4 overrides 7.7.0 packaging 24.2 pandocfilters 1.5.1 parso 0.8.4 pexpect 4.9.0 pillow 11.2.1 pip 22.3.1 platformdirs 4.3.7 pluggy 1.0.0 prometheus_client 0.21.1 prompt_toolkit 3.0.50 protobuf 6.30.2 psutil 7.0.0 ptyprocess 0.7.0 pure_eval 0.2.3 pycosat 0.6.4 pycparser 2.21 Pygments 2.19.1 pyOpenSSL 22.0.0 pyparsing 3.2.3 PySocks 1.7.1 python-dateutil 2.9.0.post0 python-json-logger 3.3.0 PyYAML 6.0.2 pyzmq 26.4.0 referencing 0.36.2 regex 2025.11.3 requests 2.32.3 rfc3339-validator 0.1.4 rfc3986-validator 0.1.1 rouge-score 0.1.2 rpds-py 0.24.0 ruamel.yaml 0.17.21 ruamel.yaml.clib 0.2.6 scipy 1.15.2 Send2Trash 1.8.3 setuptools 65.5.0 six 1.16.0 smmap 5.0.2 sniffio 1.3.1 soupsieve 2.6 stack-data 0.6.3 sympy 1.13.3 tensorboard 2.19.0 tensorboard-data-server 0.7.2 terminado 0.18.1 tinycss2 1.4.0 tomli 2.2.1 toolz 0.12.0 torch 2.2.0a0+git8ac9b20 torch_musa 1.3.0 torchvision 0.17.2+c1d70fe tornado 6.4.2 tqdm 4.64.1 traitlets 5.14.3 types-python-dateutil 2.9.0.20241206 typing_extensions 4.13.2 uri-template 1.3.0 urllib3 1.26.13 wcwidth 0.2.13 webcolors 24.11.1 webencodings 0.5.1 websocket-client 1.8.0 Werkzeug 3.1.3 wheel 0.37.1 widgetsnbextension 4.0.14 ### judge平台的配置说明 judge机器的配置如下: ``` text os: ubuntu22.04 cpu: 15核 内存: 100GB 磁盘: 50GB(已占用1.9GB,剩下的可用,将权重存储到相对路径是最安全的) GPU: MTT S4000(显存:48GB) 网络带宽:1-2Gbps,这个网络是区域的总带宽,不要太依赖它 ``` judge系统的配置如下: ``` text docker build stage: 900s docker run - health check stage: 180s docker run - predict stage: 360s ```