下载
中文
注册

vLLM 0.3.3 版本昇腾框架适配说明

vllm_npu_0.3.3版本的目录结构如下所示:

.
├── cover
│   ├── vllm
│   │   └── __init__.py
│   ├── requirements.txt
│   └── setup.py
├── examples
│   ├── start_server.sh
│   ├── test_offline.py
│   └── test_offline.sh
├── install.sh
└── vllm_npu
    ├── requirements.txt
    ├── setup.py
    ├── tests
    │   ├── models
    │   │   ├── __init__.py
    │   │   └── test_models.py
    │   └── sampler
    │       └── test_sampler.py
    └── vllm_npu
        ├── config.py
        ├── core
        │   ├── __init__.py
        │   └── scheduler.py
        ├── engine
        │   ├── __init__.py
        │   ├── llm_engine.py
        │   └── ray_utils.py
        ├── __init__.py
        ├── model_executor
        │   ├── ascend_model_loader.py
        │   ├── __init__.py
        │   ├── layers
        │   │   ├── __init__.py
        │   │   └── sampler.py
        │   ├── models
        │   │   ├── ascend
        │   │   │   ├── __init__.py
        │   │   │   └── mindie_llm_wrapper.py
        │   │   └── __init__.py
        │   └── utils.py
        ├── npu_adaptor.py
        ├── utils.py
        └── worker
            ├── ascend_worker.py
            ├── cache_engine.py
            ├── __init__.py
            └── model_runner.py

vllm_npu_0.3.3版本中主要修改了core、engine、model_executor、worker四个模块,与vllm原生框架中的同名模块一一对应进行热替换适配。

表1 各模块修改内容介绍

模块

简介

core

该模块中对于 “num_batched_tokens”的计算与后端有所不同,MindIE后端的实现方法与0.4.2版本一致,因此在此处需要修改该变量计算逻辑。

engine

该模块替换了部分内容,主要是为了确保vLLM框架可以正确获取到后续的worker与model_executor模块。例如:在ray模块启动时新增指定 “num_gpus”参数为 “parallel_config.world_size”,解决ray模块启动问题;并且在DEVICE_TO_WORKER_MODULE_MAP常量字典中新增键值对:"npu"、 "vllm_npu.worker.ascend_worker",确保在启动worker时可以正确使用ascend_worker。

model_executor

该模块负责与MindIE LLM模型的推理与后处理进行对接,包含两个子模块:“layers”和 “models”。其中,“layers” 模块主要负责后处理操作,而“models”模块则专注于模型推理。在“models”模块中,编写了一个名为 “MindIELlmWrapper”的类。这个类用于实例化 MindIE LLM提供的 “GeneratorTorch”统一接口,并从vLLM 原生框架的数据结构中提取出MindIE LLM所需的模型推理参数,进而调用统一接口进行模型推理。此外,该类还实现了在进行预热(warmup)操作时所需的虚拟数据的构造过程。

同时添加了ascend_model_loader.py文件,为后续的“ModelRunner”加载模型提供“get_model”方法。

worker

该模块覆写了“Worker”类与“ModelRunner”类中的部分函数,在ascend_worker.py和model_runner.py中与 “MindIELlmWrapper”交互,确保模型的加载、预热、推理和后处理等过程能够正确在后端执行。

此外,修改了原生框架中“CacheEngine”的“_allocate_kv_cache”函数,确保与后端接收的参数形式一致。

推理流程与部分关键代码的说明

在vLLM中从启动引擎到完成推理,有如下步骤:

  1. 输入模型架构名和权重路径:首先提供模型的基本信息。
  2. 初始化引擎参数:

    “model str”-> “EngineArgs()”->“LLMEngine.from_engine_args()”->“LLMEngine.init()”

  3. 配置模型:

    “ModelConfig()”:如果设置了“VLLM_USE_MODELSCOPE”变量,则从 ModelScope hub 下载对应的模型,并设置下载路径为模型权重路径。

  4. 初始化工作器:“LLMEngine._init_workers()”->“Worker.init_model()”:在这一步骤中,设置NPU环境、分布式配置和随机种子等。“Worker.load_model()”->“ModelRunner.load_model()”->“vllm.model_executor.model_loader.get_model()”
        def load_model(self):
            # Add an attribute dynamically, not graceful, but useful
            self.model_config.mindie_config = {
                "backend_type": BackendType.ATB,
                "model_id": self.model_config.model,
                "rank": self.rank,
                "local_rank": self.local_rank,
                "world_size": self.parallel_config.world_size,
                "npu_device_id": self.local_rank,
            }
            self.model_runner.load_model()
            del self.model_config.mindie_config
  5. 加载模型:

    “ascend_model_loader.get_model()”->“mindie_llm_wrapper.load_weights()” ->“mindie_llm.text_generator.adapter.GeneratorTorch(model_config)”,由MindIE LLM实现模型加载的逻辑。

        def load_weights(self, load_format: Literal["auto", "safetensors", "pt"] = "auto"):
            """Load model weights.
            Args:
                load_format (Literal[auto, safetensors, pt], optional): The format of weights. Defaults to "auto".
            Raises:
                ValueError: Format not supported.
            """
            if load_format not in ["auto", "safetensors", "pt"]:
                raise ValueError("load-format support [safetensors, pt]")
            self.weight_dtype = torch.get_default_dtype()
            torch.set_default_dtype(torch.float32)
            # Replacing the deprecated method with GeneratorTorch
            self.atb_model = GeneratorTorch(self.mindie_config)
            self.sampler = Sampler(self.atb_model)
            self.sampler.logits_as_hidden_states = True
            torch.set_default_dtype(self.weight_dtype)
  6. 创建模型实例:

    回到“model_loader.get_model()”函数中根据类创建模型实例。如果存在量化方法,也在此实例化。

  7. 实际推理:

    “LLMEngine.step”->“worker.execute_model”-> “model_runner.execute_model”->“mindie_llm_wrapper.foward” -> MindIE后端。其中在 “model_runner”层还需要在“prepare_input_tensor”及其私有方法中对输入进来的数据进行处理。

    同样,后处理部分也是由MindIE后端来进行处理与看护。

        def forward(
            self,
            input_ids: torch.Tensor,
            positions: torch.Tensor,
            kv_caches: List[KVCache],
            input_metadata: InputMetadata,
            lora_requests: List[LoRARequest],
        ) -> torch.Tensor:
            if kv_caches[0][0] is None:
                kv_caches, block_tables, slots = self.create_dummy_kv_cache(input_metadata, input_ids)
            else:
                if input_metadata.is_prompt:
                    block_tables = torch.tensor([0], dtype=torch.int32, device="npu")
                else:
                    block_tables = input_metadata.block_tables
                slots = input_metadata.slot_mapping
            if input_metadata.is_prompt:
                input_lengths = input_metadata.prompt_lens.to(torch.int32)
                max_seq_len = int(input_metadata.prompt_lens.max())
                lm_head_indices = (input_metadata.prompt_lens.cumsum(dim=-1) - 1).to(torch.int64)
            else:
                input_lengths = input_metadata.context_lens
                max_seq_len = input_metadata.max_context_len
                lm_head_indices = None
            adapter_ids = [lora_request.lora_name if lora_request else None for lora_request in lora_requests]
            logits = self.atb_model.forward_tensor(
                input_ids,
                positions,
                input_metadata.is_prompt,
                kv_caches,
                block_tables,
                slots,
                input_lengths,
                max_seq_len,
                lm_head_indices,
                adapter_ids=adapter_ids,  # batch_size len
            )
            return logits
        def sample(
            self,
            hidden_states: torch.Tensor,
            sampling_metadata: SamplingMetadata,
        ) -> Optional[SamplerOutput]:
            # hidden_states is logits
            next_tokens = self.sampler(hidden_states, sampling_metadata)
            return next_tokens

对应修改量

表2 对应修改量

重要组件

代码量

作用

Scheduler

~200行

修改“num_batched_tokens”计算逻辑。

Cache Engine

~30行

调整KV Cache参数结构,兼容底层接口。

Worker

~250行

构建后端统一接口所需的配置参数,并且初始化昇腾硬件环境。

Model Loader

~50行

重新编写get_model函数,对接下层调用。

Model Runner

~300行

传递配置参数,替换get_model,修改profile_run中对KV Cache的初始化方式,修改LoRA对应参数传递方式。

MindIE LLM Wrapper

~150行

负责加载、生成虚拟KV Cache、推理、后处理操作与后端的对接。

Sampler

~300行

从SamplingMetadata重构参数,传递至后端Sampler,替代vLLM自有后处理方式,并将结果恢复至vLLM结构。

除此之外,还有少部分文件需要修改,例如:config.py和utils.py等文件需要修改以解决少量在运行时出现的问题。