main
py 129 lines 4.39 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 # Adapted from https://huggingface.co/docs/transformers/perplexity
4 from typing import cast
5
6 import torch
7 from datasets import load_dataset
8
9 import tests.ci_envs as ci_envs
10 from tests.models.utils import (
11 GenerateModelInfo,
12 TokensTextLogprobsPromptLogprobs,
13 get_vllm_extra_kwargs,
14 )
15 from vllm.logprobs import Logprob
16
17 # See #24485
18 PPL_TOL = 0.01
19 MAX_LENGTH = 1024
20
21
22 @torch.inference_mode
23 def wikitext_ppl_test(
24 hf_runner,
25 vllm_runner,
26 model_info: GenerateModelInfo,
27 max_length=MAX_LENGTH,
28 vllm_extra_kwargs=None,
29 atol=PPL_TOL,
30 ):
31 vllm_extra_kwargs = get_vllm_extra_kwargs(model_info, vllm_extra_kwargs)
32
33 dataset = load_dataset("Salesforce/wikitext", "wikitext-2-raw-v1", split="test")
34
35 with vllm_runner(
36 model_info.name,
37 gpu_memory_utilization=0.7,
38 max_model_len=max_length,
39 max_num_seqs=1,
40 **vllm_extra_kwargs,
41 ) as vllm_model:
42 # Use max_num_seqs=1 to avoid OOM,
43 # and avoid batch different requests together.
44
45 model_config = vllm_model.llm.llm_engine.model_config
46
47 # Confirm whether vllm is using the correct architecture
48 if model_info.architecture:
49 assert model_info.architecture in model_config.architectures
50
51 max_length = min(model_config.max_model_len - 1, max_length)
52 stride = max_length
53
54 tokenizer = vllm_model.llm.get_tokenizer()
55 tokens = tokenizer.encode("\n\n".join(dataset["text"]))
56 n_tokens = len(tokens)
57
58 chunks = []
59 for begin_loc in range(0, n_tokens, stride):
60 end_loc = min(begin_loc + max_length, n_tokens)
61 chunks.append(tokens[begin_loc:end_loc])
62
63 outputs = vllm_model.generate_greedy_logprobs(
64 prompts=chunks,
65 max_tokens=1,
66 num_logprobs=None,
67 num_prompt_logprobs=0,
68 use_tqdm=False,
69 )
70 nll_sum = torch.tensor(0.0, dtype=torch.float32, device="cpu")
71 n_tokens = 0
72 for output in outputs:
73 output = cast(TokensTextLogprobsPromptLogprobs, output)
74 token_datas = cast(list[dict[int, Logprob] | None], output[3])
75
76 assert token_datas[0] is None
77 token_log_probs = []
78 for token_data in token_datas[1:]:
79 assert token_data is not None
80 assert len(token_data) == 1
81 token_log_prob = list(token_data.values())[0].logprob
82 token_log_probs.append(token_log_prob)
83
84 neg_log_likelihood = -torch.tensor(
85 token_log_probs, dtype=torch.float32, device="cpu"
86 ).sum()
87 nll_sum += neg_log_likelihood
88 n_tokens += len(token_log_probs)
89 vllm_ppl = float(torch.exp(nll_sum / n_tokens))
90 vllm_dtype = model_config.dtype
91 head_dtype = model_config.head_dtype
92
93 # Accelerate ppl test by setting Transformers ppl score to a constant
94 if model_info.hf_ppl is None:
95 with hf_runner(
96 model_info.name,
97 dtype=ci_envs.VLLM_CI_HF_DTYPE or model_info.hf_dtype,
98 ) as hf_model:
99 nll_sum = torch.tensor(0.0, dtype=torch.float32, device="cpu")
100 n_tokens = 0
101 for chunk in chunks:
102 inputs = hf_model.wrap_device({"input_ids": torch.tensor([chunk])})
103 input_ids = inputs["input_ids"]
104 outputs = hf_model.model(input_ids, labels=input_ids)
105 neg_log_likelihood = outputs.loss
106
107 neg_log_likelihood = neg_log_likelihood.to(torch.float32).cpu()
108
109 num_loss_tokens = len(chunk) - 1
110 nll_sum += neg_log_likelihood * num_loss_tokens
111 n_tokens += num_loss_tokens
112
113 hf_ppl = float(torch.exp(nll_sum / n_tokens))
114 hf_dtype = next(hf_model.model.parameters()).dtype
115 else:
116 hf_ppl = model_info.hf_ppl
117 hf_dtype = "Constant"
118
119 differ = (vllm_ppl - hf_ppl) / hf_ppl
120 print("Model:", model_info.name)
121 print("VLLM:", f"dtype:{vllm_dtype}", f"head_dtype:{head_dtype}", vllm_ppl)
122 print("Transformers:", hf_dtype, hf_ppl)
123 print("Difference (%):", differ * 100)
124
125 # PPL the smaller, the better
126 # We are not concerned that the vllm PPL is less than Transformers,
127 # so we only perform one-sided testing.
128 assert differ < atol
129 return vllm_ppl