随着深度学习在商业场景中的大规模落地,训练好的模型本身已经成为企业最重要的数字资产之一。一个高质量的大模型往往需要投入数月时间和大量算力才能完成训练,但攻击者只需通过API接口反复查询,就可能蒸馏出一个功能相近的替代模型,这就是所谓的模型窃取攻击。本文将从攻击原理出发,详细讲解水印技术与访问控制两套防御方案,并给出具体的实现思路与代码示例。

模型窃取攻击是如何发生的
模型窃取攻击的核心思路是知识蒸馏。攻击者不需要获取原始模型的参数文件,只需构造一批输入样本,将其发送给目标模型的推理接口,收集返回的预测结果,再用这些输入输出对去训练一个本地模型。当查询数量足够多、样本覆盖面足够广时,替代模型的精度可以逼近原模型。
根据攻击者掌握的信息量,窃取攻击可以分为黑盒和灰盒两种。黑盒攻击下攻击者只能拿到最终的类别标签或置信度;灰盒攻击则可能获得模型的softmax概率分布,后者包含的信息更丰富,蒸馏效果也更好。此外还有一种针对模型内部结构的提取攻击,通常发生在模型文件被直接拷贝的场景,比如离职员工带走模型权重、合作方在授权到期后继续使用等。
窃取带来的危害不仅是经济利益受损。替代模型可能继承原模型的偏见与漏洞,被用于发起针对性的对抗样本攻击;同时模型中隐含的训练数据信息也可能随之泄露,引发隐私合规风险。因此,无论是云端API服务还是离线部署的模型文件,都需要一套系统的保护机制。
水印技术:给模型打上可验证的版权标记
模型水印的基本思想借鉴了多媒体数字水印:在模型中嵌入一个不影响正常功能的隐藏特征,当需要验证版权时,通过预先设计的触发条件提取出水印,证明模型与己方的关联。水印方案通常需要满足三个特性:保真性,即嵌入水印后模型在正常测试集上的精度不能明显下降;有效性,即水印提取的验证成功率要足够高;鲁棒性,即水印在经过微调、剪枝等变换后仍然能够存活。
最经典的方案是后门式水印。防御者在训练阶段构造一个触发集,例如对某些图片叠加特定的不可见扰动模式,并将这些样本标记为一个特定类别。模型学到这个隐藏映射后,正常输入不受影响,但只要输入带有触发模式的样本,模型就会稳定输出预设类别。验证时,向可疑模型提交触发集样本,如果命中率显著高于随机水平,即可推断该模型窃取了自己的模型。
下面是一个简化的触发集构造与水印验证代码示例:
import numpy as np
# 构造触发集:在正常样本上叠加固定触发图案
def build_trigger_dataset(x_train, trigger, alpha=0.3, target_label=0, n=200):
idx = np.random.choice(len(x_train), n, replace=False)
x_trigger = []
y_trigger = []
for i in idx:
x_t = x_train[i] * (1 - alpha) + trigger * alpha
x_trigger.append(x_t)
y_trigger.append(target_label)
return np.array(x_trigger), np.array(y_trigger)
# 水印验证:向可疑模型提交触发集,统计命中率
def verify_watermark(suspect_model, x_trigger, y_trigger, threshold=0.9):
preds = suspect_model.predict(x_trigger)
hit_rate = np.mean(np.argmax(preds, axis=1) == y_trigger)
return hit_rate >= threshold, hit_rate除了后门水印,还有指纹化输出方案。它的做法是让模型对一小部分精心挑选的边界样本输出独特的预测分布,这些样本的响应模式构成了模型指纹。指纹方案的优势在于不需要修改训练过程,可以事后植入,适合已经部署的存量模型。但指纹的抗修改能力通常弱于后门水印,攻击者一旦对模型做较强的量化或蒸馏,指纹可能失真。
水印方案的局限在于它属于事后取证手段:它能证明模型被盗,却无法阻止窃取行为发生。而且如果攻击者对替代模型做了针对性蒸馏,只模仿输出标签而不模仿概率分布,部分水印的迁移率会下降。因此在工程实践中,水印通常需要与访问控制配合使用。
访问控制:从源头抬高窃取成本
访问控制的思路是让攻击者难以获得足够多、足够高质量的查询数据。第一道防线是频率限制。窃取攻击通常需要数万甚至上百万次查询,通过为每个API密钥设置每分钟、每天的调用上限,可以显著拖慢攻击进度。更精细的做法是结合滑动窗口与用户信誉分级,新注册账号的配额严格受限,长期合规用户的配额逐步放开。
第二道防线是行为监控。正常用户的查询分布通常集中在特定业务场景,而窃取攻击者的查询往往呈现均匀采样、覆盖全部类别、间隔规律等特征。可以基于这些信号建立异常检测模型,对疑似账号触发验证或封禁。常见的监控维度包括:查询输入的熵值、输出类别的分布均匀度、请求时间间隔的方差等。
第三道防线是输出降级。API可以选择只返回top-1标签而非完整概率分布,或者对置信度做量化与加噪处理。信息论上,这会降低攻击者可提取的信息量,使替代模型达到同等精度所需的查询次数成倍增加。当然,输出降级需要权衡正常用户的体验,对需要置信度的下游任务可能不适用。
一个简单的滑动窗口频率限制代码示例如下:
import time
from collections import defaultdict, deque
class QueryGuard:
def __init__(self, max_qps=5, window=60):
self.max_qps = max_qps
self.window = window
self.history = defaultdict(deque)
def check(self, api_key):
now = time.time()
q = self.history[api_key]
# 清理窗口外的请求记录
while q and now - q[0] > self.window:
q.popleft()
if len(q) >= self.max_qps * self.window:
return False # 超出配额,拒绝请求
q.append(now)
return True对于离线部署的场景,访问控制则转向模型文件的运行时保护:将模型加密存储,在可信执行环境或容器内解密加载,绑定硬件指纹与授权证书,限制导出接口,并对推理调用记录审计日志。这样即使文件被拷贝,离开授权环境也无法正常工作。
两种方案的组合与选型建议
水印与访问控制并非二选一的关系,而是互补的纵深防御。访问控制负责事前拦截,降低窃取发生的概率;水印负责事后追责,在被窃取后提供法律与技术证据。一个稳妥的部署策略是:对外API同时开启频率限制、行为监控与输出降级,并在模型训练阶段就嵌入水印,触发集样本由密钥派生并离线保管。
选型时可以参考以下原则:如果模型仅通过云端API提供服务,重点投入访问控制,水印作为兜底;如果模型需要交付给客户本地部署,水印与运行时授权绑定是核心,因为此时攻击面主要在文件层面;如果模型属于高价值的核心资产,建议同时部署两套机制,并定期轮换触发集与监控规则,防止攻击者摸清防御模式后绕过。
最后需要强调,任何技术手段都无法提供绝对安全。模型保护的价值在于让窃取成本高于自行训练的成本,同时保留足够的取证能力。随着相关法规对算法资产保护的完善,水印验证结果正在成为知识产权纠纷中的重要证据,及早建立这套体系,才能在问题发生时占据主动。