Ascend-SACT/Step-3.7-Flash
模型介绍文件和版本Pull Requests讨论分析
下载使用量0

Step-3.7-Flash 部署指导

1. 模型概述及场景

1.1 模型基本介绍

Step-3.7-Flash 是阶跃星辰(StepFun)推出的多模态混合专家(MoE)大模型,具备**图文理解(Vision-Language Understanding)**能力,可同时处理图像与文本输入并生成文本回复。

  • 模型类型:多模态(图像理解 + 自然语言处理)
  • 模型架构:三段式结构 —— 视觉编码器(Vision Encoder)→ 投影层(Projector)→ 语言模型(Language Model)
  • 网络结构特点:
    • 共 45 层 Transformer:第 0–2 层为稠密(Dense)层,第 3–44 层为 MoE 层
    • MoE 配置:每个 MoE 层包含 288 个专家,采用 Top-8 路由
    • 第 43–44 层使用自定义激活函数 SwigluStepAndMul(silu(gate).clamp(max=limit) * up.clamp(-limit, limit)),通过 clamp 截断抑制数值溢出,保障长序列与大词表下的数值稳定性
    • 支持多令牌预测(MTP)与滑动窗口注意力(SWA)
  • 能力范围:图像理解、视觉问答、文本生成(仅理解图像,不生成图像)

说明:本指导对应**推理(Inference)**场景。

1.2 模型文件与源码

  • 权重文件:Step-3.7-Flash 原始权重无需修改,通过 --trust-remote-code 加载模型自带的 configuration_step3p7.py 等配置文件即可。
    • 模型社区链接:https://huggingface.co/stepfun-ai/Step-3.7-Flash
  • 推理框架适配说明:vllm-ascend v0.20.2rc1 尚未原生支持 Step-3.7-Flash,需手动适配两层共 5 个文件(见 2.3 节):
    • vllm 侧(3 文件):Step3p7 模型支持 —— 新增模型定义 step3p7.py、架构注册条目、tokenizer 中文编码修复(v0.20.2 仅有父模型 Step3VL,无 Step3p7)。
    • vllm-ascend 侧(2 文件):第 43–44 层 SwigluStepAndMul 激活算子 swiglustep_and_mul(开源贡献 PR:vllm-project/vllm-ascend#10491)。

完整改动见附录 6,照此手动适配即可。

1.3 适用场景

适用于图文理解、视觉问答、文档/图表理解、多模态对话等推理场景,支持单机多卡部署。


2. 准备运行环境

2.1 配套软件版本

表 1 配套软件信息

配套版本
推理框架 vllm-ascendv0.20.2rc1(叠加 Step3.7 适配补丁 5 文件,见 2.3 节)
CANN9.0.0
Python3.11
torch2.10.0
torch_npu2.10.0

推理框架:vLLM-Ascend。

2.2 环境准备(设备支持)

项目信息
推理设备910B3(8 卡)
部署卡类型Ascend 910B3
部署方式单机 8 卡(--tensor-parallel-size 8)
操作系统ARM(openEuler / EulerOS)

2.3 镜像获取与适配

vllm-ascend v0.20.2rc1 尚未原生支持 Step-3.7-Flash,需拉取基础镜像后手动适配两层共 5 个文件:

侧文件作用
vllmmodel_executor/models/step3p7.py新增 Step3p7 模型定义(v0.20.2 仅有父模型 Step3VL)
vllmmodel_executor/models/registry.py注册 Step3p7 架构条目
vllmtokenizers/registry.pytokenizer 中文编码修复(加入 _MODEL_TYPES_WITH_INCORRECT_TOKENIZER_CLASS + get_cached_tokenizer 包装)
vllm-ascendops/activation.py新增 swiglustep_and_mul 算子(PR #10491)
vllm-ascendops/fused_moe/moe_mlp.pyMoE MLP 加 SWIGLUSTEP 分支(PR #10491)

⚠️ 5 个文件缺一不可:vllm 侧少盖 → Step3.7 架构不注册 → fallback 报 _get_num_multimodal_tokens;vllm-ascend 侧少盖 → 第 43–44 层激活数值错误。

适配步骤:

  1. 拉取基础镜像(用 2.4 节命令创建并进入容器):
docker pull quay.io/ascend/vllm-ascend:v0.20.2rc1
  1. 进入容器后,按附录 6 手动修改上述 5 个文件(vllm 侧 3 + vllm-ascend 侧 2)。

  2. 重启 vllm 服务(见第 3 节)。

5 个缺一不可:只改 vllm-ascend 2 个会跑不通(报 _get_num_multimodal_tokens);只改 vllm 侧则第 43–44 层激活数值错误。

改动来源:vllm-ascend 侧 2 文件对应 PR vllm-project/vllm-ascend#10491;vllm 侧 3 文件为 Step3.7 模型适配,完整内容见附录 6。

2.4 创建容器

docker run -itd \
  --name vllm_step3p7 \
  --net=host \
  --shm-size=500g \
  --device /dev/davinci_manager \
  --device /dev/devmm_svm \
  --device /dev/hisi_hdc \
  -v /usr/local/dcmi:/usr/local/dcmi \
  -v /usr/local/Ascend/driver/tools/hccn_tool:/usr/local/Ascend/driver/tools/hccn_tool \
  -v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \
  -v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/ \
  -v /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info \
  -v /etc/ascend_install.info:/etc/ascend_install.info \
  -v /etc/hccn.conf:/etc/hccn.conf \
  -v /mnt:/mnt \
  --privileged \
  step3p7-vllm-ascend:v0.20.2rc1 bash

2.5 下载权重

# 以 HuggingFace 镜像源为例,按实际社区链接替换 repo_id
pip install huggingface_hub

python -c "
from huggingface_hub import HfApi
api = HfApi()
api.snapshot_download(
    repo_id='stepfun-ai/Step-3.7-Flash',
    repo_type='model',
    resume_download=True,
    revision='main',
    local_dir='/mnt/models/step-3.7',
    etag_timeout=10000
)
"

权重下载完成后,确认目录下包含 config.json、configuration_step3p7.py、tokenizer_config.json 及权重文件(.safetensors)。


3. 运行指导

3.1 启动推理服务

单机 8 卡部署,使用 8 卡张量并行、float16 精度:

# 进入容器
docker exec -it vllm_step3p7 bash

# 单机环境变量(8 卡)
export ASCEND_RT_VISIBLE_DEVICES=0,1,2,3,4,5,6,7
export TASK_QUEUE_ENABLE=1
export OMP_PROC_BIND=false
export OMP_NUM_THREADS=100
export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libjemalloc.so.2:$LD_PRELOAD
export PYTORCH_NPU_ALLOC_CONF="expandable_segments:True"
export VLLM_ASCEND_ENABLE_PREFETCH_MLP=1

# 启动服务
vllm serve /mnt/models/step-3.7 \
  --served-model-name step3p7 \
  --host 0.0.0.0 \
  --port 2077 \
  --tensor-parallel-size 8 \
  --dtype float16 \
  --max-model-len 4096 \
  --max-num-seqs 32 \
  --trust-remote-code \
  --seed 1024

关键参数说明:

参数说明
--trust-remote-code必填。加载模型自带的 configuration_step3p7.py 配置(含 swiglu_limits、layer_types 等完整字段)
--tensor-parallel-size 8单机 8 卡张量并行
--dtype float16推理精度 float16(非量化路径)
--max-model-len 4096最大序列长度,按显存与业务需求调整

3.2 服务验证

服务启动后(日志出现 Application startup complete),将 0.0.0.0:2077 替换为实际部署 IP 和端口进行验证。

纯文本对话:

curl http://0.0.0.0:2077/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "step3p7",
    "max_tokens": 128,
    "messages": [
      {"role": "user", "content": "请用三句话介绍长江的地理特征。"}
    ]
  }'

多模态(图文)对话:

curl http://0.0.0.0:2077/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "step3p7",
    "max_tokens": 256,
    "messages": [
      {
        "role": "user",
        "content": [
          {"type": "image_url", "image_url": {"url": "https://example.com/image.jpg"}},
          {"type": "text", "text": "请描述这张图片的内容。"}
        ]
      }
    ]
  }'

多模态请求需将 image_url 替换为可访问的图片地址或 base64 编码。

4. 开箱性能测试

卡数输入长度 (tokens)输出长度 (tokens)TTFT (ms)TPOT (ms)并发单卡输出吞吐 (tokens/s)
810241024952.752.12018.8849
8204810241105.847.11520.8506
8409610241868.649.31520.0091
8819210241922.647.51020.3001
81638410244772.651.81017.982
83276810246891.647.4819.211
865536102429154.653412.297
8131072102460072.7734.4422.35

5. 常见问题

Q1:启动报 'Step3VLProcessor' object has no attribute '_get_num_multimodal_tokens',或提示 Step3.7 架构不被支持?(最高频) A:vllm 侧 3 个文件没盖全(常见于只盖了 vllm-ascend 2 个算子文件)。Step3.7 架构未注册 → vllm fallback 到通用 wrapper → 报此错。按 2.3 节把 5 个文件盖全(尤其 vllm 侧的 step3p7.py、models/registry.py、tokenizers/registry.py)。

Q2:启动报错找不到某配置字段(如 swiglu_limits / layer_types / max_position_embeddings)? A:确认启动命令包含 --trust-remote-code。该参数使 vllm 加载模型自带的 configuration_step3p7.py(字段完整)。缺失会改用框架内置简化配置,导致字段缺失。

切勿在 vllm 的 transformers_utils/config.py 里加 step3p7 → Step3VLConfig 映射——那会强制 trust_remote_code=False、绕过模型自带配置(治标旧方案的坑,治本方案已避免,详见 case_study 问题 1)。

Q3:MoE 层报与激活函数相关的错误 / 第 43–44 层数值异常? A:确认镜像已叠加 swiglustep 补丁(vllm-ascend 侧 2 文件,见 2.3 节)。未打补丁的 v0.20.2rc1 不支持 SwigluStepAndMul。

Q4:中文输出乱码、内容无关(幻觉)、或 prompt_tokens 异常少(7~8)? A:检查 tokenizers/registry.py 补丁是否生效(step3p7 须在 _MODEL_TYPES_WITH_INCORRECT_TOKENIZER_CLASS 集合)。该集合让 vllm 绕过错误的 LlamaTokenizerFast 包装,否则中文编码丢失。同时确认 tokenizer_config.json 含 chat_template(ChatML)。

Q5:显存不足(OOM)? A:降低 --max-model-len 与 --max-num-seqs,或提高 --gpu-memory-utilization(注意留足 KV Cache 与权重空间)。MoE 模型权重较大,建议在 8 卡环境下部署。

Q6:是否支持量化部署? A:本指导基于 float16 非量化路径,已完整验证。量化(W8A8 等)路径为后续增强项,待验证通过后补充。


6. 附录:手动适配补丁明细(v0.20.2rc1 的 5 个文件改动)

本节给出 vllm-ascend v0.20.2rc1 需手动修改的 5 个文件的完整内容。改完重启 vllm 服务生效。

6.1 vllm 侧(3 文件)

(1)新增 vllm/model_executor/models/step3p7.py —— Step3p7 模型定义(继承 v0.20.2 自带的 Step3VL):

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""Inference-only Jurassic model."""

import torch

from vllm.config import VllmConfig
from vllm.logger import init_logger
from vllm.model_executor.layers.activation import get_act_fn
from vllm.model_executor.layers.linear import ColumnParallelLinear

from .step3_vl import Step3VLForConditionalGeneration
from .step_vl import PerceptionEncoder
from .utils import WeightsMapper, init_vllm_registered_model, maybe_prefix
from .vision import run_dp_sharded_vision_model

logger = init_logger(__name__)


class Step3p7ForConditionalGeneration(Step3VLForConditionalGeneration):
    hf_to_vllm_mapper = WeightsMapper(
        orig_to_new_prefix={
            "model.vision_model.": "vision_model.",
            "model.vit_large_projector.": "vit_large_projector.",
            "model.vit_large_projector": "vit_large_projector",
            "model.language_model.": "language_model.model.",
            "model.language_model": "language_model.model",
            "model.": "language_model.model.",
            "lm_head.": "language_model.lm_head.",
            "lm_head": "language_model.lm_head",
        },
        orig_to_new_substr={
            ".attn.in_proj_weight": ".attn.qkv_proj.weight",
            ".attn.in_proj_bias": ".attn.qkv_proj.bias",
            ".mlp.c_fc": ".mlp.fc1",
            ".mlp.c_proj": ".mlp.fc2",
        },
    )

    def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
        super(Step3VLForConditionalGeneration, self).__init__()

        config = vllm_config.model_config.hf_config
        multimodal_config = vllm_config.model_config.multimodal_config
        quant_config = vllm_config.quant_config

        self.config = config
        self.multimodal_config = multimodal_config
        self.use_data_parallel = multimodal_config.mm_encoder_tp_mode == "data"

        with self._mark_tower_model(vllm_config, "image"):
            self.vision_model = PerceptionEncoder(
                config.vision_config,
                get_act_fn(config.vision_config.hidden_act),
                quant_config=quant_config,
                prefix=maybe_prefix(prefix, "vision_model"),
            )
            self.vit_large_projector = ColumnParallelLinear(
                config.vision_config.width * 4,
                config.text_config.hidden_size,
                bias=config.projector_bias,
                gather_output=True,
                quant_config=quant_config,
                prefix=maybe_prefix(prefix, "vit_large_projector"),
                disable_tp=self.use_data_parallel,
            )

        with self._mark_language_model(vllm_config):
            self.language_model = init_vllm_registered_model(
                vllm_config=vllm_config,
                hf_config=config.text_config,
                prefix=maybe_prefix(prefix, "language_model"),
            )

        self.make_empty_intermediate_tensors = (
            self.language_model.make_empty_intermediate_tensors
        )

    def _get_vision_model_output(
        self, input_tensor: torch.Tensor | None
    ) -> torch.Tensor | None:
        if input_tensor is None:
            return None
        if self.use_data_parallel:
            return run_dp_sharded_vision_model(input_tensor, self.vision_model)
        return self.vision_model(input_tensor)

    def _process_image_features(self, image_features: torch.Tensor) -> torch.Tensor:
        image_features, _ = self.vit_large_projector(image_features)
        return image_features

(2)vllm/model_executor/models/registry.py —— 在 _SPECULATIVE_DECODING_MODELS 字典(Step3p5MTP 条目附近)加一行注册:

    "Step3p7ForConditionalGeneration": ("step3p7", "Step3p7ForConditionalGeneration"),

(3)vllm/tokenizers/registry.py —— tokenizer 中文编码修复(2 处):

a) _MODEL_TYPES_WITH_INCORRECT_TOKENIZER_CLASS 集合加入 step3p7(否则中文编码丢失、prompt_tokens 只有 7~8):

_MODEL_TYPES_WITH_INCORRECT_TOKENIZER_CLASS: set[str] = {"step3_vl", "step3p7"}

b) 在 get_tokenizer 的 from_pretrained 之后,对集合内的 model_type 加 get_cached_tokenizer 包装(v0.20.2 缺此步,main 已有;缺它会报 TokenizersBackend has no attribute max_chars_per_token):

    tokenizer = tokenizer_cls_.from_pretrained(tokenizer_name, *args, **kwargs)

    if model_type in _MODEL_TYPES_WITH_INCORRECT_TOKENIZER_CLASS:
        from vllm.tokenizers.hf import get_cached_tokenizer

        tokenizer = get_cached_tokenizer(tokenizer)

6.2 vllm-ascend 侧(2 文件,对应 PR #10491)

(4)vllm_ascend/ops/activation.py —— 新增 swiglustep_and_mul 算子:

def swiglustep_and_mul(x: torch.Tensor, limit: float = 7.0) -> torch.Tensor:
    """Out-variant of swiglustep activation.

    Computes:
     silu(x[:d]).clamp(max=limit) * x[d:].clamp(-limit, limit)
    """
    gate, up = x.chunk(2, dim=-1)
    gate = torch.nn.functional.silu(gate)
    gate = gate.clamp(max=limit)
    up = up.clamp(min=-limit, max=limit)
    out = gate * up
    return out

(5)vllm_ascend/ops/fused_moe/moe_mlp.py —— MoE MLP 加 SWIGLUSTEP 分支:

a) 顶部 import 加入 swiglustep_and_mul:

from vllm_ascend.ops.activation import AscendSwigluOAIAndMul, swiglustep_and_mul

b) 非量化 MLP(unquant_apply_mlp)补充 swiglustep 分支:

    elif act_name == "swiglustep":
        limit = swiglu_limit if swiglu_limit > 0 else 7.0
        gate_up_out = swiglustep_and_mul(gate_up_out, limit=limit)

vllm-ascend 侧 2 文件对应 PR vllm-project/vllm-ascend#10491;本节给出全部 5 文件的完整改动,照此手动适配即可。