在 SVM 中使用 RBF 核的多条分离线

数据挖掘 Python 支持向量机 异常检测 离群值 核心
2022-02-24 12:05:53

下面是我的代码,它采用一个数字范围,创建一个 包含 or的新列标签-11

如果数字高于14000,我们将其标记为 -1(异常值) 如果数字低于14000,我们将其标记为 1(正常)

## Here I just import all the libraries and import the column with my dataset 
## Yes, I am trying to find anomalies using only the data from one column

df['label'] = [-1 if x >= 14000 else 1 for x in df['data_numbers']]  #What I explained above

data = df.drop('label',axis=1)                         
target = df['label']
outliers = df[df['label']==-1]

outliers = outliers.drop('label',axis=1)

from sklearn.model_selection import train_test_split
train_data, test_data, train_target, test_target = train_test_split(data, target, train_size = 0.8)
train_data.shape

nu = outliers.shape[0] / target.shape[0]
print("nu", nu)

model = svm.OneClassSVM(nu=nu, kernel='rbf', gamma=0.00005) 
model.fit(train_data)

from sklearn import metrics
preds = model.predict(train_data)
targs = train_target 
print("accuracy: ", metrics.accuracy_score(targs, preds))
print("precision: ", metrics.precision_score(targs, preds)) 
print("recall: ", metrics.recall_score(targs, preds))
print("f1: ", metrics.f1_score(targs, preds))
print("area under curve (auc): ", metrics.roc_auc_score(targs, preds))
train_preds = preds

preds = model.predict(test_data)
targs = test_target 
print("accuracy: ", metrics.accuracy_score(targs, preds))
print("precision: ", metrics.precision_score(targs, preds)) 
print("recall: ", metrics.recall_score(targs, preds))
print("f1: ", metrics.f1_score(targs, preds))
print("area under curve (auc): ", metrics.roc_auc_score(targs, preds))
test_preds = preds


from mlxtend.plotting import plot_decision_regions                                 # as rbf svm is used hence lot's of  decision boundaries are drawn unlike one in linear SVM 
# the top one central points with blue quares are outlietrs while at the bottom they are orangy triangles(normal values)
plot_decision_regions(np.array(train_data), np.array(train_target), model)
plt.show()

训练数据的输出

accuracy:  0.9050484526414505
precision:  0.9974137931034482
recall:  0.907095256762054
f1:  0.9501129131595154
area under curve (auc):  0.5876939698444417

测试数据的输出

accuracy:  0.9043451078462019
precision:  1.0
recall:  0.9040752351097179
f1:  0.9496213368455713
area under curve (auc):  0.9520376175548589

在此处输入图像描述

我的图表似乎有很多分隔线,我想我只会得到一个区分异常值和正常数据的线。

2个回答

OneClassSVM 是一种无监督算法,应该学习正态数据分布。这意味着该算法将为要从中提取数据点的高可能性区域的边界建模。

即使您知道哪些点是异常值,SVM 也只会知道大约有一部分nu的异常值,因此 14000 边界在分布方面根本不直观。

SVM 会解释你的多行是:

  • 如果在您认为的“正常区域”中有一个洞——例如,您在 2560 和 2985 之间没有数据点——SVM 可以确定这是您的分布可能性较低的区域,因此构建两个向量将其从学习的正态分布中排除。

  • 相反,如果你有几个点在 14000 以上聚集在一起,由于 SVM 不知道这应该是异常区域,它可以检测到从中提取数据点的可能性很高的区域,并构建两个向量将其包含在学习的正态分布中。

现在关于您的指标:

  • 你可能有一个不平衡的数据集,因为我们正在谈论异常检测(否则它没有意义!),所以你不应该在这里使用全局精度,因为它会误导。

  • 有一件事要提醒你:train AUC 很低,而 test AUC 很高,这是不正常的。因此,您的算法对目标正态分布的学习效果不佳。

可以解释高测试 AUC,但首先让我们使用一些符号:

  • 真正的正态数据分布将被称为“目标正态分布”,在你想学习的意义上。它是区间 [0;14000[
  • 真正的异常区域(在这种情况下不能谈论分布)将被称为“目标异常区域”。它是区间 [14000;+infinity[
  • 显然,学习到的正态分布将被称为“学习到的正态分布”。
  • 学习到的异常区域将被称为“学习异常区域”

因此,学习到的分布包含在训练数据密度较高的区域周围的小簇中。由于您(假设)有一些异常值和很多正常点,因此学习分布仍然与目标正态分布很好地相交,但与目标异常区域不相交,其中只有少数区域属于学习的正态分布。您可以在图表上验证这一点:目标异常区域中只有几个向量。

现在当你在测试集上应用该算法时,目标异常区域中测试集的一个点不太可能落入学习的正态分布和目标异常区域的交集,因为这个交集非常小。但这只是由于该异常区域中的数据点较少,而不是成功的学习过程。因此,这些点仍将被标记为异常,并且您会得到误导性的高 AUC 分数。

相反,目标正态分布和学习的正态分布正确相交,因为有更多的数据点需要学习。所以测试集中的大部分正常点都被正确分类了。

总而言之,您的算法学习的分布与您预期的完全不同您可以尝试使用更高的gamma值来平滑内核。但是您可能不会避免将 14000 以上的高密度区域分类为正常数据点。所以当心火车AUC!

用图表来研究这些陈述会很有趣,你可以作为一个例子绘制 [14000; +infinity[ 区域,突出显示决策函数边界并绘制训练集和测试集数据点。你应该得到我上面所说的确认(我会稍后再做,如果我有时间再添加图表!)

如果您知道决策边界(14000),那么为什么需要 ML 算法?您可以应用 if 条件。ML 算法是为寻找决策边界而开发的?如果你真的想在这个数据集上做实验,那么你必须知道你在做什么类型的分类?从人工标记的数据集中,我可以看到您有两个类(1,-1)。但是您使用的是 OneClassSVM。那是错误!只需删除 OneClassSVM 并使用正常的线性 svm。并且不需要使用'rbf'。'rbf' 用于非线性分离。您的分类边界是线性的。它只是一条穿过14000的线。

from sklearn import svm
clf = svm.SVC()
clf.fit(train_data, train_target)

注意:如果要进行异常值检测,请不要按 (< 14000) 条件标记数据。允许OneClassSVM仅通过查看训练特征来查找异常值。