【问题标题】:How to best determine the accuracy of a model? Repeated train/test splits or cv?如何最好地确定模型的准确性?重复训练/测试拆分或简历?
【发布时间】:2020-11-12 17:53:48
【问题描述】:

我正在创建一个分类器,它将矢量化的书籍文本作为输入,并作为输出预测这本书是“好”还是“坏”。

我有 40 本书,27 本书好,13 本书坏。我将每本书分成 5 条记录(5 个 10 页段)以增加数据量,因此总共 200 条记录。

最终,我会将模型拟合到所有书籍上,并使用它来预测未标记的书籍。

估计我的模型将具有的准确度的最佳方法是什么?我还将将此估计值用于模型比较、调整等。

我正在考虑的两个选项:

  1. 运行一个循环来测试训练将模型拆分 X 次,并查看每次拆分的准确度
  2. 使用交叉验证(特别是 GroupKFold,以便将每本书的 5 条记录保存在一起,因为如果不这样做将是重大泄漏)

我想尽快在很小的误差范围内估计准确度。重复的训练测试拆分速度较慢,因为即使我按标签分层(选择 8 本书好书和 4 本书坏书进行测试),特定模型的准确度也可能在 0.6 到 0.8 之间变化,所以我必须跑很多才能得到准确的估计。

另一方面,CV 每次运行时都给我相同的分数,并且似乎与 100 次训练测试拆分后的模型的平均准确度相对较好(在 1-1.5% 以内)。

CV 更快,所以我更喜欢使用它。 CV 在这里使用有意义吗?我目前正在使用 5 折(因此每次运行选择 8 个保留书,或总共 40 个保留记录)。

另外,CV 是否应该在我每次运行时都提供完全相同的准确度? (就此而言,以相同的顺序排列完全相同的准确度列表)。在将 X、y 和组放入 cross_val_score 之前,我正在改组我的语料库。 ShuffleSplit 会更好吗?这是我的代码:

for i in range(0,5):
    dfcopy = df.copy()
    dfcopy = dfcopy.sample(frac=1).reset_index(drop=True)
    X, y = dfcopy.text, dfcopy.label
    groups = dfcopy.title.tolist()

    
    model = MultinomialNB()
    name = 'LR'

    pipe = Pipeline([('cleaner', clean_transformer()),
                     ('vectorizer', bow_vector),
                     ('classifier', model)])

    score = cross_val_score(estimator=pipe, X=X, y=y, groups=groups, cv=GroupKFold())
    print(score)
    print(np.mean(score))

最后,我应该使用分层吗?我的想法是我应该,因为我实际上有 40 个项目要在训练和测试之间拆分,所以测试集(随机选择)可能会合理地最终成为全部/大部分好或全部/大部分坏,我不认为将是一个很好的表示准确性的测试集。

【问题讨论】:

    标签: python machine-learning scikit-learn cross-validation train-test-split


    【解决方案1】:

    我会尽量按顺序进行:

    • What's the best way to estimate the accuracy my model's going to have? I'll also use this estimate for model comparison, tuning, etc.

    • CV is much faster, so I'd prefer to use it. Does CV make sense to use here?

    如果你的折叠彼此之间非常相似,N-fold CV 和重复测试和训练之间不会有太大差异。

    • Should CV be giving the exact same accuracy every time I run it?

    这取决于两个因素,超参数和使用的数据,MultinomialNB 的超参数几乎没有改进空间。因此归结为CV折叠的分布。

    • Would a ShuffleSplit be preferable?

    ShuffleSplit 可能会有所不同,但不要指望会有很大的不同。

    正如我所看到的,至少根据我的经验,您可以做出的重大改进是停止使用 MultinomialNB - 尽管作为一个良好的基线不会给您带来疯狂的好结果 - 并开始使用一些更复杂的东西,比如SGDClassifier、随机森林、感知器,应有尽有。使用 scikit-learn 可以很容易地在分类算法之间切换,这要归功于迄今为止在标准化调用和数据方面所做的出色工作。 因此,您的模型将变为:

    model = RandomForestClassifier()

    另一件可能有用的事情是使用训练/测试/验证集和超参数优化,例如 Gridsearch,设置可能需要几个小时,但它肯定会得到回报。

    如果您决定使用训练/测试/验证,scikit-learn 会为您提供 train_test_split 功能:

    X, y = df.text, df.label    
    
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1)
    
    X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.25, random_state=1)
    

    如果您决定使用网格搜索进行超参数优化,您需要:

    (1) 定义一组可能的参数

    grid_1 = { 
      "n_estimators": [100,200,500],
      "criterion": ["gini", "entropy"],
      "max_features": ['sqrt','log2',0.2,0.5,0.8],
      "max_depth": [3,4,6,10],
      "min_samples_split": [2, 5, 20,50] 
    }
    

    (2) 启动网格搜索优化

    model = RandomForestClassifier()
    grid_search = GridSearchCV(model, grid_1, n_jobs=-1, cv=5)
    grid_search.fit(X_train, Y_train)
    

    Gridsearch 作为优化技术非常简单,但对于提供更好的结果非常有帮助。如果您想加深对该主题的理解并进一步增强您的代码,您可以找到使用更复杂的超参数优化策略的示例代码,例如 TPE here

    最后,您的数据集似乎很小,如果您在火车和另一辆火车之间等待很长时间,我建议您考虑编写一个小型缓存系统以减少加载和处理时间。你可以找到一个使用小缓存系统的示例代码here

    【讨论】:

    • 感谢您的详细回复! MultinomialNB() 只是我在该插槽中展示交叉验证代码的示例模型;在我真正的笔记本中,我测试了一堆不同的模型。我将研究缓存系统,并一直在使用 gridsearch 进行超参数调整。我主要关心的是 train_test_split 还是 cross_val_score 是否能更好地估计我的模型对新数据的准确性。
    • 不客气!答案是肯定的,交叉验证应该更好地为您提供稳定的性能,但根据我的经验,数据和数据分布是这个估计的关键,因此标准的训练-测试-验证过程可能会像交叉验证一样好验证。
    猜你喜欢
    • 1970-01-01
    • 2018-03-26
    • 1970-01-01
    • 2021-11-12
    • 1970-01-01
    • 2021-08-11
    • 1970-01-01
    • 2019-03-16
    • 1970-01-01
    相关资源
    最近更新 更多