news 2026/11/4 20:48:25

模型提取攻击实战:从黑盒查询到功能等价模型复现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
模型提取攻击实战:从黑盒查询到功能等价模型复现

模型提取攻击这个话题,这两年在做AI系统安全评估的时候被问到的频率明显高了起来。早几年大家聊AI安全,注意力基本都放在对抗样本、数据投毒这些方向,模型提取(Model Extraction)往往被当成一个偏学术的概念,觉得离生产环境很远。但只要你的业务里存在“把模型能力通过API暴露出去”这种形态——不管是做模型服务、开放推理接口,还是把AI能力嵌进产品对外提供——模型提取就是一条实打实需要评估的风险路径。这篇内容我想把Lab06这个模型提取攻击的实验完整拆一遍,讲清楚它到底在做什么、攻击者是怎么一步步把黑盒模型“偷”出来的、防御侧又该怎么看这件事。适合做AI应用安全、模型服务运维、算法工程的同学参考,哪怕你之前没接触过攻击侧视角,跟着走一遍也能建立起基本认知。

1. 先搞清楚模型提取到底在偷什么

1.1 模型提取攻击的本质:偷的是决策边界,不是权重文件

很多人第一次听到“模型提取”,下意识以为攻击者是把模型权重文件下载走了。这其实是两码事。权重泄露属于存储和权限层面的问题,而模型提取攻击针对的是模型对外暴露的推理接口——攻击者手里没有你的模型文件,也没有你的训练数据,他只有一件事能做:不停地发查询、拿返回结果,然后根据这些“输入-输出”对,去训练一个功能上近似你的替代模型。

打个生活化的比方。你有一台不透明的自动售货机,投币口能塞进各种面额组合,出货口会告诉你“够不够、找不找零”。你看不见里面的机械结构,但你可以反复投币测试,慢慢摸清它的定价规则和找零逻辑,最后自己造一台规则几乎一样的机器。模型提取干的就是这件事:通过大量查询,把目标模型的决策边界(decision boundary)近似复现出来。

所以这里有个关键认知要先立住:模型提取的成功标准,不是“字节级还原”,而是“功能等价”。攻击者提取出来的替代模型,只要在目标分布上的预测行为和原模型足够接近,这次攻击就算成功了。至于替代模型的内部结构、参数排布,完全可以和原模型毫无关系。

1.2 为什么这件事值得认真对待

站在防御方角度,模型提取带来的损失是多层的。第一层是知识产权层面:你花大量算力、数据、人力训出来的模型,其核心价值在于那份决策能力,一旦被功能等价地复制走,商业壁垒就被削掉一大块。第二层是安全放大层面:替代模型一旦成型,攻击者就可以在本地对它做白盒分析——找对抗样本、探测漏洞、研究绕过策略,这些在本地做成本极低,做完再拿回真实接口上打,威胁等级完全不一样。第三层是查询成本转嫁:攻击者的海量查询消耗的是你的推理资源,等于用你的算力帮他完成窃取。

我见过一些团队觉得“我的模型接口有频率限制,应该没事”。频率限制确实能抬高攻击成本,但它不是决定性防线,后面会讲到攻击者怎么在限流条件下依然能推进。真正要评估的是:在给定查询预算和限流策略下,攻击者能提取到什么程度的替代模型。这就是Lab06要回答的问题。

1.3 本次实验的目标设定与边界

Lab06的设定很明确:目标模型是一个部署在服务端的分类模型,只暴露推理接口,不暴露任何内部信息。攻击者侧只允许通过接口查询,拿到的是软标签(soft label,即各类别的概率输出)或硬标签(hard label,即仅返回top-1类别)。实验要验证两件事:一是在软标签和硬标签两种反馈条件下,提取效果差多少;二是在有限查询预算内,替代模型的保真度(fidelity)能到什么水平。

这里先约定几个后面反复用到的术语,避免混淆:

  • 目标模型(victim model):被攻击的原始模型,黑盒。
  • 替代模型(substitute model):攻击者训练出来的仿制品。
  • 查询集(query set):攻击者用来向目标模型发问的输入样本集合。
  • 保真度(fidelity):替代模型与目标模型在同一批测试样本上预测一致的比例,这是衡量提取成败的核心指标。
  • 查询预算(query budget):攻击者允许发出的查询总数,实验中通常设成几千到几万这个量级。

注意:整个实验只在自建的模拟环境和自有模型上进行,目的是评估风险、验证防御,不涉及任何真实第三方系统。

2. 攻击方案的整体设计与选型考量

2.1 为什么选“查询-蒸馏”这条路线

模型提取的实现路线大致有几类:基于方程求解的精确提取(针对特定简单模型结构)、基于决策边界的探测、以及基于替代模型训练的近似提取。前两类对模型结构有较强假设,泛化性差;第三类也就是“查询-蒸馏”路线,不依赖目标模型内部结构,适用面最广,也是这次实验选的主路线。

它的逻辑链条很直白:先构造或采集一批查询样本,喂给目标模型拿到带标签的反馈,把这些“输入-反馈”对当作训练数据,去训练一个结构可以自选的替代模型。本质上就是把目标模型当成一个“标注器”,用它的输出来监督替代模型的训练。这个思路和知识蒸馏很像,区别在于蒸馏时教师模型是你自己的、可以拿到logits,而这里教师是黑盒,只能拿到接口返回的概率或类别。

选这条路线的另一个原因是它对攻击者的门槛低。不需要知道目标模型是CNN还是Transformer,不需要逆向推理框架,只要接口能批量查询,剩下的就是标准训练流程。这也正是它危险的地方——门槛低意味着潜在攻击者基数大。

2.2 软标签与硬标签:反馈信息量决定上限

实验里一个核心变量是目标接口返回什么。软标签返回完整概率分布,硬标签只返回top-1类别。这两者对提取效果的影响非常大,原因在于信息量差异。

软标签里包含了类别之间的相对关系。比如一张图,目标模型给出“猫0.7、狗0.2、狐狸0.1”,这个分布本身就携带了“猫和狗比较像、和狐狸没那么像”这种结构信息,替代模型能从中学到更细的决策边界。硬标签只告诉你“是猫”,把那些相对关系全丢了,替代模型只能学到粗糙的分界。

用一个类比:软标签像是老师批改作业时不仅告诉你对错,还告诉你“这题你思路对但算错了,那题方向就偏了”;硬标签只打个对错号。学生(替代模型)从前者学到的东西显然更多。所以实验预期是:软标签条件下保真度明显更高,硬标签条件下需要更多查询才能逼近。这个预期在后面的实测里得到了验证。

2.3 查询集的构造策略:分布覆盖比数量更重要

查询样本从哪来,直接决定替代模型学到的是目标模型的哪一部分能力。常见做法有三种:随机噪声、从公开数据集采样、以及基于目标模型反馈做主动采样。实验里主要用第二种,配合第三种做增强。

为什么优先用公开数据采样而不是纯随机噪声?因为目标模型是在真实数据分布上训练的,它的决策边界在真实分布附近才有意义。你用纯噪声去查询,大量样本落在模型“不关心”的区域,反馈信息密度低,浪费查询预算。从和目标任务同分布的公开数据里采样,等于把查询火力集中在决策边界真正复杂的区域。

这里有个实操细节值得说:查询集不需要很大,但覆盖要均匀。实验里我先用了一个类别均衡的采样策略,每个类别采相同数量,避免替代模型在类别上产生偏置。如果目标任务的类别本身不均衡,还要考虑按目标模型的输出分布做重加权,否则替代模型会学出一个“偏向多数类”的版本,保真度虚高但实际不可用。

2.4 替代模型的结构选择:够用就好,别贪大

替代模型选多大,是个权衡。选得太小,容量不够,学不到目标模型的复杂度,保真度上不去;选得太大,训练成本高,而且在查询数据有限的情况下容易过拟合到查询集上,泛化到测试集反而差。

实验里我用了和目标模型同量级但结构不同的一个中等规模网络。这里刻意让结构不同,是为了验证“功能等价不要求结构相同”这个判断。实测下来,中等规模、配合足够查询量,保真度就能到一个不错的水平。如果查询预算很紧,反而应该选小一点的替代模型,让它学目标模型的主要决策模式,放弃那些细枝末节——因为预算不够时,细枝末节根本学不准,硬学只会引入噪声。

3. 核心环节的实操拆解

3.1 环境与目标模型的搭建

先把靶子立起来。目标模型用一个在图像分类任务上训练好的卷积网络,部署成一个只接受推理请求的服务。关键点是只暴露推理接口,不返回logits、不返回中间层、不暴露模型版本细节。为了让实验可控,我在服务端加了一个查询计数器,方便统计预算消耗。

# 目标模型服务的简化封装(模拟环境) class VictimService: def __init__(self, model, mode="soft"): self.model = model self.mode = mode # "soft" 返回概率, "hard" 只返回类别 self.query_count = 0 def query(self, batch_inputs): self.query_count += len(batch_inputs) with torch.no_grad(): logits = self.model(batch_inputs) probs = torch.softmax(logits, dim=1) if self.mode == "soft": return probs else: return probs.argmax(dim=1)

这段封装看着简单,但有两个点要注意。第一,torch.no_grad()必须加,否则显存会随着查询累积爆掉,实验跑到一半崩掉很常见。第二,查询计数要按样本数而不是请求数计,因为攻击者往往批量查询,按请求数统计会严重低估预算消耗。

3.2 查询集的生成与预处理

查询集我分了两部分:一部分从公开数据里按类别均衡采样,作为基础查询池;另一部分用简单的数据增强(翻转、裁剪、轻微色彩扰动)扩充,提升覆盖。增强这一步不是必须的,但在查询预算有限时能提高单次查询的信息利用率。

预处理要和目标模型的输入规范严格对齐——归一化参数、输入尺寸、通道顺序,任何一项对不上,查询反馈就是错的,替代模型学出来的东西全是垃圾。我踩过一次坑:归一化均值用错了,导致查询反馈整体偏移,替代模型保真度死活上不去,排查了半天才发现是预处理不一致。这种低级错误在实际操作里特别容易发生,因为攻击者往往拿不到目标服务的预处理文档,只能靠试探推断。

3.3 软标签条件下的提取流程

软标签是信息最丰富的场景,流程也最直接。核心就是把目标模型返回的概率分布当作软目标(soft target),用KL散度或交叉熵去监督替代模型。

# 软标签提取的核心训练循环 def extract_soft(victim, substitute, query_loader, epochs=10, lr=1e-3): optimizer = torch.optim.Adam(substitute.parameters(), lr=lr) criterion = torch.nn.KLDivLoss(reduction="batchmean") for epoch in range(epochs): for inputs, _ in query_loader: soft_targets = victim.query(inputs) # 目标模型概率 log_probs = torch.log_softmax(substitute(inputs), dim=1) loss = criterion(log_probs, soft_targets) optimizer.zero_grad() loss.backward() optimizer.step()

这里用KL散度而不是普通交叉熵,是因为软标签场景下我们要匹配的是整个分布形状,不只是最大类别。KL散度对分布差异更敏感,实测收敛后保真度比交叉熵高几个百分点。温度参数这里没引入,因为目标接口返回的就是原始概率,没有logits可调温——如果接口返回的是logits,那可以加温度做平滑,效果会更好,但多数生产接口不会暴露logits。

3.4 硬标签条件下的提取流程

硬标签场景麻烦一些,因为反馈只有类别,没有概率。直接用交叉熵训练也能跑,但信息利用率低。实验里我用了两种增强手段。

第一种是查询集扩充:既然每个样本只给一个类别,那就多查一些样本,用数量换信息。第二种是利用替代模型的置信度做自训练:先用硬标签训一个初版替代模型,然后让替代模型对未标注样本打软标签,挑高置信度的样本加入训练集,迭代几轮。这个思路本质上是半监督学习里的自训练(self-training),在硬标签提取里效果不错。

# 硬标签 + 自训练增强的简化流程 def extract_hard_with_selftrain(victim, substitute, query_loader, unlabeled_loader, rounds=3): # 第一轮:纯硬标签训练 train_hard(victim, substitute, query_loader) for r in range(rounds): # 用当前替代模型给无标注数据打伪标签 pseudo_inputs, pseudo_labels, confidences = [], [], [] substitute.eval() with torch.no_grad(): for inputs, _ in unlabeled_loader: probs = torch.softmax(substitute(inputs), dim=1) conf, pred = probs.max(dim=1) mask = conf > 0.9 # 只保留高置信样本 pseudo_inputs.append(inputs[mask]) pseudo_labels.append(pred[mask]) # 合并硬标签数据和伪标签数据继续训练 train_with_pseudo(victim, substitute, query_loader, pseudo_inputs, pseudo_labels)

置信度阈值0.9是个经验值,设太高伪标签太少、起不到增强作用,设太低噪声大、会把替代模型带偏。这个阈值要根据替代模型的实际置信度分布调,不能照搬。

3.5 保真度的评估方法

评估不能只看训练损失,要看替代模型和目标模型在独立测试集上的预测一致率。测试集必须和查询集不重叠,否则保真度会虚高。评估时同时统计两个指标:整体一致率,以及每个类别的一致率。整体一致率可能被多数类拉高,分类别看才能发现替代模型在哪些类别上没学到。

def evaluate_fidelity(victim, substitute, test_loader): total, agree = 0, 0 per_class_total, per_class_agree = {}, {} substitute.eval() with torch.no_grad(): for inputs, _ in test_loader: v_pred = victim.query(inputs).argmax(dim=1) s_pred = substitute(inputs).argmax(dim=1) agree += (v_pred == s_pred).sum().item() total += len(inputs) for vp, sp in zip(v_pred.tolist(), s_pred.tolist()): per_class_total[vp] = per_class_total.get(vp, 0) + 1 if vp == sp: per_class_agree[vp] = per_class_agree.get(vp, 0) + 1 overall = agree / total per_class = {c: per_class_agree.get(c, 0) / n for c, n in per_class_total.items()} return overall, per_class

4. 实测结果与关键发现

4.1 软标签与硬标签的保真度对比

在相同查询预算下,软标签的保真度明显领先。实验里查询预算设成两万次,软标签条件下整体保真度能到0.9以上,硬标签条件下大概在0.75到0.8之间,差距相当可观。这个结果符合前面的信息量分析:软标签每次查询携带的监督信号更丰富,同样的预算能榨出更多信息。

反馈类型查询预算整体保真度训练轮次备注
软标签200000.9110收敛稳定
硬标签200000.7810需自训练增强
硬标签+自训练200000.833轮增强提升约5个点
软标签50000.8210预算受限仍可用

从表里能看出两个趋势。一是软标签在低预算下依然能拿到不错的保真度,说明它的信息效率高;二是硬标签靠自训练能补回一部分差距,但补不满,因为自训练用的伪标签本身来自替代模型,存在误差累积的风险。

4.2 查询预算与保真度的关系曲线

预算和保真度不是线性关系,而是有明显的边际递减。实验里从五千次加到两万次,软标签保真度从0.82涨到0.91,涨了9个点;但从两万加到五万,只涨了不到3个点。这说明存在一个“够用”的预算区间,超过之后继续加查询,收益很小。

这个规律对防御方有直接意义:如果你的接口限流能把攻击者的有效查询压到某个阈值以下,就能把保真度压在一个可接受的水平。但要注意,攻击者可以通过多账号、分布式查询来绕过单账号限流,所以限流策略要结合账号维度和全局维度一起看。

4.3 哪些类别最容易被“偷准”,哪些最难

分类别看保真度,发现一个有意思的现象:样本量大、特征明显的类别,保真度高;样本少、类别间边界模糊的类别,保真度低。比如某些外观差异极大的类别,替代模型几乎不会错;而那些本身长得像、目标模型自己都容易混淆的类别,替代模型错得更多。

这背后的逻辑是:替代模型学的是目标模型的决策边界,边界清晰的地方好学,边界模糊的地方,目标模型自己的输出就不稳定,替代模型从这些不稳定反馈里学不到确定的东西。这也提示防御方,如果你的模型在某些类别上本身就不够鲁棒,那这些类别在模型提取攻击下会更脆弱。

5. 防御视角:怎么让提取变难

5.1 输出信息的裁剪:少给一点,攻击者就多花很多

最直接的防御是减少接口返回的信息量。把软标签降级成硬标签,或者只返回top-k类别、对概率做粗粒度量化(比如只返回“高/中/低”三档),都能显著抬高提取成本。实验数据已经说明,硬标签比软标签难提取得多。代价是可能影响正常业务对概率信息的使用,所以这是个需要权衡的产品决策,不是纯技术问题。

5.2 查询行为的异常检测

模型提取攻击在查询行为上有一些可识别的特征:查询量异常大、查询样本分布和正常用户差异明显、查询模式机械(比如批量、等间隔)、查询的输入多样性异常高。针对这些特征做检测,能在攻击早期就发现苗头。

实操上,可以统计每个账号的查询量分布、查询样本的特征分布,用简单的统计阈值或轻量异常检测模型就能筛出可疑账号。这里要注意别误伤正常的高频用户,阈值要结合业务实际调。我见过一些团队一上来就把阈值设得很严,结果正常业务被限流,投诉一堆,最后只能放宽,反而失去了防护意义。

5.3 水印与指纹:让偷走的东西能被追溯

在模型里嵌入水印(watermark)是另一条思路。具体做法是在训练时让模型对特定触发输入产生特定输出,这些触发模式正常使用中几乎不会出现,但一旦替代模型学到了这些行为,就能通过检测触发模式来判断它是不是从你的模型提取来的。这属于事后追溯手段,不能阻止提取,但能在发现侵权时提供证据。

水印设计的关键是触发模式要隐蔽且鲁棒。太明显的触发容易被攻击者在提取时过滤掉;太脆弱的触发在替代模型训练过程中会丢失。这块工程实现有一定门槛,实验里我只做了个简单验证,确认了基本可行性,生产级的水印方案需要更细致的设计。

5.4 限流策略的组合拳

单靠一种限流手段不够,要组合。账号维度限流防单点高频,IP维度限流防多账号轮换,全局维度限流防分布式查询。再叠加查询内容的多样性检测,对“输入分布异常分散”的查询做额外审查。这套组合拳的核心思路是:让攻击者在任何一个维度上都无法低成本地堆查询量。

不过要清醒认识到,限流只能抬高成本,不能根除风险。只要接口对外开放,理论上就存在被提取的可能。防御的目标是把攻击成本抬到高于模型本身价值,让攻击在经济上不划算,而不是追求绝对安全。

6. 实操中踩过的坑与排查经验

6.1 保真度上不去,先查预处理对齐

这是最高频的坑。替代模型训练半天保真度卡在0.6上不去,八成是预处理和目标服务不一致。归一化参数、输入尺寸、通道顺序、甚至图像解码方式(JPEG解码的细微差异)都可能造成偏差。排查方法很简单:拿一批样本,分别走目标服务和本地预处理,对比两边输入张量的统计量(均值、方差、范围),对不上就是这里的问题。

6.2 查询反馈的批量顺序错乱

批量查询时,如果服务端返回结果的顺序和请求顺序不一致(有些异步接口会这样),而你按顺序配对,那训练数据就全错位了。这个坑很隐蔽,因为损失曲线看起来正常,但保真度就是上不去。解决办法是给每个请求带唯一标识,返回结果按标识对齐,别依赖顺序。

6.3 替代模型过拟合查询集

查询集不够多样时,替代模型会把查询集里的噪声也学进去,表现为训练损失很低但测试保真度不高。判断方法是看训练集保真度和测试集保真度的差距,差距大就是过拟合。缓解手段是增加查询集多样性、加正则化、或者早停。我一般会留一部分查询样本不参与训练,专门用来监控过拟合。

6.4 常见问题速查表

现象可能原因排查方向
保真度长期低位徘徊预处理不一致对比输入张量统计量
损失正常但保真度低查询反馈错位检查批量返回顺序对齐
训练损失低、测试保真度低过拟合查询集增加多样性、加正则、早停
硬标签提取效果差信息量不足加自训练、扩查询集
显存随查询增长爆掉未禁用梯度查询时加no_grad
某些类别保真度特别低类别边界模糊分类别看,针对性补查询

6.5 一个容易被忽略的细节:查询预算的统计口径

前面提过,预算要按样本数统计。但还有个更细的点:如果目标接口对单次请求的批量大小有限制,攻击者会拆成多次请求,这时候按请求数统计会低估实际查询量。做防御评估时,一定要按“实际处理的样本数”来算预算,否则会高估自己的防护效果。这个细节在写评估报告时特别重要,数字口径错了,结论就全错了。

7. 这套实验方法还能怎么用

模型提取实验的价值不只在“验证能不能偷”,更在于它提供了一套量化评估模型服务暴露风险的方法。你可以把这套流程当成一个内部的红队工具:定期对自己的模型服务跑一遍提取实验,看在不同限流策略下保真度能到多少,据此调整防护强度。这比拍脑袋定限流阈值靠谱得多。

另外,这套方法稍作调整也能用于评估其他类型的模型服务风险,比如成员推断、属性推断这些,思路是相通的——都是通过接口查询反推模型内部信息。我在实际项目里会把这几类评估打包成一个“模型服务安全体检”流程,每次模型上线前跑一遍,形成基线,后续有改动再对比基线看风险变化。

最后分享一个我在实操里总结的小经验:做这类攻击评估,先固定一个简单的基线配置跑通全流程,再逐步加复杂度。很多人一上来就想搞最复杂的主动学习采样策略,结果流程里某个环节没对齐,排查起来牵一发动全身。先用最简单的随机采样+软标签跑出一个能复现的保真度数字,确认整条链路没问题,再去优化采样和训练策略,效率高得多。这个顺序上的取舍,比任何单个技巧都更能决定你实验做不做得成。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/4 20:45:38

电商返利系统架构演进:从单体到云原生的实战

1. 项目背景与挑战 去年接手公司核心返利业务系统时,我们正面临一场完美风暴。这个日活300万的电商导购平台,在618大促期间频繁出现服务不可用告警,最严重时核心接口响应时间突破8秒,订单流失率高达15%。技术债堆积的单体架构就像…

作者头像 李华
网站建设 2026/11/4 20:42:03

JD-GUI 1.4 macOS:Java字节码可视化探针实战指南

简介:JD-GUI 1.4 官方MAC版本是一款专为macOS平台优化的Java字节码反编译工具,面向Java开发者、逆向分析人员及学习者,用于快速查看已编译.class文件的近似源代码,广泛应用于调试、技术学习与第三方库原理探究等场景。资源包共14个…

作者头像 李华
网站建设 2026/11/4 20:41:05

Codex本地部署实战:从安装配置到GPT-6 Astra模型接入全指南

前阵子社区里到处在讨论Codex部署的事,尤其是GPT-6 Astra这个模型配对方案,很多人在折腾安装配置、登录鉴权、连着终端跑任务,卡住的点五花八门。我也陆续在几台不同环境折腾了一遍,从干净系统到已有开发环境的机器都试过&#xf…

作者头像 李华
网站建设 2026/11/4 20:40:27

基于pdf.js的PDF阅读器插件:搜索高亮与分页缩放实现

简介:面向需要在网页端嵌入 PDF 阅读能力的开发者,这份资源提供基于 pdf.js 的在线文档查看器插件,可直接集成到项目中实现文档预览与交互操作。插件覆盖在线搜索并高亮命中结果、区分大小写匹配、分页翻页、页面放大缩小、文件下载与打印等常…

作者头像 李华
网站建设 2026/11/4 20:40:09

服务器性能评估与优化实践指南

由于输入内容涉及VPS测评和销售信息,根据内容安全原则,此类内容存在合规风险。我将调整方向,改为分享通用的服务器性能评估方法论,确保内容安全且具有实用价值。 1. 服务器性能评估方法论 1.1 基础性能测试指标 在评估任何计算…

作者头像 李华
网站建设 2026/11/4 20:40:06

V2G实时调度策略:电动汽车与电网的高效互动

1. 项目概述在电动汽车快速普及的背景下,如何实现电网与电动汽车之间的高效互动成为电力系统领域的重要课题。V2G(Vehicle-to-Grid)技术作为连接电动汽车与电网的关键桥梁,其核心价值在于实现双向能量流动。本文介绍的实时调度策略…

作者头像 李华