[Bugfix] Fix deepseek percision issue and add acc ci for it (#905)
### What this PR does / why we need it? Fix deepseek percision issue on V0 and add acc ci for it Fixes https://github.com/vllm-project/vllm-ascend/issues/1062 ### How was this patch tested? CI passed with new added test. Signed-off-by: MengqingCao <cmq0113@163.com>
This commit is contained in:
@@ -41,9 +41,19 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
max-parallel: 2
|
max-parallel: 2
|
||||||
matrix:
|
matrix:
|
||||||
|
os: [linux-arm64-npu-1, linux-arm64-npu-4]
|
||||||
vllm_version: [main, v0.9.0]
|
vllm_version: [main, v0.9.0]
|
||||||
|
concurrency:
|
||||||
|
group: >
|
||||||
|
${{
|
||||||
|
matrix.os == 'linux-arm64-npu-4'
|
||||||
|
&& github.event.pull_request.number
|
||||||
|
&& format('pr-{0}-limit-npu-4-long-term', github.event.pull_request.number)
|
||||||
|
|| format('job-{0}-{1}-{2}-long-term', matrix.os, matrix.vllm_version, github.event.pull_request.number)
|
||||||
|
}}
|
||||||
|
cancel-in-progress: false
|
||||||
name: vLLM Ascend long term test
|
name: vLLM Ascend long term test
|
||||||
runs-on: linux-arm64-npu-1
|
runs-on: ${{ matrix.os }}
|
||||||
container:
|
container:
|
||||||
# TODO(yikun): Remove m.daocloud.io prefix when infra proxy ready
|
# TODO(yikun): Remove m.daocloud.io prefix when infra proxy ready
|
||||||
image: m.daocloud.io/quay.io/ascend/cann:8.1.rc1-910b-ubuntu22.04-py3.10
|
image: m.daocloud.io/quay.io/ascend/cann:8.1.rc1-910b-ubuntu22.04-py3.10
|
||||||
@@ -92,8 +102,13 @@ jobs:
|
|||||||
|
|
||||||
- name: Run vllm-project/vllm-ascend long term test
|
- name: Run vllm-project/vllm-ascend long term test
|
||||||
run: |
|
run: |
|
||||||
# spec decode test
|
if [[ "${{ matrix.os }}" == "linux-arm64-npu-1" ]]; then
|
||||||
VLLM_USE_MODELSCOPE=True pytest -sv tests/long_term/spec_decode/e2e/test_v1_mtp_correctness.py
|
# spec decode test
|
||||||
VLLM_USE_MODELSCOPE=true pytest -sv tests/long_term/spec_decode/e2e/test_v1_spec_decode.py
|
VLLM_USE_MODELSCOPE=True pytest -sv tests/long_term/spec_decode/e2e/test_v1_mtp_correctness.py
|
||||||
VLLM_USE_MODELSCOPE=True pytest -sv tests/long_term/spec_decode/e2e/test_mtp_correctness.py # it needs a clean process
|
VLLM_USE_MODELSCOPE=True pytest -sv tests/long_term/spec_decode/e2e/test_v1_spec_decode.py
|
||||||
pytest -sv tests/long_term/spec_decode --ignore=tests/long_term/spec_decode/e2e/test_mtp_correctness.py --ignore=tests/long_term/spec_decode/e2e/test_v1_spec_decode.py --ignore=tests/long_term/spec_decode/e2e/test_v1_mtp_correctness.py
|
VLLM_USE_MODELSCOPE=True pytest -sv tests/long_term/spec_decode/e2e/test_mtp_correctness.py # it needs a clean process
|
||||||
|
pytest -sv tests/long_term/spec_decode --ignore=tests/long_term/spec_decode/e2e/test_mtp_correctness.py --ignore=tests/long_term/spec_decode/e2e/test_v1_spec_decode.py --ignore=tests/long_term/spec_decode/e2e/test_v1_mtp_correctness.py
|
||||||
|
pytest -sv tests/long_term/test_accuracy.py
|
||||||
|
else
|
||||||
|
VLLM_USE_MODELSCOPE=True pytest -sv tests/long_term/test_deepseek_v2_lite_tp2_accuracy.py
|
||||||
|
fi
|
||||||
|
|||||||
@@ -354,4 +354,4 @@ def prompt_template(request):
|
|||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def ilama_lora_files():
|
def ilama_lora_files():
|
||||||
return snapshot_download(repo_id="jeeejeee/ilama-text2sql-spider")
|
return snapshot_download(repo_id="jeeejeee/ilama-text2sql-spider")
|
||||||
|
|||||||
72
tests/long_term/test_deepseek_v2_lite_tp2_accuracy.py
Normal file
72
tests/long_term/test_deepseek_v2_lite_tp2_accuracy.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
# Copyright 2023 The vLLM team.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
# This file is a part of the vllm-ascend project.
|
||||||
|
# Adapted from vllm-project/blob/main/tests/entrypoints/llm/test_accuracy.py
|
||||||
|
#
|
||||||
|
|
||||||
|
import gc
|
||||||
|
import multiprocessing
|
||||||
|
from multiprocessing import Queue
|
||||||
|
|
||||||
|
import lm_eval
|
||||||
|
import pytest
|
||||||
|
import torch
|
||||||
|
|
||||||
|
# pre-trained model path on Hugging Face.
|
||||||
|
MODELS = ["deepseek-ai/DeepSeek-V2-Lite"]
|
||||||
|
# Math reasoning benchmark (Grade School Math 8K).
|
||||||
|
TASK = "gsm8k"
|
||||||
|
# Answer validation requiring format consistency.
|
||||||
|
FILTER = "exact_match,strict-match"
|
||||||
|
# 3% relative tolerance for numerical accuracy.
|
||||||
|
RTOL = 0.03
|
||||||
|
# Baseline accuracy after VLLM optimization.
|
||||||
|
# FIXME: fix the accuracy issue
|
||||||
|
EXPECTED_VALUE = 0.000758150113722517
|
||||||
|
|
||||||
|
|
||||||
|
def run_test(model_name, queue, more_args=None):
|
||||||
|
model_args = f"pretrained={model_name},max_model_len=4096,trust_remote_code=True,tensor_parallel_size=4"
|
||||||
|
if more_args is not None:
|
||||||
|
model_args = f"{model_args},{more_args}"
|
||||||
|
results = lm_eval.simple_evaluate(
|
||||||
|
model="vllm",
|
||||||
|
model_args=model_args,
|
||||||
|
tasks=TASK,
|
||||||
|
batch_size="auto",
|
||||||
|
)
|
||||||
|
result = results["results"][TASK][FILTER]
|
||||||
|
print(100 * "*", "\nThe accuracy test result:", result)
|
||||||
|
queue.put(result)
|
||||||
|
del results
|
||||||
|
torch.npu.empty_cache()
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("model", MODELS)
|
||||||
|
def test_lm_eval_accuracy(model, monkeypatch: pytest.MonkeyPatch):
|
||||||
|
with monkeypatch.context():
|
||||||
|
result_queue: Queue[float] = multiprocessing.Queue()
|
||||||
|
p = multiprocessing.Process(target=run_test,
|
||||||
|
args=(
|
||||||
|
model,
|
||||||
|
result_queue,
|
||||||
|
))
|
||||||
|
p.start()
|
||||||
|
p.join()
|
||||||
|
result = result_queue.get()
|
||||||
|
assert (EXPECTED_VALUE - RTOL < result < EXPECTED_VALUE + RTOL), \
|
||||||
|
f"Expected: {EXPECTED_VALUE}±{RTOL} | Measured: {result}"
|
||||||
@@ -22,7 +22,6 @@ Run `pytest tests/test_offline_inference.py`.
|
|||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
|
||||||
import vllm # noqa: F401
|
import vllm # noqa: F401
|
||||||
|
|
||||||
from tests.conftest import VllmRunner
|
from tests.conftest import VllmRunner
|
||||||
@@ -47,7 +46,6 @@ def test_models_distributed_QwQ():
|
|||||||
vllm_model.generate_greedy(example_prompts, max_tokens)
|
vllm_model.generate_greedy(example_prompts, max_tokens)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(True, reason="wait for mla issue fixed on v1")
|
|
||||||
def test_models_distributed_DeepSeek():
|
def test_models_distributed_DeepSeek():
|
||||||
example_prompts = [
|
example_prompts = [
|
||||||
"vLLM is a high-throughput and memory-efficient inference and serving engine for LLMs.",
|
"vLLM is a high-throughput and memory-efficient inference and serving engine for LLMs.",
|
||||||
|
|||||||
@@ -720,6 +720,7 @@ class AscendAttentionBackendImpl(AttentionImpl):
|
|||||||
blocksparse_params: Optional[Dict[str, Any]] = None,
|
blocksparse_params: Optional[Dict[str, Any]] = None,
|
||||||
logits_soft_cap: Optional[float] = None,
|
logits_soft_cap: Optional[float] = None,
|
||||||
attn_type: str = AttentionType.DECODER,
|
attn_type: str = AttentionType.DECODER,
|
||||||
|
kv_sharing_target_layer_name: Optional[str] = None,
|
||||||
use_irope: bool = False,
|
use_irope: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.num_heads = num_heads
|
self.num_heads = num_heads
|
||||||
@@ -961,6 +962,7 @@ class AscendMLAAttentionBackendImpl(MLAAttentionImpl):
|
|||||||
blocksparse_params: Optional[Dict[str, Any]] = None,
|
blocksparse_params: Optional[Dict[str, Any]] = None,
|
||||||
logits_soft_cap: Optional[float] = None,
|
logits_soft_cap: Optional[float] = None,
|
||||||
attn_type: str = AttentionType.DECODER,
|
attn_type: str = AttentionType.DECODER,
|
||||||
|
kv_sharing_target_layer_name: Optional[str] = None,
|
||||||
**extra_impl_args,
|
**extra_impl_args,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.num_heads = num_heads
|
self.num_heads = num_heads
|
||||||
|
|||||||
@@ -186,6 +186,7 @@ class AscendAttentionBackendImpl(AttentionImpl):
|
|||||||
blocksparse_params: Optional[Dict[str, Any]] = None,
|
blocksparse_params: Optional[Dict[str, Any]] = None,
|
||||||
logits_soft_cap: Optional[float] = None,
|
logits_soft_cap: Optional[float] = None,
|
||||||
attn_type: str = AttentionType.DECODER,
|
attn_type: str = AttentionType.DECODER,
|
||||||
|
kv_sharing_target_layer_name: Optional[str] = None,
|
||||||
use_irope: bool = False,
|
use_irope: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.num_heads = num_heads
|
self.num_heads = num_heads
|
||||||
|
|||||||
@@ -9,10 +9,8 @@ from vllm.attention.backends.abstract import (AttentionBackend, AttentionLayer,
|
|||||||
MLAAttentionImpl)
|
MLAAttentionImpl)
|
||||||
from vllm.attention.backends.utils import PAD_SLOT_ID
|
from vllm.attention.backends.utils import PAD_SLOT_ID
|
||||||
from vllm.config import get_current_vllm_config
|
from vllm.config import get_current_vllm_config
|
||||||
from vllm.model_executor.layers.linear import (ColumnParallelLinear,
|
from vllm.model_executor.layers.linear import (LinearBase,
|
||||||
LinearBase, RowParallelLinear,
|
|
||||||
UnquantizedLinearMethod)
|
UnquantizedLinearMethod)
|
||||||
from vllm.model_executor.layers.rotary_embedding import RotaryEmbedding
|
|
||||||
|
|
||||||
from vllm_ascend.attention.attention_v1 import AscendAttentionState
|
from vllm_ascend.attention.attention_v1 import AscendAttentionState
|
||||||
from vllm_ascend.ops.attention import vanilla_chunked_prefill_mla
|
from vllm_ascend.ops.attention import vanilla_chunked_prefill_mla
|
||||||
@@ -422,20 +420,7 @@ class AscendMLAImpl(MLAAttentionImpl):
|
|||||||
blocksparse_params: Optional[dict[str, Any]],
|
blocksparse_params: Optional[dict[str, Any]],
|
||||||
logits_soft_cap: Optional[float],
|
logits_soft_cap: Optional[float],
|
||||||
attn_type: str,
|
attn_type: str,
|
||||||
# MLA Specific Arguments
|
kv_sharing_target_layer_name: Optional[str] = None,
|
||||||
q_lora_rank: Optional[int],
|
|
||||||
kv_lora_rank: int,
|
|
||||||
qk_nope_head_dim: int,
|
|
||||||
qk_rope_head_dim: int,
|
|
||||||
qk_head_dim: int,
|
|
||||||
v_head_dim: int,
|
|
||||||
rotary_emb: RotaryEmbedding,
|
|
||||||
# q_proj should be q_b_proj if q_lora_rank is not None, but from an
|
|
||||||
# attention backend perspective we rely on the layer to pass in the
|
|
||||||
# correct matrix
|
|
||||||
q_proj: ColumnParallelLinear,
|
|
||||||
kv_b_proj: ColumnParallelLinear,
|
|
||||||
o_proj: RowParallelLinear,
|
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.num_heads = num_heads
|
self.num_heads = num_heads
|
||||||
@@ -444,25 +429,20 @@ class AscendMLAImpl(MLAAttentionImpl):
|
|||||||
self.num_kv_heads = num_kv_heads
|
self.num_kv_heads = num_kv_heads
|
||||||
self.kv_cache_dtype = kv_cache_dtype
|
self.kv_cache_dtype = kv_cache_dtype
|
||||||
|
|
||||||
self.q_lora_rank = q_lora_rank
|
# MLA Args
|
||||||
self.kv_lora_rank = kv_lora_rank
|
self.q_lora_rank = kwargs['q_lora_rank']
|
||||||
self.qk_nope_head_dim = qk_nope_head_dim
|
self.kv_lora_rank = kwargs['kv_lora_rank']
|
||||||
self.qk_rope_head_dim = qk_rope_head_dim
|
self.qk_nope_head_dim = kwargs['qk_nope_head_dim']
|
||||||
self.qk_head_dim = qk_head_dim
|
self.qk_rope_head_dim = kwargs['qk_rope_head_dim']
|
||||||
self.v_head_dim = v_head_dim
|
self.qk_head_dim = kwargs['qk_head_dim']
|
||||||
|
self.v_head_dim = kwargs['v_head_dim']
|
||||||
# Hack for V1 for now to avoid torch library overhead (since we are
|
self.rotary_emb = kwargs['rotary_emb']
|
||||||
# already inside an attention custom op), pull out the forward
|
self.q_proj = kwargs['q_proj']
|
||||||
# method from the rotary embedding and call it directly
|
self.kv_b_proj = kwargs['kv_b_proj']
|
||||||
# TODO(lucas): we should probably find a cleaner way to do this
|
self.o_proj = kwargs['o_proj']
|
||||||
self.rotary_emb = rotary_emb
|
|
||||||
|
|
||||||
self.q_proj = q_proj
|
|
||||||
self.kv_b_proj = kv_b_proj
|
|
||||||
self.o_proj = o_proj
|
|
||||||
|
|
||||||
self.kv_a_proj_with_mqa = kwargs.get('kv_a_proj_with_mqa', None)
|
self.kv_a_proj_with_mqa = kwargs.get('kv_a_proj_with_mqa', None)
|
||||||
self.kv_a_layernorm = kwargs.get('kv_a_layernorm', None)
|
self.kv_a_layernorm = kwargs.get('kv_a_layernorm', None)
|
||||||
|
|
||||||
# Handle the differences between the flash_attn_varlen from flash_attn
|
# Handle the differences between the flash_attn_varlen from flash_attn
|
||||||
# and the one from vllm_flash_attn. The former is used on RoCM and the
|
# and the one from vllm_flash_attn. The former is used on RoCM and the
|
||||||
# latter has an additional parameter to control FA2 vs FA3
|
# latter has an additional parameter to control FA2 vs FA3
|
||||||
|
|||||||
@@ -629,6 +629,7 @@ class AscendUnquantizedFusedMoEMethod(UnquantizedFusedMoEMethod):
|
|||||||
scoring_func: str = "softmax",
|
scoring_func: str = "softmax",
|
||||||
e_score_correction_bias: Optional[torch.Tensor] = None,
|
e_score_correction_bias: Optional[torch.Tensor] = None,
|
||||||
is_prefill: bool = False,
|
is_prefill: bool = False,
|
||||||
|
enable_force_load_balance: bool = False,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
# NOTE: now npu_moe_gating_top_k can only support `group_count=256` pattern
|
# NOTE: now npu_moe_gating_top_k can only support `group_count=256` pattern
|
||||||
@@ -660,6 +661,13 @@ class AscendUnquantizedFusedMoEMethod(UnquantizedFusedMoEMethod):
|
|||||||
e_score_correction_bias=e_score_correction_bias,
|
e_score_correction_bias=e_score_correction_bias,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
topk_weights = topk_weights.to(x.dtype)
|
||||||
|
# this is a naive implementation for experts load balance so as
|
||||||
|
# to avoid accumulating too much tokens on a single rank.
|
||||||
|
# currently it is only activated when doing profile runs.
|
||||||
|
if enable_force_load_balance:
|
||||||
|
topk_ids = torch.randint_like(topk_ids, 0, global_num_experts)
|
||||||
|
|
||||||
if VLLM_ENABLE_MC2 and not is_prefill:
|
if VLLM_ENABLE_MC2 and not is_prefill:
|
||||||
return fused_experts_with_mc2(
|
return fused_experts_with_mc2(
|
||||||
hidden_states=x,
|
hidden_states=x,
|
||||||
|
|||||||
@@ -624,6 +624,8 @@ class AscendW8A8DynamicFusedMoEMethod:
|
|||||||
if enable_force_load_balance:
|
if enable_force_load_balance:
|
||||||
topk_ids = torch.randint_like(topk_ids, 0, global_num_experts)
|
topk_ids = torch.randint_like(topk_ids, 0, global_num_experts)
|
||||||
|
|
||||||
|
topk_weights = topk_weights.to(x.dtype)
|
||||||
|
|
||||||
if VLLM_ENABLE_MC2 and not is_prefill:
|
if VLLM_ENABLE_MC2 and not is_prefill:
|
||||||
return fused_experts_with_mc2(
|
return fused_experts_with_mc2(
|
||||||
hidden_states=x,
|
hidden_states=x,
|
||||||
|
|||||||
Reference in New Issue
Block a user