main
py 99 lines 3.53 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """Shared utilities for benchmark dataset sampling."""
4
5 import logging
6 import math
7
8 import numpy as np
9
10 from vllm.tokenizers import TokenizerLike
11
12 logger = logging.getLogger(__name__)
13
14 # Type alias: a single float applies to both ISL and OSL; a dict allows
15 # specifying them independently via ``{"input": …, "output": …}``.
16 RangeRatio = float | dict[str, float]
17
18
19 def _resolve_range_ratios(
20 range_ratio: RangeRatio,
21 ) -> tuple[float, float]:
22 """Return ``(input_range_ratio, output_range_ratio)`` from *range_ratio*.
23
24 *range_ratio* is either a single float (used for both input and output)
25 or a dict with ``"input"`` and ``"output"`` keys.
26 """
27 if isinstance(range_ratio, dict):
28 try:
29 return float(range_ratio["input"]), float(range_ratio["output"])
30 except KeyError as exc:
31 raise ValueError(
32 "When range_ratio is a dict it must contain 'input' and "
33 f"'output' keys, got: {sorted(range_ratio)}"
34 ) from exc
35 ratio = float(range_ratio)
36 return ratio, ratio
37
38
39 def get_sampling_params(
40 rng: np.random.Generator,
41 num_requests: int,
42 range_ratio: RangeRatio,
43 input_len: int,
44 output_len: int,
45 tokenizer: TokenizerLike,
46 ) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
47 """Sample per-request input/output token lengths and vocab offsets.
48
49 Lengths are drawn uniformly from integer ranges around the configured
50 means, controlled by *range_ratio*. It may be a single ``float``
51 (applied to both input and output) or a ``dict`` with ``"input"`` and
52 ``"output"`` keys for independent control.
53
54 Tokenizer special tokens are subtracted from ``input_len`` before
55 computing the sampling interval.
56
57 Returns:
58 (input_lens, output_lens, offsets) – three 1-D ``np.ndarray`` of
59 shape ``(num_requests,)``.
60
61 """
62 input_range_ratio, output_range_ratio = _resolve_range_ratios(range_ratio)
63
64 if not (0.0 <= input_range_ratio < 1.0):
65 raise ValueError("input_range_ratio must be in [0, 1).")
66 if not (0.0 <= output_range_ratio < 1.0):
67 raise ValueError("output_range_ratio must be in [0, 1).")
68 num_special_tokens = int(tokenizer.num_special_tokens_to_add())
69 real_input_len = max(0, int(input_len) - num_special_tokens)
70 input_low = math.floor(real_input_len * (1 - input_range_ratio))
71 input_high = math.ceil(real_input_len * (1 + input_range_ratio))
72 output_low = math.floor(output_len * (1 - output_range_ratio))
73 output_high = math.ceil(output_len * (1 + output_range_ratio))
74 # Ensure the lower bound for output length is at least 1 to
75 # prevent sampling 0 tokens.
76 output_low = max(output_low, 1)
77 output_high = max(output_high, 1)
78
79 if input_low > input_high:
80 raise ValueError(
81 f"Invalid input sampling interval: low={input_low} > high={input_high}"
82 )
83 if output_low > output_high:
84 raise ValueError(
85 f"Invalid output sampling interval: low={output_low} > high={output_high}"
86 )
87
88 logger.info(
89 "Sampling input_len from [%s, %s] and output_len from [%s, %s]",
90 input_low,
91 input_high,
92 output_low,
93 output_high,
94 )
95
96 input_lens = rng.integers(input_low, input_high + 1, size=num_requests)
97 output_lens = rng.integers(output_low, output_high + 1, size=num_requests)
98 offsets = rng.integers(0, tokenizer.vocab_size, size=num_requests)
99 return input_lens, output_lens, offsets