单分类算法:One Class SVM
安全检测常用算法有:Isolation Forest,One-Class Classification等,孤立森林参见另一篇,今天主要介绍One-Class Classification单分类算法。一,单分类算法简介 One Class Learning 比较经典的算法是One-Class-SVM,这个算法的思路非常简单,就是寻找一个超平面将样本中的正例圈出来,预测就是用这个超平面做决策,在圈内
安全检测常用算法有:Isolation Forest,One-Class Classification等,孤立森林参见另一篇,今天主要介绍One-Class Classification单分类算法。
一,单分类算法简介
One Class Learning 比较经典的算法是One-Class-SVM,这个算法的思路非常简单,就是寻找一个超平面将样本中的正例圈出来,预测就是用这个超平面做决策,在圈内的样本就认为是正样本。由于核函数计算比较耗时,在海量数据的场景用的并不多;
另一个算法是基于神经网络的算法,在深度学习中广泛使用的自编码算法可以应用在单分类的问题上,自编码是一个BP神经网络,网络输入层和输出层是一样,中间层数可以有多层,中间层的节点个数比输出层少,最简单的情况就是中间只有一个隐藏层,如下图所示,由于中间层的节点数较少,这样中间层相当于是对数据进行了压缩和抽象,实现无监督的方式学习数据的抽象特征。
如果我们只有正样本数据,没有负样本数据,或者说只关注学习正样本的规律,那么利用正样本训练一个自编码器,编码器就相当于单分类的模型,对全量数据进行预测时,通过比较输入层和输出层的相似度就可以判断记录是否属于正样本。由于自编码采用神经网络实现,可以用GPU来进行加速计算,因此比较适合海量数据的场景。
最后就是Isolation Forest方法,孤立森林是一个高效的异常点检测算法。Sklearn提供了ensemble.IsolatuibForest模块。该模块在进行检测时,会随机选取一个特征,然后在所选特征的最大值和最小值随机选择一个分切面。该算法下整个训练集的训练就像一棵树一样,递归的划分。划分的次数等于根节点到叶子节点的路径距离d。所有随机树(为了增强鲁棒性,会随机选取很多树形成森林)的d的平均值,就是我们检测函数的最终结果。
孤立森林相关笔记可以参考这里:请点击我
One-Class SVM 算法简介
sklearn提供了一些机器学习方法,可用于奇异(Novelty)点或者异常(Outlier)点检测,包括OneClassSVM,Isolation Forest,Local Outlier Factor(LOF)等,其中OneCLassSVM可以用于Novelty Dection,而后两者可用于Outlier Detection。
严格来说,OneCLassSVM不是一种outlier detection,而是一种novelty detection方法:它的训练集不应该掺杂异常点,因为模型可能会去匹配这些异常点。但在数据维度很高,或者对相关数据分布没有任何假设的情况下,OneClassSVM也可以作为一种很好的outlier detection方法。
在one-class classification中,仅仅只有一类的信息是可以用于训练,其他类别的(总称outlier)信息是缺失的,也就是区分两个类别的边界线是通过仅有的一类数据的信息学习得到的。
名词解释
- novelty detection:当训练数据中没有离群点,我们的目标是用训练好的模型去检测另外发现的新样本
- outlier dection:当训练数据中包含离群点,模型训练时要匹配训练数据的中心样本,忽视训练样本中的其他异常点。
OneClass 与二分类,多分类的区别
如果将分类算法进行划分,根据类别个数的不同可以分为单分类,二分类,多分类。常见的分类算法主要解决二分类和多分类问题,预测一封邮件是否是垃圾邮件是一个典型的二分类问题,手写体识别是一个典型的多分类问题,这些算法并不能很好的应用在单分类上,但是单分类问题在工业界广泛存在,由于每个企业刻画用户的数据都是有限的,很多二分类问题很难找到负样本,即使用一些排除法筛选出负样本,负样本也会不纯,不能保证负样本中没有正样本。所以在只能定义正样本不能定义负样本的场景中,使用单分类算法更合适。
单分类算法只关注与样本的相似或者匹配程度,对于未知的部分不妄下结论。
典型的二类问题:识别邮件是否是垃圾邮件,一类“是”,一类“不是”。
典型的多类问题:人脸识别,每个人对应的脸就是一个类,然后把待识别的脸分到对应的类去。
而OneClassClassification,它只有一个类,属于该类就返回结果“是”,不属于就返回结果“不是”。
其区别就是在二分类问题中,训练集中就由两个类的样本组成,训练出的模型是一个二分类模型;而OneClassClassification中的训练样本只有一类,因此训练出的分类器将不属于该类的所有其他样本判别为“不是”即可,而不是由于属于另一类才返回“不是”的结果。
One Class SVM算法步骤
One Class SVM也是属于支持向量机大家族的,但是它和传统的基于监督学习的分类回归支持向量机不同,它是无监督学习的方法,也就是说,它不需要我们标记训练集的输出标签。
那么没有类别标签,我们如何寻找划分的超平面以及寻找支持向量机呢?One Class SVM这个问题的解决思路有很多。这里只讲解一种特别的思想SVDD,对于SVDD来说,我们期望所有不是异常的样本都是正类别,同时它采用一个超球体而不是一个超平面来做划分,该算法在特征空间中获得数据周围的球形边界,期望最小化这个超球体的体积,从而最小化异常点数据的影响。
假设产生的超球体参数为中心 o 和对应的超球体半径 r >0,超球体体积V(r) 被最小化,中心 o 是支持行了的线性组合;跟传统SVM方法相似,可以要求所有训练数据点xi到中心的距离严格小于r。但是同时构造一个惩罚系数为 C 的松弛变量 ζi ,优化问题入下所示:
采用拉格朗日对偶求解之后,可以判断新的数据点 z 是否在内,如果 z 到中心的距离小于或者等于半径 r ,则不是异常点,如果在超球体以外,则是异常点。
在Sklearn中,我们可以采用SVM包里面的OneClassSVM来做异常点检测。OneClassSVM也支持核函数,所以普通SVM里面的调参思路在这里也使用。
相关SVDD参考:https://zhuanlan.zhihu.com/p/97522759
sklearn实现:OneClasssSVM
根据已有支持向量机的理解,算法并非对已有标签的数据进行分类判别,而是通过回答:yes or no 的方法去根据支持向量域(support vector domaindescription SVDD),将样本数据训练出一个最小的超球面(大于三维特征),其中在二维中是一个曲线,将数据全部包起来,即将异常点排除。
OneClass SVM 主要参数和方法
class sklearn.svm.OneClassSVM(kernel=’rbf’, degree=3, gamma=’auto’,
coef0=0.0, tol=0.001, nu=0.5, shrinking=True, cache_size=200, verbose=False,
max_iter=-1, random_state=None)
参数:
- kernel:核函数(一般使用高斯核)
- nu:设定训练误差(0, 1],表示异常点比例,默认值为0.5
属性:
方法:
- fit(X):训练,根据训练样本和上面两个参数探测边界。(注意是无监督)
- predict(X):返回预测值,+1就是正常样本,-1就是异常样本。
- decision_function(X):返回各样本点到超平面的函数距离(signed distance),正的维正常样本,负的为异常样本。
- set_params(**params):设置这个评估器的参数,该方法适用于简单估计器以及嵌套对象(例如管道),而后者具有表单<component>_<parameter>的参数,,因此可以更新嵌套对象的每个组件。
- get_params([deep]):获取这个评估器的参数。
- fit_predict(X[, y]):在X上执行拟合并返回X的标签,对于异常值,返回 -1 ,对于内点,返回1。
One-Class SVM with non-linear kernel (RBF)
下面使用OneClass SVM 进行奇异点检测。
OneClass SVM 是一个无监督算法,它用于学习奇异点检测的决策函数:将新数据分类为与训练集相似或者不同的数据。
sklearn实现代码如下:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.font_manager
from sklearn import svm
xx, yy = np.meshgrid(np.linspace(-5, 5, 500), np.linspace(-5, 5, 500))
# Generate train data
X = 0.3 * np.random.randn(100, 2)
X_train = np.r_[X + 2, X - 2]
X_test = np.r_[X + 2, X-2]
# Generate some abnormal novel observations
X_outliers = np.random.uniform(low=0.1, high=4, size=(20, 2))
# fit the model
clf = svm.OneClassSVM(nu=0.1, kernel='rbf', gamma=0.1)
clf.fit(X_train)
y_pred_train = clf.predict(X_train)
y_pred_test = clf.predict(X_test)
y_pred_outliers = clf.predict(X_outliers)
n_error_train = y_pred_train[y_pred_train == -1].size
n_error_test = y_pred_test[y_pred_test == -1].size
n_error_outlier = y_pred_outliers[y_pred_outliers == 1].size
# plot the line , the points, and the nearest vectors to the plane
Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.title("Novelty Detection")
plt.contourf(xx, yy, Z, levels=np.linspace(Z.min(), 0, 7), cmap=plt.cm.PuBu)
a = plt.contour(xx, yy, Z, levels=[0, Z.max()], colors='palevioletred')
s =40
b1 = plt.scatter(X_train[:, 0], X_train[:, 1], c='white', s=s, edgecolors='k')
b2 = plt.scatter(X_test[:, 0], X_test[:, 1], c='blueviolet', s=s, edgecolors='k')
c = plt.scatter(X_outliers[:, 0], X_outliers[:, 1], c='gold', s=s, edgecolors='k')
plt.axis('tight')
plt.xlim((-5, 5))
plt.ylim((-5, 5))
plt.legend([a.collections[0], b1, b2, c],
["learned frontier", 'training observations',
"new regular observations", "new abnormal observations"],
loc="upper left",
prop=matplotlib.font_manager.FontProperties(size=11))
plt.xlabel(
"error train: %d/200; errors novel regular: %d/40; errors novel abnormal:%d/40"%(
n_error_train, n_error_test, n_error_outlier) )
plt.show()
OneClassSVM 代码二
根据对已有支持向量机的理解,算法并非对已有标签的数据进行分类判别,而是通过回答“yes or no”的方式去根据支持向量域描述(support vector domaindescription SVDD),将样本数据训练出一个最小的超球面(大于三维特征),其中在二维中是一个曲线,将数据全部包起来,即将异常点排除。Sklearn包中给出的demo实验结果如上:我们可以看出在不同的数据分布下会有一些不一样的误差,其中调整参数中有一个比较重要的nu,表示异常点比例,默认值为0.5。
from sklearn import svm
import matplotlib.pyplot as plt
plt.style.use('fivethirtyeight')
from numpy import genfromtxt
def read_dataset(filePath, delimiter=','):
return genfromtxt(filePath, delimiter=delimiter)
# use the same dataset
tr_data = read_dataset('tr_data.csv')
clf = svm.OneClassSVM(nu=0.05, kernel='rbf', gamma=0.1)
'''
OneClassSVM(cache_size=200, coef0=0.0, degree=3, gamma=0.1, kernel='rbf',
max_iter=-1, nu=0.05, random_state=None, shrinking=True, tol=0.001,
verbose=False)
'''
clf.fit(tr_data)
pred = clf.predict(tr_data)
# inliers are labeled 1 , outliers are labeled -1
normal = tr_data[pred == 1]
abnormal = tr_data[pred == -1]
plt.plot(normal[:, 0], normal[:, 1], 'bx)
plt.plot(abnormal[:, 0], abnormal[:, 1], 'ro')
因为上面的代码没有数据,我这里在网上找了一张图,可以基本说明问题,如下:
参考:
更多推荐
所有评论(0)