One Class SVM训练数据占比提升时准确率上升的反常现象咨询
我的研究目标是基于breast_cancer数据集生成合成数据,核心要求是让合成数据与原数据高度相似,使得one class svm无法区分二者。我编写了如下代码:
import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn import svm from sklearn.metrics import accuracy_score from sklearn import preprocessing DATA_FILE = "breastcancer.txt" adversary_percentages = [0.01, 0.02, 0.04, 0.08, 0.16] for adversary_percentage in adversary_percentages: data = pd.read_csv(DATA_FILE, sep=",") data = data.dropna(how='any', axis=0) data = data.drop(data.columns[0], axis=1) main_data, adversary_data = train_test_split(data, test_size=adversary_percentage, random_state=1234) scaler = preprocessing.MinMaxScaler() adversary_input = scaler.fit_transform(adversary_data) svc = svm.OneClassSVM(nu=0.01, gamma=0.1).fit(adversary_input) predicted = svc.predict(adversary_input) print(f"percentage_of_data_one_class_has: {adversary_percentage} accuracy: {np.round(sum(predicted[predicted==1.0])/len(predicted),2)}")
运行代码后,我发现了一个反常现象:one class svm使用的原数据占比越高,被标记为异常的数据越少,准确率也随之上升。运行结果如下:
percentage_of_data_one_class_has: 0.01 accuracy: 0.79 percentage_of_data_one_class_has: 0.02 accuracy: 0.78 percentage_of_data_one_class_has: 0.04 accuracy: 0.88 percentage_of_data_one_class_has: 0.08 accuracy: 0.94 percentage_of_data_one_class_has: 0.16 accuracy: 0.96
现象原因解释
咱们拆解下代码逻辑和One-Class SVM的工作原理,就能明白这个“反常”其实是完全符合模型特性的:
训练数据量对分布拟合的影响:你设置的
adversary_percentage是测试集占比,也就是说,当比例为0.01时,One-Class SVM仅在1%的原数据上训练;比例升到0.16时,模型在16%的原数据上训练。One-Class SVM的核心是学习训练数据的分布边界,训练数据越多,它对原数据分布的拟合就越精准,自然能更准确地识别出属于该分布的样本——而你的测试集adversary_data本身就是原数据的子集,和训练数据完全同分布。评估逻辑的本质:你计算准确率的方式是统计被判定为正常(
predicted==1.0)的样本占比。One-Class SVM中1代表属于训练数据的分布,-1代表异常。当训练数据量极小时,模型无法准确捕捉分布边界,会误把很多正常样本判定为异常,所以准确率低;训练数据量增大后,模型对分布的刻画更清晰,能正确识别更多同分布的测试样本,准确率自然上升。参数
nu的作用:你设置的nu=0.01控制着训练数据中被标记为异常的比例上限。当训练数据量极少时,模型很难贴合这个比例,会产生更多误判;随着训练数据量增加,模型能更好地匹配nu的设定,正常样本的识别率也就随之提升。
另外要注意:你当前的实验并没有用到合成数据,只是用原数据的子集做训练和测试,所以这个结果其实是在验证“更多的训练数据能让One-Class SVM更精准识别同分布样本”,和你原本测试合成数据相似度的目标有所偏离。
内容的提问来源于stack exchange,提问作者user3104352

