vllm中的logprobs
背景介绍
在使用大模型推理引擎时,有时除了想要获得generated的text,同时还想获得生成的每个token的logprobability, 获得这个原因可以参考
https://zhuanlan.zhihu.com/p/673424860
https://zhuanlan.zhihu.com/p/673424860
vllm中的logprobs
采用vllm中的offline推理时,可以设置sample_params的logprobs(int)来获得生成过程中每个token。如下:
sampling_params = SamplingParams(
max_tokens=100,
temperature=0.7,
top_p=0.95,
logprobs=1,
stop=["\n\n"]
)
outputs = llm.generate(prompts, sampling_params=sampling_params)
vllm中对logprobs的解释为
- logprobs – Number of log probabilities to return per output token. When set to None, no probability is returned. If set to a non-None value, the result includes the log probabilities of the specified number of most likely tokens, as well as the chosen tokens. Note that the implementation follows the OpenAI API: The API will always return the log probability of the sampled token, so there may be up to logprobs+1 elements in the response.
解释: 就是说默认不设置这个参数,返回的内容就不会包含logprobs。但是当被设置为一个int时,就会包含probability前logprobs的token和最终sample出来的token。如果不使用greedy decoding(上面的参数中temperature=0.7, 所以不是greedy decoding, 如果想使用greedy decoding的话,直接将temperature设为0),这两个token可能不一致,所以返回的至多有(logprobs+1)个token及其对应的logprobs,当sample出来的token在前logprobs中,就有logprobs个token了。
问题来了,如果我只想想要sample出来的token及其对应的logprobs,一种非常简单的方法就是将参数logprobs设为0。
好了,如果想获得sample出来的token的logprobs到这里就结束了!具体的代码实现可以看最后的例子!
但是上面引发的另一个问题就是当参数logprobs >= 1时,由于返回的是一个List[Dict]类型,那sample出来的token和probability前logprobs的token的顺序是什么样的呢?vllm中的文档好像并没有明确说明这个细节,我在查了网上资料好像也没有,openai api对应的logprobs返回逻辑好像与这个不一致。
再重述下问题:当sample参数logprobs>1时,由于包含至多(logprobs + 1)个Dict的List, sample出来的token对应的logprobs是在List的最后还是最前面,于是我写了个简单的脚本进行测试,代码如下:
from vllm import LLM, SamplingParams
from pprint import pprint
from dataclasses import asdict
import json
prompts = ["### Instruction: Hello, my name is", "### Instruction: The capital of France is"] # Sample prompts.
llm = LLM(model="Qwen/Qwen2.5-0.5B-Instruct") # Create an LLM.
sampling_params = SamplingParams(
max_tokens=100,
temperature=0.7,
top_p=0.95,
logprobs=1, # there are up to (logprobs + 1) token data
stop=["\n\n"]
)
outputs = llm.generate(prompts, sampling_params=sampling_params)
for i, request_output in enumerate(outputs):
# pprint(dir(output.outputs[0]))
print(f"Generated text: {request_output.outputs[0].text}")
# Calculate and print the sum of log probabilities
sum_logprobs = 0
if request_output.outputs[0].logprobs:
# write logprobs to file for better readability
with open(f"logprobs_{i}.json", "w") as f:
logprobs_list = []
for logprobs in request_output.outputs[0].logprobs:
tmp = {}
for k in logprobs:
tmp[k] = asdict(logprobs[k])
logprobs_list.append(tmp)
json.dump(logprobs_list, f)
for logprobs in request_output.outputs[0].logprobs:
if len(logprobs) > 1:
print(f"logprobs: {logprobs}, type: {type(logprobs)}")
sum_logprobs += list(logprobs.values())[0].logprob
avg_logprob = sum_logprobs / len(request_output.outputs[0].logprobs)
print("================================================")
print(f"Average log probability: {sum_logprobs}")
print(f"Cumulative log probability: {request_output.outputs[0].cumulative_logprob}")
print(f"Average log probability: {avg_logprob}")
print("================================================")
else:
print(f"Cumulative log probability: {request_output.outputs[0].cumulative_logprob}")
print("No log probabilities available")
结论: sample出来的token logprobs 位于返回的request_output.output[0].logprobs(List[Dict])的第一个位置
一些重要的想获得的返回数据可以简单地用以下方法获得(但是需要设置logprobs参数,要不然就是None)
- output的logprobs sum: request_output.outputs[0].cumulative_logprob
- output的avg logprobs: request_output.outputs[0].cumulative_logprob / len(request_output.outputs[0].logprobs)
更多推荐


所有评论(0)