# Summary 1. **比解析文本更稳定**:不用担心模型输出 `"YES!!!"` / `"yes it does"` 之类的花样字符串。 2. **能得到概率分布**:不仅知道模型“选了 Yes”,还能知道它给 Yes 0.9、No 0.1 的分布。 3. **比赛需求**:像 Kaggle Jigsaw 这类比赛要交概率而不是 hard label,这种方法直接契合。 为什么要这么做 - **普通做法**:大模型生成一个字符串(比如 `"Yes"` 或 `"No"`),然后我们去解析这个文本。 - **这里的做法**:在 `llm.generate(..., logprobs=2)` 里,已经让 vLLM 把每个位置可能 token 的 **logits → logprob** 全部返回。 - `MultipleChoiceLogitsProcessor` 把搜索空间限制成了 **["Yes", "No"]**。 - 所以我们直接拿到这两个候选的 logprob。 - 这样就能直接得到“模型认为 Yes 的概率是多少 / No 的概率是多少”,而不是靠解码的最终字符串。 --- ## 为什么不直接看生成结果 - **生成结果只有一个 token**,丢失了概率信息。 - 模型可能生成 `"Yes"`,但其实 `"No"` 的概率也很接近。 - 在比赛(比如 Jigsaw)里,提交需要的是 **一个概率值[0,1]**,而不是单个分类标签。 - 所以直接用 logprob,可以更好地反映模型的不确定性。 - 再经过 softmax → 得到 Yes 的概率分布 → 就能作为提交分数。 对于[生成式模型](2%20第二大脑/1%20概念/4%20信息与模式/CS/人工智能/深度学习/表征学习/生成式模型.md)做分类任务,直接取 logits 比生成文本后再提取结果更高效 ```Java 输入 → Encoder → Hidden States → Logits → Softmax → 采样 → Token ID → 解码成文本 ↑ ↓ (所有词汇的概率) "0.75" ``` ```Java 输入 → Encoder → Hidden States → Logits → 我们直接在这里截取! ↑ (只看 Yes/No 的概率) ``` ## 🚀 **为什么这样更高效** 1. **计算量减少** - 不需要多次前向传播(生成多个token) - 只需要一次前向传播到 logits 层 2. **避免了采样的随机性** - 即使 temperature=0,采样仍可能有数值误差 - 直接获取概率是确定性的 3. **信息更完整** - 获得了完整的概率分布 - 不是模型"选择告诉你"的数字 ## 💡 **类比理解** **标准方法**: - 问学生:"这道题选A还是B?" - 学生思考后写下:"我选A,把握度75%" - 你读取学生写的答案 **LogitsProcessor方法**: - 直接用脑电波扫描学生大脑 - 看到大脑中:A区域活跃度75%,B区域25% - 不需要学生写出来 # Cues # Notes ## **标准 Decoder 过程 vs LogitsProcessor 方法** ### 方法一:从结果文本中正则提取 ```Java 输入 → Encoder → Hidden States → Logits → Softmax → 采样 → Token ID → 解码成文本 ↑ ↓ (所有词汇的概率) "0.75" ``` ```python # 生成 outputs = llm.generate( batch_prompts, sampling_params, lora_request=lora_request, use_tqdm=True ) # 解析输出 for output in outputs: text = output.outputs[0].text.strip() prob = parse_probability(text) all_probabilities.append(prob) # ==================== 解析概率 ==================== def parse_probability(text): """从文本中解析概率值""" try: # 尝试提取数字 import re match = re.search(r'(\d*\.?\d+)', text[:10]) if match: prob = float(match.group(1)) return min(max(prob, 0.0), 1.0) # 文本判断 text_lower = text.lower()[:20] if 'yes' in text_lower or 'violate' in text_lower: return 0.8 elif 'no' in text_lower or 'not' in text_lower: return 0.2 except: pass return 0.5 # 默认值 ``` ```python # 步骤更多,计算量更大 1. 计算所有 50000+ 个token的logits 2. Softmax 归一化 3. 采样选择 token(比如 "0") 4. 继续生成下一个 token(".") 5. 继续生成("7") 6. 继续生成("5") 7. 解码成文本 "0.75" 8. 正则提取 ``` ### 方法二:logit 分类 ```Java 输入 → Encoder → Hidden States → Logits → 我们直接在这里截取! ↑ (只看 Yes/No 的概率) ``` ```python df["prompt"] = prompts mclp = MultipleChoiceLogitsProcessor(tokenizer, choices=['Yes','No']) outputs = llm.generate( prompts, vllm.SamplingParams( skip_special_tokens=True, max_tokens=1, logits_processors=[mclp], logprobs=2, ), use_tqdm=True, lora_request=LoRARequest("default", 1, LORA_PATH) ) logprobs = [ {lp.decoded_token: lp.logprob for lp in out.outputs[0].logprobs[0].values()} for out in outputs ] logit_matrix = pd.DataFrame(logprobs)[['Yes','No']] ``` ```python # 步骤少,更高效 1. 计算所有 token 的 logits 2. 只看 "Yes" 和 "No" 的 logits 3. 计算这两个的概率 4. 直接返回概率分布 # 结束!不需要实际生成token ```