vLLM - 控制生成过程中返回对数概率信息 logprobs的输出和解释

vLLM - 控制生成过程中返回对数概率信息 logprobs的输出和解释

flyfish

在 vLLM 的代码中,logprobs 是一个控制生成过程中返回对数概率信息的参数。它决定了模型在生成每个 token 时,会返回多少个候选 token 的概率分布信息。以下是详细解释:

logprobs 参数的作用

SamplingParams 中设置 logprobs=k 时:

  • 模型会返回每个生成 token 的对数概率(即模型选择该 token 的自信程度)。
  • 同时返回概率最高的 k 个候选 token 的 ID、文本和对数概率。
  • 实际返回的候选数量可能是 k+1(包含选中的 token 本身)。
sampling_params = SamplingParams(
    logprobs=5,  # 返回选中 token 和 top-4 候选的对数概率
    # 其他参数...
)
代码
```py
from transformers import AutoProcessor
from vllm import LLM, SamplingParams
from qwen_vl_utils import process_vision_info
import os

MODEL_PATH = "Qwen/Qwen2.5-VL-7B-Instruct"
# 本地图像路径,请替换为实际图像路径
image_path = "path_to_your_image.jpg"

llm = LLM(
    model=MODEL_PATH,
    limit_mm_per_prompt={"image": 10, "video": 10},
)

# 启用logprobs功能,设置为5表示返回选中token和top4候选token的对数概率
sampling_params = SamplingParams(
    temperature=0.7,
    top_p=0.9,
    n=1,                # 只生成1个序列,专注于logprobs分析
    max_tokens=256,
    logprobs=5,         # 返回每个token的对数概率及top4候选
    repetition_penalty=1.05,
)



if not os.path.exists(image_path):
    raise FileNotFoundError(f"图像文件不存在: {image_path}")

image_messages = [
    {"role": "system", "content": "You are a helpful assistant."},
    {
        "role": "user",
        "content": [
            {
                "type": "image",
                "image": image_path,
                "min_pixels": 224 * 224,
                "max_pixels": 1280 * 1280,
            },
            {"type": "text", "text": "What is in this image?"},
        ],
    },
]

messages = image_messages

processor = AutoProcessor.from_pretrained(MODEL_PATH)
prompt = processor.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True,
)
image_inputs, video_inputs = process_vision_info(messages)

mm_data = {}
if image_inputs is not None:
    mm_data["image"] = image_inputs
if video_inputs is not None:
    mm_data["video"] = video_inputs

llm_inputs = {
    "prompt": prompt,
    "multi_modal_data": mm_data,
}

# 生成结果
outputs = llm.generate([llm_inputs], sampling_params=sampling_params)
# 获取第一个生成序列的logprobs信息
output = outputs[0].outputs[0]
print("生成的文本:")
print(output.text.strip())

# 解析并打印token_ids和logprobs信息
if output.logprobs is not None:
    print("\n=== Token IDs 和 Logprobs 详细信息 ===")
    
    # 确保token_ids和logprobs长度匹配
    token_count = min(len(output.token_ids), len(output.logprobs))
    
    for i in range(token_count):
        token_id = output.token_ids[i]
        token_text = processor.tokenizer.decode([token_id])
        logprob_info = output.logprobs[i]
        
        print(f"\nToken {i+1}:")
        print(f"  Token ID: {token_id}")
        print(f"  Token 文本: {token_text}")
        
        # 打印原始logprobs字典结构(用于调试)
        print("  Logprobs 原始数据:")
        print(logprob_info)
else:
    print("\n警告: 未获取到logprobs信息,请确认模型和vllm版本支持此功能")

输出

生成的文本:
The image depicts a vintage green truck parked on a dirt path surrounded by a lush, forested area with tall trees and mountains in the background. On top of the truck, there is a praying mantis perched on the hood. The scene has a serene, nature-inspired atmosphere, and there are red seals with Chinese characters in the top right corner, adding an artistic or cultural element to the illustration.

=== Token IDs 和 Logprobs 详细信息 ===

Token 1:
  Token ID: 785
  Token 文本: The
  Logprobs 原始数据:
{785: Logprob(logprob=-0.20143340528011322, rank=1, decoded_token='The'), 1986: 
Logprob(logprob=-1.7014334201812744, rank=2, decoded_token='This'), 641: 
Logprob(logprob=-10.826433181762695, rank=3, decoded_token='In'), 2132: 
Logprob(logprob=-16.201433181762695, rank=4, decoded_token='It'), 8420: 
Logprob(logprob=-17.701433181762695, rank=5, decoded_token='Here')}


Token 2:
  Token ID: 2168
  Token 文本:  image
  Logprobs 原始数据:
{2168: Logprob(logprob=0.0, rank=1, decoded_token='Ġimage'), 6802: 
Logprob(logprob=-17.125, rank=2, decoded_token='Ġpicture'), 107553:
 Logprob(logprob=-18.625, rank=3, decoded_token='åĽ¾åĥı'), 4654: 
 Logprob(logprob=-18.625, rank=4, decoded_token='ĠImage'), 1805: 
 Logprob(logprob=-19.375, rank=5, decoded_token='image')}

Token 3:
  Token ID: 61891
  Token 文本:  depicts
  Logprobs 原始数据:
{61891: Logprob(logprob=-0.055077165365219116, rank=1, decoded_token='Ġdepicts'), 374: 
Logprob(logprob=-3.055077075958252, rank=2, decoded_token='Ġis'), 4933: 
Logprob(logprob=-5.680077075958252, rank=3, decoded_token='Ġshows'), 4419: 
Logprob(logprob=-6.305077075958252, rank=4, decoded_token='Ġfeatures'), 7952: 
Logprob(logprob=-7.305077075958252, rank=5, decoded_token='Ġappears')}

Token 4:
  Token ID: 264
  Token 文本:  a
  Logprobs 原始数据:
{264: Logprob(logprob=-0.06197008118033409, rank=1, decoded_token='Ġa'), 458: 
Logprob(logprob=-2.8119699954986572, rank=2, decoded_token='Ġan'), 1378: 
Logprob(logprob=-13.436969757080078, rank=3, decoded_token='Ġtwo'), 279: 
Logprob(logprob=-14.811969757080078, rank=4, decoded_token='Ġthe'), 23790: 
Logprob(logprob=-16.311969757080078, rank=5, decoded_token='Ġvintage')}


Token 5:
  Token ID: 23790
  Token 文本:  vintage
  Logprobs 原始数据:
{23790: Logprob(logprob=-0.960280179977417, rank=1, decoded_token='Ġvintage'), 6176: 
Logprob(logprob=-1.710280179977417, rank=2, decoded_token='Ġgreen'), 94763: 
Logprob(logprob=-1.835280179977417, rank=3, decoded_token='Ġserene'), 32976: 
Logprob(logprob=-2.585280179977417, rank=4, decoded_token='Ġvibrant'), 69105: 
Logprob(logprob=-3.335280179977417, rank=5, decoded_token='Ġwhims')}

。。。。。。

理解模型决策过程

数据结构解析

每个token的输出包含以下核心信息:

1. 选中的token信息
Token 1:
  Token ID: 785        # 模型实际选择的token在词汇表中的唯一ID
  Token 文本: The       # 该ID对应的文本内容
2. 概率分布信息(Logprobs 原始数据)

这是一个字典,键为token ID,值为该token的详细概率信息:

{
  785: Logprob(logprob=-0.20143340528011322, rank=1, decoded_token='The'),
  1986: Logprob(logprob=-1.7014334201812744, rank=2, decoded_token='This'),
  ...
}
  • logprob:对数概率(log probability),值越小表示概率越低。例如:
    • -0.2014 对应的概率约为 exp(-0.2014) ≈ 0.818(即81.8%)
    • -1.7014 对应的概率约为 exp(-1.7014) ≈ 0.183(即18.3%)
  • rank:该token在概率分布中的排名(1表示最可能)
  • decoded_token:token ID对应的文本表示

关键指标解读

1. 模型置信度

观察选中token的对数概率:

  • 接近0的值(如 -0.2)表示模型非常确定
  • 绝对值较大的负值(如 -10)表示模型对该选择信心很低

例如,Token 1中:

  • 选中的 Thelogprob=-0.2014(高概率,模型很确定)
  • 排名第二的 Thislogprob=-1.7014(低概率)
2. 竞争候选词

查看排名靠前的其他候选token,可以了解模型在生成时的"犹豫程度":

  • Token 2中,模型在 image(ID:2168)和 picture(ID:6802)之间选择,两者的logprob差距较大(0 vs -17.125),说明模型对 image 的选择非常确定。
3. 异常token检测

注意概率分布中的异常token,例如:

  • Token 2中的 åĽ¾åĥı(ID:107553)是乱码,不知道是不是词汇表中的特殊符号或未正确解码的字符,没关系这种低概率的异常token通常可以忽略。

实际应用建议

  1. 评估生成质量

    • 高logprob(接近0)的token表示模型生成质量较好
    • 若连续多个token的logprob很低(如<-5),可能需要调整模型或提示
  2. 优化提示工程

    • 如果模型对关键token的logprob较低,可以通过修改提示来引导模型
    • 例如,在视觉问答中,明确要求"用一个词描述图像中的物体"可能提高确定性
  3. 后处理过滤

    • 对于生成任务,可以设置logprob阈值,丢弃置信度过低的结果
    • 例如,当选中token的logprob < -5时,认为生成质量不可靠

示例分析

Token 1
  • 模型以81.8%的概率选择了 The
  • 第二候选 This 的概率为18.3%
  • 模型在这两个词之间有一定"犹豫",但 The 明显更可能
Token 2
  • 模型几乎完全确定地选择了 image(概率接近100%)
  • 其他候选词的概率极低,说明模型对这个选择非常自信

分词器对句首位置的处理逻辑

核心原因:句首词前无空格

在自然语言中,句子的第一个词前面没有空格,因此分词器不会为其添加 Ġ(空格标记)。

  • Token 1 是生成文本的第一个词 The,作为句首词,其前没有空格,因此分词器直接输出 The
  • Token 2 是后续词 image,其前有空格(文本为 The image),因此分词器用 Ġimage 表示“空格+image”。

分词器的空格标记逻辑

1. Ġ 的作用范围
  • Ġ 仅用于表示 词与词之间的空格(即词首空格),不用于句首
  • 例如:
    • 文本 "The image" 会被分词为 ["The", "Ġimage"](句首词无 Ġ,后续词有 Ġ)。
    • 文本 " image"(开头有空格)会被分词为 ["Ġimage"]
2. 你的输出示例解析
# Token 1(句首词)
decoded_token='The'  # 无前缀空格,正常
# Token 2(后续词)
decoded_token='Ġimage'  # 表示前有空格,对应文本中的 " image"
  • 模型生成的完整文本可能是 The image,其中 The 是句首词,image 前有空格,因此分词器分别处理为 TheĠimage

第二个 token 有 Ġ

假设生成的文本是 The image,其分词过程如下:

  1. 文本拆分:"The" + " " + "image"
  2. 分词器处理:
    • "The" → 直接编码为 token ID 785(无 Ġ,因是句首)。
    • " image" → 空格转换为 Ġ,与 image 合并为 Ġimage,编码为 token ID 2168
  • 若文本开头没有多余空格,说明模型正确生成了句首词。
  • Ġ 是分词器的内部表示,在最终解码的文本中会被自动转换为真实空格。

Ġ 的出现

场景 分词结果示例 说明
句首词 The Ġ,因前方无空格
非句首词(有前置空格) Ġimage Ġ 表示前方有空格
连续空格 ĠĠhello 多个 Ġ 表示连续空格

对数概率(log probability)和自然指数函数的转换

1. 对数概率基础

在概率论中,对数概率(log probability)是指对概率值取自然对数(即以 e 为底的对数,记为 ln)。例如:

  • 概率 p = 0.5 对应的对数概率为 ln(0.5) ≈ -0.693
  • 概率 p = 0.1 对应的对数概率为 ln(0.1) ≈ -2.303

为什么用对数概率?

  • 概率值通常在 [0, 1] 之间,而对数概率可以将其映射到整个实数轴 (-∞, 0],便于数值计算和比较。
  • 对数概率越小(即绝对值越大),表示对应的原始概率越低。

2. 从对数概率还原原始概率

如果已知对数概率 log_p,可以通过 自然指数函数 exp() 还原原始概率 p

p = exp(log_p)

其中 exp(x) 表示 e 的 x 次幂e ≈ 2.71828)。

3. 具体计算示例

示例1:log_p = -0.2014
import math

log_p = -0.2014
p = math.exp(log_p)  # 计算 e^(-0.2014)
print(f"概率: {p:.4f} ({p:.2%})")
  • 计算过程:e^(-0.2014) ≈ 0.8177(四舍五入为 0.818,即 81.8%)。
示例2:log_p = -1.7014
log_p = -1.7014
p = math.exp(log_p)
print(f"概率: {p:.4f} ({p:.2%})")
  • 计算过程:e^(-1.7014) ≈ 0.1826(四舍五入为 0.183,即 18.3%)。

4. 直观理解

对数概率 (log_p) 原始概率 (p = exp(log_p)) 解释
0 exp(0) = 1.0 确定性事件(概率100%)
-0.5 exp(-0.5) ≈ 0.607 约60.7%的概率
-1.0 exp(-1.0) ≈ 0.368 约36.8%的概率
-2.0 exp(-2.0) ≈ 0.135 约13.5%的概率
-10.0 exp(-10.0) ≈ 0.000045 极小概率(约0.0045%)

5. 应用

在模型生成的 logprobs 数据中:

  • logprob=-0.2014 表示模型对该 token 的置信度很高(约 81.8% 的概率)。
  • logprob=-1.7014 表示其他候选 token 的概率较低(约 18.3%)。

代码

from transformers import AutoProcessor
from vllm import LLM, SamplingParams
from qwen_vl_utils import process_vision_info
import os
import math
MODEL_PATH = "Qwen/Qwen2.5-VL-7B-Instruct"
# 本地图像路径,请替换为实际图像路径
image_path = "path_to_your_image.jpg"





llm = LLM(
    model=MODEL_PATH,
    limit_mm_per_prompt={"image": 10, "video": 10},
)

# 启用logprobs功能,设置为5表示返回选中token和top4候选token的对数概率
sampling_params = SamplingParams(
    temperature=0.7,
    top_p=0.9,
    n=1,                # 只生成1个序列,专注于logprobs分析
    max_tokens=256,
    logprobs=5,         # 返回每个token的对数概率及top4候选
    repetition_penalty=1.05,
)



if not os.path.exists(image_path):
    raise FileNotFoundError(f"图像文件不存在: {image_path}")

image_messages = [
    {"role": "system", "content": "You are a helpful assistant."},
    {
        "role": "user",
        "content": [
            {
                "type": "image",
                "image": image_path,
                "min_pixels": 224 * 224,
                "max_pixels": 1280 * 1280,
            },
            {"type": "text", "text": "What is in this image?"},
        ],
    },
]

messages = image_messages

processor = AutoProcessor.from_pretrained(MODEL_PATH)
prompt = processor.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True,
)
image_inputs, video_inputs = process_vision_info(messages)

mm_data = {}
if image_inputs is not None:
    mm_data["image"] = image_inputs
if video_inputs is not None:
    mm_data["video"] = video_inputs

llm_inputs = {
    "prompt": prompt,
    "multi_modal_data": mm_data,
}

# 生成结果
outputs = llm.generate([llm_inputs], sampling_params=sampling_params)
# 获取第一个生成序列的logprobs信息
output = outputs[0].outputs[0]
print("生成的文本:")
print(output.text.strip())


if output.logprobs is not None:
    print("\n=== Token IDs 和 Logprobs 详细信息 ===")
    token_count = min(len(output.token_ids), len(output.logprobs))
    
    for i in range(token_count):
        token_id = output.token_ids[i]
        token_text = processor.tokenizer.decode([token_id])
        logprob_info = output.logprobs[i]
        
        print(f"\nToken {i+1}:")
        print(f"  Token ID: {token_id}")
        print(f"  Token 文本: {token_text}")
        
        # 解析选中token的概率
        selected_logprob = None
        selected_prob = None
        if token_id in logprob_info:
            selected_logprob = logprob_info[token_id].logprob
            selected_prob = math.exp(selected_logprob)
            print(f"  选中概率: {selected_prob:.4%}")
        
        # 解析top-k候选
        print("  Top-k候选:")
        sorted_candidates = sorted(
            logprob_info.items(), 
            key=lambda x: x[1].rank if hasattr(x[1], 'rank') else 0
        )
        for j, (candidate_id, candidate_info) in enumerate(sorted_candidates[:5]):
            candidate_text = processor.tokenizer.decode([candidate_id])
            candidate_logprob = candidate_info.logprob
            candidate_prob = math.exp(candidate_logprob)
            is_selected = candidate_id == token_id
            print(f"    {j+1}. {'*' if is_selected else ''} Token ID: {candidate_id}, 文本: {candidate_text}, 对数概率: {candidate_logprob:.4f}, 概率: {candidate_prob:.4%}")

输出

生成的文本:
The image depicts a vintage green truck parked on a dirt path in a lush, forested area with mountains in the background. The truck has a classic design with a large front grille and round headlights. On top of the truck, there is a praying mantis perched. The scene is serene and natural, with trees, rocks, and wildflowers surrounding the vehicle. There is also a red stamp with Chinese characters in the upper right corner of the image.

=== Token IDs 和 Logprobs 详细信息 ===

Token 1:
  Token ID: 785
  Token 文本: The
  选中概率: 81.7558%
  Top-k候选:
    1. * Token ID: 785, 文本: The, 对数概率: -0.2014, 概率: 81.7558%
    2.  Token ID: 1986, 文本: This, 对数概率: -1.7014, 概率: 18.2422%
    3.  Token ID: 641, 文本: In, 对数概率: -10.8264, 概率: 0.0020%
    4.  Token ID: 2132, 文本: It, 对数概率: -16.2014, 概率: 0.0000%
    5.  Token ID: 8420, 文本: Here, 对数概率: -17.7014, 概率: 0.0000%

Token 2:
  Token ID: 2168
  Token 文本:  image
  选中概率: 100.0000%
  Top-k候选:
    1. * Token ID: 2168, 文本:  image, 对数概率: 0.0000, 概率: 100.0000%
    2.  Token ID: 6802, 文本:  picture, 对数概率: -17.1250, 概率: 0.0000%
    3.  Token ID: 107553, 文本: 图像, 对数概率: -18.6250, 概率: 0.0000%
    4.  Token ID: 4654, 文本:  Image, 对数概率: -18.6250, 概率: 0.0000%
    5.  Token ID: 1805, 文本: image, 对数概率: -19.3750, 概率: 0.0000%

Token 3:
  Token ID: 61891
  Token 文本:  depicts
  选中概率: 94.6412%
  Top-k候选:
    1. * Token ID: 61891, 文本:  depicts, 对数概率: -0.0551, 概率: 94.6412%
    2.  Token ID: 374, 文本:  is, 对数概率: -3.0551, 概率: 4.7119%
    3.  Token ID: 4933, 文本:  shows, 对数概率: -5.6801, 概率: 0.3413%
    4.  Token ID: 4419, 文本:  features, 对数概率: -6.3051, 概率: 0.1827%
    5.  Token ID: 7952, 文本:  appears, 对数概率: -7.3051, 概率: 0.0672%

你可能感兴趣的:(大模型,vLLM)