单位:东北大学 作者:王文举
网络入侵检测方法,是通过收集网络流量或主机流量的信息并进行分析的一种技术方法 。通过xgboost学习方法可以有效地判断当前网络是否异常,提升校检的精度。
采用的是 NSL-KDD 数据集,数据集是 KDD Cup 99 的改进版数据集,消除了 KDD Cup 99 数据集中冗余的数据。此外,也对数据集类别标签比例进行调整改进,避免样本类别分布不均匀现象。
本文XGBoost原理理论的部分不在过多叙述,具体可参考链接:
https://blog.csdn.net/weixin_43214046/article/details/106993291
0.数据样本介绍
KDD Cup 1999数据集: 是与KDD-99第五届数据挖掘国际竞赛使用的数据集。任务是建立一个网络入侵检测器,这是一种能够区分称为入侵或攻击的“不良”连接和“良好”的正常连接的预测模型。
训练数据集有 125973 行数据,测试数据集有 22544 行数据,每个数据集都有 42 列特征维度,最后一列是标记特征(Label),其他前41项特征共分为四大类。
TCP连接基本特征(共9种,序号1~9)
TCP连接的内容特征(共13种,序号10~22)
基于时间的网络流量统计特征 (共9种,序号23~31)
基于主机的网络流量统计特征 (共10种,序号32~41)
其样本特征见下表:
0,tcp,http,SF,239,486,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,8,8,0.00,0.00,0.00,0.00,1.00,0.00,0.00,19,19,1.00,0.00,0.05,0.00,0.00,0.00,0.00,0.00,normal.
下图为读取后的特征数据,每个特征取值空间和类型属性不在叙述:
1.NSL-KDD数据预处理
由于XGBoost所采用的弱评估器为CART决策树,故需要将特征空间的字符型进行映射操作,使其转换数据的形式。常见的数据预处理的操作大致可以包括以下几点:
(0)类别标签不均衡处理:
类别不均衡指不同的类别的训练样例数目有较大的差异。假定正例偏少,负例偏多的情况下,有效地采用3种解决方法。
(a):过采样。增加正例的数据使得正反例数目大致接近。如果对正例进行重复采样操作,会造成过拟合现象,常见的方法大多为插值采样。
(b):欠采样。减少负例的数据使得正反例数目大致接近,采样可能会造成重要特征丢失,一般用于集成学习算法中。
©:再缩放操作。文中二分类采用binary:logistic 为损失函数。当前仅当预测值y满足:
才可以预测为正例。
(1)特征选择:
XGBoost采用随机森林的思想,选取部分特征,采用多线程并行处理方式,计算每个特征划分下,以结构分数分差的最大值为依据,选取特征分裂。
(2)数据稀疏和缺失值处理
对于特征值有缺失的样本,xgboost可以自动学习它的分裂方向。在决策树中,一般统计在缺失特征下,每个取值所占据整个样本空间的比例,并根据比例将该样本划分不同的权重。
(3)字符型进行映射操作
一般看字符型特征是否有序,有序采用LabelEncoder编码映射。本文中udp、tpc、http均为无序,采用OneHotEncoder编码。
(4)PCA降维技术
特征空间较小不做 pca 降维技术。
(5)数据标准化/归一化处理
决策树、朴素贝叶斯、XGBoost均为类条件概率模型,不做标准化归一化处理。
2.SKlearn API 接口使用
(0)导库:
from sklearn.metrics import accuracy_score
from xgboost import XGBClassifier as XGBC
from sklearn.model_selection import KFold, cross_val_score as CVS, train_test_split as TTS
from sklearn.metrics import mean_squared_error as MSE
import pandas as pd
from sklearn.metrics import r2_score,mean_squared_error as MSE
from sklearn.model_selection import learning_curve
import numpy as np
import matplotlib.pyplot as plt
from time import time
import datetime
import pickle
(0)读数据并保存数据:
'''
# KDDtrain_data = pd.read_csv("xgboost\\DATA\\KDDTrain+.txt" ,
# names = list(range(43)), header=0)
# pickle.dump(KDDtrain_data, open("KDDtrain_data.dat","wb"))
#
# KDDTest = pd.read_csv("xgboost\\DATA\\KDDTest+.txt",
# names=list(range(43)), header=0 )
# pickle.dump(KDDTest, open("KDDTest.dat","wb"))
# 加载文件
'''
(1)数据预处理:
def processdata(data):
sp = [] #记录哪些时是字符型的列
for col in range(41):
if data[col].dtypes == data[41].dtypes:
sp.append(col)
le = LabelEncoder()
le.fit(data[col])
data[col] = le.transform(data[col])
# one-code编码 max(data[2])
df_processed = pd.get_dummies(data[col],prefix_sep="_")
# 转化后的属性
df_processed.columns = [str(col)+'.' + str(int(i)) \
for i in range(df_processed.shape[1])]
data = pd.concat([data, df_processed], axis=1)
#实现标签二分类处理,存储标签
data[41] = np.where(data[41] == 'normal', 1, 0)
label = data[41]
# 去除替换后的字符型数据,同时把网络受攻击程度42列去掉,
# 得到样本特征空间
data = data.drop(data.columns[sp+[41,42]], axis=1) # axis=1,试图指定列
datas = pd.concat([data, label], axis=1)
return datas
(2)走XGBoost流程:
# xt, yt, x_test, y_test分别表示训练集、训练集标签、测试集、测试标签
def ytest(xt, yt, x_test, y_test):
time0 = time()
xgbt = XGBC(n_estimators=150,
subsample=0.8,
learning_rate=0.5,
random_state=20,
gamma=0.25,
reg_lambda=10,
max_depth = 4,
object = 'binary:logistic',
min_child_weight=2,
seed= 0,
booster='gbtree')
y_predict = xgbt.fit(xt, yt).predict(x_test)
x_predict = xgbt.fit(xt, yt).predict(xt)
print('训练集 :',accuracy_score(x_predict,yt))
print('测试集 :',accuracy_score(y_predict, y_test))
print('测试r2指标 :',r2_score(y_predict, y_test))
print('测试MSE指标 :',MSE(y_predict, y_test))
print('运行时间 :',time() - time0)
(3)经验设置调参,先跑一遍看看结果:
选择经验参数,训练测试的准确率均达到99.9%以上,但测试集偏低。
3.调参
(1)训练集样本大小影响:
将上述训练集进行5折交叉验证(用于训练,用于验证),将每轮验证的结果求均值,这样就返回不同样本参数下的模型的Score。这里的Score就是上述说的指标。
def sample_influence(xt,yt):
# 训练集样本的大小影响
cv = KFold(n_splits=5, shuffle=True, random_state=42) # 交叉验证模式
plot_learning_curve(XGBC(n_estimators=100, random_state=42), \
"XGBC", xt, yt, ax=None, cv=cv)
plt.show()
def plot_learning_curve(estimator, title, X, y,
ax=None, # 选择子图
ylim=None, # 设置纵座标的取值范围
cv=None, # 交叉验证
n_jobs=None # 设定索要使用的线程
):
train_sizes, train_scores, test_scores = learning_curve(estimator, X, y
, shuffle=True
, cv=cv
, random_state=420
, n_jobs=n_jobs)
if ax == None:
ax = plt.gca()
else:
ax = plt.figure()
ax.set_title(title)
if ylim is not None:
ax.set_ylim(*ylim)
ax.set_xlabel("Training examples")
ax.set_ylabel("Score")
ax.plot(train_sizes, np.mean(train_scores, axis=1), 'o-'
, color="r", label="Training score")
ax.plot(train_sizes, np.mean(test_scores, axis=1), 'o-'
, color="g", label="Test score")
ax.legend(loc="best")
return ax
结果:
(2)基学习器个数(n_estimators):
def ntrees_estimators(Xtrain,Ytrain):
cv = KFold(n_splits=5, shuffle=True, random_state=42) # 交叉验证模式
axisx = range(50, 400,50)
rs = []
var = []
ge = []
for i in axisx:
print(i)
reg = XGBC(n_estimators=i, random_state=420,
subsample=0.8,
learning_rate=0.2)
cvresult = CVS(reg, Xtrain, Ytrain, cv=cv)
rs.append(cvresult.mean())
var.append(cvresult.var())
ge.append((1 - cvresult.mean()) ** 2 + cvresult.var())
print(axisx[rs.index(max(rs))], max(rs), var[rs.index(max(rs))])
print(axisx[var.index(min(var))], rs[var.index(min(var))], min(var))
print(axisx[ge.index(min(ge))], rs[ge.index(min(ge))], var[ge.index(min(ge))], min(ge))
rs = np.array(rs)
var = np.array(var)
plt.plot(axisx, rs, c="black", label="XGB")
# 添加方差线
plt.plot(axisx, rs + var, c="red", linestyle='-.')
plt.plot(axisx, rs - var, c="red", linestyle='-.')
plt.legend()
plt.show()
评估器大范围调节在【50,400】每50取值,下图可见在150附近最好。均在99.8%以上。
同理,确定最大树的深度max_depth参数优化如下图:
同理,惩罚项gamma参数优化如下图:
4.评价指标
代码主程序:
if __name__ == '__main__':
KDDtrain_data = pickle.load(open("KDDtrain_data.dat", "rb"))
KDDTest = pickle.load(open("KDDTest.dat", "rb"))
dataxtrain = processdata(KDDtrain_data)
dataxtext = processdata(KDDTest)
# 训练集、测试集划分
xt, yt, x_test, y_test = dataxtrain.iloc[:, 0:41], \
dataxtrain.iloc[:, 41], dataxtext.iloc[:, 0:41], dataxtext.iloc[:, 41]
# sample_influence(xt, yt)
# ntrees_estimators(xt,yt) #弱学习器的个数
# gamasolve(xt,yt)
ytest(xt, yt, x_test, y_test)
XGBoost经调参后,,MSE有明显的提升。从0.42提升至0.64,MSE从原先0.0005降低到0.00022,准确率提升至接近4个9。
模型的特征重要性:
ROC曲线绘制
代码:
y_score = xgbt.fit(xt, yt).predict_proba(x_test)
fpr,tpr,threshold=roc_curve(y_test,y_score[:, 1])
roc_auc=auc(fpr,tpr)
plt.figure(figsize=(10,10))
plt.plot(fpr, tpr, color='darkorange',
lw=2, label='ROC curve (area = %0.2f)' % roc_auc) ###假正率为横座标,真正率为纵座标做曲线
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver operating characteristic example')
plt.legend(loc="lower right")
plt.show()
运行结果:
说明:文中具体的代码上传至Github,欢迎查看。
链接: https://github.com/WANGWENJUS/xgboost.