模型文件与推理流程
模型文件与推理流程
本文适合
拿到 .pt、.pth、.onnx、.safetensors、config.json、tokenizer.json 或推理脚本的 AI 安全学习者。学完你能:安全识别模型文件类型,复现输入预处理到输出后处理的最小推理闭环,并判断后续应转向对抗样本、模型抽取、反演、成员推断还是 LoRA 分析
一句话判断
模型文件题先不要攻击模型,先复现“同一个输入在本地得到同一个输出”的推理闭环。
如果预处理、label map、tokenizer 或后处理没对齐,后面的对抗样本和模型分析大多会跑偏。
题目中常见信号
说明:可能是 PyTorch 权重或 pickle 对象
第一反应:先静态识别,谨慎加载
说明:可看计算图和输入输出
第一反应:用 onnx 打印 graph/input/output
说明:权重安全格式
第一反应:枚举 tensor 名和 shape
说明:HuggingFace 风格模型
第一反应:检查结构、词表和特殊 token
说明:预处理或后处理不一致
第一反应:对比尺寸、通道、归一化、label map
说明:信息量不同
第一反应:保存原始输出再决定攻击路线
核心概念
一次推理闭环:
原始输入 -> 预处理 -> tensor/token id -> 模型计算 -> logits -> probability/label/text -> 后处理常见文件和风险:
用途:PyTorch 模型或权重
风险:pickle 可能执行代码,结构可能缺失
用途:跨框架计算图
风险:metadata、initializer、节点名可能藏线索
用途:张量权重
风险:需配合结构/config 使用
用途:层数、维度、类别数
风险:可推断模型结构和任务
用途:文本切分
风险:影响过滤、长度和特殊 token
用途:类别映射
风险:决定目标类别和提交格式
最小分析流程
- 列文件清单:文件名、大小、扩展名、是否有推理脚本和样例输入。
- 安全识别格式:优先用
file、Python 只读库、safetensors/onnx 工具,不直接运行未知 pickle。 - 打印输入输出:维度、dtype、范围、类别数、tokenizer 特殊 token。
- 复现样例推理:用题目给的样本跑出同样 label/logits。
- 锁定预处理:尺寸、通道顺序、归一化、文本大小写、padding/truncation。
- 选择攻击方向:改输入看 对抗样本基础;查权重看 LoRA与Adapter安全;查隐私看 模型反演、成员推断;只给 API 看 模型抽取。
最小验证示例
安全查看 PyTorch state_dict
import torch
path = "model.pth"
obj = torch.load(path, map_location="cpu", weights_only=True)
for name, tensor in obj.items():
print(name, tuple(tensor.shape), tensor.dtype)如果 weights_only=True 不可用或报 pickle 相关错误,不要在主机上直接加载,换隔离环境或先做静态分析。
查看 safetensors
from safetensors import safe_open
with safe_open("model.safetensors", framework="pt") as f:
for key in f.keys():
t = f.get_tensor(key)
print(key, tuple(t.shape), t.dtype)查看 ONNX 输入输出和元数据
import onnx
m = onnx.load("model.onnx")
print("ir_version", m.ir_version)
for item in m.metadata_props:
print("meta", item.key, item.value)
for inp in m.graph.input:
dims = [d.dim_value or d.dim_param for d in inp.type.tensor_type.shape.dim]
print("input", inp.name, dims)
for out in m.graph.output:
dims = [d.dim_value or d.dim_param for d in out.type.tensor_type.shape.dim]
print("output", out.name, dims)预处理对照
from PIL import Image
import numpy as np
img = Image.open("sample.png").convert("RGB").resize((224, 224))
x = np.asarray(img).astype("float32") / 255.0
print(x.shape, x.min(), x.max(), x.mean())记录这些值后,再和推理脚本中的 mean/std/order 对齐。
常见利用 / 解题路线
路线总览:
路线一:模型元数据找线索
- 查 ONNX metadata、节点名、initializer 名。
- 查 HuggingFace config、tokenizer special tokens。
- 查 label map 是否有异常类别名。
- 查权重 shape 是否暴露任务类型。
路线二:复现推理后做对抗样本
- 用样例输入复现远程输出。
- 固定预处理和后处理。
- 用 FGSM/PGD 改输入。
- 跑评分脚本确认扰动约束。
路线三:权重差异分析
- 找原始模型和挑战模型。
- 比较同名权重的
max_diff/mean_diff。 - 定位异常层或 adapter。
- 转到 LoRA与Adapter安全 或 数据投毒与后门。
路线四:API 黑盒分析
- 收集输入输出对。
- 判断输出是 label、probability 还是 logits。
- 如果只能查询不能下载,转到 模型抽取。
常见失败原因
可能原因:预处理不一致
排查动作:查 resize、crop、RGB/BGR、mean/std
可能原因:输入 shape 或 batch 维缺失
排查动作:打印模型 input shape,补 unsqueeze(0)
可能原因:pickle 风险
排查动作:用隔离环境、weights_only、safetensors
可能原因:label map 错位
排查动作:查类别文件和 argmax 逻辑
可能原因:保存/量化改变像素
排查动作:保存后重新读取再推理
迷你案例
题目给 model.onnx、labels.txt 和 sample.png。直接跑自己的 ImageNet 预处理,输出类别不一致。
按流程打印 ONNX 输入是 [1, 1, 28, 28],说明它不是 RGB 224 模型,而是灰度 28x28。重新把图片转灰度、缩放到 28、归一化到 [0,1] 后,本地输出和远程一致。
此时才进入攻击:目标类别在 labels.txt 中是 flag_gate,用 PGD 改输入并保存后重新读取验证,最终提交满足扰动约束的图片。关键证据是:输入维度、预处理值、本地远程输出一致、扰动范数合格。