如何用SGLang部署openjev:外部模型包开发+吞吐提升1.5-3倍的完整部署指南
【免费下载链接】openjev项目地址: https://ai.gitcode.com/hf_mirrors/AlexWortega/openjev
openjev是一个基于 Qwen3.5 训练的 NLI 交叉编码器(jev 模型):给它一段前提(premise)和假设(hypothesis),它输出"蕴含/矛盾/中性"三类概率。这个单一原语可以完成答案重排、内容评分、事实核查,甚至实时玩游戏。本文将带你用SGLang部署 openjev,介绍项目如何开发外部模型包补上 SGLang 缺失的序列分类头,并实测带来1.5-3 倍的吞吐提升。
为什么需要 SGLang 部署?
transformers 加载 openjev 开箱即用,但批量推理时吞吐有限。SGLang 的连续批处理(continuous batching)和 KV 缓存复用天然适合高并发打分场景,可惜:
SGLang 官方没有为 Qwen3.5 提供序列分类架构,只有生成式的
Qwen3_5ForConditionalGeneration。
于是 openjev 项目开发了一个外部模型包(external model package),给 SGLang 的 Qwen3.5 模型"补上"一个线性打分头,而混合缓存(hybrid GDN cache)、mrope 和多模态视觉塔全部保持原生不变。文本和图片输入都能工作,/classify接口直接返回三个原始 logits。
这个方案实测:预测结果与 transformers 完全一致,吞吐提升 1.5-3 倍。
一键部署步骤
整个过程只需两步。首先下载模型(只需小体积的 0.8B v2s-long 检查点 + code 目录):
hf download AlexWortega/openjev --include "qwen3.5-0.8b-nli-v2s-long/*" "code/*" --local-dir openjev然后启动 SGLang 服务(项目已在 serve_sglang.sh 中封装好所有关键参数,在 sglang 0.5.19 上验证过):
cd openjev/code && bash serve_sglang.sh ../qwen3.5-0.8b-nli-v2s-long 30000脚本做了两件关键的事:
- 设置环境变量
SGLANG_EXTERNAL_MODEL_PACKAGE=sglang_openjev,让 SGLang 加载外部模型包; - 以
--is-embedding模式启动,并用--json-model-override-args '{"architectures": ["Qwen3_5ForConditionalGeneration"]}'指定架构名,让 SGLang 按多模态条件生成模型的路径装载模型(这正是外部包"劫持"注册表入口的手法)。
不想要脚本也可以手动执行:把code/加入PYTHONPATH,设置同名环境变量,再运行python -m sglang.launch_server --model-path <dir> --is-embedding --json-model-override-args '{"architectures": ["Qwen3_5ForConditionalGeneration"]}'即可。
验证预测并调用服务
服务起来后,用项目自带的客户端 sglang_client.py 即可调用,接口与 transformers 版predict/rerank完全对齐:
from sglang_client import OpenJevSGLang jev = OpenJevSGLang("http://127.0.0.1:30000") jev.predict([("A man is playing a guitar.", "Someone is making music.")]) # [[con, ent, neu]] jev.predict([("A photograph of a scene:", "There is a dog.")], images=["dog.jpg"]) # 支持图片客户端内置多线程批量请求(默认 16 个 worker 并发),服务端自动跨请求批处理。还支持--check参数,与 transformers 逐对对比概率:
python sglang_client.py --check ckpt/qwen3.5-0.8b-nli-v2s-long外部模型包是如何开发的?
外部包位于 code/sglang_openjev/,核心文件是 qwen3_5_seqcls.py,思路非常精巧,值得新手学习:
- 不注册新架构名,而是覆写已有入口。SGLang 按架构名字来查找混合缓存、mrope 配置和多模态处理器,因此外部包直接继承官方
Qwen3_5ForConditionalGeneration并重写该注册项(注册表加载外部包时使用overwrite=True)。 - 只在打分路径上加头。模型新增一个
nn.Linear(hidden_size, num_labels)的score头和 LAST-token 池化器;生成路径完全走原生super().forward(),不受影响。 - 走 embedding 通道返回 logits。请求
get_embedding时,取最后一个非 pad token 的隐状态过线性头,与 transformers 的池化方式一致,三个原始 logits 作为"embedding"返回。
整个包对外只有一个入口声明EntryClass = [Qwen3_5ForSequenceClassification](见 sglang_client.py 的配套用法),开发者只需关心一个文件就能理解全貌。
吞吐量基准:1.5-3 倍实测数据
基准脚本为 bench_sglang.py,测试环境:qwen3.5-0.8b-nli-v2s-long运行在一张与其他任务共享的 RTX A6000 上(因此绝对速度是下限),transformers 侧批大小 32。精度基本一致,速度提升明显:
| 任务 | 对数 | tokens/对 | 精度(SGLang) | 精度(transformers) | SGLang 对/秒 | transformers 对/秒 | 加速比 |
|---|---|---|---|---|---|---|---|
| MNLI m+mm | 19647 | 44 | 86.63 | 86.64 | 321 | 152 | 2.1x |
| WANLI | 5000 | 40 | 73.7 | 73.7 | 400 | 196 | 2.0x |
| ANLI r3 | 1200 | 92 | 48.6 | 48.6 | 218 | 95 | 2.3x |
| SciTail | 2126 | 43 | 93.3 | 93.4 | 319 | 217 | 1.5x |
| ConTRoL(长文本) | 805 | 612 | 51.9 | 51.4 | 45 | 15 | 3.1x |
| ARC-Challenge(重排) | 9374 | 49 | 49.2 | 49.2 | 334 | 197 | 1.7x |
规律很清晰:文本越长、批量越大,SGLang 的优势越明显(长文本任务高达 3.1 倍),且所有任务准确率与 transformers 持平。
常见问题与调优建议
- flashinfer 编译失败?脚本默认
ATTN_BACKEND=triton,因为 flashinfer 需要用系统 nvcc 做 JIT 编译,nvcc 低于 12.4 会失败。如遇同类问题,保持 triton 后端即可。 - GPU 被多进程共享?通过
MEM_FRACTION环境变量控制显存占比,注意它是整卡占比而非进程占比。例如 48GB 卡上已有 32GB 被占用,可设MEM_FRACTION=0.85启动。 - 想换更大模型?服务脚本接受任意检查点目录,仓库中还提供 4B 文生图版
qwen3.5-4b-nli-v2/和 MoE 版qwen3.5-35b-a3b-nli/(后者需用 modeling_qwen35_moe_seqcls.py 配合)。
相关文件索引
- 外部模型包源码:code/sglang_openjev/qwen3_5_seqcls.py
- 一键启动脚本:code/serve_sglang.sh
- 客户端(含
--check一致性校验):code/sglang_client.py - 吞吐基准:code/bench_sglang.py
- 共享前缀优化测试:test_shared_prefix.py
- transformers 版封装(
predict/rerank/grade):modeling_openjev.py
按照以上步骤,你只需两行命令就能把 openjev 以高吞吐 API 服务跑起来;而外部模型包的覆写注册技巧,也值得作为"给推理引擎扩展自定义架构"的范例收藏。
【免费下载链接】openjev项目地址: https://ai.gitcode.com/hf_mirrors/AlexWortega/openjev
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考