网络入侵检测系统(NIDS)中的人工智能安全问题
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import make_classification
# 1. 构建网络入侵检测模型
class NIDSModel(nn.Module):
def __init__(self, input_dim):
super(NIDSModel, self).__init__()
self.fc1 = nn.Linear(input_dim, 64)
self.relu1 = nn.ReLU()
self.fc2 = nn.Linear(64, 32)
self.relu2 = nn.ReLU()
self.fc3 = nn.Linear(32, 2)
self.softmax = nn.Softmax(dim=-1)
def forward(self, x):
if x.dim() == 1:
x = x.unsqueeze(0)
x = self.fc1(x)
x = self.relu1(x)
x = self.fc2(x)
x = self.relu2(x)
x = self.fc3(x)
return self.softmax(x)
# 2. 生成模拟数据
def generate_data():
X, y = make_classification(n_samples=1000, n_features=20, n_informative=15,
n_redundant=5, n_classes=2, random_state=42)
scaler = StandardScaler()
X = scaler.fit_transform(X)
return torch.FloatTensor(X), torch.LongTensor(y)
# 3. 训练模型
def train_model(model, dataset, epochs=10):
loader = DataLoader(dataset, batch_size=32, shuffle=True)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)
model.train()
for epoch in range(epochs):
total_loss = 0
for batch_x, batch_y in loader:
optimizer.zero_grad()
outputs = model(batch_x)
loss = criterion(outputs, batch_y)
loss.backward()
optimizer.step()
total_loss += loss.item()
print(f"Epoch {epoch+1}/{epochs}, Loss: {total_loss/len(loader):.4f}")
# 4. FGSM 对抗攻击函数 (核心修复)
def fgsm_attack(data, target, model, epsilon=0.1):
# 创建一个新的张量,基于原始数据,但独立于之前的计算图,并开启梯度
adv_data = data.clone().detach().requires_grad_(True)
# 前向传播
output = model(adv_data)
# 计算损失
loss = nn.functional.cross_entropy(output, target)
# 清零模型参数的梯度 (防止干扰,虽然我们要的是输入的梯度)
model.zero_grad()
# 反向传播:计算损失相对于输入 (adv_data) 的梯度
# 关键点:retian_graph=False (默认),因为只需要一次梯度
loss.backward()
# 检查梯度是否成功计算
if adv_data.grad is None:
raise RuntimeError("梯度计算失败:adv_data.grad 为 None。请检查模型是否正确接收了带梯度的输入。")
data_grad = adv_data.grad.data.sign()
# 生成对抗样本
adv_data = adv_data + epsilon * data_grad
# 裁剪范围
adv_data = torch.clamp(adv_data, min=-2.0, max=2.0)
# 返回时不需要梯度了,detach 掉,否则后续绘图或保存会报错
return adv_data.detach()
# 5. 评估模型准确率
def evaluate(model, loader):
model.eval()
correct = 0
total = 0
with torch.no_grad():
for data, target in loader:
outputs = model(data)
_, predicted = torch.max(outputs.data, 1)
total += target.size(0)
correct += (predicted == target).sum().item()
return correct / total
# --- 主流程 ---
if __name__ == "__main__":
print("正在初始化环境...")
torch.manual_seed(42)
np.random.seed(42)
X, y = generate_data()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
train_dataset = TensorDataset(X_train, y_train)
test_dataset = TensorDataset(X_test, y_test)
test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)
input_dim = X_train.shape[1]
model = NIDSModel(input_dim)
print("\n=== 开始训练模型 ===")
train_model(model, train_dataset, epochs=20)
original_acc = evaluate(model, test_loader)
print(f"\n=== 原始模型准确率: {original_acc:.4f} ===")
print("\n=== 开始 FGSM 对抗攻击 ===")
epsilon = 0.5
model.eval() # 设置为评估模式
adv_correct = 0
total = 0
for data, target in test_loader:
# 1. 生成对抗样本 (这里需要梯度,所以不能包在 no_grad 里)
adv_data = fgsm_attack(data, target, model, epsilon)
# 2. 使用对抗样本进行预测
# 此时我们可以用 no_grad 来加速并节省内存,因为预测不需要梯度
with torch.no_grad():
output = model(adv_data)
_, predicted = torch.max(output.data, 1)
total += target.size(0)
adv_correct += (predicted == target).sum().item()
adv_acc = adv_correct / total
print(f"=== 对抗样本下的准确率 (Epsilon={epsilon}): {adv_acc:.4f} ===")
print(f"=== 性能下降幅度: {(original_acc - adv_acc) * 100:.2f}% ===")
if adv_acc < original_acc:
print("\n[警告] 模型受到对抗性攻击影响,鲁棒性不足!")
else:
print("\n[信息] 模型对该强度的攻击表现出一定的鲁棒性。")
网络入侵检测系统(NIDS)中的人工智能安全问题,具体展示了如何使用快速梯度符号法(FGSM)对深度学习模型进行对抗性攻击。
代码分为五个主要部分:模型构建、数据生成、模型训练、攻击算法实现以及攻击效果评估。
1. 导入库与基础设置
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import make_classification
- PyTorch (
torch): 用于构建神经网络、自动求导(核心攻击机制依赖于此)和张量运算。 - Scikit-learn: 用于生成模拟的网络流量数据(
make_classification)、数据标准化(StandardScaler)和划分训练/测试集。 - NumPy: 用于数值计算和随机种子设置,确保结果可复现。
2. 构建网络入侵检测模型 (NIDSModel)
这是一个简单的前馈神经网络(MLP),用于二分类任务(正常流量 vs 攻击流量)。
class NIDSModel(nn.Module):
def __init__(self, input_dim):
super(NIDSModel, self).__init__()
# 输入层 -> 隐藏层1 (64神经元)
self.fc1 = nn.Linear(input_dim, 64)
self.relu1 = nn.ReLU()
# 隐藏层1 -> 隐藏层2 (32神经元)
self.fc2 = nn.Linear(64, 32)
self.relu2 = nn.ReLU()
# 隐藏层2 -> 输出层 (2类:正常/攻击)
self.fc3 = nn.Linear(32, 2)
# Softmax用于输出概率分布,dim=-1表示对最后一个维度(类别维度)归一化
self.softmax = nn.Softmax(dim=-1)
def forward(self, x):
# 【鲁棒性处理】如果输入是一维向量(单样本),强制增加批次维度 (1, features)
if x.dim() == 1:
x = x.unsqueeze(0)
x = self.fc1(x)
x = self.relu1(x)
x = self.fc2(x)
x = self.relu2(x)
x = self.fc3(x)
return self.softmax(x)
- 关键点:
forward函数中的if x.dim() == 1是为了防止在单样本预测或攻击时出现维度不匹配的错误。 - 输出: 返回一个形状为
(Batch_Size, 2)的张量,表示属于每一类的概率。
3. 数据生成与预处理 (generate_data)
由于没有使用真实的网络数据集(如NSL-KDD),这里使用 sklearn 生成模拟数据。
def generate_data():
# 生成1000个样本,20个特征,2个类别
X, y = make_classification(n_samples=1000, n_features=20, n_informative=15,
n_redundant=5, n_classes=2, random_state=42)
# 【重要】数据标准化:将特征缩放到均值为0,方差为1。
# 神经网络对特征尺度敏感,标准化能加速收敛并提高稳定性。
scaler = StandardScaler()
X = scaler.fit_transform(X)
# 转换为 PyTorch 张量
return torch.FloatTensor(X), torch.LongTensor(y)
- 现实意义: 在真实场景中,这里的
X会是网络流的统计特征(如包长度、持续时间、标志位计数等)。
4. 模型训练 (train_model)
标准的监督学习训练流程。
def train_model(model, dataset, epochs=10):
loader = DataLoader(dataset, batch_size=32, shuffle=True)
criterion = nn.CrossEntropyLoss() # 分类任务常用损失函数
optimizer = optim.Adam(model.parameters(), lr=0.01)
model.train() # 切换到训练模式(启用Dropout等,虽然本模型没用到)
for epoch in range(epochs):
total_loss = 0
for batch_x, batch_y in loader:
optimizer.zero_grad() # 清空旧梯度
outputs = model(batch_x) # 前向传播
loss = criterion(outputs, batch_y) # 计算损失
loss.backward() # 反向传播,计算参数梯度
optimizer.step() # 更新参数
total_loss += loss.item()
print(f"Epoch {epoch+1}/{epochs}, Loss: {total_loss/len(loader):.4f}")
5. FGSM 对抗攻击核心 (fgsm_attack)
这是代码中最关键的部分,展示了AI安全漏洞的原理。
原理:

def fgsm_attack(data, target, model, epsilon=0.1):
# 1. 准备输入张量
# clone().detach(): 创建数据的副本,切断与之前计算图的联系,避免副作用
# requires_grad_(True): 关键!告诉 PyTorch 我们需要计算相对于“输入数据”的梯度,而不仅仅是模型参数
adv_data = data.clone().detach().requires_grad_(True)
# 2. 前向传播
output = model(adv_data)
# 3. 计算损失
# 我们希望通过最大化这个损失来欺骗模型,但在实现上通常计算损失后取梯度的反方向或直接利用梯度符号
loss = nn.functional.cross_entropy(output, target)
# 4. 反向传播
model.zero_grad() # 清空模型参数的梯度(虽然我们要的是输入的梯度,但习惯上清空)
loss.backward() # 计算梯度。此时 adv_data.grad 包含了损失对输入的敏感度
# 5. 检查梯度
if adv_data.grad is None:
raise RuntimeError("梯度计算失败")
# 6. 获取梯度符号并生成扰动
# sign() 函数将梯度简化为 -1, 0, 1,只保留扰动的方向,忽略大小
data_grad = adv_data.grad.data.sign()
# 7. 构造对抗样本
# epsilon 控制扰动的强度。越大越容易绕过检测,但也越容易被人类或规则发现
adv_data = adv_data + epsilon * data_grad
# 8. 数据裁剪 (Clamping)
# 确保生成的对抗样本特征值在合理范围内(例如,包长度不能为负数)
# 在标准化数据中,[-2.0, 2.0] 是一个常见的合理范围
adv_data = torch.clamp(adv_data, min=-2.0, max=2.0)
# 9. 返回结果
# detach(): 再次切断梯度图,因为后续只需要用它做预测,不需要继续反向传播
return adv_data.detach()
安全启示:
这段代码证明了攻击者不需要知道模型的内部结构细节(如果是黑盒则通过替代模型),只要能得到梯度信息(或通过查询估算),就能构造出让人眼(或传统规则)无法察觉,但能让AI“失明”的恶意流量。
6. 评估与主流程 (__main__)
评估函数
def evaluate(model, loader):
model.eval() # 切换到评估模式(关闭Dropout等)
correct = 0
total = 0
with torch.no_grad(): # 评估时不需要计算梯度,节省内存并加速
for data, target in loader:
outputs = model(data)
_, predicted = torch.max(outputs.data, 1)
total += target.size(0)
correct += (predicted == target).sum().item()
return correct / total
主执行逻辑
if __name__ == "__main__":
# ... 数据准备和模型训练 ...
# 1. 测试原始准确率
original_acc = evaluate(model, test_loader)
print(f"=== 原始模型准确率: {original_acc:.4f} ===")
# 2. 执行攻击
print("\n=== 开始 FGSM 对抗攻击 ===")
epsilon = 0.5 # 设置较强的扰动强度
model.eval() # 固定模型参数
adv_correct = 0
total = 0
# 【关键细节】主循环不能在 torch.no_grad() 中!
# 因为 fgsm_attack 内部需要计算梯度 (loss.backward())
for data, target in test_loader:
# A. 生成对抗样本 (需要梯度计算)
adv_data = fgsm_attack(data, target, model, epsilon)
# B. 用对抗样本测试模型 (不需要梯度,所以包裹在 no_grad 中)
with torch.no_grad():
output = model(adv_data)
_, predicted = torch.max(output.data, 1)
total += target.size(0)
adv_correct += (predicted == target).sum().item()
adv_acc = adv_correct / total
# 3. 输出结果对比
print(f"=== 对抗样本下的准确率: {adv_acc:.4f} ===")
print(f"=== 性能下降幅度: {(original_acc - adv_acc) * 100:.2f}% ===")
if adv_acc < original_acc:
print("\n[警告] 模型受到对抗性攻击影响,鲁棒性不足!")
print("这意味着攻击者可以通过微调恶意流量的特征,轻松绕过该AI检测系统。")
总结
- AI 的脆弱性: 即使模型在正常数据上准确率高达 95%+,在面对精心设计的对抗样本(
epsilon=0.5)时,准确率可能会暴跌(例如降至 10%-30%)。 - 攻击成本低: 攻击只需要一次前向和一次反向传播(FGSM是单步攻击),计算成本极低,可以实时进行。
- 防御的必要性: 这强调了在部署 AI 驱动的 NIDS 时,不能只看测试集准确率,必须进行对抗训练(Adversarial Training)(即在训练集中加入这类对抗样本重新训练)或采用防御性蒸馏等技术来提高模型的鲁棒性。

浙公网安备 33010602011771号