想象你是一家餐厅的熟客,最钟爱大厨那道“江南秘制鱼”。有天你想在家复刻这道菜,但大厨的厨房从不对外开放,你拿不到配方。你唯一的办法是:一遍遍去店里点这道菜,细细品味咸淡、火候、配料,记住每一次入口的感觉;有时坐在不同位置观察上菜次序,甚至偷偷计时等待时长。几十次之后,你竟然在家做出一道几乎一模一样的秘制鱼——虽然你从未走进厨房。这正是“模型窃取攻击”的镜像:一个商业AI模型就像那位大厨,它的“配方”是训练好的神经网络参数;攻击者没有权重文件,却能通过无数次 API“点菜”,训练出一个行为几乎一致的“替代模型”。本文将带你以“美食侦探”的视角,拆解模型窃取的套路、原理、攻防与实战。

岗位关联

模型窃取是 AI 商业化后最隐蔽的资产流失方式之一。理解攻击原理与防御手段,是算法工程师守住模型核心价值、参与安全风控体系设计的必备能力。

目录
  1. 可视化演示
  2. 攻击概述——一位“偷学”的大厨粉丝
  3. 窃取类型——偷尝的四种姿势
  4. 核心原理——靠品尝还原配方
  5. 攻击流程——从点菜到复刻
  6. 防御对策——给秘方上锁
  7. 实战应用——真实战场的攻与防
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示模型窃取攻击的端到端工作流、方程求解与学习型范式对比、多层防御策略,支持步进式探索攻击各阶段。

本节配有交互动效(进入视口后自动加载,约 3KB)。

攻击工作流五阶段 侦察:收集目标模型API信息与限制 查询:生成输入样本发送查询请求 训练:用输入输出对训练替代模型 成功率 ∝ 查询量 × 模型复杂度 端到端攻击分五步完成模型窃取

攻击概述——一位“偷学”的大厨粉丝

老饕小陈爱上了“满堂春”大厨的招牌菜。他每周去点三次,尝遍火候与佐料,三个月后自己在家复刻出九成相似的味道。这个过程中,小陈从未拿到过厨房的配方,他唯一的“接口”就是点菜和上菜。这正是模型窃取攻击的起点:看似只能“输入问题、得到答案”的黑盒,其实藏着一座金山。

模型窃取攻击(Model Stealing Attack)指攻击者在无法获得目标模型内部参数的前提下,仅通过查询接口获取输入输出对,训练一个“替代模型”来复制目标模型功能的过程。这里的关键假设是:目标模型以 API 形式对外提供服务时,“输入-输出”通道是完全暴露的。攻击者不需要理解模型内部如何运转,只需要足够的(输入,输出)样本,就能训练出一个功能等价物。

按攻击目标不同,模型窃取可以大致分为“功能复制”和“信息提取”两类:前者要的是“行为像”,后者要的是“参数知”。威胁模型上,黑盒攻击假设攻击者没有权重、没有梯度、没有训练数据,只有查询权;白盒攻击则假设攻击者可以拿到部分内部表征。真实攻击场景非常普遍——商业 API(图像分类、语音识别、风控评分)一旦上线,就暴露在全世界“食客”的目光下,任何一次正常调用都可能为攻击者积累素材。

正如小陈每次点菜都在为“复刻计划”积累情报,攻击者的每一次“正常查询”都在描绘目标模型决策边界的等高线。最危险的攻击往往不是突然的暴力破解,而是藏在成千上万次普通调用里的“温柔试探”。

窃取类型——偷尝的四种姿势

想知道大厨的秘方,有人选择反复点菜自己试味,有人买通后厨内应偷看配方,有人盯着服务员上菜顺序反推流程,还有人竖起耳朵听后厨煎炒的节奏。四种姿势,殊途同归。

对应到攻击技术,四种主要类型是:黑盒窃取、白盒窃取、置信度泄露利用、侧信道窃取。

  • 黑盒窃取(Black-box Stealing):攻击者只拿到硬标签或概率输出,通过大量采样 + 替代模型蒸馏实现功能近似。这是最普遍的形态,也是本文演示动画展示的典型场景。
  • 白盒窃取(White-box Stealing):当模型权重、梯度或中间特征因事故/内鬼泄露时,攻击者可以直接在本地对模型进行微调、压缩,甚至删除签名后转卖。
  • 置信度泄露(Confidence Leakage):API 返回 softmax 概率向量时,“最可能类别”以外的概率异常值会暴露样本到决策边界的距离信息,让蒸馏攻击效果成倍提升。
  • 侧信道窃取(Side-channel Stealing):利用输出长度、响应时间、内存占用、功耗等物理信号,反推模型结构(如层数、宽度、量化位数),再在本地重建近似模型。

白盒是直接“拿到配方本”,黑盒是“纯靠嘴尝”,置信度泄露相当于“点菜时偷偷看了别人的点评卡”,侧信道则像“听后厨声音判断是爆炒还是慢炖”。四种方式泄露的信息量和隐藏难度各不相同,攻击者会按现实条件选择最顺手的一种。

核心原理——靠品尝还原配方

小陈要复刻一道菜,最关键的不是“记住好吃”,而是搞清楚“为什么好吃”:多咸、多辣、什么火候。模型窃取的精髓也一样:把输出行为还原成决策几何。

决策边界提取是基础。二分类器的输出标签本质上是在回答“这个样本落在决策边界的哪一侧”,每次查询就是一次“边界触碰实验”。攻击者只要在边界两侧多探几针,就能把边界形状勾勒出来。模型蒸馏则更进一步——如果 API 返回的是 softmax 概率,攻击者就用概率分布作为“软标签”来训练替代模型,让替代模型不只在类别上一致,在每个类别的置信度上也逼近目标模型:

替代模型训练是最后一步。攻击者用一个公开数据集或主动采样得到的样本集,以目标模型的输出为监督信号,训练自己的模型:

查询预算控制是攻击的现实约束:商用 API 按次计费、有风控限速,攻击者必须在有限预算内最大化信息收益,因此倾向于主动采样——不确定性采样、边界采样、生成式对抗采样。每一次品尝都告诉小陈“这道菜的某个维度对不对”,软标签则告诉他“差多少”。当替代模型的决策边界逐渐贴上目标模型,一份“复刻版秘制鱼”就诞生了。

Ldistill=∑i=1NDKL(σ(z(i)T)  ∥  σ(z′(i)T))\mathcal{L}_{\text{distill}} = \sum_{i=1}^{N} D_{\mathrm{KL}}\left( \sigma\left(\frac{z^{(i)}}{T}\right) \;\middle\|\; \sigma\left(\frac{z'^{(i)}}{T}\right) \right)
这个公式描述“替身演员”的排练目标:让自己的输出尽量贴近原演员,误差平方和越小越像。
  1. 最小化目标寻找替代模型参数 $\theta'$,使整体误差最小,相当于不断调整复刻配方里的每一种调料比例。
    min⁡θ′\displaystyle \min_{\theta'}
  2. 平均误差用 $\frac{1}{N}\sum$ 对所有查询样本取平均,避免样本数量影响损失值。
    1N∑i=1N\displaystyle \frac{1}{N} \sum_{i=1}^{N}
  3. 目标模型输出$f(x_i)$ 是目标模型(大厨)对第 i 个样本的判定结果,是攻击者的“标准答案”。
    f(xi)\displaystyle f(x_i)
  4. 平方误差$\|\cdot\|^2$ 对误差取平方,放大较大偏差,让优化方向更关注“差得远”的样本。
    ∥⋅∥2\displaystyle \| \cdot \|^2

攻击流程——从点菜到复刻

小陈的复刻不是心血来潮的乱尝,而是有一套精密流程:先摸清餐厅招牌菜的稳定菜单(确认输入格式与输出种类),再有计划地覆盖不同口味(主动采样),同步记录口感笔记(标签采集),最后请朋友盲测对比真假(性能评估)。

模型窃取的攻击流程同样分为四步:

1. 目标探测:确认 API 的输入输出维度、类别数量、限速规则。相当于“看菜单、数菜品、了解上菜规矩”。

2. 样本生成:生成能覆盖决策边界的高信息量样本。常用方法包括边界扰动、对抗样本、生成对抗网络、公开数据集增强。这一步决定了查询预算的使用效率。

3. 标签采集:批量查询目标模型,记录输出并去重去噪。注意控制查询频率以躲避风控,必要时使用代理池分散来源。

4. 性能评估:用独立的测试集比较替代模型与目标模型的预测一致性。准确率(accuracy)衡量替代模型自身水平,一致性(agreement)衡量它是否真的“继承了原模型的脾气”。

这四步听起来简单,真正的技术含量在于“第二步怎么选样本”和“第四步怎么判断像不像”。高手与小白的差距,不在查询次数,而在每一问是否问在决策边界的刀刃上。

防御对策——给秘方上锁

大厨发现食客在复刻他的菜,会怎么办?他会在菜里藏一个只有自己人知道的“暗号”(水印),会盯住一天来点十次同一道菜的可疑熟客(指纹检测),会规定每桌限量供应(限速),还会故意把口味调得有点飘忽(输出扰动)。

  • 模型水印:训练时在特定触发器样本上植入独特行为,事后通过触发器样本集验证某模型是否衍生自自己的模型。这相当于在菜里埋入“只有自己人尝得出的暗号”,即使菜被复刻,暗号也会被一起带走,成为维权的铁证。
  • 指纹验证:记录查询 IP、频率、样本相似度,异常模式触发告警。相当于服务员熟记熟客面孔,发现形迹可疑的“打卡狂人”就重点盯防。
  • 查询限速:限制单一账户的查询并发与总量,强制攻击者拉长攻击时间、抬高查询成本,让“偷学”在经济上变得不划算。
  • 输出扰动:对置信度向量取整、加噪或截断,减小软标签的信息量。最常见的做法是保留小数点后一位:

以上对策不能完全阻止窃取——就像暗号和限购也挡不住最执着的食客——但能把攻击成本从“点三次菜”抬升到“点三百次菜”,让攻击者在投入产出比面前主动放弃。

p~j=⌊10k⋅pj⌋10k\tilde{p}_j = \frac{\lfloor 10^k \cdot p_j \rfloor}{10^k}
这个公式描述“模糊口味”:把原始置信度 $p_j$ 的第 k 位小数之后全部截掉,让攻击者拿到的软标签失去精细的边界距离信息。
  1. 原始置信度$p_j$ 表示目标模型认为样本属于第 j 类的概率,是攻击者最想得到的“细腻口感描述”。
    pj\displaystyle p_j
  2. 放大取整$\lfloor 10^k \cdot p_j \rfloor$ 先把概率放大 $10^k$ 倍再向下取整,去掉多余的小数位。
    ⌊10k⋅pj⌋\displaystyle \lfloor 10^k \cdot p_j \rfloor
  3. 还原输出除以 $10^k$ 得到保留 k 位小数的近似概率,k 越小,泄露的信息越粗糙。
    ⌊10k⋅pj⌋10k\displaystyle \frac{\lfloor 10^k \cdot p_j \rfloor}{10^k}

实战应用——真实战场的攻与防

从街角小馆到连锁集团,“被偷菜谱”的故事每天都在上演。AI 领域同样如此。

  • 语音识别 API 窃取:攻击者用公共语音数据集查询商业 ASR 服务,蒸馏出可部署在本地设备的紧凑模型,避开按分钟计费的商业授权。研究表明,只需数千小时查询即可获得接近商用模型的词错误率。
  • 图像分类复刻:某云服务商图像分类 API 被攻击者用约 1 万次查询克隆,替代模型与目标模型的决策一致性超过 92%,攻击成本不到官方授权费的五分之一。
  • API 商业化防盗版:API 供应商集成水印与用量监控,检测到分布式攻击后动态调整置信度输出精度,将单次查询的信息量压到最低。
  • 合规评估:安全审计团队使用模拟攻击评估模型资产的可盗性,输出风险报告,指导风控策略和保险定价。

在这个“点菜就能偷学”的时代,没有哪个模型是绝对安全的。安全是持续博弈的艺术:了解攻击者的手法,就是握住防守的主动权。

AI 应用场景

商业 API 安全红队评估

阶段:模型部署与运维

解决的问题:企业发布付费 API 后,攻击者可低成本复制模型功能。传统渗透测试只关注接口越权,忽略了“合法查询也可以偷走模型”。红队主动模拟攻击,量化窃取风险,为限速、扰动、水印策略提供数据依据。

具体案例:某金融科技公司的反欺诈模型 API,红队用“不确定性采样”策略在 800 次查询内训练出替代模型,与目标模型在测试集上达成 90% 以上一致率。团队据此将置信度输出从浮点改为一位小数,并将单 IP 日查询上限从 5000 降到 500,攻击成本上升约 6 倍。

模型版权归属鉴定

阶段:事后审计取证

解决的问题:怀疑竞品模型复制了自家能力,但拿不出直接证据。利用模型水印触发器样本集作为“验钞笔”,在盲测中验证对方模型是否携带自家唯一的水印行为。

具体案例:某语音助手厂商在训练时植入 12 个特殊音频触发器,使其在特定噪声下必然输出特定短语。厂商对竞品 App 发送触发器样本,发现其中 8 个被竞品以相同方式响应,据此申请司法鉴定,促成庭外和解。

模型资产防窃取部署

阶段:模型上线阶段

解决的问题:如何在保证正常用户体验的前提下,提高模型窃取成本。单纯关停 API 不现实,需要在“服务可用”与“信息泄露”之间取平衡。

具体案例:某图像分类 SaaS 上线时集成三层防护:置信度取整加动态抖动、按 Key 限速、分布式风控。上线三个月内检测到 3 起疑似窃取尝试,攻击者均在查询量达到约 2000 次后因成本过高而放弃。

Python 演示

python93 行
"""模型窃取攻击模拟: 通过主动采样查询训练替代模型"""import numpy as npfrom sklearn.linear_model import LogisticRegressionfrom sklearn.datasets import make_classificationfrom sklearn.model_selection import train_test_split  class TargetModel:    """模拟一个商业AI分类API, 只暴露predict接口"""    def __init__(self):        self.model = LogisticRegression(C=1.0)     def train(self, X, y):        self.model.fit(X, y)     def predict(self, X):        """返回置信度向量, 模拟软标签输出"""        return self.model.predict_proba(X)  class Attacker:    """攻击者: 通过有限次查询偷学目标模型"""    def __init__(self, target, budget=80):        self.target = target        self.budget = budget        self.queries = []        self.labels = []     def active_sample(self, X_pool, K=5):        """不确定性采样: 选择模型最拿不准的K个样本"""        proba = self.target.predict(X_pool)        uncertainty = 1 - np.max(proba, axis=1)        idx = np.argsort(uncertainty)[::-1][:K]        return idx     def steal(self, X_pool, seed=42):        """执行窃取: 主动采样 -> 查询 -> 训练替代模型"""        rng = np.random.default_rng(seed)        used = set()        steps = 0        while len(self.queries) < self.budget and steps < 100:            steps += 1            remain = [i for i in range(len(X_pool)) if i not in used]            if len(remain) < 5:                break            Xc = X_pool[remain]            idx = self.active_sample(Xc, K=5)            for i in idx:                if len(self.queries) >= self.budget:                    break                self.queries.append(Xc[i])                self.labels.append(self.target.predict(Xc[i].reshape(1, -1))[0])                used.add(remain[i])        Q = np.array(self.queries)        if len(Q) == 0:            return None        L = np.array(self.labels)        y = np.argmax(L, axis=1)        surrogate = LogisticRegression()        surrogate.fit(Q, y)        return surrogate  def main():    X, y = make_classification(        n_samples=500,        n_features=4,        n_informative=3,        n_classes=2,        random_state=42    )    X_train, X_test, y_train, y_test = train_test_split(        X, y, test_size=0.3, random_state=42    )    target = TargetModel()    target.train(X_train, y_train)    attacker = Attacker(target, budget=80)    X_pool = X_train[:-10]    surrogate = attacker.steal(X_pool)    if surrogate is None:        return    pred_sur = surrogate.predict(X_test)    pred_tgt = np.argmax(target.predict(X_test), axis=1)    true_acc = np.mean(pred_sur == y_test)    clone_acc = np.mean(pred_sur == pred_tgt)    target_acc = np.mean(pred_tgt == y_test)    print(f"目标模型精度:   {target_acc:.3f}")    print(f"替代模型精度:   {true_acc:.3f}")    print(f"替代模型一致率: {clone_acc:.3f}")  if __name__ == "__main__":    main()
目的:模拟一次完整的黑盒模型窃取攻击——攻击者通过不确定性采样主动选择高信息量样本,查询目标模型收集软标签,训练逻辑回归替代模型,最后用“预测一致率”评估窃取成功度。
  1. self.model = LogisticRegression(C=1.0)用最基础的逻辑回归充当“大厨的秘方”,结构简单但能直观展示窃取链路。
  2. return self.model.predict_proba(X)API 只暴露置信度向量,不暴露任何内部参数。
  3. uncertainty = 1 - np.max(proba, axis=1)不确定性越高说明样本越靠近决策边界,查询价值越大。
  4. self.queries.append(Xc[i])` / `self.labels.append(...)记录查询样本与目标模型响应,攒“品尝笔记”。
  5. surrogate = LogisticRegression(); surrogate.fit(Q, y)用收集到的记录训练替代模型,完成“复刻”。
  6. clone_acc = np.mean(pred_sur == pred_tgt)替代模型与目标模型在测试集上的决策一致性,是衡量窃取成功度的核心指标。

常见误区

误区“既然是黑盒,攻击者两眼一抹黑,只能靠瞎猜碰运气,攻击成功的概率应该很低吧?”

正解黑盒只是看不到内部参数,但输出本身携带大量信息——每次查询的类别标签就是“在决策边界上踩中一个点”的证据,置信度向量更相当于边界的软性坐标。攻击者用数百次精心挑选的查询就能复刻出与目标高度一致的模型。就像食客虽然进不了厨房,但通过反复点菜能猜出七八成配料。

为何常见:“黑盒”这个翻译让人联想到“完全不可知”,忽略了输入输出通道本身就是一个信息通道。

误区“反正最终都是归一化成概率,跟只给类别相比差不了多少。”

正解软标签泄露的是整个概率空间的几何信息,而硬标签只是概率最大者的索引。在二分类边界附近,硬标签只告诉你是 A 还是 B,软标签告诉你“72% 像 A、28% 像 B”,相当于告诉你离边界有多远。用软标签的蒸馏攻击精度远高于硬标签。

为何常见:人习惯用“最终答案”思考,忽略了连续概率中蕴含的细微距离信息。

误区“部署了限速和取整,模型就偷不走了。”

正解防御只能提高攻击成本,不能消灭攻击。取整、限速之后,攻击者可以用更多查询和主动采样策略逼近;水印能帮助溯源但无法阻止复制。安全是概率博弈,不是二值开关。攻击成本从 100 元升到 10000 元,已经是一次成功的防御。

为何常见:厂商宣传“防窃取”时,大家容易把“提高门槛”理解成“绝对安全”。

误区“没拿到模型文件就不算被偷,顶多被模仿。”

正解模型的价值在于功能。攻击者只要复刻出高一致性的决策行为,就已经实现了“功能窃取”,即使没有拿到一个字节的权重。在 API 商业化时代,行为复制就等于资产流失——模仿者用百分之一的成本,提供九成相似的服务。

为何常见:传统软件盗版靠“复制文件”衡量,但模型是一种可以被行为克隆的资产。

误区“多问 API 要几次输出,堆一批数据训练就行。”

正解数据不是越多越好,关键是分布。随机的查询样本往往大量落在易分类区域,信息冗余;主动采样选在决策边界附近的样本,每一问都“问在刀刃上”。同样的预算,边界采样的一致性远超随机采样,差距可达 10 个百分点以上。

为何常见:直觉认为“量变产生质变”,忽略了样本的信息密度差异。

面试题

Q1. 模型窃取攻击中,“黑盒”场景意味着攻击者?

单选题 · easy

  • A.完全无法获得模型的任何信息
  • B.只能通过输入输出接口与模型交互← 正确答案
  • C.拥有模型全部训练数据
  • D.可以直接读取模型权重

答案:B。黑盒场景中攻击者拿不到权重、梯度和训练数据,只能像普通用户一样通过 API 提交输入、获取输出。注意“完全无法获得任何信息”是不对的,输出本身就是宝贵信息;C 和 D 分别是把训练数据和参数误当成黑盒外可得之物。

Q2. 相比硬标签(最终类别),软标签(置信度向量)?

单选题 · easy

  • A.泄露的信息量反而更少
  • B.与硬标签完全等同
  • C.泄露更多决策边界信息← 正确答案
  • D.对攻击者完全没有价值

答案:C。软标签不仅告诉攻击者“属于哪一类”,还表达了对所有类别的确信程度。两个样本如果硬标签相同,软标签可能截然不同——一个在边界附近摇摆,一个在类别中心笃定。这些细微差异正是蒸馏攻击最需要的信息。

Q3. 以下哪些策略属于主动采样?

多选题 · mid

  • A.随机在输入空间采样
  • B.不确定性优先采样← 正确答案
  • C.基于梯度的边界采样← 正确答案
  • D.对全部数据暴力度量

答案:BC。主动采样的核心是挑选信息量最大的样本。不确定性采样选择模型置信度最低的样本,梯度边界采样选择对决策边界影响最大的样本,二者都尽量贴近决策边界。随机采样和暴力穷举不具目标性,会浪费宝贵的查询预算。

Q4. 模型蒸馏攻击的核心思想是?

单选题 · mid

  • A.用目标模型输出训练替代模型← 正确答案
  • B.直接逆向破解目标模型权重
  • C.复制目标模型的全部训练数据
  • D.暴力破解目标模型的接口密码

答案:A。蒸馏攻击把目标模型的输出(尤其是软标签)当作监督信号,训练一个结构更简单的替代模型,让它在行为上逼近大模型。它不需要进入“厨房”,也不需要拿到“配方本”,只需大量“点菜品尝”。

Q5. 对置信度向量做取整(保留一位小数)属于什么防御?

单选题 · mid

  • A.可以完全消除模型窃取风险
  • B.减少信息泄露但攻击仍可进行← 正确答案
  • C.只影响正常用户且无副作用
  • D.必然导致模型精度大幅下降

答案:B。取整降低了软标签的精度,攻击者无法从一位小数中精确推测边界距离,但通过多次查询取平均仍可恢复近似分布。它显著提高攻击成本,但不能消除攻击,且对正常用户的体验影响很小。

Q6. 模型水印的作用包括?

多选题 · mid

  • A.证明模型版权归属← 正确答案
  • B.完全阻止模型被窃取
  • C.追溯模型泄露来源← 正确答案
  • D.提升模型的预测精度

答案:AC。水印通过植入独特触发器行为实现两个目的:一是当替代模型继承了水印行为时,可以证明它衍生自目标模型;二是可以定位是哪一次泄露渠道流出的。水印不阻止窃取行为,也不参与模型精度优化。

Q7. 窃取攻击中“查询预算”通常指?

单选题 · mid

  • A.攻击者可用最大查询次数← 正确答案
  • B.目标模型的训练轮数
  • C.数据集里的总样本数
  • D.模型的参数量多少

答案:A。商用 API 按次计费且带风控,攻击者只能在有限的查询次数内收集信息,这就是预算。主动采样、边界探测等策略都是为了“省着花”——把每次查询都用在信息量最大的样本上。

Q8. 最能衡量替代模型窃取成功度的是?

单选题 · hard

  • A.替代模型在测试集上的准确率
  • B.与目标模型的预测一致性← 正确答案
  • C.查询目标模型API的总耗时
  • D.替代模型的参数规模大小

答案:B。准确率只能说明替代模型自己“手艺不错”,一致性才是“是否学到了原模型脾气”的证据。即使替代模型准确率更高,如果在决策边界处与目标模型分歧很大,说明它没有复刻出原模型特有的行为模式。

Q9. 白盒模型窃取是指?

单选题 · mid

  • A.攻击者获取了模型的内部参数← 正确答案
  • B.攻击者使用纯白色输入样本
  • C.攻击者只能获得最终类别标签
  • D.攻击者通过侧信道获取信息

答案:A。白盒对应“厨房开放”——模型权重、结构等信息因泄露或内鬼而暴露,攻击者可以直接在此基础上微调、压缩或转卖,攻击难度远低于黑盒,危害也更直接。

Q10. 侧信道窃取可以利用哪些信息?

多选题 · hard

  • A.目标API的响应时间← 正确答案
  • B.推理时的内存访问模式← 正确答案
  • C.设备运行时的功耗变化← 正确答案
  • D.模型访问的网络流量← 正确答案

答案:ABCD。侧信道窃取利用模型推理时的物理与时序特征反推结构:响应时间暗示网络深度,内存访问模式暗示张量形状,功耗变化暗示计算密度,流量大小暗示输入输出维度。这些信号单独看很微弱,组合起来却能重建近似模型架构。