KMeans算法检测网络异常入侵

非监督学习技术

决策树算法预测森林植被

我们可以体会到属于监督学习的分类和回归技术的强大,可以预测“即将发生”的事情

使用监督学习技术有一个很关键的前提:需要大量的数据对模型进行训练,模型能够从已知的数据中学习规律进而预测未知的数据

然而在某些场景下,并不是都能提供监督学习所需要的样本数据来训练模型,有可能只能给出部分正确的输出,甚至一个输出都没有

这种情况下,监督学习的技术就不能够使用了

此时,对应监督学习,另一种非监督学习技术就可以排上用场了

异常检查

顾名思义,异常检测就是要找出不同寻常的情况,异常是一种未知的情况,也就是说,无论何时何地,我们都无法归纳总结出所有的异常分类

如果可以,那么使用监督学习技术可以轻易的将网站的每个访问划分为“正常”或者“异常”

举个例子来说,我们永远不知道黑客有什么新的技术手段可以入侵你的网站系统,即使今天你有所有已知的黑客手段,但是谁知道明天又会有新的漏洞被黑客利用?

所以说,当一个访问请求被处理的时候,如果使用监督学习技术,恰好这是一个异常访问的请求,又恰好这是一种全新的异常类别

此时监督学习技术就会束手无策

在这种场景下,使用非监督学习技术可以有效的解决这个问题,通过学习,它们能够知道什么是正常的输入

从而能够判别出新数据和历史数据的差别,注意,这里的有差别并不意味着该数据就是异常数据,只是说它和历史的正常数据有差异,值得进一步调查

所以,非监督学习并不是要将数据精确地划分到哪个类别中,而是对历史数据进行对比分析找出差异

KMeans均值聚类

聚类是最有名的非监督学习技术,它试图找到数据中的自然群组

一群特征相似而又与其他数据不同的数据点往往代表某种意义,从而将这些数据点划分为一个族群

聚类算法就是要将所有数据中的相似数据划分到同一个族群中

在网络的异常检测中,聚类算法是十分合适的,通过其将所有访问请求划分为一个个族群,将正常和异常的访问隔离开

我们可以进一步在异常的族群中分析这些数据是否属于网络入侵

K均值聚类是运行的最广泛的聚类算法,根据人为定义的一个k值,该算法会将数据聚类为k个族群

关于k值的确定需要结合业务场景和数据特性,并在反复的实验中得到一个最优的参数

KMeans聚类的详细说明可以参考:

mahout运行测试与数据挖掘算法之聚类分析(一)kmeans算法解析

程序开发

数据集

案例中使用的是KDD Cup1999的数据集,可以在这里下载

进入下载页面后可以看到有很多数据集,本篇只用到kddcup.data.*数据文件,一个是完整数据集,解压缩之后有743M,一个是10%的数据集,解压缩之后只有45M

可以先使用10%的数据集进行代码测试,得到一个比较好的结果之后再运用到全部数据集中

数据集中的每一行代表一个网络请求,描述了该请求的所有信息,在决策树算法预测森林植被

我们知道特征分为数值型和类别型,该数据集中也包含了这两种特征

每行数据的最后一个特征是目标特征,例如大部分请求被标记为normal表示正常访问

还有其他各种异常标记

正如之前讨论的,我们完全可以使用特征向量+目标特征的形式使用监督学习技术来训练模型进行预测

但是如果出现一个异常请求不在所有的目标类别中,这不糟糕了~

所以为了找出“未知的攻击”,我们先不在算法中使用这些目标特征

聚类的初步尝试

将要使用的数据集上传到HDFS之后,我们先来看看这些数据的基本信息:

val conf = new SparkConf().setAppName("KMeans")
val sc = new SparkContext(conf)
//读取数据
val rawData = sc.textFile("/spark_data/ch05/kddcup.data")
//根据类别查看统计信息,各个类别下有多少数据
//根据","分割,只保留最后的类别
val catStatsData = rawData.map(_.split(",").last)
  //对类别的数目进行统计,并根据统计的数量从小打到排序
  .countByValue().toSeq.sortBy(_._2)
  //转换为从大到小排序
  .reverse
catStatsData.foreach(println)

运行结果:

(smurf.,280790)
(neptune.,107201)
(normal.,97278)
(back.,2203)
(satan.,1589)
(ipsweep.,1247)
(portsweep.,1040)
(warezclient.,1020)
(teardrop.,979)
(pod.,264)
(nmap.,231)
(guess_passwd.,53)
(buffer_overflow.,30)
(land.,21)
(warezmaster.,20)
(imap.,12)
(rootkit.,10)
(loadmodule.,9)
(ftp_write.,8)
(multihop.,7)
(phf.,4)
(perl.,3)
(spy.,2)

总共有23种不同类型,其中smurf和neptune的攻击类型最多,竟然比normal的正常访问还要多

之前提到的,由于样本数据中包含有类别型特征(下标为1,2,3和最后一个目标特征)

但是KMeans算法要求特征都要为数值型,我们首先不对这些类别型特征做处理,直接跳过:

val labelsAndData = rawData.map { line =>
  //buffer是一个可变列表
  val buffer = line.split(",").toBuffer
  //下标1-3的元素
  buffer.remove(1, 3)
  //最后一个元素为label
  val label = buffer.remove(buffer.length - 1)
  //转换为Vector
  val vector = Vectors.dense(buffer.map(_.toDouble).toArray)
  (label, vector)
}
//数据只用到values部分
val data=labelsAndData.values.cache()

拿到可以进行模型训练的数据之后就可以编写聚类的代码了:

//训练模型
val kmeans = new KMeans()
val model = kmeans.run(data)
//输出所有聚类中心
model.clusterCenters.foreach(println)

输出结果为两个向量:

[47.979395571029514,1622.078830816566,868.5341828266062,4.453261001578883E-5,0.006432937937735314,1.4169466823205539E-5,0.03451682118132869,1.5181571596291647E-4,0.14824703453301485,0.01021213716043885,1.1133152503947209E-4,3.6435771831099954E-5,0.011351767134933808,0.0010829521072021374,1.0930731549329986E-4,0.0010080563539937655,0.0,0.0,0.0013865835391279706,332.2862475203433,292.9071434354884,0.17668541759442943,0.17660780940042914,0.05743309987449898,0.05771839196793656,0.7915488441762945,0.020981640419421355,0.028996862475203923,232.4707319541719,188.6660459090725,0.7537812031901686,0.030905611108870867,0.6019355289259973,0.006683514837454898,0.17675395732966057,0.1764416217966883,0.05811762681672766,0.057411116958826745]

[2.0,6.9337564E8,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,57.0,3.0,0.79,0.67,0.21,0.33,0.05,0.39,0.0,255.0,3.0,0.01,0.09,0.22,0.0,0.18,0.67,0.05,0.33]

现在我们用这个模型来为每个数据分配族群,并输出每个聚类中心有哪些类别,各有多少个数据:

//输出每个聚类中心有哪些类别各有多少个数据
val clusterLabelCount = labelsAndData.map { case (label, datum) =>
  //为样本数据划分聚类中心
  val cluster = model.predict(datum)
  //返回数据的中心和类别二元组
  (cluster, label)
}.countByValue()
//排序之后格式化输出
clusterLabelCount.toSeq.sorted.foreach { case ((cluster, label), count) =>
  println(f"$cluster%1s$label%1s$count")
}

由于我们没有人为设置k的值,程序就自作主张设置了k=2,但是我们知道,数据的类别有23个,因此这个模型肯定是错误的

可以看到输出的结果为:

0   back.               2203
0   buffer_overflow.    30
0   ftp_write.          8
0   guess_passwd.       53
0   imap.               12
0   ipsweep.            1247
0   land.               21
0   loadmodule.         9
0   multihop.           7
0   neptune.            107201
0   nmap.               231
0   normal.             97278
0   perl.               3
0   phf.                4
0   pod.                264
0   portsweep.          1039
0   rootkit.            10
0   satan.              1589
0   smurf.              280790
0   spy.                2
0   teardrop.           979
0   warezclient.        1020
0   warezmaster.        20
1   portsweep.          1

族群1只有一个数据点

K值的选择

k的值到底设置为多少比较合适呢?

在示例的样本数据中,有23个类别,那么意味着k至少等于23

通常情况下我们要通过多次尝试才能找到最好的k值

那么什么样的k值才是“最好”的呢?

如果每个数据点都紧靠最近的质心,那么这个聚类是较优的

为了能够判断每个数据点到质心的距离,我们可以编写以下函数来判断:

/**
  * 计算两个向量之间的距离
  *
  * @param a 向量1
  * @param b 向量2
  *          欧式距离:空间上两个点的距离=两个向量相应元素的差的平方和的平方根
  **/
def distance(a: Vector, b: Vector) = {
  //求平方根
  math.sqrt(
    //将两个向量合并
    a.toArray.zip(b.toArray)
      //两个向量中的每个值相减
      .map(d => d._1 - d._2)
      //相间的值平方
      .map(d => d * d)
      //之后相加
      .sum)
}

/**
  * 计算数据点到聚类中心质心的距离
  *
  * @param datum 数据点
  * @param model kmeans模型
  **/
def distToCentrolid(datum: Vector, model: KMeansModel) = {
  //得到该数据点的聚类中心
  val cluster = model.predict(datum)
  //得到该聚类中心的质心
  val centrolid = model.clusterCenters(cluster)
  //计算距离
  distance(centrolid, datum)
}

/**
  * 根据各个数据点到该数据点聚类中心质心的距离来判断该模型优劣
  * @param data 样本数据
  * @param k k值
  * */
def clusteringScore(data: RDD[Vector], k: Int) = {
  val kmeans = new KMeans()
  //设置k值
  kmeans.setK(k)
  val model = kmeans.run(data)
  //计算样本数据到其各自质心的记录的平均值
  data.map { datum =>
    distToCentrolid(datum, model)
  }.mean()
}

有了判断模型优劣的标准之后,就可以通过取不同的k值来观察模型:

//取不同k值观察模型优劣
(5 to 40 by 5).map { k =>
  (k, clusteringScore(data, k))
}.foreach(println)

输出结果如下:

(5,1779.3473960726312)
(10,1054.5660956505587)
(15,998.6026754769782)
(20,438.0714456623944)
(25,386.70458251397577)
(30,329.4472646112194)
(35,644.939843705805)
(40,221.3547720824891)

可以看到,平均距离随着k的增大而降低

这点是毫无疑问的,因为随着族群点的增加,数据点离最近的质心肯定更近,当族群点等于数据量的时候平均距离为0,每个数据点都是自己构成的质心

而一个很奇怪的现象是,k=35时的距离竟然比k=30的时候要大

这是因为KMeans的迭代过程是从一个随机点开始的,因此可能收敛于一个局部最小值

k=35的情况可能是随机初始的质心造成的,也可能是由于算法在达到局部最小值之前就结束了

为了解决这个可能存在的问题,我们可以通过对固定的k值多次聚类,每次都随机不同的初始质心,然后在其中选择最优的

Spark Mllib提供了设置KMeans运行次数的方法,在clusteringSore函数中加入:

//设置该k值的聚类次数
kmeans.setRuns(10)
//设置迭代过程中,质心的最小移动值,默认为1.0e-4
kmeans.setEpsilon(1.0e-6)

setEpsilon设置迭代过程中,质心的最小移动值,移动值越小使得迭代的时间越多,聚类的结果更优化

现在我们重新选择k值进行评估:

(50 to 130 by 10).map { k =>
  (k, clusteringScore(data, k))
}.foreach(println)

输出结果:

(50,186.72154668205906)
(60,144.68385906795461)
(70,119.93958735623515)
(80,109.46520683932486)
(90,94.08809036196241)
(100,76.37377878951273)
(110,74.78271035271912)
(120,71.515517236215)
(130,65.13241545688685)

这个时候随着k的增大,平均距离在持续减小

但是这个减少的幅度是有临界点的,当k值超过这个临界点,即使继续增大,也不会显著地降低距离

这个临界点就是我们要找的最好的k值

从输出结果中可以看到,k值应该取130

我们再次打印出每个族群包含的类别和个数信息:

0   neptune.    48517
0   nmap.   93
0   normal. 2974
0   portsweep.  904
0   rootkit.    3
0   satan.  222
0   teardrop.   865
0   warezmaster.    1
1   portsweep.  1
2   warezclient.    59
3   multihop.   1
3   normal. 1
4   normal. 1
5   normal. 310
6   normal. 1
6   warezmaster.    15
7   normal. 22
8   normal. 713
9   normal. 19
10  normal. 1
11  normal. 6
12  normal. 1
13  normal. 2
14  back.   2155
15  normal. 1
16  normal. 102
17  normal. 17
18  smurf.  227840
19  normal. 1
...
...
117 warezmaster.    1
118 normal. 3
119 normal. 2
120 back.   9
120 normal. 23
121 back.   1
121 normal. 362
122 multihop.   1
122 normal. 5174
122 smurf.  177
122 warezclient.    31
123 normal. 1242
124 normal. 351
125 back.   18
125 normal. 5
126 normal. 2
127 normal. 1
128 multihop.   1
128 normal. 376
128 rootkit.    1
129 normal. 7

结果比第一次好上很多了

现在可以使用这个模型对全体数据进行聚类了,使用这个模型可以将数据中离质心最远的点找出来

将这个点到质心的距离设置为阈值

当有新的数据进来时,判断这个数据到其质心的距离是否超过这个阈值

超过就发出警报进行异常检车

总结

在本篇中,样本数据的类别型特征被直接跳过,但是在实际场景中是不能这么做的

正确的做法应该是讲类别型特征转换为数值型特征来训练,得到的模型和之前讨论过的将不太一样

但是模型模型的训练和寻找最优k值的过程是一致的

KMeans训练出来的模型还可以和Spark Streaming相结合,搭建出实时的网络流量异常预警系统

Github源码地址

作者:@小黑

时间: 2024-10-09 19:53:18

KMeans算法检测网络异常入侵的相关文章

网络异常检测

网络异常检查 2 作者 CppLive | 发表于 2012-02-17 文章分类 : C语言, Linux, Windows, 应用与编程, 网络 标签: Android, iPhone, Linux, Windows, 套接字, 路由 一.本文目的 在涉及网络编程的实际项目应用中,由于网络不可能一直处于理想状态,TCP长连接也可能随时正常或异常地断开,如果不予处理,那么就可能因此而给程序带来很多潜在的问题. 编写该文档的目的就在于针对网络程序中可能遇到的各种问题,拿出来与大家探讨一下具体问题

k-means算法处理聚类标签不足的异常

k-means算法在人群聚类场景中,是一个非常实用的工具.(该算法的原理可以参考K-Means算法的Python实现) 常见调用方式 该算法常规的调用方式如下: # 从sklearn引包 from sklearn import cluster # 初始化并设定聚类数 k_means = cluster.KMeans(n_clusters=9) # 指定聚类特征 df_pct = stat_score['feature_1', 'feture_2', 'feature_3'] k_means.fi

mahout运行测试与kmeans算法解析

在使用mahout之前要安装并启动hadoop集群 将mahout的包上传至linux中并解压即可 mahout下载地址: 点击打开链接 mahout中的算法大致可以分为三大类: 聚类,协同过滤和分类 其中 常用聚类算法有:canopy聚类,k均值算法(kmeans),模糊k均值,层次聚类,LDA聚类等 常用分类算法有:贝叶斯,逻辑回归,支持向量机,感知器,神经网络等 下面将运行mahout中自带的example例子jar包来查看mahou是否能正确运行 练习数据下载地址: 点击打开链接 上面的

目标检测网络之 YOLOv2

YOLOv1基本思想 YOLO将输入图像分成SxS个格子,若某个物体 Ground truth 的中心位置的坐标落入到某个格子,那么这个格子就负责检测出这个物体. 每个格子预测B个bounding box及其置信度(confidence score),以及C个类别概率.bbox信息(x,y,w,h)为物体的中心位置相对格子位置的偏移及宽度和高度,均被归一化.置信度反映是否包含物体以及包含物体情况下位置的准确性,定义为\(Pr(Object) \times IOU^{truth}_{pred},

K-Means算法的10个有趣用例

https://www.jianshu.com/p/162c9ec713cf 摘要: 让我们走进K-Means算法的"前世今生"以及和它有关的十个有趣的应用案例. K-means算法具有悠久的历史,并且也是最常用的聚类算法之一.K-means算法实施起来非常简单,因此,它非常适用于机器学习新手爱好者.首先我们来回顾K-Means算法的起源,然后介绍其较为典型的应用场景. 起源 1967年,James MacQueen在他的论文<用于多变量观测分类和分析的一些方法>中首次提出

算法 - k-means算法

一.聚类思想 所谓聚类算法是指将一堆没有标签的数据自动划分成几类的方法,属于无监督学习方法,这个方法要保证同一类的数据有相似的特征,如下图所示: 根据样本之间的距离或者说是相似性(亲疏性),把越相似.差异越小的样本聚成一类(簇),最后形成多个簇,使同一个簇内部的样本相似度高,不同簇之间差异性高. 二.k-means聚类分析算法 相关概念: K值:要得到的簇的个数 质心:每个簇的均值向量,即向量各维取平均即可 距离量度:常用欧几里得距离和余弦相似度(先标准化) 算法流程: 1.首先确定一个k值,即

[数据挖掘] - 聚类算法:K-means算法理解及SparkCore实现

聚类算法是机器学习中的一大重要算法,也是我们掌握机器学习的必须算法,下面对聚类算法中的K-means算法做一个简单的描述: 一.概述 K-means算法属于聚类算法中的直接聚类算法.给定一个对象(或记录)的集合,将这些对象划分为多个组或者“聚簇”,从而使同组内的对象间比较相似而不同组对象间差异比较大:换言之,聚类算法就是将相似的对象放到同一个聚簇中,而将不相似的对象放到不同的聚簇中.由于在聚类过程中不使用到类别标签,所以相似性的概念要基于对象的属性进行定义.应用不同则相似性规则和聚类算法一般不太

k-means算法的优缺点以及改进

大家接触的第一个聚类方法,十有八九都是K-means聚类啦.该算法十分容易理解,也很容易实现.其实几乎所有的机器学习和数据挖掘算法都有其优点和缺点.那么K-means的缺点是什么呢? 总结为下: (1)对于离群点和孤立点敏感: (2)k值选择; (3)初始聚类中心的选择: (4)只能发现球状簇. 对于这4点呢的原因,读者可以自行思考下,不难理解.针对上述四个缺点,依次介绍改进措施. 改进1 首先针对(1),对于离群点和孤立点敏感,如何解决?笔者在前面的一篇博客中,提到过离群点检测的LOF算法,通

K-means算法原理与R语言实例

聚类是将相似对象归到同一个簇中的方法,这有点像全自动分类.簇内的对象越相似,聚类的效果越好.支持向量机.神经网络所讨论的分类问题都是有监督的学习方式,现在我们所介绍的聚类则是无监督的.其中,K均值(K-means)是最基本.最简单的聚类算法. 在K均值算法中,质心是定义聚类原型(也就是机器学习获得的结果)的核心.在介绍算法实施的具体过程中,我们将演示质心的计算方法.而且你将看到除了第一次的质心是被指定的以外,此后的质心都是经由计算均值而获得的. 首先,选择K个初始质心(这K个质心并不要求来自于样