一、项目背景:为什么需要新一代入侵检测系统?
现在的网络攻击越来越隐蔽——从DDoS到后门入侵,传统的“规则匹配”检测方法(比如防火墙)根本防不住:
- 传统方法只能识别已知攻击,遇到新型攻击就“失明”;
- 网络流量数据越来越大(日均TB级),人工分析根本来不及;
- 攻击样本严重不平衡(正常流量占90%+,攻击流量不足10%),普通模型只会“偏向”预测正常流量,漏检攻击。
我的毕业设计目标就是解决这些问题:用深度学习构建一个“能识别新型攻击、处理大数据、解决样本不平衡”的入侵检测系统——融合CNN(提取空间特征)和BiGRU(提取时间特征),再用混合采样算法平衡数据,最终在UNSW-NB15数据集上实现85.55%的检测准确率,比传统机器学习模型(如随机森林)高10%以上。
二、核心技术栈:从数据到模型的全套工具
整个项目分“数据处理→模型构建→实验验证”3步,技术栈聚焦深度学习和数据工程,都是研究生阶段常用工具,本科生稍加学习就能掌握:
| 技术模块 | 具体工具/算法 | 核心作用 |
|---|---|---|
| 数据处理 | Python + Pandas + Scikit-learn | 清洗数据(非数值特征转数值、归一化)、平衡数据(ADASYN过采样+RENN欠采样)、特征选择(RFP算法)。 |
| 深度学习框架 | TensorFlow + Keras | 搭建CNN(提取流量空间特征)、BiGRU(提取流量时间特征)混合模型,实现端到端检测。 |
| 数据集 | UNSW-NB15 | 网络入侵检测经典公开数据集,包含9种攻击类型(Fuzzers、DoS、Shellcode等),共25万+样本,适合验证模型泛化性。 |
| 评估工具 | Matplotlib + Scikit-learn | 画模型训练曲线(准确率/损失)、计算评估指标(准确率、精确率、召回率、F1值),对比不同模型性能。 |
| 关键算法 | ADRDB采样 + RFP特征选择 + CNN-BiGRU | ADRDB解决样本不平衡,RFP剔除冗余特征,CNN-BiGRU提升特征提取能力。 |
三、项目全流程:5步实现深度学习入侵检测系统
3.1 第一步:数据预处理——解决“脏数据”和“不平衡”问题
网络流量原始数据就是“垃圾堆”:有非数值特征(比如“协议类型”是TCP/UDP,不是数字)、有缺失值、攻击样本极少。必须先处理才能喂给模型:
3.1.1 数据清洗:把“非数值”转“数值”
原始数据里的“协议类型”“攻击标签”都是文本,需要用LabelEncoder转成数字,再用Min-Max归一化把特征值缩到[0,1](避免大值特征掩盖小值特征):
import pandas as pd
from sklearn.preprocessing import LabelEncoder, MinMaxScaler
# 1. 读取UNSW-NB15数据集
df = pd.read_csv("UNSW-NB15.csv")
# 2. 非数值特征转数值(比如proto列:TCP→0,UDP→1)
cat_features = ["proto", "service", "state", "attack_cat"]
le = LabelEncoder()
for col in cat_features:
df[col] = le.fit_transform(df[col])
# 3. Min-Max归一化(把特征值缩到[0,1])
scaler = MinMaxScaler()
num_features = df.columns.drop(["id", "label", "attack_cat"]) # 排除非特征列
df[num_features] = scaler.fit_transform(df[num_features])
# 4. 保存清洗后的数据
df.to_csv("UNSW-NB15_clean.csv", index=False)
print("数据清洗完成!形状:", df.shape) # (257673, 49)
3.1.2 数据平衡:ADRDB混合采样算法
最大的问题是“攻击样本太少”——正常样本9.3万条,Shellcode攻击只有1511条。直接训练模型会“偷懒”:全预测成正常样本,准确率也很高,但漏检所有攻击。
我设计的ADRDB算法解决这个问题:
- 对少数类(攻击样本)用ADASYN过采样:根据样本密度合成新样本(避免SMOTE算法的“盲目合成”);
- 对多数类(正常样本)用RENN欠采样:剔除与少数类重叠的样本(避免误删有用样本);
- 最后用DBSCAN聚类剔除噪声样本(合成的假样本会影响模型)。
核心代码(基于Scikit-learn扩展):
from imblearn.over_sampling import ADASYN
from imblearn.under_sampling import RepeatedEditedNearestNeighbours
from sklearn.cluster import DBSCAN
def adrdb_sampling(X, y):
# 1. 少数类过采样(ADASYN)
ada = ADASYN(random_state=42, sampling_strategy="minority")
X_ada, y_ada = ada.fit_resample(X, y)
# 2. 多数类欠采样(RENN)
renn = RepeatedEditedNearestNeighbours(n_neighbors=5, max_iter=10)
X_renn, y_renn = renn.fit_resample(X_ada, y_ada)
# 3. DBSCAN剔除噪声(eps=0.5,min_samples=5)
dbscan = DBSCAN(eps=0.5, min_samples=5)
cluster_labels = dbscan.fit_predict(X_renn)
# 保留非噪声样本(cluster_labels != -1)
X_clean = X_renn[cluster_labels != -1]
y_clean = y_renn[cluster_labels != -1]
return X_clean, y_clean
# 调用函数平衡数据
X = df.drop(["label", "attack_cat"], axis=1)
y = df["label"] # 0=正常,1=攻击
X_balanced, y_balanced = adrdb_sampling(X, y)
print("平衡前:正常样本{},攻击样本{}".format(sum(y==0), sum(y==1))) # 93000, 164673
print("平衡后:正常样本{},攻击样本{}".format(sum(y_balanced==0), sum(y_balanced==1))) # 120000, 118000
3.1.3 特征选择:RFP算法剔除冗余特征
原始数据有47个特征,很多是冗余的(比如“spkts”和“sbytes”相关性0.96,重复携带信息)。用RFP算法(随机森林+皮尔逊相关)筛选出28个关键特征,减少模型计算量:
from sklearn.ensemble import RandomForestClassifier
import numpy as np
def rfp_feature_selection(X, y, threshold=0.001):
# 1. 随机森林计算特征重要性
rf = RandomForestClassifier(n_estimators=100, random_state=42)
rf.fit(X, y)
importances = pd.Series(rf.feature_importances_, index=X.columns)
# 2. 皮尔逊相关分析(剔除高相关特征)
corr_matrix = X.corr()
high_corr_features = set()
for i in range(len(corr_matrix.columns)):
for j in range(i):
if abs(corr_matrix.iloc[i, j]) > 0.9: # 相关性>0.9视为高相关
colname = corr_matrix.columns[i]
high_corr_features.add(colname)
# 3. 保留:重要性>阈值 + 非高相关特征
selected_features = [f for f in X.columns
if importances[f] > threshold and f not in high_corr_features]
return X[selected_features], selected_features
# 调用函数选择特征
X_selected, selected_features = rfp_feature_selection(X_balanced, y_balanced)
print("选择的特征数:", len(selected_features)) # 28个
print("选择的特征:", selected_features) # 如"dur", "spkts", "dpkts", "sbytes"...
3.2 第二步:模型构建——CNN+BiGRU融合模型
网络流量既有“空间特征”(比如不同特征的数值分布),也有“时间特征”(比如流量的时序变化)。单一模型只能抓一种特征,所以我融合CNN和BiGRU:
- CNN部分:用3个SRFCNN(分离-残差-融合卷积)模块,提取流量的空间特征(比如“数据包大小”和“协议类型”的关联);
- BiGRU部分:用2层双向GRU,提取流量的时间特征(比如“连续数据包的时序规律”);
- 分类层:用Softmax输出“正常”或“攻击”(二分类),或具体攻击类型(多分类)。
3.2.1 模型核心代码(TensorFlow实现)
import tensorflow as tf
from tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, AveragePooling2D, Concatenate, Reshape, Bidirectional, GRU, Dense
from tensorflow.keras.models import Model
def build_cnn_bigru(input_shape):
# 1. 输入层(输入是28维特征,reshape成[28,1,1]适配CNN)
inputs = Input(shape=input_shape)
x = Reshape((input_shape[0], 1, 1))(inputs) # (None, 28, 1, 1)
# 2. SRFCNN模块(3个,提取空间特征)
def srfcnn_block(x, filters, kernel_size):
# 分离卷积
x1 = Conv2D(filters, kernel_size, padding="same", activation="relu")(x)
x2 = Conv2D(filters, (kernel_size+2, 1), padding="same", activation="relu")(x)
# 残差连接
x_res = Conv2D(filters, 1, padding="same")(x)
x = Concatenate()([x1, x2]) + x_res
# 融合池化(最大池化+平均池化)
x_max = MaxPooling2D((2,1))(x)
x_avg = AveragePooling2D((2,1))(x)
return Concatenate()([x_max, x_avg])
x = srfcnn_block(x, 32, 3) # (None, 14, 1, 64)
x = srfcnn_block(x, 64, 3) # (None, 7, 1, 128)
x = srfcnn_block(x, 128, 3) # (None, 3, 1, 256)
# 3. 展平,适配BiGRU
x = Reshape((-1, 256))(x) # (None, 3, 256)
# 4. BiGRU模块(提取时间特征)
x = Bidirectional(GRU(128, return_sequences=True))(x) # (None, 3, 256)
x = Bidirectional(GRU(64))(x) # (None, 128)
# 5. 分类层(二分类:正常/攻击)
outputs = Dense(1, activation="sigmoid")(x)
# 6. 构建模型
model = Model(inputs=inputs, outputs=outputs)
model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["accuracy"])
return model
# 初始化模型(输入是28维特征)
input_shape = (28,)
model = build_cnn_bigru(input_shape)
model.summary() # 打印模型结构
3.3 第三步:模型训练与验证——在UNSW-NB15上测试
用平衡后的数据集训练模型,分训练集(70%)和测试集(30%),重点验证“模型是否比传统方法好”“采样算法是否有效”:
3.3.1 训练代码
from sklearn.model_selection import train_test_split
from tensorflow.keras.callbacks import EarlyStopping
# 1. 划分训练集/测试集
X_train, X_test, y_train, y_test = train_test_split(
X_selected, y_balanced, test_size=0.3, random_state=42, stratify=y_balanced
)
# 2. 早停(避免过拟合)
early_stopping = EarlyStopping(monitor="val_loss", patience=5, restore_best_weights=True)
# 3. 训练模型
history = model.fit(
X_train, y_train,
batch_size=64,
epochs=50,
validation_split=0.2,
callbacks=[early_stopping],
verbose=1
)
# 4. 保存模型
model.save("cnn_bigru_intrusion_detection.h5")
print("模型保存完成!")
3.3.2 关键实验结果
我做了7组对比实验,验证模型各模块的有效性,核心结果如下:
| 实验内容 | 关键结论 |
|---|---|
| 单一模型vs混合模型 | CNN准确率84.01%,BiGRU准确率82.58%,CNN+BiGRU准确率85.55%(融合后提升1-3%)。 |
| 不同采样方法对比 | ADRDB算法(85.55%)比SMOTE(79.38%)、Random Undersampling(61.12%)高6-24%。 |
| 不同特征选择方法对比 | RFP算法(85.55%)比PCA(82.17%)、AE(84.91%)高0.6-3%,且特征数从47减到28。 |
| 深度学习vs传统机器学习 | CNN+BiGRU(85.55%)比随机森林(75.41%)、决策树(73.37%)高10-12%。 |
3.3.3 训练曲线可视化(Matplotlib)
import matplotlib.pyplot as plt
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 画准确率曲线
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history["accuracy"], label="训练准确率")
plt.plot(history.history["val_accuracy"], label="验证准确率")
plt.title("CNN-BiGRU模型准确率曲线")
plt.xlabel("epoch")
plt.ylabel("准确率")
plt.legend()
# 画损失曲线
plt.subplot(1, 2, 2)
plt.plot(history.history["loss"], label="训练损失")
plt.plot(history.history["val_loss"], label="验证损失")
plt.title("CNN-BiGRU模型损失曲线")
plt.xlabel("epoch")
plt.ylabel("损失")
plt.legend()
# 保存图片
plt.tight_layout()
plt.savefig("model_training_curve.png", dpi=300)
plt.show()
效果:训练15轮后,验证准确率稳定在85%左右,损失降到0.3以下,无明显过拟合(训练/验证曲线趋势一致)。
3.4.1 评估结果解读
- 准确率85.55%:整体分类正确的样本占比,比传统随机森林(75.41%)高10%,证明深度学习对复杂流量特征的学习能力更强;
- 召回率85.55%:所有真实攻击样本中,被正确检测出的比例——意味着每100次攻击,只会漏检14次,远低于SMOTE采样模型(召回率80.74%);
- 混淆矩阵:正常流量误判为攻击(FP)的有3200条,攻击流量漏检(FN)的有3150条,两类错误数量接近,说明模型没有“偏向”某一类样本,平衡效果达标。
3.5 第五步:多场景验证——确保模型实用性
光在UNSW-NB15上表现好还不够,还要验证模型在“不同攻击类型”“不同数据量”下的泛化性:
3.5.1 不同攻击类型的检测效果
UNSW-NB15包含9种攻击,我统计了模型对每种攻击的F1值:
| 攻击类型 | F1值 | 关键结论 |
|---|---|---|
| DoS(拒绝服务) | 0.89 | 特征明显(流量突发大),模型检测效果最好; |
| Fuzzers(模糊攻击) | 0.87 | 虽为高频攻击,但特征分散,F1值略低于DoS; |
| Shellcode(shell攻击) | 0.78 | 样本量极少(仅1511条),但F1值仍高于75%,证明ADRDB采样有效; |
| Worms(蠕虫) | 0.75 | 样本最少(174条),检测效果最差,但比传统模型(0.68)仍有提升。 |
3.5.2 不同数据量下的性能
测试模型在“1万条→5万条→10万条”样本下的训练时间和准确率:
| 样本量 | 训练时间(分钟) | 准确率 | 关键结论 |
|---|---|---|---|
| 1万条 | 8 | 0.821 | 数据量过少时,模型欠拟合,准确率低; |
| 5万条 | 25 | 0.847 | 数据量增加后,准确率提升明显,训练时间可控; |
| 10万条 | 42 | 0.855 | 数据量达10万条后,准确率趋于稳定,再增加数据对性能提升有限。 |
结论:模型在“5万-10万条”样本下性价比最高,既保证准确率,又不会让训练时间过长(适合普通PC运行)。
四、毕业设计复盘:踩过的坑与经验
4.1 那些踩过的坑
-
模型过拟合:训练准确率95%,测试准确率75%
- 问题:一开始用复杂的5层CNN+3层BiGRU,训练集准确率很高,但测试集差20%,明显过拟合;
- 解决:① 减少模型层数(改成3层CNN+2层BiGRU);② 加早停(EarlyStopping),验证损失5轮不下降就停止训练;③ 用DBSCAN剔除噪声样本,最终测试准确率提升到85%。
-
数据不平衡:攻击样本漏检率30%
- 问题:一开始只用ADASYN过采样,合成的样本有很多噪声,导致模型漏检攻击;
- 解决:加RENN欠采样(剔除多数类中与少数类重叠的样本),再用DBSCAN去噪声,漏检率从30%降到14%。
-
特征冗余:47维特征训练,模型跑不动
- 问题:原始47维特征直接喂给模型,训练1轮要1小时,还容易过拟合;
- 解决:用RFP算法筛选出28维关键特征,训练时间缩短到42分钟(10万样本),准确率还提升了2%。
4.2 给学弟学妹的建议
-
先复现基础模型,再创新
不要一开始就做“CNN+BiGRU+混合采样”的复杂模型——先复现简单的CNN或BiGRU,确保能跑通流程,再逐步叠加创新点(比如加采样算法、改特征选择),否则出问题都不知道在哪。 -
重视数据预处理,比模型创新更重要
我一开始花2周调模型结构,准确率只提升5%;后来花1周优化数据预处理(平衡数据、选特征),准确率直接提升10%——网络入侵检测中,“数据质量”比“模型复杂度”更关键。 -
实验设计要“控制变量”
对比不同方法时,要保证其他条件一致:比如测试“不同采样方法”时,模型结构、特征数、训练轮数必须相同,否则无法判断是“采样方法好”还是“模型结构好”。
五、项目资源获取
完整项目包含:
- 代码文件:数据清洗(含ADRDB采样、RFP特征选择)、模型构建(CNN+BiGRU)、评估代码(附注释);
- 数据集:UNSW-NB15预处理后的数据(已平衡、已选特征,可直接训练);
- 答辩资料:模型训练曲线、混淆矩阵、对比实验表格(可直接插入PPT);
- 避坑指南:过拟合解决方法、数据不平衡处理步骤、特征选择代码调试技巧。
如果本文对你的网络安全学习、深度学习实践或毕业设计有帮助,欢迎点赞 + 收藏 + 关注,后续会分享更多“攻防实战”案例!