A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 14, 05:30 UTC
[V0 Deprecation] Remove V0 Samplers test (#23862)
Woosuk Kwon committed
Aug 28, 2025 at 18:05 UTC
b668055a114086b8968d9ff4a53586f1d8ea0b47
2 files changed
-855
tests/samplers/test_sampler.py
deleted
-769
@@ -1,769 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-
4
-import itertools
5
-import random
6
-from dataclasses import dataclass
7
-from typing import Optional
8
-from unittest.mock import Mock, patch
9
-
10
-import pytest
11
-import torch
12
-from transformers import GenerationConfig, GenerationMixin
13
-
14
-import vllm.envs as envs
15
-from vllm.model_executor.layers.sampler import Sampler
16
-from vllm.model_executor.sampling_metadata import SamplingMetadata
17
-from vllm.model_executor.utils import set_random_seed
18
-from vllm.sequence import SamplingParams, SequenceData, SequenceGroupMetadata
19
-from vllm.utils import Counter, is_pin_memory_available
20
-
21
-
22
-@pytest.fixture(scope="function", autouse=True)
23
-def use_v0_only(monkeypatch):
24
- """
25
- This file tests V0 internals, so set VLLM_USE_V1=0.
26
- """
27
- monkeypatch.setenv('VLLM_USE_V1', '0')
28
-
29
-
30
-class MockLogitsSampler(Sampler):
31
-
32
- def __init__(self, fake_logits: torch.Tensor):
33
- super().__init__()
34
- self.fake_logits = fake_logits
35
-
36
- def forward(self, *args, **kwargs):
37
- return super().forward(*args, **kwargs)
38
-
39
-
40
-def _prepare_test(
41
- batch_size: int
42
-) -> tuple[torch.Tensor, torch.Tensor, MockLogitsSampler]:
43
- input_tensor = torch.rand((batch_size, 1024), dtype=torch.float16)
44
- fake_logits = torch.full((batch_size, VOCAB_SIZE),
45
- 1e-2,
46
- dtype=input_tensor.dtype)
47
- sampler = MockLogitsSampler(fake_logits)
48
- return input_tensor, fake_logits, sampler
49
-
50
-
51
-VOCAB_SIZE = 32000
52
-RANDOM_SEEDS = list(range(128))
53
-CUDA_DEVICES = [
54
- f"cuda:{i}" for i in range(1 if torch.cuda.device_count() == 1 else 2)
55
-]
56
-
57
-
58
-def _do_sample(
59
- batch_size: int,
60
- input_tensor: torch.Tensor,
61
- sampler: MockLogitsSampler,
62
- sampling_params: SamplingParams,
63
- device: str,
64
-):
65
- seq_group_metadata_list: list[SequenceGroupMetadata] = []
66
- seq_lens: list[int] = []
67
- for i in range(batch_size):
68
- seq_group_metadata_list.append(
69
- SequenceGroupMetadata(
70
- request_id=f"test_{i}",
71
- is_prompt=True,
72
- seq_data={0: SequenceData.from_seqs([1, 2, 3])},
73
- sampling_params=sampling_params,
74
- block_tables={0: [1]},
75
- ))
76
- seq_lens.append(seq_group_metadata_list[-1].seq_data[0].get_len())
77
-
78
- sampling_metadata = SamplingMetadata.prepare(
79
- seq_group_metadata_list,
80
- seq_lens,
81
- query_lens=seq_lens,
82
- device=device,
83
- pin_memory=is_pin_memory_available())
84
- return sampler(logits=input_tensor, sampling_metadata=sampling_metadata)
85
-
86
-
87
-@pytest.mark.parametrize("seed", RANDOM_SEEDS)
88
-@pytest.mark.parametrize("device", CUDA_DEVICES)
89
-def test_sampler_all_greedy(seed: int, device: str):
90
- set_random_seed(seed)
91
- torch.set_default_device(device)
92
- batch_size = random.randint(1, 256)
93
- input_tensor, fake_logits, sampler = _prepare_test(batch_size)
94
-
95
- sampling_params = SamplingParams(temperature=0)
96
- sampler_output = _do_sample(batch_size, fake_logits, sampler,
97
- sampling_params, device)
98
- expected = torch.argmax(fake_logits, dim=-1)
99
- for i, sequence_output in enumerate(sampler_output):
100
- for nth_output in sequence_output.samples:
101
- assert nth_output.output_token == expected[i].item()
102
-
103
-
104
-@pytest.mark.parametrize("seed", RANDOM_SEEDS)
105
-@pytest.mark.parametrize("device", CUDA_DEVICES)
106
-def test_sampler_all_random(seed: int, device: str):
107
- set_random_seed(seed)
108
- torch.set_default_device(device)
109
- batch_size = random.randint(1, 256)
110
- _, fake_logits, sampler = _prepare_test(batch_size)
111
-
112
- for i in range(batch_size):
113
- fake_logits[i, i] = 1e2
114
-
115
- sampling_params = SamplingParams(
116
- temperature=1.0,
117
- n=random.randint(1, 10),
118
- )
119
- sampler_output = _do_sample(batch_size, fake_logits, sampler,
120
- sampling_params, device)
121
-
122
- for i, sequence_output in enumerate(sampler_output):
123
- for nth_output in sequence_output.samples:
124
- assert nth_output.output_token == i
125
-
126
-
127
-@pytest.mark.parametrize("seed", RANDOM_SEEDS)
128
-@pytest.mark.parametrize("device", CUDA_DEVICES)
129
-def test_sampler_all_random_seed(seed: int, device: str):
130
- set_random_seed(seed)
131
- torch.set_default_device(device)
132
- batch_size = random.randint(1, 256)
133
- _, fake_logits, sampler = _prepare_test(batch_size)
134
-
135
- for i in range(batch_size):
136
- fake_logits[i, i] = 1e2
137
-
138
- sampling_params = SamplingParams(
139
- temperature=1.0,
140
- n=random.randint(1, 10),
141
- seed=random.randint(0, 10000),
142
- )
143
- sampler_output = _do_sample(batch_size, fake_logits, sampler,
144
- sampling_params, device)
145
-
146
- for i, sequence_output in enumerate(sampler_output):
147
- for nth_output in sequence_output.samples:
148
- assert nth_output.output_token == i
149
-
150
-
151
-@pytest.mark.parametrize("seed", RANDOM_SEEDS)
152
-@pytest.mark.parametrize("device", CUDA_DEVICES)
153
-def test_sampler_all_random_seed_deterministic(seed: int, device: str):
154
- set_random_seed(seed)
155
- torch.set_default_device(device)
156
- batch_size = random.randint(1, 256)
157
- _, fake_logits, sampler = _prepare_test(batch_size)
158
-
159
- sampling_params = SamplingParams(
160
- temperature=1.0,
161
- n=random.randint(1, 10),
162
- seed=random.randint(0, 10000),
163
- )
164
- first_sampler_output = _do_sample(batch_size, fake_logits, sampler,
165
- sampling_params, device)
166
-
167
- second_sampler_output = _do_sample(batch_size, fake_logits, sampler,
168
- sampling_params, device)
169
-
170
- assert first_sampler_output == second_sampler_output
171
-
172
-
173
-@pytest.mark.parametrize("seed", RANDOM_SEEDS)
174
-@pytest.mark.parametrize("device", CUDA_DEVICES)
175
-def test_sampler_min_tokens_penalty(seed: int, device: str):
176
- seq_id_counter = Counter(start=random.randint(0, 100))
177
- set_random_seed(seed)
178
- torch.set_default_device(device)
179
-
180
- def create_sampling_params(min_tokens,
181
- eos_token_id=0,
182
- *,
183
- stop_token_ids: Optional[list[int]] = None,
184
- prompt_logprobs: Optional[int] = None):
185
- sampling_params = SamplingParams(
186
- min_tokens=min_tokens,
187
- max_tokens=9999, # keep higher than max of min_tokens
188
- stop_token_ids=stop_token_ids,
189
- # requesting prompt_logprobs changes the structure of `logits`
190
- prompt_logprobs=prompt_logprobs,
191
- )
192
- sampling_params.all_stop_token_ids.add(eos_token_id)
193
- return sampling_params
194
-
195
- def create_sequence_data(num_input=3, num_generated=0):
196
- seq_data = SequenceData.from_seqs(
197
- random.choices(range(0, VOCAB_SIZE), k=num_input))
198
- if num_generated > 0:
199
- seq_data.output_token_ids = random.choices(range(0, VOCAB_SIZE),
200
- k=num_generated)
201
- return seq_data
202
-
203
- def generate_test_case():
204
- # generate multiple seq groups but limit total batch size
205
- batch_size = random.randint(1, 128)
206
-
207
- expected_penalization = []
208
- sequence_metadata_list: list[SequenceGroupMetadata] = []
209
- # 20% chance to generate seq group metadata list with all prompts
210
- is_prompt = random.random() < 0.2
211
- while batch_size > 0:
212
- num_seqs = 1 if is_prompt else random.randint(1, batch_size)
213
-
214
- eos_token_id = random.randint(0, VOCAB_SIZE - 1)
215
- min_tokens = random.randint(0, 50)
216
- num_stop_tokens = random.randint(0, 8)
217
- if num_stop_tokens > 0:
218
- stop_token_ids = random.choices(range(0, VOCAB_SIZE - 1),
219
- k=num_stop_tokens)
220
- else:
221
- stop_token_ids = None
222
-
223
- sampling_params = create_sampling_params(
224
- min_tokens=min_tokens,
225
- eos_token_id=eos_token_id,
226
- stop_token_ids=stop_token_ids)
227
-
228
- seq_data: dict[int, SequenceData] = {}
229
- seq_group_penalization: list[bool] = []
230
- for _ in range(num_seqs):
231
- num_input = random.randint(1, 100)
232
- num_generated = 0 if is_prompt else random.randint(1, 100)
233
- seq_data[next(seq_id_counter)] = create_sequence_data(
234
- num_input=num_input, num_generated=num_generated)
235
- seq_group_penalization.append(num_generated < min_tokens)
236
-
237
- expected_penalization.extend(seq_group_penalization)
238
- sequence_metadata_list.append(
239
- SequenceGroupMetadata(
240
- request_id=f"test_{batch_size}",
241
- is_prompt=is_prompt,
242
- seq_data=seq_data,
243
- sampling_params=sampling_params,
244
- block_tables={},
245
- ))
246
- batch_size -= num_seqs
247
-
248
- return {
249
- "expected_penalization": expected_penalization,
250
- "seq_group_metadata_list": sequence_metadata_list,
251
- }
252
-
253
- # define some explicit test cases for edge case behavior
254
- prompt_without_penalization = {
255
- "expected_penalization": [False],
256
- "seq_group_metadata_list": [
257
- SequenceGroupMetadata(
258
- request_id="test_1",
259
- is_prompt=True,
260
- seq_data={
261
- next(seq_id_counter): create_sequence_data(),
262
- },
263
- sampling_params=create_sampling_params(0),
264
- block_tables={},
265
- ),
266
- ]
267
- }
268
-
269
- prompt_with_penalization = {
270
- "expected_penalization": [True],
271
- "seq_group_metadata_list": [
272
- SequenceGroupMetadata(
273
- request_id="test_1",
274
- is_prompt=True,
275
- seq_data={
276
- next(seq_id_counter): create_sequence_data(),
277
- },
278
- sampling_params=create_sampling_params(1),
279
- block_tables={},
280
- ),
281
- ]
282
- }
283
-
284
- prompt_with_penalization_and_prompt_logprobs = {
285
- "expected_penalization": [False, False, True],
286
- "seq_group_metadata_list": [
287
- SequenceGroupMetadata(
288
- request_id="test_1",
289
- is_prompt=True,
290
- seq_data={
291
- next(seq_id_counter): create_sequence_data(num_input=3),
292
- },
293
- sampling_params=create_sampling_params(1, prompt_logprobs=3),
294
- block_tables={},
295
- ),
296
- ]
297
- }
298
-
299
- stop_penalizing_after_min_tokens = {
300
- "expected_penalization": [False],
301
- "seq_group_metadata_list": [
302
- SequenceGroupMetadata(
303
- request_id="test_1",
304
- is_prompt=False,
305
- seq_data={
306
- next(seq_id_counter):
307
- create_sequence_data(num_generated=1),
308
- },
309
- sampling_params=create_sampling_params(1),
310
- block_tables={},
311
- )
312
- ]
313
- }
314
-
315
- stop_token_ids = [42, 99, 42, 0] # intentional duplication
316
- prompt_combination = {
317
- "expected_penalization": [False, True, False],
318
- "seq_group_metadata_list": [
319
- SequenceGroupMetadata(
320
- request_id="test_2",
321
- is_prompt=True,
322
- seq_data={
323
- next(seq_id_counter): create_sequence_data(num_input=2),
324
- },
325
- sampling_params=create_sampling_params(1, prompt_logprobs=3),
326
- block_tables={},
327
- ),
328
- SequenceGroupMetadata(
329
- request_id="test_3",
330
- is_prompt=True,
331
- seq_data={
332
- next(seq_id_counter): create_sequence_data(),
333
- },
334
- sampling_params=create_sampling_params(
335
- 0, stop_token_ids=stop_token_ids),
336
- block_tables={},
337
- )
338
- ]
339
- }
340
-
341
- stop_token_ids = [1, 999, 37, 37] # intentional duplication
342
- decode_combination = {
343
- "expected_penalization": [True, False, False, True, False],
344
- "seq_group_metadata_list": [
345
- SequenceGroupMetadata(
346
- request_id="test_1",
347
- is_prompt=False,
348
- seq_data={
349
- next(seq_id_counter):
350
- create_sequence_data(num_generated=1),
351
- next(seq_id_counter):
352
- create_sequence_data(num_generated=100),
353
- },
354
- sampling_params=create_sampling_params(
355
- 2, stop_token_ids=stop_token_ids),
356
- block_tables={},
357
- ),
358
- SequenceGroupMetadata(
359
- request_id="test_2",
360
- is_prompt=False,
361
- seq_data={
362
- next(seq_id_counter):
363
- create_sequence_data(num_generated=20),
364
- next(seq_id_counter):
365
- create_sequence_data(num_generated=1),
366
- next(seq_id_counter):
367
- create_sequence_data(num_generated=10),
368
- },
369
- sampling_params=create_sampling_params(
370
- 10, prompt_logprobs=5, stop_token_ids=stop_token_ids),
371
- block_tables={},
372
- ),
373
- ]
374
- }
375
-
376
- if seed == 0:
377
- test_cases = [
378
- prompt_without_penalization,
379
- prompt_with_penalization,
380
- prompt_with_penalization_and_prompt_logprobs,
381
- stop_penalizing_after_min_tokens,
382
- prompt_combination,
383
- decode_combination,
384
- ]
385
- else:
386
- test_cases = [generate_test_case()]
387
-
388
- def run_test_case(*, expected_penalization: list[bool],
389
- seq_group_metadata_list: list[SequenceGroupMetadata]):
390
- assert expected_penalization, \
391
- "Invalid test case, need expected_penalization"
392
- assert seq_group_metadata_list, \
393
- "Invalid test case, need seq_group_metadata_list"
394
-
395
- batch_size = 0
396
- seq_lens: list[int] = []
397
- sampling_params_per_row: list[SamplingParams] = []
398
- for sgm in seq_group_metadata_list:
399
- sampling_params = sgm.sampling_params
400
-
401
- num_rows = len(sgm.seq_data)
402
- if sgm.is_prompt:
403
- # a prompt seq_group has only one sequence
404
- seq_data = next(iter(sgm.seq_data.values()))
405
- prompt_len = seq_data.get_prompt_len()
406
- seq_lens.append(prompt_len)
407
-
408
- assert sgm.sampling_params is not None
409
- if sgm.sampling_params.prompt_logprobs:
410
- # with prompt_logprobs each token in the prompt has a row in
411
- # logits
412
- num_rows = prompt_len
413
-
414
- batch_size += num_rows
415
- sampling_params_per_row.extend(
416
- itertools.repeat(sampling_params, num_rows))
417
-
418
- assert len(
419
- expected_penalization
420
- ) == batch_size, \
421
- ("Invalid test case, expected_penalization does not match computed"
422
- "batch size")
423
-
424
- _, fake_logits, sampler = _prepare_test(batch_size)
425
- sampling_metadata = SamplingMetadata.prepare(
426
- seq_group_metadata_list,
427
- seq_lens=seq_lens if seq_lens else None,
428
- query_lens=seq_lens if seq_lens else [1] * batch_size,
429
- device=device,
430
- pin_memory=is_pin_memory_available())
431
- # the logits tensor is modified in-place by the sampler
432
- _ = sampler(logits=fake_logits, sampling_metadata=sampling_metadata)
433
-
434
- for logits_idx, (should_penalize, sampling_params) in enumerate(
435
- zip(expected_penalization, sampling_params_per_row)):
436
-
437
- tokens_to_check = sampling_params.all_stop_token_ids
438
-
439
- if should_penalize:
440
- for token_id in tokens_to_check:
441
- assert fake_logits[logits_idx, token_id] == -float(
442
- 'inf'
443
- ), f"Expected token {token_id} for logits row {logits_idx}"
444
- " to be penalized"
445
- # no other tokens should be set to -inf
446
- assert torch.count_nonzero(
447
- fake_logits[logits_idx, :] == -float('inf')) == len(
448
- tokens_to_check
449
- ), f"Expected only {len(tokens_to_check)} to be penalized"
450
- else:
451
- # no tokens should be set to -inf
452
- assert torch.count_nonzero(
453
- fake_logits[logits_idx, :] ==
454
- -float('inf')) == 0, "No tokens should have been penalized"
455
-
456
- for test_case in test_cases:
457
- run_test_case(**test_case)
458
-
459
-
460
-@pytest.mark.parametrize("seed", RANDOM_SEEDS)
461
-@pytest.mark.parametrize("device", CUDA_DEVICES)
462
-def test_sampler_mixed(seed: int, device: str):
463
- set_random_seed(seed)
464
- torch.set_default_device(device)
465
- batch_size = random.randint(1, 256)
466
- input_tensor, fake_logits, sampler = _prepare_test(batch_size)
467
-
468
- seq_group_metadata_list: list[SequenceGroupMetadata] = []
469
- expected_tokens: list[Optional[list[int]]] = []
470
- seq_lens: list[int] = []
471
- for i in range(batch_size):
472
- expected: Optional[list[int]] = None
473
- sampling_type = random.randint(0, 2)
474
- if sampling_type == 0:
475
- sampling_params = SamplingParams(temperature=0)
476
- expected = [int(torch.argmax(fake_logits[i], dim=-1).item())]
477
- elif sampling_type in (1, 2):
478
- n = random.randint(1, 10)
479
- sampling_params = SamplingParams(
480
- temperature=random.random() + 0.1,
481
- top_p=min(random.random() + 0.1, 1),
482
- top_k=random.randint(0, 10),
483
- n=n,
484
- presence_penalty=random.randint(0, 1),
485
- )
486
- if sampling_type == 2:
487
- sampling_params.seed = random.randint(0, 10000)
488
- else:
489
- for idx in range(n):
490
- fake_logits[i, i + idx] = 1e2
491
- expected = list(range(i, i + n))
492
-
493
- expected_tokens.append(expected)
494
- seq_group_metadata_list.append(
495
- SequenceGroupMetadata(
496
- request_id=f"test_{i}",
497
- is_prompt=True,
498
- seq_data={0: SequenceData.from_seqs([1, 2, 3])},
499
- sampling_params=sampling_params,
500
- block_tables={0: [1]},
501
- ))
502
- seq_lens.append(seq_group_metadata_list[-1].seq_data[0].get_len())
503
-
504
- generators: dict[str, torch.Generator] = {}
505
-
506
- def test_sampling():
507
- sampling_metadata = SamplingMetadata.prepare(
508
- seq_group_metadata_list,
509
- seq_lens,
510
- query_lens=seq_lens,
511
- device=device,
512
- pin_memory=is_pin_memory_available(),
513
- generators=generators)
514
- sampler_output = sampler(logits=fake_logits,
515
- sampling_metadata=sampling_metadata)
516
-
517
- for i, (sequence_output, metadata) in enumerate(
518
- zip(sampler_output, seq_group_metadata_list)):
519
- assert metadata.sampling_params is not None
520
-
521
- if (metadata.sampling_params.seed is not None
522
- and expected_tokens[i] is None):
523
- # Record seeded random result to compare with results of
524
- # second invocation
525
- expected_tokens[i] = [
526
- nth_output.output_token
527
- for nth_output in sequence_output.samples
528
- ]
529
- continue
530
-
531
- expected_tokens_item = expected_tokens[i]
532
- assert expected_tokens_item is not None
533
-
534
- for n, nth_output in enumerate(sequence_output.samples):
535
- assert metadata.sampling_params is not None
536
-
537
- if (metadata.sampling_params.temperature == 0
538
- or metadata.sampling_params.seed is not None):
539
- # Ensure exact matches for greedy or random with seed
540
- assert nth_output.output_token == expected_tokens_item[n]
541
- else:
542
- # For non-seeded random check that one of the high-logit
543
- # tokens were chosen
544
- assert nth_output.output_token in expected_tokens_item
545
-
546
- # Test batch
547
- test_sampling()
548
-
549
- # Shuffle the batch and resample
550
- target_index = list(range(batch_size))
551
- for list_to_shuffle in (target_index, seq_group_metadata_list,
552
- expected_tokens, seq_lens):
553
- random.Random(seed).shuffle(list_to_shuffle)
554
- target_index = torch.tensor(target_index)
555
- input_tensor.data = input_tensor.index_select(0, target_index)
556
- fake_logits.data = fake_logits.index_select(0, target_index)
557
-
558
- # This time, results of seeded random samples will be compared with
559
- # the corresponding sample in the pre-shuffled batch
560
- test_sampling()
561
-
562
-
563
-@pytest.mark.parametrize("seed", RANDOM_SEEDS)
564
-@pytest.mark.parametrize("device", CUDA_DEVICES)
565
-def test_sampler_top_k_top_p(seed: int, device: str):
566
- set_random_seed(seed)
567
- batch_size = random.randint(1, 256)
568
- top_k = random.randint(100, 500)
569
- top_p = random.random() * 0.1
570
- vocab_size = 32000
571
- input_tensor = torch.rand((batch_size, 1024),
572
- device=device,
573
- dtype=torch.float16)
574
- fake_logits = torch.normal(0,
575
- 5,
576
- size=(batch_size, vocab_size),
577
- device=input_tensor.device,
578
- dtype=input_tensor.dtype)
579
- sampler = MockLogitsSampler(fake_logits)
580
-
581
- generation_model = GenerationMixin()
582
- generation_config = GenerationConfig(top_k=top_k,
583
- top_p=top_p,
584
- do_sample=True)
585
-
586
- @dataclass
587
- class MockConfig:
588
- is_encoder_decoder: bool = False
589
-
590
- generation_model.config = MockConfig() # needed by the following method
591
- generation_model._prepare_special_tokens(generation_config, device=device)
592
- processors = generation_model._get_logits_processor(generation_config,
593
- None,
594
- None,
595
- None, [],
596
- device=device)
597
- assert len(processors) == 2 # top_p and top_k
598
-
599
- seq_group_metadata_list: list[SequenceGroupMetadata] = []
600
- seq_lens: list[int] = []
601
- for i in range(batch_size):
602
- seq_group_metadata_list.append(
603
- SequenceGroupMetadata(
604
- request_id=f"test_{i}",
605
- is_prompt=True,
606
- seq_data={0: SequenceData.from_seqs([1, 2, 3])},
607
- sampling_params=SamplingParams(
608
- temperature=1,
609
- top_k=top_k,
610
- top_p=top_p,
611
- ),
612
- block_tables={0: [1]},
613
- ))
614
- seq_lens.append(seq_group_metadata_list[-1].seq_data[0].get_len())
615
-
616
- sampling_metadata = SamplingMetadata.prepare(
617
- seq_group_metadata_list,
618
- seq_lens,
619
- query_lens=seq_lens,
620
- device=device,
621
- pin_memory=is_pin_memory_available())
622
-
623
- sample_probs = None
624
-
625
- def mock_sample(probs, *args, **kwargs):
626
- nonlocal sample_probs
627
- sample_probs = probs
628
- return ([[prob.topk(1, dim=-1).indices.tolist(), [0]]
629
- for prob in probs], None)
630
-
631
- # top-k and top-p is only calculated when flashinfer kernel is not available
632
- with patch("vllm.model_executor.layers.sampler._sample", mock_sample), \
633
- patch("vllm.model_executor.layers.sampler."
634
- "flashinfer_top_k_top_p_sampling", None):
635
- sampler(logits=fake_logits, sampling_metadata=sampling_metadata)
636
-
637
- assert sample_probs is not None
638
-
639
- hf_probs = processors(torch.zeros_like(fake_logits), fake_logits.clone())
640
- hf_probs = torch.softmax(hf_probs, dim=-1, dtype=torch.float)
641
- torch.testing.assert_close(hf_probs, sample_probs, rtol=0.0, atol=1e-5)
642
- assert torch.equal(hf_probs.eq(0), sample_probs.eq(0))
643
-
644
-
645
-@pytest.mark.parametrize("seed", RANDOM_SEEDS)
646
-@pytest.mark.parametrize("device", CUDA_DEVICES)
647
-def test_flashinfer_fallback(seed: int, device: str):
648
- if not envs.VLLM_USE_FLASHINFER_SAMPLER:
649
- pytest.skip("Flashinfer sampler is disabled")
650
-
651
- pytest.skip("After FlashInfer 0.2.3, sampling will never fail")
652
-
653
- set_random_seed(seed)
654
- torch.set_default_device(device)
655
- batch_size = random.randint(1, 256)
656
- _, fake_logits, sampler = _prepare_test(batch_size)
657
-
658
- def failing_flashinfer_sampling(*_args, **_kwargs):
659
- return None, torch.zeros(batch_size, device=device, dtype=torch.int32)
660
-
661
- sampling_params = SamplingParams(
662
- temperature=1.0,
663
- n=random.randint(1, 10),
664
- seed=random.randint(0, 10000),
665
- )
666
- sampler_output = _do_sample(batch_size, fake_logits, sampler,
667
- sampling_params, device)
668
-
669
- with patch(
670
- "vllm.model_executor.layers.sampler."
671
- "flashinfer_top_k_top_p_sampling", failing_flashinfer_sampling):
672
- fallback_sampler_output = _do_sample(batch_size, fake_logits, sampler,
673
- sampling_params, device)
674
-
675
- assert sampler_output == fallback_sampler_output
676
-
677
-
678
-@pytest.mark.parametrize("device", CUDA_DEVICES)
679
-def test_sampler_repetition_penalty_mixed(device: str):
680
-
681
- vocab_size = 8
682
-
683
- def test_sampling_params(sampling_params: list[SamplingParams]):
684
-
685
- seq_group_metadata_list: list[SequenceGroupMetadata] = []
686
- seq_lens: list[int] = []
687
- for i in range(2):
688
- seq_group_metadata_list.append(
689
- SequenceGroupMetadata(
690
- request_id=f"test_{i}",
691
- is_prompt=True,
692
- seq_data={0: SequenceData.from_seqs([1, 2, 3])},
693
- sampling_params=sampling_params[i],
694
- block_tables={0: [1]},
695
- ))
696
- seq_lens.append(seq_group_metadata_list[-1].seq_data[0].get_len())
697
-
698
- sampling_metadata = SamplingMetadata.prepare(
699
- seq_group_metadata_list,
700
- seq_lens,
701
- query_lens=seq_lens,
702
- device=device,
703
- pin_memory=is_pin_memory_available())
704
-
705
- fake_logits = torch.full((2, vocab_size),
706
- 1e-2,
707
- device=device,
708
- dtype=torch.float16)
709
-
710
- fake_logits[:, 5] = 1.1e-2
711
- fake_logits[:, 1] = 1.2e-2
712
-
713
- sampler = MockLogitsSampler(fake_logits)
714
-
715
- sampler_output = sampler(logits=fake_logits,
716
- sampling_metadata=sampling_metadata)
717
-
718
- generated_tokens = []
719
- for output in sampler_output:
720
- generated_tokens.append(output.samples[0].output_token)
721
-
722
- return generated_tokens
723
-
724
- # one configuration is greedy with repetition_penalty
725
- sampling_params_rep = SamplingParams(
726
- temperature=0.0,
727
- repetition_penalty=2.0,
728
- )
729
-
730
- # other configuration is sampling w/o repetition_penalty
731
- sampling_params_sample = SamplingParams(
732
- temperature=1.0,
733
- top_k=1,
734
- seed=42,
735
- )
736
-
737
- tokens1 = test_sampling_params(
738
- [sampling_params_rep, sampling_params_sample])
739
-
740
- tokens2 = test_sampling_params(
741
- [sampling_params_sample, sampling_params_rep])
742
-
743
- assert tokens1[0] == tokens2[1]
744
- assert tokens1[1] == tokens2[0]
745
-
746
-
747
-@pytest.mark.parametrize("device", CUDA_DEVICES)
748
-def test_sampler_include_gpu_probs_tensor(device: str):
749
- set_random_seed(42)
750
- torch.set_default_device(device)
751
- batch_size = random.randint(1, 256)
752
- _, fake_logits, sampler = _prepare_test(batch_size)
753
- sampler.include_gpu_probs_tensor = True
754
- sampler.should_modify_greedy_probs_inplace = False
755
-
756
- sampling_params = SamplingParams(temperature=0)
757
-
758
- mock_inplace = Mock()
759
- with patch(
760
- "vllm.model_executor.layers.sampler._modify_greedy_probs_inplace",
761
- mock_inplace):
762
-
763
- sampler_output = _do_sample(batch_size, fake_logits, sampler,
764
- sampling_params, device)
765
- mock_inplace.assert_not_called()
766
-
767
- assert sampler_output.sampled_token_probs is not None
768
- assert sampler_output.logprobs is not None
769
- assert sampler_output.sampled_token_ids is not None
tests/samplers/test_seeded_generate.py
deleted
-86
@@ -1,86 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-"""Verify that seeded random sampling is deterministic.
4
-
5
-Run `pytest tests/samplers/test_seeded_generate.py`.
6
-"""
7
-import copy
8
-import random
9
-from itertools import combinations
10
-
11
-import pytest
12
-
13
-from vllm import SamplingParams
14
-from vllm.model_executor.utils import set_random_seed
15
-
16
-MODEL = "facebook/opt-125m"
17
-RANDOM_SEEDS = list(range(5))
18
-
19
-
20
-@pytest.fixture
21
-def vllm_model(vllm_runner, monkeypatch):
22
- # This file relies on V0 internals.
23
- monkeypatch.setenv("VLLM_USE_V1", "0")
24
- with vllm_runner(MODEL, dtype="half") as vllm_model:
25
- yield vllm_model
26
-
27
-
28
-@pytest.mark.parametrize("seed", RANDOM_SEEDS)
29
-def test_random_sample_with_seed(
30
- vllm_model,
31
- example_prompts,
32
- seed: int,
33
-) -> None:
34
- set_random_seed(seed)
35
-
36
- sampling_params = SamplingParams(
37
- # Parameters to ensure sufficient randomness
38
- temperature=3.0,
39
- top_p=min(random.random() + 0.3, 1),
40
- top_k=random.randint(5, 20),
41
- n=random.randint(1, 10),
42
- presence_penalty=random.randint(0, 1),
43
- max_tokens=8,
44
- ignore_eos=True,
45
- )
46
-
47
- sampling_params_seed_1 = copy.deepcopy(sampling_params)
48
- sampling_params_seed_1.seed = 100
49
- sampling_params_seed_2 = copy.deepcopy(sampling_params)
50
- sampling_params_seed_2.seed = 200
51
-
52
- llm = vllm_model.llm
53
-
54
- for prompt in example_prompts:
55
- for params in (
56
- sampling_params,
57
- sampling_params_seed_1,
58
- sampling_params_seed_2,
59
- sampling_params,
60
- sampling_params_seed_1,
61
- sampling_params_seed_2,
62
- ):
63
- llm._add_request(prompt, params=params)
64
-
65
- results = llm._run_engine(use_tqdm=False)
66
- all_outputs = [[out.token_ids for out in output.outputs]
67
- for output in results]
68
-
69
- for i in range(0, len(example_prompts), 6):
70
- outputs = all_outputs[i:i + 6]
71
-
72
- # verify all non-seeded requests differ
73
- for output_a, output_b in combinations(
74
- (outputs[0], outputs[1], outputs[2], outputs[3]),
75
- 2,
76
- ):
77
- assert output_a != output_b
78
-
79
- # verify requests with the same seed match
80
- assert outputs[1] == outputs[4]
81
- assert outputs[2] == outputs[5]
82
-
83
- # verify generations within the same parallel sampling group differ
84
- for output in outputs:
85
- for sub_output_a, sub_output_b in combinations(output, 2):
86
- assert sub_output_a != sub_output_b