如何最好地确定模型的准确性?重复训练/测试拆分或简历?

Posted

技术标签:

【中文标题】如何最好地确定模型的准确性?重复训练/测试拆分或简历?【英文标题】: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 条记录。

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

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

我正在考虑的两个选项:

    运行一个循环来测试训练将模型拆分 X 次,并查看每次拆分的准确度 使用交叉验证(特别是 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 个项目要在训练和测试之间拆分,所以测试集(随机选择)可能会合理地最终成为全部/大部分好或全部/大部分坏,我不认为将是一个很好的表示准确性的测试集。

【问题讨论】:

【参考方案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 是否能更好地估计我的模型对新数据的准确性。 不客气!答案是肯定的,交叉验证应该更好地为您提供稳定的性能,但根据我的经验,数据和数据分布是这个估计的关键,因此标准的训练-测试-验证过程可能会像交叉验证一样好验证。

以上是关于如何最好地确定模型的准确性?重复训练/测试拆分或简历?的主要内容,如果未能解决你的问题,请参考以下文章

如何有效地比较所有模型的准确性

如何拆分测试和训练大小

5倍交叉验证如何理解

如何使用一个热编码数据训练测试拆分?

SVM模型进行分类预测时的参数调整技巧

Caffe 如何确定测试集的准确性?