有没有一种方法可以在One-Class SVM上执行网格搜索超参数优化?

15

有没有一种方法可以使用GridSearchCV或任何其他内置的sklearn函数来查找OneClassSVM分类器的最佳超参数?

目前我所做的是像这样使用训练/测试集进行搜索:

Gamma和nu的值定义如下:

gammas = np.logspace(-9, 3, 13)
nus = np.linspace(0.01, 0.99, 99)

探索所有可能的超参数并找到最佳超参数的函数:

clf = OneClassSVM()

results = []

train_x = vectorizer.fit_transform(train_contents)
test_x = vectorizer.transform(test_contents)

for gamma in gammas:
    for nu in nus:
        clf.set_params(gamma=gamma, nu=nu)

        clf.fit(train_x)

        y_pred = clf.predict(test_x)

        if 1. in y_pred:  # Check if at least 1 review is predicted to be in the class
            results.append(((gamma, nu), (accuracy_score(y_true, y_pred),
                                              precision_score(y_true, y_pred),
                                              recall_score(y_true, y_pred),
                                              f1_score(y_true, y_pred),
                                              roc_auc_score(y_true, y_pred),
                                              ))
                               )

    # Determine and print the best parameter settings and their performance
    print_best_parameters(results, best_parameters(results))

结果存储在元组列表中,其形式为:

((gamma, nu)(accuracy_score, precision_score, recall_score, f1_score, roc_auc_score))

为了找到最佳准确率、f1和roc_auc分数以及参数,我编写了自己的函数:

best_parameters(results)


你有尝试使用GridSearchCV吗?你遇到了什么错误吗? - Vivek Kumar
1
我如何在不应用交叉验证的情况下实现这一点,因为One-Class SVM只需要适合于属于分类器正在处理的类别的数据。我的做法是:对属于该类的实例进行80%的训练,然后将其余的20%与不属于该类的实例组合起来用于测试。 - Yustx
你是如何将数据分成训练集和测试集的? - Vivek Kumar
2
@Yustx,您能否分享一下您如何解决OC-SVM的问题?我也遇到了同样的问题,但不确定该如何将您的问题与答案结合起来使其正常工作。 - duichwer
2个回答

11
我遇到了同样的问题,在寻找解决方案时找到了这个问题。最终我找到了一个使用GridSearchCV的解决方案,并留下这个答案供其他搜索并发现此问题的人参考。 GridSearchCV类的cv参数可以将其输入作为可迭代项,生成(train, test)数组索引拆分的形式。您可以生成仅使用训练折中正类数据,在测试折中使用剩余的正类数据和所有负类数据的拆分。
您可以使用sklearn.model_selection.KFold来进行划分。
from sklearn.model_selection import KFold

假设Xpos是正类的nXp numpy数据数组,用于OneClassSVM,而Xneg是已知异常示例的mXp数据数组。
您可以首先使用以下方法生成对Xpos的划分:
splits = KFold(n_splits=5).split(Xpos)

这将构建一个元组生成器,形式为 (train, test),其中 train 是包含训练集折叠示例索引的整数 numpy 数组,test 是包含测试集折叠示例索引的 numpy 数组。
然后,您可以使用以下方法将 XposXneg 组合成单个数据集。
X = np.concatenate([Xpos, Xneg], axis=0)
< p > OneClassSVM 会对它认为属于正类的样本进行预测,预测结果为1.0,对于它认为是异常的样本,预测结果为-1.0。我们可以使用标签来为我们的数据进行分类。

y = np.concatenate([np.repeat(1.0, len(Xpos)), np.repeat(-1.0, len(Xneg))])

我们可以使用包含异常样本索引的测试折叠来生成新的 (train, test) 拆分生成器。
n, m = len(Xpos), len(Xneg)

splits = ((train, np.concatenate([test, np.arange(n, n + m)], axis=0)
          for train, test in splits)

您可以使用数据 X, y,以及您希望的评分方法和其他参数将这些拆分传递给GridSearchCV

grid_search = GridSearchCV(estimator, param_grid, cv=splits, scoring=...)

编辑:我没有注意到Vivek Kumar在其他答案的评论中建议了这种方法,而问题提出者拒绝使用此方法,因为他们不相信它能与他们选择最佳参数的方法一起使用。但我仍然更喜欢我所描述的方法,因为GridSearchCV将自动处理多进程,并提供异常处理和有用的警告和错误信息。

此外,它在评分方法的选择上非常灵活。您可以通过传递一个将字符串映射到评分可调用项的字典来使用多个评分方法,甚至定义自定义评分可调用项。这在Scikit-learn文档here中有描述。可以使用文档中描述的字典方法包括问题提出者使用的所有指标以选择最佳参数。

您可以在here找到一个真实世界的示例。当这个示例合并到主分支中时,我会做出更改链接的备注。


@asj3 这行代码是否有语法错误 splits = ((train, np.concatenate([test, np.arange(n, n + m), axis=0) for train, test in splits)? - tisch
@tisch 很好的发现。有一个括号丢失了。现在已经修复了。 - Albert Steppi
@asj3 我也不认为你可以将X和y传递给GridSearchCV。它稍后会传递给grid_Search.fit(X,y)。 - tisch

5
是的,在不对输入数据进行交叉验证的情况下,有一种搜索超参数的方法。这种方法称为ParameterGrid(),存储在sklearn.model_selection中。以下是官方文档链接:http://scikit-learn.org/stable/modules/generated/sklearn.model_selection.ParameterGrid.html
你的情况可能如下所示:
grid = {'gamma' : np.logspace(-9, 3, 13),
        'nu' : np.linspace(0.01, 0.99, 99)}

要使用grid的所有可能步骤,您可以键入list(ParameterGrid(grid))。我们还可以通过len(list(ParameterGrid(grid)))检查其长度,共计1287个模型可用于训练数据的拟合。
要使用该方法,您必须使用for循环。假设您有一个未拟合的one-class SVM变量clf,导入自sklearn.svm,则循环将如下所示:
for z in ParameterGrid(grid):
    clf.set_params(**z)
    clf.fit(X_train, y_train)
    clf.predict(X_test)
    ...

我希望这足够了。不要忘记,在网格中的名称应该与单类SVM的参数一致。要获取这些参数的名称,您可以键入clf.get_params().keys(),然后您会看到'gamma'和'nu'。


这个解决方案很好。但是,OP必须维护有关分数、适合度、参数等的所有信息。GridSearchCV将自动完成此操作。由于用户正在将数据分成训练和测试,因此我们可以使用自定义cv迭代器来相应地拆分数据。 - Vivek Kumar
对我来说也有点混淆。我会做你指出的同样的事情。但我不确定这个for循环比基本的GridSearchCV更耗时,或者它们几乎相等。 - E.Z
1
我不能确定这个for循环,但GridSearchCV将并行地拟合不同参数,因此可能比这个更具有稍微更高的性能。 - Vivek Kumar
哦,是的。肯定会更快。 - E.Z
1
它通过减少一个缩进来减少嵌套。性能似乎差不多。然而,这并不是非常有用的,因为我仍然必须使用自己的实现来找到最佳的超参数。 - Yustx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接