Commit 4851c202 authored by zhuwenwen's avatar zhuwenwen
Browse files

Merge tag 'v0.6.1' into v0.6.1-dev

parents 9b902f9e 3fd2b0d2
...@@ -7,26 +7,6 @@ import pytest ...@@ -7,26 +7,6 @@ import pytest
from tests.quantization.utils import is_quant_method_supported from tests.quantization.utils import is_quant_method_supported
# In this test we hardcode prompts and generations for the model so we don't
# need to require the AQLM package as a dependency
example_prompts = [
'vLLM is a high-throughput and memory-efficient inference and serving '
'engine for LLMs.\n',
'Briefly describe the major milestones in the development of artificial '
'intelligence from 1950 to 2020.\n',
'Compare and contrast artificial intelligence with human intelligence in '
'terms of processing information.\n',
'Describe the basic components of a neural network and how it can be '
'trained.\n',
'Write a short story about a robot that dreams for the first time.\n',
'Analyze the impact of the COVID-19 pandemic on global economic structures '
'and future business models.\n',
'Explain the cultural significance of the Mona Lisa painting, and how its '
'perception might vary in Western versus Eastern societies.\n',
"Translate the following English sentence into Japanese, French, and "
"Swahili: 'The early bird catches the worm.'\n"
]
# These ground truth generations were generated using `transformers==4.38.1 # These ground truth generations were generated using `transformers==4.38.1
# aqlm==1.1.0 torch==2.2.0` # aqlm==1.1.0 torch==2.2.0`
# and the below code: # and the below code:
......
import types import types
from typing import List, Optional, Tuple, Type from typing import List, Optional, Tuple, Type, Union
import pytest import pytest
import torch import torch
...@@ -9,7 +9,8 @@ from transformers import AutoConfig ...@@ -9,7 +9,8 @@ from transformers import AutoConfig
from vllm.multimodal.utils import rescale_image_size from vllm.multimodal.utils import rescale_image_size
from vllm.utils import is_cpu from vllm.utils import is_cpu
from ..conftest import IMAGE_ASSETS, HfRunner, VllmRunner, _ImageAssets from ..conftest import (IMAGE_ASSETS, HfRunner, PromptImageInput, VllmRunner,
_ImageAssets)
from .utils import check_logprobs_close from .utils import check_logprobs_close
pytestmark = pytest.mark.vlm pytestmark = pytest.mark.vlm
...@@ -20,6 +21,7 @@ HF_IMAGE_PROMPTS = IMAGE_ASSETS.prompts({ ...@@ -20,6 +21,7 @@ HF_IMAGE_PROMPTS = IMAGE_ASSETS.prompts({
"cherry_blossom": "cherry_blossom":
"<|im_start|>User\n<image>\nWhat is the season?<|im_end|>\n<|im_start|>Assistant\n", # noqa: E501 "<|im_start|>User\n<image>\nWhat is the season?<|im_end|>\n<|im_start|>Assistant\n", # noqa: E501
}) })
HF_MULTIIMAGE_IMAGE_PROMPT = "<|im_start|>User\nImage-1: <image>\nImage-2: <image>\nDescribe the two images in detail.<|im_end|>\n<|im_start|>Assistant\n" # noqa: E501
models = [ models = [
"OpenGVLab/InternVL2-1B", "OpenGVLab/InternVL2-1B",
...@@ -64,13 +66,13 @@ def generate( ...@@ -64,13 +66,13 @@ def generate(
def run_test( def run_test(
hf_runner: Type[HfRunner], hf_runner: Type[HfRunner],
vllm_runner: Type[VllmRunner], vllm_runner: Type[VllmRunner],
image_assets: _ImageAssets, inputs: List[Tuple[List[str], PromptImageInput]],
model: str, model: str,
*, *,
size_factors: List[float],
dtype: str, dtype: str,
max_tokens: int, max_tokens: int,
num_logprobs: int, num_logprobs: int,
mm_limit: int,
tensor_parallel_size: int, tensor_parallel_size: int,
distributed_executor_backend: Optional[str] = None, distributed_executor_backend: Optional[str] = None,
): ):
...@@ -83,12 +85,6 @@ def run_test( ...@@ -83,12 +85,6 @@ def run_test(
Note, the text input is also adjusted to abide by vllm contract. Note, the text input is also adjusted to abide by vllm contract.
The text output is sanitized to be able to compare with hf. The text output is sanitized to be able to compare with hf.
""" """
images = [asset.pil_image for asset in image_assets]
inputs_per_image = [(
[prompt for _ in size_factors],
[rescale_image_size(image, factor) for factor in size_factors],
) for image, prompt in zip(images, HF_IMAGE_PROMPTS)]
# NOTE: take care of the order. run vLLM first, and then run HF. # NOTE: take care of the order. run vLLM first, and then run HF.
# vLLM needs a fresh new process without cuda initialization. # vLLM needs a fresh new process without cuda initialization.
...@@ -110,13 +106,21 @@ def run_test( ...@@ -110,13 +106,21 @@ def run_test(
self.max_num = self.config.max_dynamic_patch self.max_num = self.config.max_dynamic_patch
self.image_size = self.vision_config.image_size self.image_size = self.vision_config.image_size
def __call__(self, text: str, images: Image, **kwargs): def __call__(self, text: str, images: Union[Image, List[Image]],
**kwargs):
from vllm.model_executor.models.internvl import ( from vllm.model_executor.models.internvl import (
IMG_CONTEXT, IMG_END, IMG_START, image_to_pixel_values) IMG_CONTEXT, IMG_END, IMG_START, image_to_pixel_values)
pixel_values = image_to_pixel_values( images = [images] if isinstance(images, Image) else images
images, self.image_size, self.min_num, self.max_num, pixel_values = [
self.use_thumbnail).to(self.dtype) image_to_pixel_values(image, self.image_size, self.min_num,
num_patches_list = [pixel_values.shape[0]] self.max_num,
self.use_thumbnail).to(self.dtype)
for image in images
]
num_patches_list = [
pixel_value.shape[0] for pixel_value in pixel_values
]
pixel_values = torch.cat(pixel_values, dim=0)
for num_patches in num_patches_list: for num_patches in num_patches_list:
context_tokens = IMG_CONTEXT * self.num_image_token \ context_tokens = IMG_CONTEXT * self.num_image_token \
* num_patches * num_patches
...@@ -130,6 +134,7 @@ def run_test( ...@@ -130,6 +134,7 @@ def run_test(
with vllm_runner(model, with vllm_runner(model,
max_model_len=4096, max_model_len=4096,
dtype=dtype, dtype=dtype,
limit_mm_per_prompt={"image": mm_limit},
tensor_parallel_size=tensor_parallel_size, tensor_parallel_size=tensor_parallel_size,
distributed_executor_backend=distributed_executor_backend, distributed_executor_backend=distributed_executor_backend,
enforce_eager=True) as vllm_model: enforce_eager=True) as vllm_model:
...@@ -138,7 +143,7 @@ def run_test( ...@@ -138,7 +143,7 @@ def run_test(
max_tokens, max_tokens,
num_logprobs=num_logprobs, num_logprobs=num_logprobs,
images=images) images=images)
for prompts, images in inputs_per_image for prompts, images in inputs
] ]
with hf_runner(model, dtype=dtype) as hf_model: with hf_runner(model, dtype=dtype) as hf_model:
...@@ -156,7 +161,7 @@ def run_test( ...@@ -156,7 +161,7 @@ def run_test(
num_logprobs=num_logprobs, num_logprobs=num_logprobs,
images=hf_images, images=hf_images,
eos_token_id=eos_token_id) eos_token_id=eos_token_id)
for prompts, hf_images in inputs_per_image for prompts, hf_images in inputs
] ]
for hf_outputs, vllm_outputs in zip(hf_outputs_per_image, for hf_outputs, vllm_outputs in zip(hf_outputs_per_image,
...@@ -264,15 +269,64 @@ if is_cpu(): ...@@ -264,15 +269,64 @@ if is_cpu():
@torch.inference_mode() @torch.inference_mode()
def test_models(hf_runner, vllm_runner, image_assets, model, size_factors, def test_models(hf_runner, vllm_runner, image_assets, model, size_factors,
dtype: str, max_tokens: int, num_logprobs: int) -> None: dtype: str, max_tokens: int, num_logprobs: int) -> None:
images = [asset.pil_image for asset in image_assets]
inputs_per_image = [(
[prompt for _ in size_factors],
[rescale_image_size(image, factor) for factor in size_factors],
) for image, prompt in zip(images, HF_IMAGE_PROMPTS)]
run_test( run_test(
hf_runner, hf_runner,
vllm_runner, vllm_runner,
image_assets, inputs_per_image,
model,
dtype=dtype,
max_tokens=max_tokens,
num_logprobs=num_logprobs,
mm_limit=1,
tensor_parallel_size=1,
)
@pytest.mark.parametrize("model", models)
@pytest.mark.parametrize(
"size_factors",
[
# No image
[],
# Single-scale
[1.0],
# Single-scale, batched
[1.0, 1.0, 1.0],
# Multi-scale
[0.5, 0.75, 1.0],
],
)
@pytest.mark.parametrize("dtype", [target_dtype])
@pytest.mark.parametrize("max_tokens", [128])
@pytest.mark.parametrize("num_logprobs", [5])
@torch.inference_mode()
def test_multi_images_models(hf_runner, vllm_runner, image_assets, model,
size_factors, dtype: str, max_tokens: int,
num_logprobs: int) -> None:
images = [asset.pil_image for asset in image_assets]
inputs_per_case = [
([HF_MULTIIMAGE_IMAGE_PROMPT for _ in size_factors],
[[rescale_image_size(image, factor) for image in images]
for factor in size_factors])
]
run_test(
hf_runner,
vllm_runner,
inputs_per_case,
model, model,
size_factors=size_factors,
dtype=dtype, dtype=dtype,
max_tokens=max_tokens, max_tokens=max_tokens,
num_logprobs=num_logprobs, num_logprobs=num_logprobs,
mm_limit=2,
tensor_parallel_size=1, tensor_parallel_size=1,
) )
......
from typing import List, Optional, Tuple, Type from typing import List, Optional, Tuple, Type, overload
import pytest import pytest
from transformers import (AutoConfig, AutoModelForVision2Seq, AutoTokenizer, from transformers import (AutoConfig, AutoModelForVision2Seq, AutoTokenizer,
...@@ -8,11 +8,14 @@ from vllm.multimodal.utils import rescale_image_size ...@@ -8,11 +8,14 @@ from vllm.multimodal.utils import rescale_image_size
from vllm.sequence import SampleLogprobs from vllm.sequence import SampleLogprobs
from vllm.utils import STR_DTYPE_TO_TORCH_DTYPE from vllm.utils import STR_DTYPE_TO_TORCH_DTYPE
from ..conftest import IMAGE_ASSETS, HfRunner, VllmRunner, _ImageAssets from ..conftest import (IMAGE_ASSETS, HfRunner, PromptImageInput, VllmRunner,
_ImageAssets)
from .utils import check_logprobs_close from .utils import check_logprobs_close
pytestmark = pytest.mark.vlm pytestmark = pytest.mark.vlm
_LIMIT_IMAGE_PER_PROMPT = 4
HF_IMAGE_PROMPTS = IMAGE_ASSETS.prompts({ HF_IMAGE_PROMPTS = IMAGE_ASSETS.prompts({
"stop_sign": "stop_sign":
"USER: <image>\nWhat's the content of the image?\nASSISTANT:", "USER: <image>\nWhat's the content of the image?\nASSISTANT:",
...@@ -52,6 +55,7 @@ def vllm_to_hf_output(vllm_output: Tuple[List[int], str, ...@@ -52,6 +55,7 @@ def vllm_to_hf_output(vllm_output: Tuple[List[int], str,
return hf_output_ids, hf_output_str, out_logprobs return hf_output_ids, hf_output_str, out_logprobs
@overload
def run_test( def run_test(
hf_runner: Type[HfRunner], hf_runner: Type[HfRunner],
vllm_runner: Type[VllmRunner], vllm_runner: Type[VllmRunner],
...@@ -64,6 +68,78 @@ def run_test( ...@@ -64,6 +68,78 @@ def run_test(
num_logprobs: int, num_logprobs: int,
tensor_parallel_size: int, tensor_parallel_size: int,
distributed_executor_backend: Optional[str] = None, distributed_executor_backend: Optional[str] = None,
):
...
@overload
def run_test(
hf_runner: Type[HfRunner],
vllm_runner: Type[VllmRunner],
image_assets: _ImageAssets,
model: str,
*,
sizes: List[Tuple[int, int]],
dtype: str,
max_tokens: int,
num_logprobs: int,
tensor_parallel_size: int,
distributed_executor_backend: Optional[str] = None,
):
...
def run_test(
hf_runner: Type[HfRunner],
vllm_runner: Type[VllmRunner],
image_assets: _ImageAssets,
model: str,
*,
size_factors: Optional[List[float]] = None,
sizes: Optional[List[Tuple[int, int]]] = None,
dtype: str,
max_tokens: int,
num_logprobs: int,
tensor_parallel_size: int,
distributed_executor_backend: Optional[str] = None,
):
images = [asset.pil_image for asset in image_assets]
if size_factors is not None:
inputs_per_image = [(
[prompt for _ in size_factors],
[rescale_image_size(image, factor) for factor in size_factors],
) for image, prompt in zip(images, HF_IMAGE_PROMPTS)]
elif sizes is not None:
inputs_per_image = [(
[prompt for _ in sizes],
[image.resize(size) for size in sizes],
) for image, prompt in zip(images, HF_IMAGE_PROMPTS)]
else:
raise ValueError("You must provide either `size_factors` or `sizes`")
_run_test(hf_runner,
vllm_runner,
inputs_per_image,
model,
dtype=dtype,
max_tokens=max_tokens,
num_logprobs=num_logprobs,
tensor_parallel_size=tensor_parallel_size,
distributed_executor_backend=distributed_executor_backend)
def _run_test(
hf_runner: Type[HfRunner],
vllm_runner: Type[VllmRunner],
inputs: List[Tuple[List[str], PromptImageInput]],
model: str,
*,
dtype: str,
max_tokens: int,
num_logprobs: int,
tensor_parallel_size: int,
distributed_executor_backend: Optional[str] = None,
): ):
"""Inference result should be the same between hf and vllm. """Inference result should be the same between hf and vllm.
...@@ -85,13 +161,6 @@ def run_test( ...@@ -85,13 +161,6 @@ def run_test(
else: else:
mantis_processor = None mantis_processor = None
images = [asset.pil_image for asset in image_assets]
inputs_per_image = [(
[prompt for _ in size_factors],
[rescale_image_size(image, factor) for factor in size_factors],
) for image, prompt in zip(images, HF_IMAGE_PROMPTS)]
# NOTE: take care of the order. run vLLM first, and then run HF. # NOTE: take care of the order. run vLLM first, and then run HF.
# vLLM needs a fresh new process without cuda initialization. # vLLM needs a fresh new process without cuda initialization.
# if we run HF first, the cuda initialization will be done and it # if we run HF first, the cuda initialization will be done and it
...@@ -100,15 +169,18 @@ def run_test( ...@@ -100,15 +169,18 @@ def run_test(
# max_model_len should be greater than image_feature_size # max_model_len should be greater than image_feature_size
with vllm_runner(model, with vllm_runner(model,
dtype=dtype, dtype=dtype,
max_model_len=4096,
tensor_parallel_size=tensor_parallel_size, tensor_parallel_size=tensor_parallel_size,
distributed_executor_backend=distributed_executor_backend, distributed_executor_backend=distributed_executor_backend,
enforce_eager=True) as vllm_model: enforce_eager=True,
limit_mm_per_prompt={"image": _LIMIT_IMAGE_PER_PROMPT
}) as vllm_model:
vllm_outputs_per_image = [ vllm_outputs_per_image = [
vllm_model.generate_greedy_logprobs(prompts, vllm_model.generate_greedy_logprobs(prompts,
max_tokens, max_tokens,
num_logprobs=num_logprobs, num_logprobs=num_logprobs,
images=images) images=images)
for prompts, images in inputs_per_image for prompts, images in inputs
] ]
if mantis_processor is not None: if mantis_processor is not None:
...@@ -131,7 +203,7 @@ def run_test( ...@@ -131,7 +203,7 @@ def run_test(
max_tokens, max_tokens,
num_logprobs=num_logprobs, num_logprobs=num_logprobs,
images=images) images=images)
for prompts, images in inputs_per_image for prompts, images in inputs
] ]
for hf_outputs, vllm_outputs in zip(hf_outputs_per_image, for hf_outputs, vllm_outputs in zip(hf_outputs_per_image,
...@@ -181,6 +253,51 @@ def test_models(hf_runner, vllm_runner, image_assets, model, size_factors, ...@@ -181,6 +253,51 @@ def test_models(hf_runner, vllm_runner, image_assets, model, size_factors,
) )
@pytest.mark.parametrize("model", models)
@pytest.mark.parametrize("dtype", ["half"])
@pytest.mark.parametrize("max_tokens", [128])
@pytest.mark.parametrize("num_logprobs", [5])
def test_models_multiple_image_inputs(hf_runner, vllm_runner, image_assets,
model, dtype, max_tokens,
num_logprobs) -> None:
stop_sign = image_assets[0].pil_image
cherry_blossom = image_assets[1].pil_image
inputs = [(
[
"USER: <image><image>\nDescribe 2 images.\nASSISTANT:",
"USER: <image><image>\nDescribe 2 images.\nASSISTANT:",
"USER: <image><image><image><image>\nDescribe 4 images.\nASSISTANT:", # noqa: E501
"USER: <image>\nWhat is the season?\nASSISTANT:",
],
[
[stop_sign, cherry_blossom],
# Images with different sizes and aspect-ratios
[
rescale_image_size(stop_sign, 0.1),
stop_sign,
],
[
stop_sign,
rescale_image_size(stop_sign, 0.25),
cherry_blossom.resize((183, 488)),
cherry_blossom.resize((488, 183))
],
cherry_blossom,
])]
_run_test(
hf_runner,
vllm_runner,
inputs,
model,
dtype=dtype,
max_tokens=max_tokens,
num_logprobs=num_logprobs,
tensor_parallel_size=1,
)
@pytest.mark.parametrize("model", models) @pytest.mark.parametrize("model", models)
def test_context_length_too_short(vllm_runner, image_assets, model): def test_context_length_too_short(vllm_runner, image_assets, model):
images = [asset.pil_image for asset in image_assets] images = [asset.pil_image for asset in image_assets]
......
from typing import List, Optional, Tuple, Type, overload
import pytest
import transformers
from transformers import AutoConfig, AutoModelForVision2Seq, AutoTokenizer
from vllm.multimodal.utils import (rescale_video_size, resize_video,
sample_frames_from_video)
from vllm.sequence import SampleLogprobs
from ..conftest import VIDEO_ASSETS, HfRunner, VllmRunner, _VideoAssets
from .utils import check_logprobs_close
pytestmark = pytest.mark.vlm
_PREFACE = (
"A chat between a curious human and an artificial intelligence assistant. "
"The assistant gives helpful, detailed, and polite answers to the human's "
"questions.")
HF_VIDEO_PROMPTS = VIDEO_ASSETS.prompts({
"sample_demo_1":
f"{_PREFACE}USER: <video>\nWhy is this video funny? ASSISTANT:"
})
models = ["llava-hf/LLaVA-NeXT-Video-7B-hf"]
def vllm_to_hf_output(vllm_output: Tuple[List[int], str,
Optional[SampleLogprobs]],
model: str):
"""Sanitize vllm output to be comparable with hf output."""
output_ids, output_str, out_logprobs = vllm_output
config = AutoConfig.from_pretrained(model)
video_token_id = config.video_token_index
tokenizer = AutoTokenizer.from_pretrained(model)
eos_token_id = tokenizer.eos_token_id
hf_output_ids = [
token_id for idx, token_id in enumerate(output_ids)
if token_id != video_token_id or output_ids[idx - 1] != video_token_id
]
assert output_str[0] == " "
hf_output_str = output_str[1:]
if hf_output_ids[-1] == eos_token_id:
hf_output_str = hf_output_str + tokenizer.decode(eos_token_id)
return hf_output_ids, hf_output_str, out_logprobs
@overload
def run_test(
hf_runner: Type[HfRunner],
vllm_runner: Type[VllmRunner],
video_assets: _VideoAssets,
model: str,
*,
size_factors: List[float],
dtype: str,
max_tokens: int,
num_logprobs: int,
num_frames: int,
tensor_parallel_size: int,
distributed_executor_backend: Optional[str] = None,
):
...
@overload
def run_test(
hf_runner: Type[HfRunner],
vllm_runner: Type[VllmRunner],
video_assets: _VideoAssets,
model: str,
*,
sizes: List[Tuple[int, int]],
dtype: str,
max_tokens: int,
num_logprobs: int,
num_frames: int,
tensor_parallel_size: int,
distributed_executor_backend: Optional[str] = None,
):
...
def run_test(
hf_runner: Type[HfRunner],
vllm_runner: Type[VllmRunner],
video_assets: _VideoAssets,
model: str,
*,
size_factors: Optional[List[float]] = None,
sizes: Optional[List[Tuple[int, int]]] = None,
dtype: str,
max_tokens: int,
num_logprobs: int,
num_frames: int,
tensor_parallel_size: int,
distributed_executor_backend: Optional[str] = None,
):
videos = [
sample_frames_from_video(asset.np_ndarrays, num_frames)
for asset in video_assets
]
for video in videos:
print(video.shape)
if size_factors is not None:
inputs_per_video = [(
[prompt for _ in size_factors],
[rescale_video_size(video, factor) for factor in size_factors],
) for video, prompt in zip(videos, HF_VIDEO_PROMPTS)]
elif sizes is not None:
inputs_per_video = [(
[prompt for _ in sizes],
[resize_video(video, size) for size in sizes],
) for video, prompt in zip(videos, HF_VIDEO_PROMPTS)]
else:
raise ValueError("You must provide either `size_factors` or `sizes`")
# max_model_len should be greater than image_feature_size
with vllm_runner(model,
dtype=dtype,
max_model_len=4096,
tensor_parallel_size=tensor_parallel_size,
distributed_executor_backend=distributed_executor_backend,
enforce_eager=True) as vllm_model:
vllm_outputs_per_video = [
vllm_model.generate_greedy_logprobs(prompts,
max_tokens,
num_logprobs=num_logprobs,
videos=videos)
for prompts, videos in inputs_per_video
]
with hf_runner(model, dtype=dtype,
auto_cls=AutoModelForVision2Seq) as hf_model:
hf_outputs_per_video = [
hf_model.generate_greedy_logprobs_limit(prompts,
max_tokens,
num_logprobs=num_logprobs,
videos=videos)
for prompts, videos in inputs_per_video
]
for hf_outputs, vllm_outputs in zip(hf_outputs_per_video,
vllm_outputs_per_video):
# TODO: Check whether using original CLIPVisionModel can improve
# consistency against HF
check_logprobs_close(
outputs_0_lst=hf_outputs,
outputs_1_lst=[
vllm_to_hf_output(vllm_output, model)
for vllm_output in vllm_outputs
],
name_0="hf",
name_1="vllm",
)
@pytest.mark.skipif(transformers.__version__ < "4.45",
reason="Waiting for next transformers release")
@pytest.mark.parametrize("model", models)
@pytest.mark.parametrize(
"size_factors",
[
# No video
[],
# Single-scale
[1.0],
# Single-scale, batched
[1.0, 1.0, 1.0],
# Multi-scale
[0.25, 0.5, 1.0],
],
)
@pytest.mark.parametrize("dtype", ["half"])
@pytest.mark.parametrize("max_tokens", [128])
@pytest.mark.parametrize("num_logprobs", [5])
@pytest.mark.parametrize("num_frames", [16])
def test_models(hf_runner, vllm_runner, video_assets, model, size_factors,
dtype, max_tokens, num_logprobs, num_frames) -> None:
"""Inference result should be the same between hf and vllm.
All the image fixtures for the test is under tests/videos.
For huggingface runner, we provide the np.ndarray as input.
For vllm runner, we provide MultiModalDataDict objects
and corresponding MultiModalConfig as input.
Note, the text input is also adjusted to abide by vllm contract.
The text output is sanitized to be able to compare with hf.
"""
run_test(
hf_runner,
vllm_runner,
video_assets,
model,
size_factors=size_factors,
dtype=dtype,
max_tokens=max_tokens,
num_logprobs=num_logprobs,
num_frames=num_frames,
tensor_parallel_size=1,
)
@pytest.mark.skipif(transformers.__version__ < "4.45",
reason="Waiting for next transformers release")
@pytest.mark.parametrize("model", models)
@pytest.mark.parametrize(
"sizes",
[[(1669, 2560), (2560, 1669), (183, 488), (488, 183)]],
)
@pytest.mark.parametrize("dtype", ["half"])
@pytest.mark.parametrize("max_tokens", [128])
@pytest.mark.parametrize("num_logprobs", [5])
@pytest.mark.parametrize("num_frames", [16])
def test_models_fixed_sizes(hf_runner, vllm_runner, video_assets, model, sizes,
dtype, max_tokens, num_logprobs,
num_frames) -> None:
run_test(
hf_runner,
vllm_runner,
video_assets,
model,
sizes=sizes,
dtype=dtype,
max_tokens=max_tokens,
num_logprobs=num_logprobs,
num_frames=num_frames,
tensor_parallel_size=1,
)
...@@ -41,3 +41,43 @@ def test_models( ...@@ -41,3 +41,43 @@ def test_models(
name_0="hf", name_0="hf",
name_1="vllm", name_1="vllm",
) )
@pytest.mark.parametrize("model", MODELS[1:])
@pytest.mark.parametrize("dtype", ["bfloat16"])
@pytest.mark.parametrize("max_tokens", [64])
@pytest.mark.parametrize("num_logprobs", [5])
def test_mistral_format(
vllm_runner,
example_prompts,
model: str,
dtype: str,
max_tokens: int,
num_logprobs: int,
) -> None:
with vllm_runner(
model,
dtype=dtype,
tokenizer_mode="auto",
load_format="safetensors",
config_format="hf",
) as hf_format_model:
hf_format_outputs = hf_format_model.generate_greedy_logprobs(
example_prompts, max_tokens, num_logprobs)
with vllm_runner(
model,
dtype=dtype,
tokenizer_mode="mistral",
load_format="mistral",
config_format="mistral",
) as mistral_format_model:
mistral_format_outputs = mistral_format_model.generate_greedy_logprobs(
example_prompts, max_tokens, num_logprobs)
check_logprobs_close(
outputs_0_lst=hf_format_outputs,
outputs_1_lst=mistral_format_outputs,
name_0="hf",
name_1="mistral",
)
# flake8: noqa
"""Tests Model Optimizer fp8 models against ground truth generation
Note: these tests will only pass on H100
"""
import os
from typing import List
import pytest
from transformers import AutoTokenizer
from tests.quantization.utils import is_quant_method_supported
from vllm import LLM, SamplingParams
os.environ["TOKENIZERS_PARALLELISM"] = "true"
MAX_MODEL_LEN = 1024
MODELS = ["nvidia/Llama-3.1-8B-Instruct-FP8"]
EXPECTED_STRS_MAP = {
"nvidia/Llama-3.1-8B-Instruct-FP8": [
"You're referring to VLLM, a high-performance Large Language Model (LLM) inference and",
'Here are the major milestones in the development of artificial intelligence (AI) from 1950 to ',
'The comparison between artificial intelligence (AI) and human intelligence in terms of processing information is a complex and',
'A neural network is a complex system modeled after the human brain, consisting of interconnected nodes or "ne',
'**The Spark of Imagination**\n\nZeta-5, a sleek and efficient robot, whir',
'The COVID-19 pandemic has had a profound impact on global economic structures and business models, leading to',
'The Mona Lisa, painted by Leonardo da Vinci in the early 16th century, is one of',
'Here are the translations:\n\n**Japanese:** 「早起きは早く獲物をとる'
]
}
# This test compares against golden strings for exact match since
# there is no baseline implementation to compare against
# and is unstable w.r.t specifics of the fp8 implementation or
# the hardware being run on.
# Disabled to prevent it from breaking the build
@pytest.mark.skip(
reason=
"Prevent unstable test based on golden strings from breaking the build.")
@pytest.mark.skipif(not is_quant_method_supported("fp8"),
reason="fp8 is not supported on this GPU type.")
@pytest.mark.parametrize("model_name", MODELS)
def test_models(example_prompts, model_name) -> None:
model = LLM(
model=model_name,
max_model_len=MAX_MODEL_LEN,
trust_remote_code=True,
enforce_eager=True,
quantization="modelopt",
)
tokenizer = AutoTokenizer.from_pretrained(model_name)
formatted_prompts = [
tokenizer.apply_chat_template([{
"role": "user",
"content": prompt
}],
tokenize=False,
add_generation_prompt=True)
for prompt in example_prompts
]
params = SamplingParams(max_tokens=20, temperature=0)
generations: List[str] = []
# Note: these need to be run 1 at a time due to numerical precision,
# since the expected strs were generated this way.
for prompt in formatted_prompts:
outputs = model.generate(prompt, params)
generations.append(outputs[0].outputs[0].text)
del model
print(model_name, generations)
expected_strs = EXPECTED_STRS_MAP[model_name]
for i in range(len(example_prompts)):
generated_str = generations[i]
expected_str = expected_strs[i]
assert expected_str == generated_str, (
f"Test{i}:\nExpected: {expected_str!r}\nvLLM: {generated_str!r}")
import os import os
import re import re
from typing import List, Optional, Tuple, Type, Union from typing import List, Optional, Tuple, Type
import pytest import pytest
from PIL import Image
from transformers import AutoTokenizer from transformers import AutoTokenizer
from vllm.multimodal.utils import rescale_image_size from vllm.multimodal.utils import rescale_image_size
from vllm.sequence import SampleLogprobs from vllm.sequence import SampleLogprobs
from vllm.utils import is_cpu, is_hip from vllm.utils import is_cpu, is_hip
from ..conftest import IMAGE_ASSETS, HfRunner, VllmRunner from ..conftest import IMAGE_ASSETS, HfRunner, PromptImageInput, VllmRunner
from .utils import check_logprobs_close from .utils import check_logprobs_close
pytestmark = pytest.mark.vlm pytestmark = pytest.mark.vlm
...@@ -60,8 +59,7 @@ if is_hip(): ...@@ -60,8 +59,7 @@ if is_hip():
def run_test( def run_test(
hf_runner: Type[HfRunner], hf_runner: Type[HfRunner],
vllm_runner: Type[VllmRunner], vllm_runner: Type[VllmRunner],
inputs: List[Tuple[List[str], Union[List[Image.Image], inputs: List[Tuple[List[str], PromptImageInput]],
List[List[Image.Image]]]]],
model: str, model: str,
*, *,
dtype: str, dtype: str,
......
"""Compare the outputs of HF and vLLM for Mistral models using greedy sampling.
Run `pytest tests/models/test_mistral.py`.
"""
import pytest
from vllm.sampling_params import SamplingParams
pytestmark = pytest.mark.vlm
MODELS = ["mistralai/Pixtral-12B-2409"]
@pytest.mark.skip(
reason=
"Model is too big, test passed on A100 locally but will OOM on CI machine."
)
@pytest.mark.parametrize("model", MODELS)
@pytest.mark.parametrize("dtype", ["bfloat16"])
@pytest.mark.parametrize("max_tokens", [64])
@pytest.mark.parametrize("num_logprobs", [5])
def test_models(
vllm_runner,
example_prompts,
model: str,
dtype: str,
max_tokens: int,
num_logprobs: int,
) -> None:
image_urls = [
"https://picsum.photos/id/237/200/300",
"https://picsum.photos/seed/picsum/200/300"
]
expected = [
"The image depicts a black dog lying on a wooden surface, looking directly at the camera with a calm expression.", # noqa
"The image depicts a serene landscape with a snow-covered mountain under a pastel-colored sky during sunset." # noqa
]
prompt = "Describe the image in one short sentence."
sampling_params = SamplingParams(max_tokens=512, temperature=0.0)
with vllm_runner(model, dtype=dtype,
tokenizer_mode="mistral") as vllm_model:
for i, image_url in enumerate(image_urls):
messages = [
{
"role":
"user",
"content": [{
"type": "text",
"text": prompt
}, {
"type": "image_url",
"image_url": {
"url": image_url
}
}]
},
]
outputs = vllm_model.model.chat(messages,
sampling_params=sampling_params)
assert outputs[0].outputs[0].text == expected[i]
from typing import Type import pathlib
from typing import List, Optional, Type
import pytest import pytest
from ..conftest import HfRunner, VllmRunner from vllm.multimodal.utils import rescale_image_size
from ..conftest import IMAGE_ASSETS, HfRunner, VllmRunner, _ImageAssets
from .utils import check_logprobs_close from .utils import check_logprobs_close
models = ["qwen/qwen-vl"] pytestmark = pytest.mark.vlm
text_only_models = [
"Qwen/Qwen-7B-Chat" # Has no visual component
]
@pytest.mark.parametrize("dtype", ["half"]) multimodal_models = ["Qwen/Qwen-VL"]
@pytest.mark.parametrize("max_tokens", [32])
@pytest.mark.parametrize("num_logprobs", [5]) HF_IMAGE_PROMPTS = IMAGE_ASSETS.prompts({
@pytest.mark.parametrize("model", models) "stop_sign":
def test_text_only_qwen_model( "Picture 1: <img></img>\nWhat's the content of the image?: ",
"cherry_blossom":
"Picture 1: <img></img>\nWhat is the season?: ",
})
### Tests for multimodal Qwen models
def run_test(
tmp_path: pathlib.PosixPath,
hf_runner: Type[HfRunner], hf_runner: Type[HfRunner],
vllm_runner: Type[VllmRunner], vllm_runner: Type[VllmRunner],
example_prompts, image_assets: _ImageAssets,
model: str, model: str,
*, *,
size_factors: List[float],
dtype: str, dtype: str,
max_tokens: int, max_tokens: int,
num_logprobs: int, num_logprobs: int,
tensor_parallel_size: int,
distributed_executor_backend: Optional[str] = None,
): ):
# This test checks language inputs only, since the visual component """Inference result should be the same between hf and vllm.
# for qwen-vl is still unsupported in VLLM. In the near-future, the
# implementation and this test will be extended to consider All the image fixtures for the test is under tests/images.
# visual inputs as well. For huggingface runner, we provide the PIL images as input.
For vllm runner, we provide MultiModalDataDict objects
and corresponding MultiModalConfig as input.
Note, the text input is also adjusted to abide by vllm contract.
The text output is sanitized to be able to compare with hf.
"""
images = [asset.pil_image for asset in image_assets]
# Export the images to a tempdir and substitute it into the hf prompt;
# the contents between <img>/</img> will be ignored by VLLM, but the
# transformers implementation for the visual transformer parses this to
# reload it in the forward call; the contents are treated as a URL or a
# local path.
for idx, asset in enumerate(image_assets):
image_tmp_path = tmp_path / f"{asset.name}.jpg"
asset.pil_image.save(image_tmp_path)
HF_IMAGE_PROMPTS[idx] = HF_IMAGE_PROMPTS[idx].replace(
"<img></img>", f"<img>{image_tmp_path}</img>")
inputs_per_image = [(
[prompt for _ in size_factors],
[rescale_image_size(image, factor) for factor in size_factors],
) for image, prompt in zip(images, HF_IMAGE_PROMPTS)]
# NOTE: take care of the order. run vLLM first, and then run HF.
# vLLM needs a fresh new process without cuda initialization.
# if we run HF first, the cuda initialization will be done and it
# will hurt multiprocessing backend with fork method (the default method).
# max_model_len should be greater than image_feature_size
# Qwen encodes images into a fixed content size of 256
with vllm_runner(model,
max_model_len=300,
max_num_seqs=1,
dtype=dtype,
tensor_parallel_size=tensor_parallel_size,
distributed_executor_backend=distributed_executor_backend,
enforce_eager=True) as vllm_model:
vllm_outputs_per_image = [
vllm_model.generate_greedy_logprobs(prompts,
max_tokens,
num_logprobs=num_logprobs,
images=images)
for prompts, images in inputs_per_image
]
with hf_runner(model, dtype=dtype) as hf_model: with hf_runner(model, dtype=dtype) as hf_model:
hf_outputs = hf_model.generate_greedy_logprobs_limit( hf_outputs_per_image = [
example_prompts, hf_model.generate_greedy_logprobs_limit(prompts,
max_tokens, max_tokens,
num_logprobs=num_logprobs, num_logprobs=num_logprobs,
images=images)
for prompts, images in inputs_per_image
]
for hf_outputs, vllm_outputs in zip(hf_outputs_per_image,
vllm_outputs_per_image):
check_logprobs_close(
outputs_0_lst=hf_outputs,
outputs_1_lst=vllm_outputs,
name_0="hf",
name_1="vllm",
) )
@pytest.mark.parametrize("model", multimodal_models)
@pytest.mark.parametrize(
"size_factors",
[
# No image
[],
# Single-scale
[1.0],
# Single-scale, batched
[1.0, 1.0, 1.0],
# Multi-scale
[0.25, 0.5, 1.0],
],
)
@pytest.mark.parametrize("dtype", ["bfloat16"])
@pytest.mark.parametrize("max_tokens", [8])
@pytest.mark.parametrize("num_logprobs", [5])
def test_multimodal_models(tmp_path, hf_runner, vllm_runner, image_assets,
model, size_factors, dtype, max_tokens,
num_logprobs) -> None:
run_test(
tmp_path,
hf_runner,
vllm_runner,
image_assets,
model,
size_factors=size_factors,
dtype=dtype,
max_tokens=max_tokens,
num_logprobs=num_logprobs,
tensor_parallel_size=1,
)
# Ensure that a text-only Qwen model can still be loaded and
# used for inference in VLLM without throwing.
@pytest.mark.parametrize("model", text_only_models)
@pytest.mark.parametrize("dtype", ["bfloat16"])
@pytest.mark.parametrize("max_tokens", [32])
@pytest.mark.parametrize("num_logprobs", [5])
def test_text_only_qwen_model_can_be_loaded_and_run(
vllm_runner: Type[VllmRunner],
example_prompts,
model: str,
*,
dtype: str,
max_tokens: int,
num_logprobs: int,
):
with vllm_runner(model, dtype=dtype) as vllm_model: with vllm_runner(model, dtype=dtype) as vllm_model:
vllm_outputs = vllm_model.generate_greedy_logprobs( vllm_model.generate_greedy_logprobs(
example_prompts, example_prompts,
max_tokens, max_tokens,
num_logprobs=num_logprobs, num_logprobs=num_logprobs,
) )
check_logprobs_close(
outputs_0_lst=hf_outputs,
outputs_1_lst=vllm_outputs,
name_0="hf",
name_1="vllm",
)
import pytest import pytest
import transformers
from vllm.model_executor.models import _MODELS, ModelRegistry from vllm.model_executor.models import _MODELS, ModelRegistry
@pytest.mark.parametrize("model_cls", _MODELS) @pytest.mark.parametrize("model_cls", _MODELS)
def test_registry_imports(model_cls): def test_registry_imports(model_cls):
if (model_cls == "Qwen2VLForConditionalGeneration"
and transformers.__version__ < "4.45"):
pytest.skip("Waiting for next transformers release")
# Ensure all model classes can be imported successfully # Ensure all model classes can be imported successfully
ModelRegistry.resolve_model_cls([model_cls]) ModelRegistry.resolve_model_cls([model_cls])
...@@ -57,7 +57,7 @@ def test_multi_step_llm( ...@@ -57,7 +57,7 @@ def test_multi_step_llm(
GPU -> CPU output transfer GPU -> CPU output transfer
num_prompts: number of example prompts under test num_prompts: number of example prompts under test
num_logprobs: corresponds to the `logprobs` argument to the OpenAI num_logprobs: corresponds to the `logprobs` argument to the OpenAI
completions endpoint; `None` -> no logprobs completions endpoint; `None` -> 1 logprob returned.
""" """
prompts = example_prompts prompts = example_prompts
......
...@@ -56,7 +56,7 @@ def test_compressed_tensors_w8a8_static_setup(vllm_runner, model_args): ...@@ -56,7 +56,7 @@ def test_compressed_tensors_w8a8_static_setup(vllm_runner, model_args):
assert qkv_proj.weight_scale.dtype is torch.float32 assert qkv_proj.weight_scale.dtype is torch.float32
assert qkv_proj.input_scale.dtype is torch.float32 assert qkv_proj.input_scale.dtype is torch.float32
output = llm.generate_greedy("Hello my name is", max_tokens=20) output = llm.generate_greedy(["Hello my name is"], max_tokens=20)
assert output assert output
...@@ -85,7 +85,7 @@ def test_compressed_tensors_w8a8_dynanmic_per_token(vllm_runner, model_args): ...@@ -85,7 +85,7 @@ def test_compressed_tensors_w8a8_dynanmic_per_token(vllm_runner, model_args):
assert qkv_proj.scheme.strategy == strategy assert qkv_proj.scheme.strategy == strategy
assert qkv_proj.weight.dtype is torch.int8 assert qkv_proj.weight.dtype is torch.int8
output = llm.generate_greedy("Hello my name is", max_tokens=20) output = llm.generate_greedy(["Hello my name is"], max_tokens=20)
assert output assert output
......
import asyncio
import os
from itertools import cycle from itertools import cycle
from typing import Dict, List, Optional, Sequence, Tuple, Union from typing import List, Optional, Tuple
import pytest import pytest
import ray
import torch
from vllm import LLM from vllm import LLM, SamplingParams
from vllm.engine.arg_utils import AsyncEngineArgs
from vllm.engine.async_llm_engine import AsyncLLMEngine
from vllm.lora.request import LoRARequest
from vllm.model_executor.utils import set_random_seed from vllm.model_executor.utils import set_random_seed
from vllm.multimodal import MultiModalDataDict
from vllm.outputs import RequestOutput
from vllm.prompt_adapter.request import PromptAdapterRequest
from vllm.sampling_params import SamplingParams
from vllm.sequence import Logprob
from vllm.usage.usage_lib import UsageContext
from vllm.utils import Counter, random_uuid
from ...conftest import cleanup from ...conftest import cleanup
from ...utils import wait_for_gpu_memory_to_clear from ...models.utils import check_logprobs_close, check_outputs_equal
from ...utils import RemoteOpenAIServer
PROMPTS = [
class AsyncLLM: "Hello, my name is",
"""AsyncLLM "The president of the United States is",
"The capital of France is",
Note: Current LLM class in vllm don't support async mode, for test purpose, "The future of AI is",
we implement async one in here. Maybe we could move to "San Francisco is know for its",
vllm/entrypoints/llm.py in future. "Facebook was created in 2004 by",
"Curious George is a",
Below AsyncLLM is directly borrow from vllm/entrypoints/llm.py with changes "Python 3.11 brings improvements to its",
to make to work in async mode. ]
"""
def __init__(
self,
model: str,
tokenizer: Optional[str] = None,
tokenizer_mode: str = "auto",
skip_tokenizer_init: bool = False,
trust_remote_code: bool = False,
tensor_parallel_size: int = 1,
dtype: str = "auto",
quantization: Optional[str] = None,
revision: Optional[str] = None,
tokenizer_revision: Optional[str] = None,
seed: int = 0,
gpu_memory_utilization: float = 0.9,
swap_space: int = 4,
enforce_eager: bool = False,
max_seq_len_to_capture: int = 8192,
disable_custom_all_reduce: bool = False,
**kwargs,
) -> None:
if "disable_log_stats" not in kwargs:
kwargs["disable_log_stats"] = True
# Needed to engine_use_ray works as a deprecated feature,
# otherwise the following constructor will raise an exception
os.environ["VLLM_ALLOW_ENGINE_USE_RAY"] = "1"
engine_args = AsyncEngineArgs(
model=model,
tokenizer=tokenizer,
tokenizer_mode=tokenizer_mode,
skip_tokenizer_init=skip_tokenizer_init,
trust_remote_code=trust_remote_code,
tensor_parallel_size=tensor_parallel_size,
dtype=dtype,
quantization=quantization,
revision=revision,
tokenizer_revision=tokenizer_revision,
seed=seed,
gpu_memory_utilization=gpu_memory_utilization,
swap_space=swap_space,
enforce_eager=enforce_eager,
max_seq_len_to_capture=max_seq_len_to_capture,
# For now use ray for the distributed back-end, since
# we rely on the use of engine_use_ray=True to avoid
# reinitializing CUDA in the same process (driver worker)
engine_use_ray=True,
distributed_executor_backend="ray",
disable_custom_all_reduce=disable_custom_all_reduce,
**kwargs,
)
self.request_counter = Counter()
self.llm_engine = AsyncLLMEngine.from_engine_args(
engine_args, usage_context=UsageContext.LLM_CLASS)
def generate(
self,
prompts: Optional[Union[str, List[str]]] = None,
sampling_params: Optional[Union[SamplingParams,
List[SamplingParams]]] = None,
prompt_token_ids: Optional[List[List[int]]] = None,
use_tqdm: bool = True,
lora_request: Optional[LoRARequest] = None,
multi_modal_data: Optional[MultiModalDataDict] = None,
prompt_adapter_request: Optional[PromptAdapterRequest] = None
) -> List[RequestOutput]:
if prompts is None:
raise ValueError("prompts must be provided.")
if isinstance(prompts, str):
# Convert a single prompt to a list.
prompts = [prompts]
if prompts is not None:
num_requests = len(prompts)
if sampling_params is None:
# Use default sampling params.
sampling_params = SamplingParams()
elif isinstance(sampling_params,
list) and len(sampling_params) != num_requests:
raise ValueError("The lengths of prompts and "
"sampling_params must be the same.")
async def get_output(prompt, sampling_param) -> RequestOutput:
request_id = random_uuid()
results_generator = self.llm_engine.generate(
prompt, sampling_param, request_id)
final_output = None
async for request_output in results_generator:
final_output = request_output
assert final_output is not None
return final_output
outputs: List[RequestOutput] = []
try:
for i in range(num_requests):
prompt = prompts[i] if prompts is not None else None
params = sampling_params[i] if isinstance(
sampling_params, Sequence) else sampling_params
res = asyncio.run(get_output(prompt, params))
outputs.append(res)
finally:
ray.shutdown()
return outputs
@pytest.fixture @pytest.fixture
def baseline_llm_generator(request, common_llm_kwargs, def test_llm_generator(common_llm_kwargs, per_test_common_llm_kwargs,
per_test_common_llm_kwargs, baseline_llm_kwargs,
seed):
return create_llm_generator("baseline", request, common_llm_kwargs,
per_test_common_llm_kwargs,
baseline_llm_kwargs, seed)
@pytest.fixture
def test_llm_generator(request, common_llm_kwargs, per_test_common_llm_kwargs,
test_llm_kwargs, seed): test_llm_kwargs, seed):
return create_llm_generator("test", request, common_llm_kwargs,
per_test_common_llm_kwargs, test_llm_kwargs,
seed)
def generate():
kwargs = {
**common_llm_kwargs,
**per_test_common_llm_kwargs,
**test_llm_kwargs,
}
llm = LLM(**kwargs)
def create_llm_generator(baseline_or_test, request, common_llm_kwargs,
per_test_common_llm_kwargs, distinct_llm_kwargs,
seed):
kwargs = {
**common_llm_kwargs,
**per_test_common_llm_kwargs,
**distinct_llm_kwargs,
}
test_name = request.node.name
model = kwargs["model"]
draft_model = kwargs.get("speculative_model", None)
same_draft_target_model = (draft_model is not None
and draft_model == model)
def generator_inner():
wait_for_gpu_memory_to_clear(
devices=list(range(torch.cuda.device_count())),
threshold_bytes=2 * 2**30,
timeout_s=60,
)
use_async = False
if "use_async" in kwargs:
use_async = kwargs.pop("use_async")
print(f'{use_async=}')
print(f'Creating {baseline_or_test=} LLM for {test_name=}. {kwargs=}')
llm = AsyncLLM(**kwargs) if use_async else LLM(**kwargs)
# Override logging interval to 0 for spec decode test run to
# log all metrics in time.
if (baseline_or_test == "test" and not use_async
and llm.llm_engine.log_stats):
for sate_logger in llm.llm_engine.stat_loggers.values():
sate_logger.local_interval = 0
if seed is not None: if seed is not None:
set_random_seed(seed) set_random_seed(seed)
yield llm yield llm
del llm del llm
cleanup() cleanup()
def generator_outer(): return generate
for llm in generator_inner():
yield llm
del llm
# Set an attribute to the generator_outer function to allow us to
# determine whether to further check the acceptance rate in tests.
generator_outer.same_draft_target_model = same_draft_target_model # type: ignore
return generator_outer
def maybe_assert_ngram_worker(llm): def maybe_assert_ngram_worker(llm):
# Verify the proposer worker is ngram if ngram is specified. # Verify the proposer worker is ngram if ngram is specified.
if (not isinstance(llm, AsyncLLM) if (llm.llm_engine.speculative_config is not None
and llm.llm_engine.speculative_config is not None
and llm.llm_engine.speculative_config.ngram_prompt_lookup_max > 0): and llm.llm_engine.speculative_config.ngram_prompt_lookup_max > 0):
from vllm.spec_decode.ngram_worker import NGramWorker from vllm.spec_decode.ngram_worker import NGramWorker
assert isinstance( assert isinstance(
...@@ -251,118 +81,165 @@ def get_output_from_llm_generator( ...@@ -251,118 +81,165 @@ def get_output_from_llm_generator(
return tokens, token_ids, acceptance_rate return tokens, token_ids, acceptance_rate
def get_logprobs_from_llm_generator( def run_logprob_correctness_test(vllm_runner,
llm_generator, prompts, common_llm_kwargs,
sampling_params) -> List[List[Dict[int, Logprob]]]: per_test_common_llm_kwargs,
"""Returns a dict of (token_id: Logprob) for each generated position, for baseline_llm_kwargs,
each sequence in the batch. test_llm_kwargs,
""" batch_size: int,
for llm in llm_generator(): max_output_len: int,
outputs = llm.generate(prompts, sampling_params, use_tqdm=True) seed: Optional[int] = 0,
logprobs = [output.outputs[0].logprobs[:] for output in outputs] temperature: float = 0.0,
del llm logprobs: int = 1):
org_args = {
**common_llm_kwargs,
**per_test_common_llm_kwargs,
**baseline_llm_kwargs,
}
return logprobs sd_args = {
**common_llm_kwargs,
**per_test_common_llm_kwargs,
**test_llm_kwargs,
}
prompts = [prompt for prompt, _ in zip(cycle(PROMPTS), range(batch_size))]
def run_greedy_equality_correctness_test(baseline_llm_generator, sampling_params = SamplingParams(temperature=temperature,
test_llm_generator, max_tokens=max_output_len,
batch_size, seed=seed,
max_output_len, logprobs=logprobs)
force_output_len: bool,
print_tokens: bool = False, with vllm_runner(**org_args) as vllm_model:
ensure_all_accepted: bool = False): org_outputs = vllm_model.generate_w_logprobs(prompts, sampling_params)
"""Helper method that compares the outputs of both the baseline LLM and
the test LLM. It asserts greedy equality, e.g. that the outputs are exactly
the same when temperature is zero.
"""
run_equality_correctness_test(baseline_llm_generator, with vllm_runner(**sd_args) as vllm_model:
test_llm_generator, sd_outputs = vllm_model.generate_w_logprobs(prompts, sampling_params)
batch_size,
max_output_len, check_logprobs_close(outputs_0_lst=org_outputs,
force_output_len, outputs_1_lst=sd_outputs,
temperature=0.0, name_0="org",
seeded=False, name_1="sd")
print_tokens=print_tokens,
ensure_all_accepted=ensure_all_accepted)
def run_equality_correctness_test( def run_equality_correctness_test(
baseline_llm_generator, vllm_runner,
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len, baseline_llm_kwargs,
force_output_len: bool, test_llm_kwargs,
temperature: float, batch_size: int,
seeded: bool, max_output_len: int,
print_tokens: bool = False, seed: Optional[int] = 0,
temperature: float = 0.0,
disable_seed: bool = False,
ignore_eos: bool = True,
ensure_all_accepted: bool = False, ensure_all_accepted: bool = False,
expected_acceptance_rate: Optional[float] = None): expected_acceptance_rate: Optional[float] = None):
org_args = {
**common_llm_kwargs,
**per_test_common_llm_kwargs,
**baseline_llm_kwargs,
}
sd_args = {
**common_llm_kwargs,
**per_test_common_llm_kwargs,
**test_llm_kwargs,
}
prompts = [prompt for prompt, _ in zip(cycle(PROMPTS), range(batch_size))]
if disable_seed:
seed = None
sampling_params = SamplingParams(temperature=temperature,
max_tokens=max_output_len,
seed=seed,
ignore_eos=ignore_eos)
with vllm_runner(**org_args) as vllm_model:
org_outputs = vllm_model.generate(prompts, sampling_params)
with vllm_runner(**sd_args) as vllm_model:
if ensure_all_accepted or expected_acceptance_rate is not None:
# Force log interval to be 0 to catch all metrics.
stat_logger = vllm_model.model.llm_engine.stat_loggers[
'prometheus']
stat_logger.local_interval = -100
sd_outputs = vllm_model.generate(prompts, sampling_params)
if ensure_all_accepted or expected_acceptance_rate is not None:
acceptance_rate = (stat_logger.metrics.
gauge_spec_decode_draft_acceptance_rate.labels(
**stat_logger.labels)._value.get())
if ensure_all_accepted:
assert True
# FIXME: ci fails to log acceptance rate.
# It works locally.
# assert acceptance_rate == 1.0
if expected_acceptance_rate is not None:
assert acceptance_rate >= expected_acceptance_rate - 1e-2
check_outputs_equal(outputs_0_lst=org_outputs,
outputs_1_lst=sd_outputs,
name_0="org",
name_1="sd")
def run_equality_correctness_test_tp(model,
common_llm_kwargs,
per_test_common_llm_kwargs,
baseline_llm_kwargs,
test_llm_kwargs,
batch_size: int,
max_output_len: int,
seed: int = 0,
temperature: float = 0.0):
"""Helper method that compares the outputs of both the baseline LLM and """Helper method that compares the outputs of both the baseline LLM and
the test LLM. It asserts greedy equality, e.g. that the outputs are exactly the test LLM. It asserts greedy equality, e.g. that the outputs are exactly
the same when temperature is zero (or when temperature is > 0 and seeded). the same when temperature is zero.
""" """
arg1 = common_llm_kwargs + per_test_common_llm_kwargs + baseline_llm_kwargs
prompts = [ arg2 = common_llm_kwargs + per_test_common_llm_kwargs + test_llm_kwargs
"Hello, my name is", env1 = env2 = None
"The president of the United States is",
"The capital of France is", max_wait_seconds = 240
"The future of AI is", results = []
"San Francisco is know for its",
"Facebook was created in 2004 by", prompts = [prompt for prompt, _ in zip(cycle(PROMPTS), range(batch_size))]
"Curious George is a",
"Python 3.11 brings improvements to its", for args, env in ((arg1, env1), (arg2, env2)):
] with RemoteOpenAIServer(model,
args,
prompts = [prompt for prompt, _ in zip(cycle(prompts), range(batch_size))] env_dict=env,
max_wait_seconds=max_wait_seconds) as server:
# If the test requires that we generated max_output_len tokens, then set the client = server.get_client()
# sampling params to ignore eos token.
ignore_eos = force_output_len completion = client.completions.create(model=model,
prompt=prompts,
if seeded: max_tokens=max_output_len,
sampling_params = [ seed=seed,
SamplingParams( temperature=temperature)
max_tokens=max_output_len,
ignore_eos=ignore_eos, results.append({
temperature=temperature, "test":
seed=i, "seeded_sampling",
) for i in range(len(prompts)) "text": [choice.text for choice in completion.choices],
] "finish_reason":
else: [choice.finish_reason for choice in completion.choices],
sampling_params = SamplingParams( "usage":
max_tokens=max_output_len, completion.usage,
ignore_eos=ignore_eos, })
temperature=temperature,
) n = len(results) // 2
arg1_results = results[:n]
(spec_batch_tokens, spec_batch_token_ids, arg2_results = results[n:]
acceptance_rate) = get_output_from_llm_generator(test_llm_generator, for arg1_result, arg2_result in zip(arg1_results, arg2_results):
prompts, sampling_params) assert arg1_result == arg2_result, (
f"Results for {model=} are not the same with {arg1=} and {arg2=}. "
(baseline_batch_tokens, baseline_batch_token_ids, f"{arg1_result=} != {arg2_result=}")
_) = get_output_from_llm_generator(baseline_llm_generator, prompts,
sampling_params)
assert len(baseline_batch_token_ids) == len(prompts)
assert len(spec_batch_token_ids) == len(prompts)
for i, (baseline_token_ids, baseline_tokens, spec_token_ids,
spec_tokens) in enumerate(
zip(baseline_batch_token_ids, baseline_batch_tokens,
spec_batch_token_ids, spec_batch_tokens)):
if print_tokens:
print(f'{i=} {baseline_tokens=}')
print(f'{i=} {spec_tokens=}')
print(f'{i=} {baseline_token_ids=}')
print(f'{i=} {spec_token_ids=}')
assert baseline_token_ids == spec_token_ids
print(f'{acceptance_rate=}')
if ensure_all_accepted:
assert acceptance_rate == 1.0
if expected_acceptance_rate is not None:
assert acceptance_rate >= expected_acceptance_rate - 1e-2
...@@ -21,7 +21,7 @@ correctess for the target model outputs. ...@@ -21,7 +21,7 @@ correctess for the target model outputs.
import pytest import pytest
from .conftest import run_greedy_equality_correctness_test from .conftest import run_equality_correctness_test
# main model # main model
MAIN_MODEL = "JackFram/llama-68m" MAIN_MODEL = "JackFram/llama-68m"
...@@ -53,7 +53,7 @@ PRECISION = "float32" ...@@ -53,7 +53,7 @@ PRECISION = "float32"
"dtype": PRECISION, "dtype": PRECISION,
# Main model # Main model
"model": MAIN_MODEL, "model_name": MAIN_MODEL,
}]) }])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}]) @pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
...@@ -68,15 +68,16 @@ PRECISION = "float32" ...@@ -68,15 +68,16 @@ PRECISION = "float32"
]) ])
@pytest.mark.parametrize("batch_size", [1, 32]) @pytest.mark.parametrize("batch_size", [1, 32])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_eagle_e2e_greedy_correctness(baseline_llm_generator, def test_eagle_e2e_greedy_correctness(vllm_runner, common_llm_kwargs,
test_llm_generator, batch_size: int, per_test_common_llm_kwargs,
output_len: int): baseline_llm_kwargs, test_llm_kwargs,
"""Verify greedy equality with different batch size.""" batch_size: int, output_len: int,
run_greedy_equality_correctness_test(baseline_llm_generator, seed: int):
test_llm_generator,
batch_size, run_equality_correctness_test(vllm_runner, common_llm_kwargs,
max_output_len=output_len, per_test_common_llm_kwargs,
force_output_len=True) baseline_llm_kwargs, test_llm_kwargs,
batch_size, output_len, seed)
@pytest.mark.parametrize( @pytest.mark.parametrize(
...@@ -94,7 +95,7 @@ def test_eagle_e2e_greedy_correctness(baseline_llm_generator, ...@@ -94,7 +95,7 @@ def test_eagle_e2e_greedy_correctness(baseline_llm_generator,
"dtype": PRECISION, "dtype": PRECISION,
# Main model # Main model
"model": MAIN_MODEL, "model_name": MAIN_MODEL,
}]) }])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}]) @pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
...@@ -109,17 +110,16 @@ def test_eagle_e2e_greedy_correctness(baseline_llm_generator, ...@@ -109,17 +110,16 @@ def test_eagle_e2e_greedy_correctness(baseline_llm_generator,
]) ])
@pytest.mark.parametrize("batch_size", [1, 32]) @pytest.mark.parametrize("batch_size", [1, 32])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_eagle_e2e_greedy_correctness_cuda_graph(baseline_llm_generator, def test_eagle_e2e_greedy_correctness_cuda_graph(
test_llm_generator, vllm_runner, common_llm_kwargs, per_test_common_llm_kwargs,
batch_size: int, baseline_llm_kwargs, test_llm_kwargs, batch_size: int, output_len: int,
output_len: int): seed: int):
"""Verify greedy equality with cuda graph enabled and different """Verify greedy equality with cuda graph enabled and different
batch sizes.""" batch sizes."""
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test(vllm_runner, common_llm_kwargs,
test_llm_generator, per_test_common_llm_kwargs,
batch_size, baseline_llm_kwargs, test_llm_kwargs,
max_output_len=output_len, batch_size, output_len, seed)
force_output_len=True)
@pytest.mark.parametrize( @pytest.mark.parametrize(
...@@ -140,7 +140,7 @@ def test_eagle_e2e_greedy_correctness_cuda_graph(baseline_llm_generator, ...@@ -140,7 +140,7 @@ def test_eagle_e2e_greedy_correctness_cuda_graph(baseline_llm_generator,
"dtype": PRECISION, "dtype": PRECISION,
# Main model # Main model
"model": MAIN_MODEL, "model_name": MAIN_MODEL,
}]) }])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}]) @pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
...@@ -158,18 +158,17 @@ def test_eagle_e2e_greedy_correctness_cuda_graph(baseline_llm_generator, ...@@ -158,18 +158,17 @@ def test_eagle_e2e_greedy_correctness_cuda_graph(baseline_llm_generator,
]) ])
@pytest.mark.parametrize("batch_size", [4]) @pytest.mark.parametrize("batch_size", [4])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_eagle_e2e_greedy_correctness_with_preemption(baseline_llm_generator, def test_eagle_e2e_greedy_correctness_with_preemption(
test_llm_generator, vllm_runner, common_llm_kwargs, per_test_common_llm_kwargs,
batch_size: int, baseline_llm_kwargs, test_llm_kwargs, batch_size: int, output_len: int,
output_len: int): seed: int):
"""Verify greedy equality, even when some sequences are preempted mid- """Verify greedy equality, even when some sequences are preempted mid-
generation. generation.
""" """
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test(vllm_runner, common_llm_kwargs,
test_llm_generator, per_test_common_llm_kwargs,
batch_size, baseline_llm_kwargs, test_llm_kwargs,
max_output_len=output_len, batch_size, output_len, seed)
force_output_len=True)
@pytest.mark.parametrize( @pytest.mark.parametrize(
...@@ -185,7 +184,7 @@ def test_eagle_e2e_greedy_correctness_with_preemption(baseline_llm_generator, ...@@ -185,7 +184,7 @@ def test_eagle_e2e_greedy_correctness_with_preemption(baseline_llm_generator,
"dtype": PRECISION, "dtype": PRECISION,
# Main model # Main model
"model": MAIN_MODEL, "model_name": MAIN_MODEL,
}]) }])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}]) @pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
...@@ -207,16 +206,17 @@ def test_eagle_e2e_greedy_correctness_with_preemption(baseline_llm_generator, ...@@ -207,16 +206,17 @@ def test_eagle_e2e_greedy_correctness_with_preemption(baseline_llm_generator,
32, 32,
]) ])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_eagle_different_k(baseline_llm_generator, test_llm_generator, def test_eagle_different_k(vllm_runner, common_llm_kwargs,
batch_size: int, output_len: int): per_test_common_llm_kwargs, baseline_llm_kwargs,
test_llm_kwargs, batch_size: int, output_len: int,
seed: int):
"""Verify that eagle speculative decoding produces exact equality """Verify that eagle speculative decoding produces exact equality
to without spec decode with different values of num_speculative_tokens. to without spec decode with different values of num_speculative_tokens.
""" """
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test(vllm_runner, common_llm_kwargs,
test_llm_generator, per_test_common_llm_kwargs,
batch_size, baseline_llm_kwargs, test_llm_kwargs,
max_output_len=output_len, batch_size, output_len, seed)
force_output_len=True)
@pytest.mark.parametrize( @pytest.mark.parametrize(
...@@ -232,7 +232,7 @@ def test_eagle_different_k(baseline_llm_generator, test_llm_generator, ...@@ -232,7 +232,7 @@ def test_eagle_different_k(baseline_llm_generator, test_llm_generator,
"dtype": PRECISION, "dtype": PRECISION,
# Main model # Main model
"model": MAIN_MODEL, "model_name": MAIN_MODEL,
}]) }])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}]) @pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
...@@ -250,17 +250,18 @@ def test_eagle_different_k(baseline_llm_generator, test_llm_generator, ...@@ -250,17 +250,18 @@ def test_eagle_different_k(baseline_llm_generator, test_llm_generator,
32, 32,
]) ])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_eagle_disable_queue(baseline_llm_generator, test_llm_generator, def test_eagle_disable_queue(vllm_runner, common_llm_kwargs,
batch_size: int, output_len: int): per_test_common_llm_kwargs, baseline_llm_kwargs,
test_llm_kwargs, batch_size: int, output_len: int,
seed: int):
"""Verify that eagle speculative decoding produces exact equality """Verify that eagle speculative decoding produces exact equality
to without spec decode when speculation is disabled for large to without spec decode when speculation is disabled for large
batch sizes. batch sizes.
""" """
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test(vllm_runner, common_llm_kwargs,
test_llm_generator, per_test_common_llm_kwargs,
batch_size, baseline_llm_kwargs, test_llm_kwargs,
max_output_len=output_len, batch_size, output_len, seed)
force_output_len=True)
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -4,7 +4,9 @@ other features, e.g. cuda graphs. ...@@ -4,7 +4,9 @@ other features, e.g. cuda graphs.
import pytest import pytest
from .conftest import run_greedy_equality_correctness_test from .conftest import run_equality_correctness_test
MAIN_MODEL = "JackFram/llama-68m"
@pytest.mark.parametrize( @pytest.mark.parametrize(
...@@ -15,7 +17,7 @@ from .conftest import run_greedy_equality_correctness_test ...@@ -15,7 +17,7 @@ from .conftest import run_greedy_equality_correctness_test
# Verify equality when cuda graphs allowed. # Verify equality when cuda graphs allowed.
"enforce_eager": False, "enforce_eager": False,
"model": "JackFram/llama-68m", "model_name": "JackFram/llama-68m",
}]) }])
@pytest.mark.parametrize( @pytest.mark.parametrize(
"per_test_common_llm_kwargs", "per_test_common_llm_kwargs",
...@@ -31,23 +33,27 @@ from .conftest import run_greedy_equality_correctness_test ...@@ -31,23 +33,27 @@ from .conftest import run_greedy_equality_correctness_test
@pytest.mark.parametrize("batch_size", [8]) @pytest.mark.parametrize("batch_size", [8])
@pytest.mark.parametrize("output_len", [32]) @pytest.mark.parametrize("output_len", [32])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_spec_decode_cuda_graph(baseline_llm_generator, test_llm_generator, def test_spec_decode_cuda_graph(vllm_runner, common_llm_kwargs,
batch_size, output_len): per_test_common_llm_kwargs,
baseline_llm_kwargs, test_llm_kwargs,
batch_size: int, output_len: int, seed: int):
"""Verify spec decode equality when cuda graphs are enabled. """Verify spec decode equality when cuda graphs are enabled.
""" """
run_greedy_equality_correctness_test( run_equality_correctness_test(vllm_runner,
baseline_llm_generator, common_llm_kwargs,
test_llm_generator, per_test_common_llm_kwargs,
batch_size, baseline_llm_kwargs,
max_output_len=output_len, test_llm_kwargs,
force_output_len=True, batch_size,
) max_output_len=output_len,
seed=seed,
temperature=0.0)
@pytest.mark.parametrize( @pytest.mark.parametrize(
"common_llm_kwargs", "common_llm_kwargs",
[{ [{
"model": "JackFram/llama-160m", "model_name": "JackFram/llama-160m",
# Skip cuda graph recording for fast test. # Skip cuda graph recording for fast test.
"enforce_eager": True, "enforce_eager": True,
...@@ -80,13 +86,19 @@ def test_spec_decode_cuda_graph(baseline_llm_generator, test_llm_generator, ...@@ -80,13 +86,19 @@ def test_spec_decode_cuda_graph(baseline_llm_generator, test_llm_generator,
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
@pytest.mark.parametrize("batch_size", [2]) @pytest.mark.parametrize("batch_size", [2])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_speculative_model_quantization_config(baseline_llm_generator, def test_speculative_model_quantization_config(vllm_runner, common_llm_kwargs,
test_llm_generator, per_test_common_llm_kwargs,
batch_size: int): baseline_llm_kwargs,
test_llm_kwargs,
batch_size: int, seed: int):
"""Verify spec decode works well with draft model quantization configs. """Verify spec decode works well with draft model quantization configs.
""" """
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test(vllm_runner,
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len=32, baseline_llm_kwargs,
force_output_len=True) test_llm_kwargs,
batch_size,
max_output_len=32,
seed=seed,
temperature=0.0)
...@@ -7,42 +7,39 @@ import torch ...@@ -7,42 +7,39 @@ import torch
from vllm.utils import is_hip from vllm.utils import is_hip
from .conftest import run_greedy_equality_correctness_test from .conftest import run_equality_correctness_test_tp
@pytest.mark.skipif(torch.cuda.device_count() < 2, @pytest.mark.skipif(torch.cuda.device_count() < 2,
reason="Need at least 2 GPUs to run the test.") reason="Need at least 2 GPUs to run the test.")
@pytest.mark.parametrize( @pytest.mark.parametrize(
"common_llm_kwargs", "common_llm_kwargs",
[{ [[
"model": "JackFram/llama-68m",
# Skip cuda graph recording for fast test. # Skip cuda graph recording for fast test.
"enforce_eager": True, "--enforce-eager",
# Required for spec decode. # Required for spec decode.
"use_v2_block_manager": True, "--use-v2-block-manager",
"tensor_parallel_size": 2, "--tensor-parallel-size",
"2"
# Use AsyncLLM engine, so that the engine runs in its own process. ]])
# Otherwise, since vLLM does not follow true SPMD, the test runner @pytest.mark.parametrize("per_test_common_llm_kwargs", [[]])
# process will have both the engine and the rank0 worker. NCCL is not @pytest.mark.parametrize("baseline_llm_kwargs", [[]])
# cleaned up properly, and its server host thread leaks, causing the
# second run of the test to fail with internal NCCL error.
"use_async": True,
}])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}])
@pytest.mark.parametrize("test_llm_kwargs", [ @pytest.mark.parametrize("test_llm_kwargs", [
{ [
"speculative_model": "JackFram/llama-68m", "--speculative-model",
"num_speculative_tokens": 3, "JackFram/llama-68m",
}, "--num-speculative-tokens",
{ "3",
"speculative_model": "[ngram]", ],
"num_speculative_tokens": 5, [
"ngram_prompt_lookup_max": 3, "--speculative-model",
}, "[ngram]",
"--num-speculative-tokens",
"5",
"--ngram-prompt-lookup-max",
"3",
],
]) ])
@pytest.mark.parametrize("batch_size", [2]) @pytest.mark.parametrize("batch_size", [2])
@pytest.mark.parametrize( @pytest.mark.parametrize(
...@@ -52,75 +49,75 @@ from .conftest import run_greedy_equality_correctness_test ...@@ -52,75 +49,75 @@ from .conftest import run_greedy_equality_correctness_test
32, 32,
]) ])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_target_model_tp_gt_1(baseline_llm_generator, test_llm_generator, def test_target_model_tp_gt_1(common_llm_kwargs, per_test_common_llm_kwargs,
batch_size: int, output_len: int): baseline_llm_kwargs, test_llm_kwargs,
batch_size: int, output_len: int, seed: int):
"""Verify greedy equality when tensor parallelism is used. """Verify greedy equality when tensor parallelism is used.
""" """
if is_hip(): if is_hip():
pytest.skip("hip is not well-supported yet") pytest.skip("hip is not well-supported yet")
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test_tp("JackFram/llama-68m",
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len=output_len, baseline_llm_kwargs,
force_output_len=True) test_llm_kwargs,
batch_size,
output_len,
seed,
temperature=0.0)
@pytest.mark.skipif(torch.cuda.device_count() < 2, @pytest.mark.skipif(torch.cuda.device_count() < 2,
reason="Need at least 2 GPUs to run the test.") reason="Need at least 2 GPUs to run the test.")
@pytest.mark.parametrize( @pytest.mark.parametrize(
"common_llm_kwargs", "common_llm_kwargs",
[{ [[
# Skip cuda graph recording for fast test. # Skip cuda graph recording for fast test.
"enforce_eager": True, "--enforce-eager",
# Required for spec decode. # Required for spec decode.
"use_v2_block_manager": True, "--use_v2_block_manager",
"tensor_parallel_size": 2, "--tensor_parallel_size",
"2",
# Use AsyncLLM engine, so that the engine runs in its own process.
# Otherwise, since vLLM does not follow true SPMD, the test runner
# process will have both the engine and the rank0 worker. NCCL is not
# cleaned up properly, and its server host thread leaks, causing the
# second run of the test to fail with internal NCCL error.
"use_async": True,
# precision # precision
"dtype": "float32", "--dtype",
}]) "bfloat16",
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) ]])
@pytest.mark.parametrize( @pytest.mark.parametrize("per_test_common_llm_kwargs", [[]])
"per_test_common_llm_kwargs, test_llm_kwargs", @pytest.mark.parametrize("baseline_llm_kwargs", [[]])
[ @pytest.mark.parametrize("model, test_llm_kwargs",
( [("JackFram/llama-68m", [
{ "--speculative-model",
# Use a small model for a fast test. "JackFram/llama-68m",
# Note this is repeated in the test body; to initialize a "--num_speculative-tokens",
# tokenizer. "5",
"model": "JackFram/llama-68m", "--speculative-draft-tensor-parallel-size",
}, "1",
{ ]),
"speculative_model": "JackFram/llama-68m", ("ibm-granite/granite-3b-code-instruct", [
"num_speculative_tokens": 5, "--speculative-model",
"speculative_draft_tensor_parallel_size": 1, "ibm-granite/granite-3b-code-instruct",
}), "--num_speculative-tokens",
({ "5",
"model": "ibm-granite/granite-3b-code-instruct", "--speculative-draft-tensor-parallel-size",
}, { "1",
"speculative_model": ])])
"ibm-granite/granite-3b-code-instruct-accelerator",
"num_speculative_tokens": 5,
"speculative_draft_tensor_parallel_size": 1,
})
])
@pytest.mark.parametrize("batch_size", [2]) @pytest.mark.parametrize("batch_size", [2])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_draft_model_tp_lt_target_model_tp2(test_llm_generator, def test_draft_model_tp_lt_target_model_tp2(model, common_llm_kwargs,
baseline_llm_generator, per_test_common_llm_kwargs,
batch_size: int): baseline_llm_kwargs,
test_llm_kwargs, batch_size: int,
seed: int):
"""Verify spec decode works well with smaller tp for draft models. """Verify spec decode works well with smaller tp for draft models.
""" """
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test_tp(model,
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len=32, baseline_llm_kwargs,
force_output_len=True) test_llm_kwargs,
batch_size,
max_output_len=32,
seed=seed,
temperature=0.0)
...@@ -2,98 +2,97 @@ ...@@ -2,98 +2,97 @@
tensor parallelism. tensor parallelism.
""" """
import openai
import pytest import pytest
import torch import torch
from .conftest import run_greedy_equality_correctness_test from .conftest import run_equality_correctness_test_tp
MAIN_MODEL = "JackFram/llama-68m"
SPEC_MODEL = "JackFram/llama-68m"
@pytest.mark.skipif(torch.cuda.device_count() < 4, @pytest.mark.skipif(torch.cuda.device_count() < 4,
reason="Need at least 4 GPUs to run the test.") reason="Need at least 4 GPUs to run the test.")
@pytest.mark.parametrize( @pytest.mark.parametrize(
"common_llm_kwargs", "common_llm_kwargs",
[{ [[
# Use a small model for a fast test.
# Note this is repeated in the test body; to initialize a tokenizer.
"model": "JackFram/llama-68m",
# Skip cuda graph recording for fast test. # Skip cuda graph recording for fast test.
"enforce_eager": True, "--enforce_eager",
# Required for spec decode. # Required for spec decode.
"use_v2_block_manager": True, "--use-v2-block-manager",
"tensor_parallel_size": 4, "--tensor-parallel-size",
"4",
# Use AsyncLLM engine, so that the engine runs in its own process. ]])
# Otherwise, since vLLM does not follow true SPMD, the test runner
# process will have both the engine and the rank0 worker. NCCL is not
# cleaned up properly, and its server host thread leaks, causing the
# second run of the test to fail with internal NCCL error.
"use_async": True,
}])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [ @pytest.mark.parametrize("per_test_common_llm_kwargs", [
{ [
"speculative_model": "JackFram/llama-68m", "--speculative-model",
"num_speculative_tokens": 5, f"{SPEC_MODEL}",
}, "--num-speculative-tokens",
"5",
],
]) ])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [[]])
@pytest.mark.parametrize( @pytest.mark.parametrize(
"test_llm_kwargs", "test_llm_kwargs",
[ [
#TODO(wooyeon): add spec_draft_dp=2 case #TODO(wooyeon): add spec_draft_dp=2 case
{ [
"speculative_draft_tensor_parallel_size": 1, "--speculative-draft-tensor-parallel-size",
}, "1",
],
]) ])
@pytest.mark.parametrize("batch_size", [2]) @pytest.mark.parametrize("batch_size", [2])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_draft_model_tp_lt_target_model_tp4(test_llm_generator, def test_draft_model_tp_lt_target_model_tp4(common_llm_kwargs,
baseline_llm_generator, per_test_common_llm_kwargs,
batch_size: int): baseline_llm_kwargs,
test_llm_kwargs, batch_size: int,
seed: int):
"""Verify spec decode works well with smaller tp for draft models. """Verify spec decode works well with smaller tp for draft models.
""" """
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test_tp(MAIN_MODEL,
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len=32, baseline_llm_kwargs,
force_output_len=True) test_llm_kwargs,
batch_size,
max_output_len=32,
seed=seed,
temperature=0.0)
@pytest.mark.skipif(torch.cuda.device_count() < 4, @pytest.mark.skipif(torch.cuda.device_count() < 4,
reason="Need at least 4 GPUs to run the test.") reason="Need at least 4 GPUs to run the test.")
@pytest.mark.parametrize( @pytest.mark.parametrize(
"common_llm_kwargs", "common_llm_kwargs",
[{ [[
"model": "JackFram/llama-160m",
# Skip cuda graph recording for fast test. # Skip cuda graph recording for fast test.
"enforce_eager": True, "--enforce-eager",
# Required for spec decode. # Required for spec decode.
"use_v2_block_manager": True, "--use-v2-block-manager",
"tensor_parallel_size": 4, "--tensor-parallel-size",
"4",
# Use AsyncLLM engine, so that the engine runs in its own process. ]])
# Otherwise, since vLLM does not follow true SPMD, the test runner @pytest.mark.parametrize("per_test_common_llm_kwargs", [[]])
# process will have both the engine and the rank0 worker. NCCL is not @pytest.mark.parametrize("baseline_llm_kwargs", [[]])
# cleaned up properly, and its server host thread leaks, causing the
# second run of the test to fail with internal NCCL error.
"use_async": True,
}])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}])
@pytest.mark.parametrize( @pytest.mark.parametrize(
"test_llm_kwargs", "test_llm_kwargs",
[ [
{ [
"speculative_model": "JackFram/llama-68m", "--speculative-model",
"num_speculative_tokens": 5, f"{SPEC_MODEL}",
"--num-speculative-tokens",
"5",
# Artificially limit the draft model max model len; this forces vLLM # Artificially limit the draft model max model len; this forces vLLM
# to skip speculation once the sequences grow beyond 32-k tokens. # to skip speculation once the sequences grow beyond 32-k tokens.
"speculative_max_model_len": 32, "--speculative-max-model-len",
}, "32",
],
]) ])
@pytest.mark.parametrize("batch_size", [8]) @pytest.mark.parametrize("batch_size", [8])
@pytest.mark.parametrize( @pytest.mark.parametrize(
...@@ -105,8 +104,9 @@ def test_draft_model_tp_lt_target_model_tp4(test_llm_generator, ...@@ -105,8 +104,9 @@ def test_draft_model_tp_lt_target_model_tp4(test_llm_generator,
64, 64,
]) ])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_skip_speculation(baseline_llm_generator, test_llm_generator, def test_skip_speculation(common_llm_kwargs, per_test_common_llm_kwargs,
batch_size: int, output_len: int): baseline_llm_kwargs, test_llm_kwargs,
batch_size: int, output_len: int, seed: int):
"""Verify job failure with RuntimeError when all sequences skip speculation. """Verify job failure with RuntimeError when all sequences skip speculation.
We do this by setting the max model len of the draft model to an We do this by setting the max model len of the draft model to an
artificially low value, such that when the sequences grow beyond it, they artificially low value, such that when the sequences grow beyond it, they
...@@ -114,9 +114,13 @@ def test_skip_speculation(baseline_llm_generator, test_llm_generator, ...@@ -114,9 +114,13 @@ def test_skip_speculation(baseline_llm_generator, test_llm_generator,
TODO: fix it to pass without raising Error. (#5814) TODO: fix it to pass without raising Error. (#5814)
""" """
with pytest.raises(RuntimeError): with pytest.raises(openai.APIConnectionError):
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test_tp(MAIN_MODEL,
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len=output_len, baseline_llm_kwargs,
force_output_len=True) test_llm_kwargs,
batch_size,
output_len,
seed,
temperature=0.0)
import math
from itertools import cycle from itertools import cycle
import pytest import pytest
from vllm import SamplingParams from vllm import SamplingParams
from .conftest import get_logprobs_from_llm_generator from .conftest import run_logprob_correctness_test
@pytest.mark.parametrize( @pytest.mark.parametrize(
"common_llm_kwargs", "common_llm_kwargs",
[{ [{
"model": "JackFram/llama-68m", "model_name": "JackFram/llama-68m",
# Skip cuda graph recording for fast test. # Skip cuda graph recording for fast test.
"enforce_eager": True, "enforce_eager": True,
# Required for spec decode. # Required for spec decode.
"use_v2_block_manager": True, "use_v2_block_manager": True,
"max_logprobs": 6,
}]) }])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}]) @pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
...@@ -36,64 +34,29 @@ from .conftest import get_logprobs_from_llm_generator ...@@ -36,64 +34,29 @@ from .conftest import get_logprobs_from_llm_generator
7, 7,
]) ])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_logprobs_equality(baseline_llm_generator, test_llm_generator, @pytest.mark.parametrize("logprobs", [1, 6])
batch_size: int, output_len: int): def test_logprobs_equality(vllm_runner, common_llm_kwargs,
per_test_common_llm_kwargs, baseline_llm_kwargs,
test_llm_kwargs, batch_size: int, output_len: int,
seed: int, logprobs: int):
"""Verify output logprobs are equal with and without speculative decoding. """Verify output logprobs are equal with and without speculative decoding.
""" """
run_greedy_logprobs_correctness_test(baseline_llm_generator, run_logprob_correctness_test(vllm_runner,
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len=output_len, baseline_llm_kwargs,
force_output_len=True) test_llm_kwargs,
batch_size,
output_len,
seed,
temperature=0.0,
logprobs=logprobs)
@pytest.mark.parametrize( @pytest.mark.parametrize(
"common_llm_kwargs", "common_llm_kwargs",
[{ [{
"model": "JackFram/llama-68m", "model_name": "JackFram/llama-68m",
# Skip cuda graph recording for fast test.
"enforce_eager": True,
# Required for spec decode.
"use_v2_block_manager": True,
"max_logprobs": 6,
}])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}])
@pytest.mark.parametrize("test_llm_kwargs",
[{
"speculative_model": "JackFram/llama-160m",
"num_speculative_tokens": 3,
"disable_logprobs_during_spec_decoding": False,
}])
@pytest.mark.parametrize("batch_size", [1])
@pytest.mark.parametrize("num_logprobs", [6])
@pytest.mark.parametrize(
"output_len",
[
# Use smaller output len for fast test.
7,
])
@pytest.mark.parametrize("seed", [1])
def test_diff_num_logprobs(baseline_llm_generator, test_llm_generator,
batch_size: int, output_len: int,
num_logprobs: int):
"""Verify output logprobs are equal with and without spec decode.
This specifies a number of logprobs >1.
"""
run_greedy_logprobs_correctness_test(baseline_llm_generator,
test_llm_generator,
batch_size,
max_output_len=output_len,
force_output_len=True,
logprob_rank=num_logprobs)
@pytest.mark.parametrize(
"common_llm_kwargs",
[{
"model": "JackFram/llama-68m",
# Skip cuda graph recording for fast test. # Skip cuda graph recording for fast test.
"enforce_eager": True, "enforce_eager": True,
...@@ -121,21 +84,29 @@ def test_diff_num_logprobs(baseline_llm_generator, test_llm_generator, ...@@ -121,21 +84,29 @@ def test_diff_num_logprobs(baseline_llm_generator, test_llm_generator,
32, 32,
]) ])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_logprobs_different_k(baseline_llm_generator, test_llm_generator, @pytest.mark.parametrize("logprobs", [1, 6])
batch_size: int, output_len: int): def test_logprobs_different_k(vllm_runner, common_llm_kwargs,
per_test_common_llm_kwargs, baseline_llm_kwargs,
test_llm_kwargs, batch_size: int,
output_len: int, seed: int, logprobs: int):
"""Veriy logprob greedy equality with different speculation lens. """Veriy logprob greedy equality with different speculation lens.
""" """
run_greedy_logprobs_correctness_test(baseline_llm_generator, run_logprob_correctness_test(vllm_runner,
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len=output_len, baseline_llm_kwargs,
force_output_len=True) test_llm_kwargs,
batch_size,
output_len,
seed,
temperature=0.0,
logprobs=logprobs)
@pytest.mark.parametrize( @pytest.mark.parametrize(
"common_llm_kwargs", "common_llm_kwargs",
[{ [{
"model": "JackFram/llama-68m", "model_name": "JackFram/llama-68m",
# Skip cuda graph recording for fast test. # Skip cuda graph recording for fast test.
"enforce_eager": True, "enforce_eager": True,
...@@ -164,22 +135,30 @@ def test_logprobs_different_k(baseline_llm_generator, test_llm_generator, ...@@ -164,22 +135,30 @@ def test_logprobs_different_k(baseline_llm_generator, test_llm_generator,
32, 32,
]) ])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_logprobs_when_skip_speculation(baseline_llm_generator, @pytest.mark.parametrize("logprobs", [1])
test_llm_generator, batch_size: int, def test_logprobs_when_skip_speculation(vllm_runner, common_llm_kwargs,
output_len: int): per_test_common_llm_kwargs,
baseline_llm_kwargs, test_llm_kwargs,
batch_size: int, output_len: int,
seed: int, logprobs: int):
"""Verify logprobs greedy equality when some sequences skip speculation. """Verify logprobs greedy equality when some sequences skip speculation.
""" """
run_greedy_logprobs_correctness_test(baseline_llm_generator, run_logprob_correctness_test(vllm_runner,
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len=output_len, baseline_llm_kwargs,
force_output_len=True) test_llm_kwargs,
batch_size,
output_len,
seed,
temperature=0.0,
logprobs=logprobs)
@pytest.mark.parametrize( @pytest.mark.parametrize(
"common_llm_kwargs", "common_llm_kwargs",
[{ [{
"model": "JackFram/llama-68m", "model_name": "JackFram/llama-68m",
# Skip cuda graph recording for fast test. # Skip cuda graph recording for fast test.
"enforce_eager": True, "enforce_eager": True,
...@@ -203,19 +182,17 @@ def test_logprobs_when_skip_speculation(baseline_llm_generator, ...@@ -203,19 +182,17 @@ def test_logprobs_when_skip_speculation(baseline_llm_generator,
32, 32,
]) ])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_logprobs_temp_1(baseline_llm_generator, test_llm_generator, @pytest.mark.parametrize("logprobs", [6])
batch_size: int, output_len: int): def test_logprobs_temp_1(vllm_runner, common_llm_kwargs,
per_test_common_llm_kwargs, baseline_llm_kwargs,
test_llm_kwargs, batch_size: int, output_len: int,
seed: int, logprobs: int):
"""Verify at least one logprob result has num_logprobs+1, which tests the """Verify at least one logprob result has num_logprobs+1, which tests the
case where the sampled token is not in top-k logprobs. case where the sampled token is not in top-k logprobs.
Ideally, this test should validate equality with non-spec by getting Ideally, this test should validate equality with non-spec by getting
logprobs. This is left as future improvement. logprobs. This is left as future improvement.
""" """
batch_size = 8
max_output_len = output_len
force_output_len = True
logprob_rank = 5
temperature = 1.0 temperature = 1.0
prompts = [ prompts = [
...@@ -231,129 +208,40 @@ def test_logprobs_temp_1(baseline_llm_generator, test_llm_generator, ...@@ -231,129 +208,40 @@ def test_logprobs_temp_1(baseline_llm_generator, test_llm_generator,
prompts = [prompt for prompt, _ in zip(cycle(prompts), range(batch_size))] prompts = [prompt for prompt, _ in zip(cycle(prompts), range(batch_size))]
# If the test requires that we generated max_output_len tokens, then set the
# sampling params to ignore eos token.
ignore_eos = force_output_len
sampling_params = SamplingParams( sampling_params = SamplingParams(
max_tokens=max_output_len, max_tokens=output_len,
ignore_eos=ignore_eos, ignore_eos=True,
temperature=temperature, temperature=temperature,
logprobs=logprob_rank, logprobs=logprobs,
) )
spec_batch_logprobs = get_logprobs_from_llm_generator( sd_args = {
test_llm_generator, prompts, sampling_params) **common_llm_kwargs,
**per_test_common_llm_kwargs,
**test_llm_kwargs,
}
with vllm_runner(**sd_args) as vllm_model:
sd_outputs = vllm_model.generate_w_logprobs(prompts, sampling_params)
num_returned_logprobs = [ num_returned_logprobs = [
len(logprob_dict) for seq_logprobs in spec_batch_logprobs len(seq_logprobs) for seq_logprobs in sd_outputs[-1]
for logprob_dict in seq_logprobs
] ]
# Assert one of the returned logprobs has > num_logprobs (indicating the # Assert one of the returned logprobs has > num_logprobs (indicating the
# sampled token is not in top-k). # sampled token is not in top-k).
assert any([ assert any(
num_returned > logprob_rank for num_returned in num_returned_logprobs [num_returned > logprobs for num_returned in num_returned_logprobs])
])
def run_greedy_logprobs_correctness_test(baseline_llm_generator,
test_llm_generator,
batch_size,
max_output_len,
force_output_len: bool,
logprob_rank: int = 1):
"""Helper method that compares the logprobs outputs of both the baseline LLM
and the test LLM. It asserts greedy equality of the logprobs when the
temperature is zero.
"""
temperature = 0.0
prompts = [
"Hello, my name is",
"The president of the United States is",
"The capital of France is",
"The future of AI is",
"San Francisco is know for its",
"Facebook was created in 2004 by",
"Curious George is a",
"Python 3.11 brings improvements to its",
]
prompts = [prompt for prompt, _ in zip(cycle(prompts), range(batch_size))]
# If the test requires that we generated max_output_len tokens, then set the
# sampling params to ignore eos token.
ignore_eos = force_output_len
sampling_params = SamplingParams(
max_tokens=max_output_len,
ignore_eos=ignore_eos,
temperature=temperature,
logprobs=logprob_rank,
)
spec_batch_logprobs = get_logprobs_from_llm_generator(
test_llm_generator, prompts, sampling_params)
baseline_batch_logprobs = get_logprobs_from_llm_generator(
baseline_llm_generator, prompts, sampling_params)
assert len(baseline_batch_logprobs) == len(prompts)
assert len(spec_batch_logprobs) == len(prompts)
# For each sequence in the batch.
for i, (baseline_logprobs, spec_logprobs) in enumerate(
zip(baseline_batch_logprobs, spec_batch_logprobs)):
assert len(spec_logprobs) == len(baseline_logprobs)
# For each generated position of the sequence.
for pos, (spec_pos_logprobs, baseline_pos_logprobs) in enumerate(
zip(spec_logprobs, baseline_logprobs)):
# Map rank to token/logprob in spec output.
spec_rank_to_token_id = {
value.rank: key
for key, value in spec_pos_logprobs.items()
}
spec_rank_to_logprob = {
value.rank: value.logprob
for key, value in spec_pos_logprobs.items()
}
# Map rank to token/logprob in baseline output.
baseline_rank_to_token_id = {
value.rank: key
for key, value in baseline_pos_logprobs.items()
}
baseline_rank_to_logprob = {
value.rank: value.logprob
for key, value in baseline_pos_logprobs.items()
}
# Assert set of ranks returned is equal.
assert set(spec_rank_to_token_id.keys()) == set(
baseline_rank_to_token_id.keys())
# Assert each logprob/token id is correct, keyed by rank.
for rank in sorted(set(spec_rank_to_token_id.keys())):
assert spec_rank_to_token_id[
rank] == baseline_rank_to_token_id[rank], f"{rank}"
assert math.isclose(
a=spec_rank_to_logprob[rank],
b=baseline_rank_to_logprob[rank],
abs_tol=1e-1,
)
@pytest.mark.parametrize( @pytest.mark.parametrize(
"common_llm_kwargs", "common_llm_kwargs",
[{ [{
"model": "JackFram/llama-160m", "model_name": "JackFram/llama-160m",
# Skip cuda graph recording for fast test. # Skip cuda graph recording for fast test.
"enforce_eager": True, "enforce_eager": True,
# Required for spec decode. # Required for spec decode.
"use_v2_block_manager": True, "use_v2_block_manager": True,
"max_logprobs": 6,
}]) }])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}]) @pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
...@@ -364,57 +252,28 @@ def run_greedy_logprobs_correctness_test(baseline_llm_generator, ...@@ -364,57 +252,28 @@ def run_greedy_logprobs_correctness_test(baseline_llm_generator,
"disable_logprobs_during_spec_decoding": True, "disable_logprobs_during_spec_decoding": True,
}]) }])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_logprobs_disabled(baseline_llm_generator, test_llm_generator): @pytest.mark.parametrize("batch_size", [4])
@pytest.mark.parametrize(
"output_len",
[
# Use smaller output len for fast test.
32,
])
@pytest.mark.parametrize("logprobs", [0])
def test_logprobs_disabled(vllm_runner, common_llm_kwargs,
per_test_common_llm_kwargs, baseline_llm_kwargs,
test_llm_kwargs, batch_size: int, output_len: int,
seed: int, logprobs: int):
"""Check the behavior when logprobs are disabled. """Check the behavior when logprobs are disabled.
Token choices should match with the base model. Token choices should match with the base model.
""" """
prompts = [ run_logprob_correctness_test(vllm_runner,
"Hello, my name is", common_llm_kwargs,
"The president of the United States is", per_test_common_llm_kwargs,
"The capital of France is", baseline_llm_kwargs,
"The future of AI is", test_llm_kwargs,
"San Francisco is know for its", batch_size,
"Facebook was created in 2004 by", output_len,
"Curious George is a", seed,
"Python 3.11 brings improvements to its", temperature=0.0,
] logprobs=logprobs)
prompts = [prompt for prompt, _ in zip(cycle(prompts), range(4))]
sampling_params = SamplingParams(
# Use smaller output len for fast test
max_tokens=7,
ignore_eos=True,
temperature=0.0,
logprobs=2,
)
spec_batch_logprobs = get_logprobs_from_llm_generator(
test_llm_generator, prompts, sampling_params)
baseline_batch_logprobs = get_logprobs_from_llm_generator(
baseline_llm_generator, prompts, sampling_params)
assert len(baseline_batch_logprobs) == len(prompts)
assert len(spec_batch_logprobs) == len(prompts)
# For each sequence in the batch.
for _, (baseline_logprobs, spec_logprobs) in enumerate(
zip(baseline_batch_logprobs, spec_batch_logprobs)):
assert len(spec_logprobs) == len(baseline_logprobs)
# For each generated position of the sequence.
for _, (spec_pos_logprobs, baseline_pos_logprobs) in enumerate(
zip(spec_logprobs, baseline_logprobs)):
assert len(spec_pos_logprobs) == 1
spec_top_token_id = list(spec_pos_logprobs)[0]
spec_top_logprob = spec_pos_logprobs[spec_top_token_id]
assert spec_top_logprob.logprob == 0.0
assert spec_top_logprob.rank == -1
# check that the chosen token matches the base model
baseline_logprob = baseline_pos_logprobs[spec_top_token_id]
assert baseline_logprob.rank == 1
assert spec_top_logprob.decoded_token \
== baseline_logprob.decoded_token
...@@ -21,7 +21,7 @@ correctess for the target model outputs. ...@@ -21,7 +21,7 @@ correctess for the target model outputs.
import pytest import pytest
from .conftest import run_greedy_equality_correctness_test from .conftest import run_equality_correctness_test
# main model # main model
# lmsys/vicuna-7b-v1.3 was to be used but it's causing # lmsys/vicuna-7b-v1.3 was to be used but it's causing
...@@ -55,7 +55,7 @@ PRECISION = "float32" ...@@ -55,7 +55,7 @@ PRECISION = "float32"
"dtype": PRECISION, "dtype": PRECISION,
# Main model # Main model
"model": MAIN_MODEL, "model_name": MAIN_MODEL,
}]) }])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}]) @pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
...@@ -70,15 +70,21 @@ PRECISION = "float32" ...@@ -70,15 +70,21 @@ PRECISION = "float32"
]) ])
@pytest.mark.parametrize("batch_size", [1, 32]) @pytest.mark.parametrize("batch_size", [1, 32])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_medusa_e2e_greedy_correctness(baseline_llm_generator, def test_medusa_e2e_greedy_correctness(vllm_runner, common_llm_kwargs,
test_llm_generator, batch_size: int, per_test_common_llm_kwargs,
output_len: int): baseline_llm_kwargs, test_llm_kwargs,
batch_size: int, output_len: int,
seed: int):
"""Verify greedy equality with different batch size.""" """Verify greedy equality with different batch size."""
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test(vllm_runner,
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len=output_len, baseline_llm_kwargs,
force_output_len=True) test_llm_kwargs,
batch_size,
max_output_len=output_len,
seed=seed,
temperature=0.0)
@pytest.mark.parametrize( @pytest.mark.parametrize(
...@@ -96,7 +102,7 @@ def test_medusa_e2e_greedy_correctness(baseline_llm_generator, ...@@ -96,7 +102,7 @@ def test_medusa_e2e_greedy_correctness(baseline_llm_generator,
"dtype": PRECISION, "dtype": PRECISION,
# Main model # Main model
"model": MAIN_MODEL, "model_name": MAIN_MODEL,
}]) }])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}]) @pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
...@@ -111,17 +117,21 @@ def test_medusa_e2e_greedy_correctness(baseline_llm_generator, ...@@ -111,17 +117,21 @@ def test_medusa_e2e_greedy_correctness(baseline_llm_generator,
]) ])
@pytest.mark.parametrize("batch_size", [1, 32]) @pytest.mark.parametrize("batch_size", [1, 32])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_medusa_e2e_greedy_correctness_cuda_graph(baseline_llm_generator, def test_medusa_e2e_greedy_correctness_cuda_graph(
test_llm_generator, vllm_runner, common_llm_kwargs, per_test_common_llm_kwargs,
batch_size: int, baseline_llm_kwargs, test_llm_kwargs, batch_size: int, output_len: int,
output_len: int): seed: int):
"""Verify greedy equality with cuda graph enabled and different """Verify greedy equality with cuda graph enabled and different
batch sizes.""" batch sizes."""
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test(vllm_runner,
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len=output_len, baseline_llm_kwargs,
force_output_len=True) test_llm_kwargs,
batch_size,
max_output_len=output_len,
seed=seed,
temperature=0.0)
@pytest.mark.parametrize( @pytest.mark.parametrize(
...@@ -142,7 +152,7 @@ def test_medusa_e2e_greedy_correctness_cuda_graph(baseline_llm_generator, ...@@ -142,7 +152,7 @@ def test_medusa_e2e_greedy_correctness_cuda_graph(baseline_llm_generator,
"dtype": PRECISION, "dtype": PRECISION,
# Main model # Main model
"model": MAIN_MODEL, "model_name": MAIN_MODEL,
}]) }])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}]) @pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
...@@ -160,18 +170,22 @@ def test_medusa_e2e_greedy_correctness_cuda_graph(baseline_llm_generator, ...@@ -160,18 +170,22 @@ def test_medusa_e2e_greedy_correctness_cuda_graph(baseline_llm_generator,
]) ])
@pytest.mark.parametrize("batch_size", [4]) @pytest.mark.parametrize("batch_size", [4])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_medusa_e2e_greedy_correctness_with_preemption(baseline_llm_generator, def test_medusa_e2e_greedy_correctness_with_preemption(
test_llm_generator, vllm_runner, common_llm_kwargs, per_test_common_llm_kwargs,
batch_size: int, baseline_llm_kwargs, test_llm_kwargs, batch_size: int, output_len: int,
output_len: int): seed: int):
"""Verify greedy equality, even when some sequences are preempted mid- """Verify greedy equality, even when some sequences are preempted mid-
generation. generation.
""" """
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test(vllm_runner,
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len=output_len, baseline_llm_kwargs,
force_output_len=True) test_llm_kwargs,
batch_size,
max_output_len=output_len,
seed=seed,
temperature=0.0)
@pytest.mark.parametrize( @pytest.mark.parametrize(
...@@ -187,7 +201,7 @@ def test_medusa_e2e_greedy_correctness_with_preemption(baseline_llm_generator, ...@@ -187,7 +201,7 @@ def test_medusa_e2e_greedy_correctness_with_preemption(baseline_llm_generator,
"dtype": PRECISION, "dtype": PRECISION,
# Main model # Main model
"model": MAIN_MODEL, "model_name": MAIN_MODEL,
}]) }])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}]) @pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
...@@ -209,16 +223,22 @@ def test_medusa_e2e_greedy_correctness_with_preemption(baseline_llm_generator, ...@@ -209,16 +223,22 @@ def test_medusa_e2e_greedy_correctness_with_preemption(baseline_llm_generator,
32, 32,
]) ])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_medusa_different_k(baseline_llm_generator, test_llm_generator, def test_medusa_different_k(vllm_runner, common_llm_kwargs,
batch_size: int, output_len: int): per_test_common_llm_kwargs, baseline_llm_kwargs,
test_llm_kwargs, batch_size: int, output_len: int,
seed: int):
"""Verify that medusa speculative decoding produces exact equality """Verify that medusa speculative decoding produces exact equality
to without spec decode with different values of num_speculative_tokens. to without spec decode with different values of num_speculative_tokens.
""" """
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test(vllm_runner,
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len=output_len, baseline_llm_kwargs,
force_output_len=True) test_llm_kwargs,
batch_size,
max_output_len=output_len,
seed=seed,
temperature=0.0)
@pytest.mark.parametrize( @pytest.mark.parametrize(
...@@ -234,7 +254,7 @@ def test_medusa_different_k(baseline_llm_generator, test_llm_generator, ...@@ -234,7 +254,7 @@ def test_medusa_different_k(baseline_llm_generator, test_llm_generator,
"dtype": PRECISION, "dtype": PRECISION,
# Main model # Main model
"model": MAIN_MODEL, "model_name": MAIN_MODEL,
}]) }])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}]) @pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
...@@ -252,17 +272,23 @@ def test_medusa_different_k(baseline_llm_generator, test_llm_generator, ...@@ -252,17 +272,23 @@ def test_medusa_different_k(baseline_llm_generator, test_llm_generator,
32, 32,
]) ])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_medusa_disable_queue(baseline_llm_generator, test_llm_generator, def test_medusa_disable_queue(vllm_runner, common_llm_kwargs,
batch_size: int, output_len: int): per_test_common_llm_kwargs, baseline_llm_kwargs,
test_llm_kwargs, batch_size: int,
output_len: int, seed: int):
"""Verify that medusa speculative decoding produces exact equality """Verify that medusa speculative decoding produces exact equality
to without spec decode when speculation is disabled for large to without spec decode when speculation is disabled for large
batch sizes. batch sizes.
""" """
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test(vllm_runner,
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len=output_len, baseline_llm_kwargs,
force_output_len=True) test_llm_kwargs,
batch_size,
max_output_len=output_len,
seed=seed,
temperature=0.0)
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -25,8 +25,7 @@ import pytest ...@@ -25,8 +25,7 @@ import pytest
from vllm.model_executor.layers.vocab_parallel_embedding import pad_vocab_size from vllm.model_executor.layers.vocab_parallel_embedding import pad_vocab_size
from .conftest import (run_equality_correctness_test, from .conftest import run_equality_correctness_test
run_greedy_equality_correctness_test)
# main model # main model
MAIN_MODEL = "JackFram/llama-160m" MAIN_MODEL = "JackFram/llama-160m"
...@@ -58,7 +57,7 @@ PRECISION = "float32" ...@@ -58,7 +57,7 @@ PRECISION = "float32"
"dtype": PRECISION, "dtype": PRECISION,
# Main model # Main model
"model": MAIN_MODEL, "model_name": MAIN_MODEL,
}]) }])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}]) @pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
...@@ -72,14 +71,21 @@ PRECISION = "float32" ...@@ -72,14 +71,21 @@ PRECISION = "float32"
]) ])
@pytest.mark.parametrize("batch_size", [1, 32]) @pytest.mark.parametrize("batch_size", [1, 32])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_mlp_e2e_greedy_correctness(baseline_llm_generator, test_llm_generator, def test_mlp_e2e_greedy_correctness(vllm_runner, common_llm_kwargs,
batch_size: int, output_len: int): per_test_common_llm_kwargs,
baseline_llm_kwargs, test_llm_kwargs,
batch_size: int, output_len: int,
seed: int):
"""Verify greedy equality with different batch size.""" """Verify greedy equality with different batch size."""
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test(vllm_runner,
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len=output_len, baseline_llm_kwargs,
force_output_len=True) test_llm_kwargs,
batch_size,
max_output_len=output_len,
seed=seed,
temperature=0.0)
@pytest.mark.parametrize( @pytest.mark.parametrize(
...@@ -98,7 +104,7 @@ def test_mlp_e2e_greedy_correctness(baseline_llm_generator, test_llm_generator, ...@@ -98,7 +104,7 @@ def test_mlp_e2e_greedy_correctness(baseline_llm_generator, test_llm_generator,
"dtype": PRECISION, "dtype": PRECISION,
# Main model # Main model
"model": MAIN_MODEL, "model_name": MAIN_MODEL,
}]) }])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}]) @pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
...@@ -110,17 +116,21 @@ def test_mlp_e2e_greedy_correctness(baseline_llm_generator, test_llm_generator, ...@@ -110,17 +116,21 @@ def test_mlp_e2e_greedy_correctness(baseline_llm_generator, test_llm_generator,
@pytest.mark.parametrize("output_len", [2048]) @pytest.mark.parametrize("output_len", [2048])
@pytest.mark.parametrize("batch_size", [1, 32]) @pytest.mark.parametrize("batch_size", [1, 32])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_mlp_e2e_acceptance_rate(baseline_llm_generator, test_llm_generator, def test_mlp_e2e_acceptance_rate(vllm_runner, common_llm_kwargs,
batch_size: int, output_len: int): per_test_common_llm_kwargs,
baseline_llm_kwargs, test_llm_kwargs,
batch_size: int, output_len: int, seed: int):
"""Verify acceptance rate with different batch size and large output """Verify acceptance rate with different batch size and large output
length.""" length."""
run_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test(vllm_runner,
test_llm_generator, common_llm_kwargs,
per_test_common_llm_kwargs,
baseline_llm_kwargs,
test_llm_kwargs,
batch_size, batch_size,
max_output_len=output_len, max_output_len=output_len,
temperature=0.0, temperature=0.0,
seeded=True, seed=seed,
force_output_len=True,
expected_acceptance_rate=0.48) expected_acceptance_rate=0.48)
...@@ -140,7 +150,7 @@ def test_mlp_e2e_acceptance_rate(baseline_llm_generator, test_llm_generator, ...@@ -140,7 +150,7 @@ def test_mlp_e2e_acceptance_rate(baseline_llm_generator, test_llm_generator,
"dtype": PRECISION, "dtype": PRECISION,
# Main model # Main model
"model": MAIN_MODEL, "model_name": MAIN_MODEL,
# Speculative model # Speculative model
"speculative_model": SPEC_MODEL, "speculative_model": SPEC_MODEL,
...@@ -151,28 +161,35 @@ def test_mlp_e2e_acceptance_rate(baseline_llm_generator, test_llm_generator, ...@@ -151,28 +161,35 @@ def test_mlp_e2e_acceptance_rate(baseline_llm_generator, test_llm_generator,
@pytest.mark.parametrize("output_len", [64]) @pytest.mark.parametrize("output_len", [64])
@pytest.mark.parametrize("batch_size", [1, 32]) @pytest.mark.parametrize("batch_size", [1, 32])
@pytest.mark.parametrize("temperature", [0.1, 1.0]) @pytest.mark.parametrize("temperature", [0.1, 1.0])
@pytest.mark.parametrize("seed", [None]) @pytest.mark.parametrize("seed", [1])
def test_mlp_e2e_seeded_correctness(baseline_llm_generator, test_llm_generator, def test_mlp_e2e_seeded_correctness(vllm_runner, common_llm_kwargs,
per_test_common_llm_kwargs,
baseline_llm_kwargs, test_llm_kwargs,
batch_size: int, output_len: int, batch_size: int, output_len: int,
temperature: float): temperature: float, seed: int):
"""Verify seeded runs produce the same output.""" """Verify seeded runs produce the same output."""
run_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test(vllm_runner,
test_llm_generator, common_llm_kwargs,
per_test_common_llm_kwargs,
baseline_llm_kwargs,
test_llm_kwargs,
batch_size, batch_size,
max_output_len=output_len, max_output_len=output_len,
temperature=temperature, temperature=temperature,
seeded=True, seed=seed)
force_output_len=True)
# Ensure this same test does fail if we _don't_ include per-request seeds # Ensure this same test does fail if we _don't_ include per-request seeds
with pytest.raises(AssertionError): with pytest.raises(AssertionError):
run_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test(vllm_runner,
test_llm_generator, common_llm_kwargs,
per_test_common_llm_kwargs,
baseline_llm_kwargs,
test_llm_kwargs,
batch_size, batch_size,
max_output_len=output_len, max_output_len=output_len,
temperature=temperature, temperature=temperature,
seeded=False, seed=seed,
force_output_len=True) disable_seed=True)
@pytest.mark.parametrize( @pytest.mark.parametrize(
...@@ -193,7 +210,7 @@ def test_mlp_e2e_seeded_correctness(baseline_llm_generator, test_llm_generator, ...@@ -193,7 +210,7 @@ def test_mlp_e2e_seeded_correctness(baseline_llm_generator, test_llm_generator,
"dtype": PRECISION, "dtype": PRECISION,
# Main model # Main model
"model": MAIN_MODEL, "model_name": MAIN_MODEL,
}]) }])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}]) @pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
...@@ -210,18 +227,22 @@ def test_mlp_e2e_seeded_correctness(baseline_llm_generator, test_llm_generator, ...@@ -210,18 +227,22 @@ def test_mlp_e2e_seeded_correctness(baseline_llm_generator, test_llm_generator,
]) ])
@pytest.mark.parametrize("batch_size", [4]) @pytest.mark.parametrize("batch_size", [4])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_mlp_e2e_greedy_correctness_with_preemption(baseline_llm_generator, def test_mlp_e2e_greedy_correctness_with_preemption(
test_llm_generator, vllm_runner, common_llm_kwargs, per_test_common_llm_kwargs,
batch_size: int, baseline_llm_kwargs, test_llm_kwargs, batch_size: int, output_len: int,
output_len: int): seed: int):
"""Verify greedy equality, even when some sequences are preempted mid- """Verify greedy equality, even when some sequences are preempted mid-
generation. generation.
""" """
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test(vllm_runner,
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len=output_len, baseline_llm_kwargs,
force_output_len=True) test_llm_kwargs,
batch_size,
max_output_len=output_len,
seed=seed,
temperature=0.0)
@pytest.mark.parametrize( @pytest.mark.parametrize(
...@@ -242,7 +263,7 @@ def test_mlp_e2e_greedy_correctness_with_preemption(baseline_llm_generator, ...@@ -242,7 +263,7 @@ def test_mlp_e2e_greedy_correctness_with_preemption(baseline_llm_generator,
"dtype": PRECISION, "dtype": PRECISION,
# Main model # Main model
"model": MAIN_MODEL, "model_name": MAIN_MODEL,
}]) }])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}]) @pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
...@@ -259,10 +280,10 @@ def test_mlp_e2e_greedy_correctness_with_preemption(baseline_llm_generator, ...@@ -259,10 +280,10 @@ def test_mlp_e2e_greedy_correctness_with_preemption(baseline_llm_generator,
]) ])
@pytest.mark.parametrize("batch_size", [4]) @pytest.mark.parametrize("batch_size", [4])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_mlp_e2e_greedy_correctness_with_padding(baseline_llm_generator, def test_mlp_e2e_greedy_correctness_with_padding(
test_llm_generator, vllm_runner, common_llm_kwargs, per_test_common_llm_kwargs,
batch_size: int, baseline_llm_kwargs, test_llm_kwargs, batch_size: int, output_len: int,
output_len: int): seed: int):
"""Verify greedy equality when the vocab dimension is padded """Verify greedy equality when the vocab dimension is padded
""" """
...@@ -273,11 +294,15 @@ def test_mlp_e2e_greedy_correctness_with_padding(baseline_llm_generator, ...@@ -273,11 +294,15 @@ def test_mlp_e2e_greedy_correctness_with_padding(baseline_llm_generator,
with patch( with patch(
"vllm.model_executor.layers.vocab_parallel_embedding.pad_vocab_size", "vllm.model_executor.layers.vocab_parallel_embedding.pad_vocab_size",
patched_pad_vocab_size): patched_pad_vocab_size):
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test(vllm_runner,
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len=output_len, baseline_llm_kwargs,
force_output_len=True) test_llm_kwargs,
batch_size,
max_output_len=output_len,
seed=seed,
temperature=0.0)
@pytest.mark.parametrize( @pytest.mark.parametrize(
...@@ -293,7 +318,7 @@ def test_mlp_e2e_greedy_correctness_with_padding(baseline_llm_generator, ...@@ -293,7 +318,7 @@ def test_mlp_e2e_greedy_correctness_with_padding(baseline_llm_generator,
"dtype": PRECISION, "dtype": PRECISION,
# Main model # Main model
"model": MAIN_MODEL, "model_name": MAIN_MODEL,
}]) }])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}]) @pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
...@@ -315,16 +340,22 @@ def test_mlp_e2e_greedy_correctness_with_padding(baseline_llm_generator, ...@@ -315,16 +340,22 @@ def test_mlp_e2e_greedy_correctness_with_padding(baseline_llm_generator,
32, 32,
]) ])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_mlp_different_k(baseline_llm_generator, test_llm_generator, def test_mlp_different_k(vllm_runner, common_llm_kwargs,
batch_size: int, output_len: int): per_test_common_llm_kwargs, baseline_llm_kwargs,
test_llm_kwargs, batch_size: int, seed: int,
output_len: int):
"""Verify that mlp speculative decoding produces exact equality """Verify that mlp speculative decoding produces exact equality
to without spec decode with different values of num_speculative_tokens. to without spec decode with different values of num_speculative_tokens.
""" """
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test(vllm_runner,
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len=output_len, baseline_llm_kwargs,
force_output_len=True) test_llm_kwargs,
batch_size,
max_output_len=output_len,
seed=seed,
temperature=0.0)
@pytest.mark.parametrize( @pytest.mark.parametrize(
...@@ -340,7 +371,7 @@ def test_mlp_different_k(baseline_llm_generator, test_llm_generator, ...@@ -340,7 +371,7 @@ def test_mlp_different_k(baseline_llm_generator, test_llm_generator,
"dtype": PRECISION, "dtype": PRECISION,
# Main model # Main model
"model": MAIN_MODEL, "model_name": MAIN_MODEL,
}]) }])
@pytest.mark.parametrize("per_test_common_llm_kwargs", [{}]) @pytest.mark.parametrize("per_test_common_llm_kwargs", [{}])
@pytest.mark.parametrize("baseline_llm_kwargs", [{}]) @pytest.mark.parametrize("baseline_llm_kwargs", [{}])
...@@ -357,14 +388,20 @@ def test_mlp_different_k(baseline_llm_generator, test_llm_generator, ...@@ -357,14 +388,20 @@ def test_mlp_different_k(baseline_llm_generator, test_llm_generator,
32, 32,
]) ])
@pytest.mark.parametrize("seed", [1]) @pytest.mark.parametrize("seed", [1])
def test_mlp_disable_queue(baseline_llm_generator, test_llm_generator, def test_mlp_disable_queue(vllm_runner, common_llm_kwargs,
batch_size: int, output_len: int): per_test_common_llm_kwargs, baseline_llm_kwargs,
test_llm_kwargs, batch_size: int, seed: int,
output_len: int):
"""Verify that mlp speculative decoding produces exact equality """Verify that mlp speculative decoding produces exact equality
to without spec decode when speculation is disabled for large to without spec decode when speculation is disabled for large
batch sizes. batch sizes.
""" """
run_greedy_equality_correctness_test(baseline_llm_generator, run_equality_correctness_test(vllm_runner,
test_llm_generator, common_llm_kwargs,
batch_size, per_test_common_llm_kwargs,
max_output_len=output_len, baseline_llm_kwargs,
force_output_len=True) test_llm_kwargs,
batch_size,
max_output_len=output_len,
seed=seed,
temperature=0.0)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment