概率分布泄露:小模型如何逆向提取大模型隐藏思维链
2026/8/29 9:49:53 网站建设 项目流程

前阵子和几个做模型安全评测的朋友讨论时,有人提到一项比较敏感的研究:小模型通过分析大模型的 token 概率分布,居然能反推出被刻意隐藏的思维链内容。更麻烦的是,论文标题里点名的三款头部模型,现有多数防蒸馏方案在特定条件下都能被绕过,其中某模型的概率分布还出现了可复现的统计异常。这个方向不只是在学术圈有争议,对于做 RAG 应用、微调小模型、搭模型网关的同学来说,都可能直接影响技术选型和合规判断。下面我会按“概念 → 原理 → 实验思路 → 防御分析 → 工程建议”的顺序,把这件事讲清楚。

1. 背景与核心概念

1.1 什么是大模型蒸馏

大模型蒸馏(Knowledge Distillation)这个概念最早可以追溯到分类模型压缩,核心思路是让一个小模型去学习大模型的“行为输出”,从而在推理成本大幅降低的前提下保留大部分能力。

放到生成式大模型场景里,蒸馏通常有几种做法:

  • 在线蒸馏:直接调用大模型 API,把输入输出对收集起来,用作小模型的训练数据。
  • 离线蒸馏:先构造大批量指令数据,批量请求大模型生成答案,清洗后形成 SFT(监督微调)数据集。
  • logits 蒸馏:除了文本输出,还尝试获取模型在每个 token 上的概率分布,让小模型不仅学答案,还学“答案背后的置信度”。

前两种做法已经非常普遍,很多电商客服、法律助手、编程助手的小模型都是这么训练出来的。第三种做法更接近学术意义上的蒸馏,但对 API 有额外要求,因为普通接口根本不会返回 token 级概率。

1.2 防蒸馏机制为什么会出现

既然蒸馏能低成本复制模型能力,那么模型服务方自然有理由限制这种行为。防蒸馏机制(Anti-Distillation Mechanism)通常出现在商业化大模型的 API 层,常见手段包括:

防御手段技术思路局限性
限制返回字段不返回 logprobs、token 级概率无法阻止基于文本的蒸馏
拦截高频相似请求检测同账号、同 IP 的重复请求模式分布式代理可绕过
输出扰动对生成结果随机采样扰动影响服务质量与体验
隐藏思维链在推理时不暴露中间步骤若外部可推断则失效
协议层检测识别爬虫或自动化调用特征无法覆盖正常 SDK 调用

可以看到,防蒸馏机制本质上是在“服务可用性”和“数据防护”之间找平衡。防得太死,正常开发者调用体验就会变差;防得太松,模型能力又容易被批量抽走。

1.3 隐藏思维链(Hidden Chain-of-Thought)的作用

思维链(Chain-of-Thought,CoT)是大模型在解决复杂推理问题时生成的中间步骤,例如数学题的分步计算、代码问题的逻辑拆解。对模型服务方来说,思维链是模型的“核心商业秘密”之一,因为:

  • 思维链内容往往包含模型如何从 prompt 推导出答案的关键路径。
  • 高质量思维链可以直接用于训练更强的小模型,压缩大量数据标注成本。
  • 思维链暴露后,竞争对手可以分析模型内部推理偏好,反向改进自家模型。

所以很多模型在 API 输出时会刻意隐藏思维链,只给用户最终答案。但这项研究关注的问题恰恰是:隐藏并不等于消失。如果模型内部确实在生成思维链,那么这些推理痕迹会不会残留在输出概率分布里?

2. 论文核心发现:防蒸馏机制为何被攻破

2.1 攻击思路:让“防”失去对象

很多防蒸馏措施把精力放在“输入端”,比如检测请求频率、识别相同 prompt、校验账号资质。但这篇研究的核心思路是换一个角度:不去破解服务端防护,而是从合法范围的模型输出里提取额外信息

具体来说,攻击方并不需要绕过 API 的鉴权,也不需要拿到 logprobs 字段,只需要:

  1. 构造大量需要“隐式推理”的复杂问题。
  2. 获得模型返回的最终答案。
  3. 对答案的不同候选 token 统计概率特征。
  4. 利用这些统计特征反推模型内部是否产生了 CoT 以及 CoT 的大致内容。

这种思路最麻烦的地方在于:它没有违反任何 API 调用规则。调用方就是普通开发者,请求就是普通问题,只是分析和聚合方式超出了模型服务方的预期。

2.2 三大模型防蒸馏机制的共性弱点

论文中提到三款头部模型都存在可被利用的弱点。从公开技术资料和常见实现来看,这些模型在防蒸馏上存在一些共性:

  • 能力越强,痕迹越明显:模型推理能力越强,内部 CoT 过程往往越长、越结构化,输出分布上残留的信号就越多。
  • 隐藏逻辑独立于生成逻辑:很多模型的 CoT 隐藏是在“显示层”做的,也就是先把答案算出来,再把中间过程从返回值里删除。但删除发生在输出层,并不影响模型在计算过程中的 token 概率分配。
  • 温度采样暴露概率特征:即使 API 最终只返回一个答案,采样过程本身基于内部概率分布,多个独立请求的统计结果可以近似还原这个分布。

这三点组合起来,等于给防蒸馏机制开了一道结构性后门:防护层拦截的是“内容”,而攻击方读取的是“统计特征”

2.3 Kimi-K3 重现概率异常是怎么回事

“Kimi-K3 重现概率异常”是论文标题里比较吸引眼球的部分。按照论文描述,研究者在特定 benchmark 上对某模型(标题中记为 Kimi-K3)做了重复采样实验,发现模型在回答某些推理题时,不同次返回结果之间的 token 概率分布存在明显异常,异常模式与模型是否生成了内部 CoT 高度相关。

从技术角度看,“重现概率异常”这种说法可以拆成几个层面理解:

  • 同一问题多次采样,答案选项的分布不稳定:如果模型只是基于表面知识作答,概率分布应该是稳定的;如果涉及内部 CoT,不同采样路径可能推导出不同结论,分布会呈现多峰。
  • 低概率 token 区域出现“规律性尖峰”:正常生成时,低概率 token 应该是相对随机分布的;但存在 CoT 痕迹时,某些 token 会持续获得异常概率,说明模型内部已经为这些 token 分配了特殊权重。
  • 后验分布与文本不完全一致:模型最终输出的文字可能只包含最终结论,但概率分布中残留的是推理路径上的中间 token 信息。

这种异常不是个案。论文的核心贡献就是把这些异常模式系统化,并提出一套可以复现的检测方法。

3. 技术原理:小模型如何“套出”隐藏思维链

3.1 第一步:获得概率分布

要分析概率分布,首先得拿到概率数据。对普通开发者来说,最直接的办法是使用支持 logprobs 的 API。OpenAI 兼容接口里通常有这种参数:

import requests def fetch_logprobs(prompt: str, api_key: str, base_url: str = "https://api.example.com/v1") -> dict: headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } payload = { "model": "your-model-name", "messages": [ {"role": "user", "content": prompt} ], "max_tokens": 512, "logprobs": True, "top_logprobs": 10, "temperature": 0.0 } resp = requests.post(f"{base_url}/chat/completions", headers=headers, json=payload, timeout=60) resp.raise_for_status() return resp.json() # 使用示例 if __name__ == "__main__": data = fetch_logprobs("一个笼子里有鸡和兔,一共有35个头、94只脚,请问鸡和兔各有多少只?", api_key="sk-xxx") for item in data["choices"][0]["logprobs"]["content"]: token = item.get("token") top = item.get("top_logprobs", []) if top: prob = top[0].get("logprob") print(f"{token}\tlogprob={prob:.4f}")

如果目标模型不提供 logprobs,那就只能退而求其次,用“重复采样 + 频率统计”来近似概率分布。比如同一个问题请求 50 次,统计每个候选答案出现的频次。这种方法的精度会低一些,但对于检测明显的多峰分布已经足够。

3.2 第二步:在分布中寻找推理痕迹

拿到概率数据后,分析重点不是“哪个 token 概率高”,而是概率分布的形状

正常模型的输出概率分布一般比较平滑,高概率 token 集中在最终答案附近。但如果模型内部执行了隐形 CoT,概率分布往往会出现以下特征:

  • token 级别的熵异常降低:某些中间推理 token 对应的 top 概率异常集中,说明模型内部已经产生了确定性的中间结论。
  • 答案 token 之间存在“逻辑跳变”:从“所以”“因此”“then”等连接词对应的 token 概率可以看出模型是否在组织推理结构。
  • 候选答案分布呈现多峰:例如数学题在同一题上多次采样,答案可能是两个不同值,各自占比都不低,说明模型内部走过多条推理分支。

下面是一个简单的熵和分布形状分析示例,用于判断某组输出是否存在异常集中趋势:

import math from collections import Counter def compute_entropy(counter: Counter) -> float: total = sum(counter.values()) if total == 0: return 0.0 entropy = 0.0 for count in counter.values(): p = count / total if p > 0: entropy -= p * math.log(p) return entropy def analyze_answer_distribution(samples): counter = Counter(samples) entropy = compute_entropy(counter) top_freq = counter.most_common(1)[0][1] / sum(counter.values()) if counter else 0 return { "unique_answers": len(counter), "entropy": round(entropy, 4), "top_ratio": round(top_freq, 4), "distribution": dict(counter) } # 示例:同一道推理题请求 30 次,记录最终答案 answers = ["鸡23只,兔12只", "鸡23只,兔12只", "鸡24只,兔11只", "鸡23只,兔12只"] result = analyze_answer_distribution(answers) print(result)

如果entropy明显高于同类别普通题的基线,或者top_ratio出现异常抖动,就需要进一步检查是否存在隐藏推理路径。

3.3 第三步:重构隐藏思维链

概率分析能告诉你“模型内部发生过推理”,但要重构思维链内容,还需要更精细的方法。

常见做法是把整个输出过程当做一个“隐变量推断问题”来处理。假设模型内部维护了一个隐含推理状态 H,最终输出是 O,那么:

  • P(O | prompt) 可以直接通过 API 采样得到。
  • 若能找到一个中间符号序列 C,使得 P(O | C, prompt) × P(C | prompt) 与观测分布高度相关,C 大概率就是隐藏思维链。

实际重构时,研究者通常采用两类手段:

1. 间接重构法:构造多个提示词变体,逐步“诱导”模型在最终答案中暴露与内部 CoT 相关的信息。例如要求“请解释你得到这个答案的主要依据”,虽然模型返回的是后置解释,但解释中会出现与内部推理相关的关键词,通过跨样本对齐可以还原出推理骨架。

2. 统计对齐法:用候选思维链集合,验证哪条思维链更容易解释观测到的概率异常。这类似用贝叶斯方法做“逆向推理”。

需要说明的是,重构出来的思维链不一定是模型内部真实记录的逐字内容,更准确的说法是“与模型内部推理路径高度一致的近似重建”。

4. 实验方法示例:如何复现概率异常检测

下面用一段完整的 Python 实验流程演示概率异常检测的基本步骤。重点不是复现完整的论文实验,而是帮助理解检测思路。

4.1 环境准备

建议使用 Python 3.9+,需要安装以下依赖:

pip install numpy scipy requests matplotlib

示例项目结构:

cot_probe/ ├── collect.py # 概率采集脚本 ├── analyze.py # 异常检测脚本 ├── data/ │ ├── raw_results.json │ └── baseline.json └── output/ └── charts/

4.2 概率采集脚本

为了减少对单一模型的依赖,我们按 OpenAI 兼容接口来写采集逻辑。如果你的目标是国内模型,多数云厂商也提供兼容接口,只需调整base_urlmodel字段。

# collect.py import json import time import requests API_KEY = "sk-your-key" BASE_URL = "https://api.example.com/v1" MODEL = "your-model" QUESTIONS = [ "一家商店将某种商品按进价提高40%后打八折出售,结果仍盈利24元,这种商品的进价是多少?", "甲乙两人从相距270千米的两地同时相向而行,甲每小时行45千米,乙每小时行40千米,几小时后两人相遇?" ] def collect_once(question: str) -> dict: headers = {"Authorization": f"Bearer {API_KEY}"} payload = { "model": MODEL, "messages": [{"role": "user", "content": question}], "temperature": 0.7, "max_tokens": 512, "logprobs": True, "top_logprobs": 5 } resp = requests.post(f"{BASE_URL}/chat/completions", headers=headers, json=payload, timeout=60) resp.raise_for_status() data = resp.json() return { "question": question, "answer": data["choices"][0]["message"]["content"], "logprobs": data["choices"][0].get("logprobs", {}).get("content", []) } def main(): results = [] for q in QUESTIONS: for _ in range(20): try: results.append(collect_once(q)) except Exception as e: print(f"[error] {e}") time.sleep(0.5) with open("data/raw_results.json", "w", encoding="utf-8") as f: json.dump(results, f, ensure_ascii=False, indent=2) if __name__ == "__main__": main()

4.3 异常检测脚本

# analyze.py import json import numpy as np from collections import Counter def load_json(path): with open(path, "r", encoding="utf-8") as f: return json.load(f) def token_logprob_stats(records): all_probs = [] for rec in records: for item in rec["logprobs"]: top = item.get("top_logprobs", []) if top: all_probs.append(np.exp(top[0].get("logprob", -20))) arr = np.array(all_probs) return { "mean": float(arr.mean()), "std": float(arr.std()), "p5": float(np.percentile(arr, 5)), "p95": float(np.percentile(arr, 95)) } def answer_entropy(records): answers = [r["answer"].strip()[:20] for r in records] counter = Counter(answers) total = len(answers) ent = -sum((c / total) * np.log(c / total) for c in counter.values()) return ent, counter.most_common(3) if __name__ == "__main__": records = load_json("data/raw_results.json") stats = token_logprob_stats(records) ent, top_answers = answer_entropy(records) print("token概率统计:", stats) print("答案熵:", round(ent, 4)) print("高频答案:", top_answers)

4.4 结果判断

运行后可以关注两个信号:

  • 如果答案熵偏高,说明同一道题多次采样得到的结论分散,可能存在多分支推理。
  • 如果 top token 概率的方差异常大,说明部分 token 被模型以极高置信度“锁定”,这往往是内部推理结论的残留信号。

为了做对比,建议再用一组简单事实题作为 baseline。如果推理题的答案熵明显高于事实题,而普通问答没有这种差异,那概率异常就具备了统计显著性。

5. 防蒸馏机制为什么难以彻底防御

5.1 从输出侧无法完全封堵

模型服务方可以隐藏中间推理过程,但无法隐藏“模型确实进行了复杂推理”这一事实必然留下的概率痕迹。根本原因在于:

  • 采样必须基于内部概率分布。
  • 概率分布是模型推理结果的必要中间状态。
  • 只要保留采样,就必然保留分布信息。

从信息论角度看,API 返回的每一个 token 都由内部分布决定,样本分布本身就携带内部计算的信息。想彻底阻止蒸馏,理论上需要让返回 token 与内部推理统计无关,这在实际中几乎不可能,因为那会直接毁掉模型的生成能力。

5.2 概率分布是“天然泄露源”

即使服务方不显示 logprobs,侧面统计也能逼近真实分布。研究者只需要控制采样次数,对返回文本做足够多的统计,就能把一个离散概率分布的基本形状重建出来。

数学概率的直觉:

  • 如果模型内部某个中间结论 C 出现了,后续 token 的条件分布会显著改变。
  • 无论是否在文本中暴露 C,这种条件分布改变都会体现在采样频率中。
  • 当采样次数足够大时,频率会收敛到概率。

这就意味着,服务方真正能做的不是“消除泄露”,而是“提高攻击成本”。例如限制并发、限制同一问题的重复调用次数、加入随机采样温度扰动。但这些手段无法从根本上解决分布的统计可识别性。

5.3 语义级防护的局限性

有些模型会在输出层增加“安全过滤器”,检测返回文本中是否包含与内部推理相关的敏感内容。这类防护有两个天然弱点:

  • 它只能检测“文本层”的泄露,无法覆盖“统计层”的泄露。
  • 过滤本身必须基于某种规则,而规则可以被提示词变体绕过,因为同样的语义可以由无限多种表面形式表达。

所以,当前更现实的防御策略是组合式的:限制请求频率 + 监控异常采样 + 法律条款约束 + 提高小模型复制的数据成本

6. 对普通开发者的影响与实用建议

6.1 使用大模型 API 时的注意事项

如果你只是正常调用大模型 API 做应用,不必过度恐慌。但要注意以下几点:

  • 不要在生产环境依赖 logprobs 字段:很多模型的 logprobs 仅供调试,生产环境可能随时关闭。
  • 缓存策略要合理:如果你把大模型输出缓存下来做二次分析,注意数据合规,尤其是用户隐私和商业数据。
  • 关注模型服务条款:某些模型禁止使用输出训练竞争模型,违规可能导致账号封禁。

6.2 自查:你的应用是否泄露了模型痕迹

如果你负责的是一款基于大模型 API 的 B 端应用,建议做一次“痕迹自查”:

  • 你的应用是否会把模型的完整推理过程直接返回给最终用户?
  • 你的日志系统是否记录了 prompt 和完整响应?
  • 如果有人在你的应用页面上重复提交相同问题,系统是否能识别并限制?

这些问题不一定是安全漏洞,但在合规审计和商业保护上值得提前管理。

6.3 做模型安全评测时怎么设计实验

如果你在研究或评测场景中需要验证某个模型的防蒸馏能力,建议按以下框架设计实验:

  1. 选定基准任务:数学推理、逻辑推断、代码生成等需要多步推理的任务。
  2. 定义泄露指标:答案熵、top token 概率波动、跨样本一致性等。
  3. 设置对照组:用简单事实题做基线,排除随机噪声。
  4. 多模型对比:同一评测脚本跑多个模型,观察分布差异。
  5. 记录环境变量:temperature、top_p、max_tokens、提示词模板都要固定。

这套流程既适合学术研究,也适合企业内部的安全评估。

7. 常见问题与排查

问题现象可能原因解决思路
请求 API 返回 401API Key 无效或权限不足检查密钥、确认账号是否有 logprobs 权限
logprobs 字段始终为空模型不支持该字段改用重复采样统计法,或换用兼容模型
多个采样结果完全一样温度设置过低或服务端固定温度为 0将 temperature 调到 0.5 以上再观察分布
熵异常偏高但找不到规律问题本身存在多解或歧义更换更严谨的推理题,避免开放式问题
对比实验不显著采样次数太少同一问题至少采样 20~50 次
检测结果在不同批次不一致服务端动态调整采样参数记录调用时间与响应头,作为环境变量一起分析

如果你也遇到“明明加了 temperature,结果还是一成不变”的情况,可以优先怀疑服务端强制覆盖了采样参数,这点在部分商业化模型中确实存在。

8. 最佳实践与工程建议

8.1 从防御方看

如果我们是模型服务提供方或企业内部的模型网关负责人,需要从工程角度设计防蒸馏策略:

  • 限流分层:按账号、IP、组织三个维度配置采样频率限制,对高频重复请求自动降级。
  • 输出扰动:在不明显影响服务质量的前提下,对返回加入微小文本扰动,提高蒸馏数据的噪声水平。
  • 监控告警:建立“答案熵”和“重复度”监控指标,异常时自动触发告警。
  • 法律合规:在服务协议中明确禁止未经授权的模型蒸馏行为,尤其是用于训练竞争模型。

8.2 从研究/评测方看

如果是研究者或安全评测人员,更重要的原则是:

  • 合法授权:评测之前确认使用条款允许相关测试。
  • 最小化数据:只采集完成评测所需的最少样本,不用真实用户数据做实验。
  • 负责任披露:如果发现高危泄露漏洞,先联系厂商再公开发布细节。
  • 测试环境隔离:不要在生产环境的大模型接口上直接做批量攻击测试。

8.3 从应用开发者看

普通应用开发者的最佳实践可以概括为“三不三要”:

  • 不把大模型完整输出直接当最终产品展示,重要场景需要后处理。
  • 不盲目采集大量模型输出做“自由微调”,先确认数据来源和授权。
  • 不认为“模型隐藏了思维链就万事大吉”,概率分布一样能泄露信息。
  • 要做输出缓存和限流,保护自己的 API 成本。
  • 要做日志脱敏,避免用户数据随模型调用泄露。
  • 要做回归测试,保证模型升级后应用行为不会异常漂移。

9. 总结与学习路线

这篇文章的核心是让大家理解一件事:大模型防蒸馏不是绝对的安全边界,而是一道需要持续对抗的防线。论文揭示的“概率分布泄露”现象提醒我们,判断一个模型是否容易被蒸馏,不能只看 API 是否返回了 logprobs,还要考虑侧面统计、重复采样、跨样本对齐等更隐蔽的信号。

如果你想继续深入学习,建议按这个路线推进:

  1. 掌握基础知识:理解大模型的采样机制、温度参数、top-p 采样的作用。
  2. 学习概率统计:重点掌握熵、KL 散度、互信息等概念,这是分析概率分布的基本工具。
  3. 研究蒸馏方向:熟悉 SFT、DPO、logits distillation 的主流实现,了解小模型训练的数据需求。
  4. 关注安全评测:多读模型安全、对抗样本、隐私泄露方向的论文,建立“防御方视角”。
  5. 动手实践:用一份公开数据集、一个开源模型接口,尝试做最简单的概率异常检测实验。

如果你最近也在研究大模型蒸馏、模型安全或成本优化,欢迎在评论区交流你的实验数据和踩坑经验。也可以把这篇文章收藏备用,后面做防蒸馏评测时直接参考里面的实验框架。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询