在GridSearchCV中明确指定测试/训练集

16
我有一个关于sklearn的GridSearchCVcv参数的问题。
我正在处理具有时间组件的数据,因此我认为在KFold交叉验证中进行随机洗牌似乎不明智。
相反,我想在GridSearchCV中明确指定训练、验证和测试数据的截止点。我可以这样做吗?
为了更好地阐明这个问题,这是我手动执行的方法。
import numpy as np
import pandas as pd
from sklearn.linear_model import Ridge
np.random.seed(444)

index = pd.date_range('2014', periods=60, freq='M')
X, y = make_regression(n_samples=60, n_features=3, random_state=444, noise=90.)
X = pd.DataFrame(X, index=index, columns=list('abc'))
y = pd.Series(y, index=index, name='y')

# Train on the first 30 samples, validate on the next 10, test on
#     the final 10.
X_train, X_val, X_test = np.array_split(X, [35, 50])
y_train, y_val, y_test = np.array_split(y, [35, 50])

param_grid = {'alpha': np.linspace(0, 1, 11)}
model = None
best_param_ = None
best_score_ = -np.inf

# Manual implementation
for alpha in param_grid['alpha']:
    ridge = Ridge(random_state=444, alpha=alpha).fit(X_train, y_train)
    score = ridge.score(X_val, y_val)
    if score > best_score_:
        best_score_ = score
        best_param_ = alpha
        model = ridge

print('Optimal alpha parameter: {:0.2f}'.format(best_param_))
print('Best score (on validation data): {:0.2f}'.format(best_score_))
print('Test set score: {:.2f}'.format(model.score(X_test, y_test)))
# Optimal alpha parameter: 1.00
# Best score (on validation data): 0.64
# Test set score: 0.22

这里的流程如下:
  • 对于X和Y,我需要一个训练集、验证集和测试集。训练集是时间序列中的前35个样本。验证集是接下来的15个样本。测试集是最后的10个。
  • 使用训练集和验证集来确定岭回归中最优的alpha参数。在这里,我测试了alphas的值为(0.0,0.1,...,0.9,1.0)。
  • 测试集被保留为“实际”测试数据,作为未看到的数据。
无论如何...似乎我想做这样的事情,但不确定在此处传递什么给cv:
from sklearn.model_selection import GridSearchCV
grid_search = GridSearchCV(Ridge(random_state=444), param_grid, cv= ???)
grid_search.fit(...?)

我遇到了解释困难的文档,其中规定:

cv : 整数,交叉验证生成器或可迭代对象,可选参数

确定交叉验证分割策略。cv 的可能输入为:

  • None,使用默认的3折交叉验证,
  • 整数,指定(Stratified)KFold中的折数,
  • 用作交叉验证生成器的对象,
  • 产生训练集和测试集分割的可迭代对象。

对于整数/None输入,如果评估器是分类器且 y 是二元或多元分类,则使用 StratifiedKFold。在所有其他情况下,使用 KFold。


请参见 https://dev59.com/jlwZ5IYBdhLWcg3wBcQJ - Ben Reiniger
3个回答

23

正如 @MaxU 所说,最好让 GridSearchCV 处理拆分,但如果您想强制执行问题中设置的拆分,则可以使用 PredefinedSplit 实现这一点。

因此,您需要对代码进行以下更改。

# Here X_test, y_test is the untouched data
# Validation data (X_val, y_val) is currently inside X_train, which will be split using PredefinedSplit inside GridSearchCV
X_train, X_test = np.array_split(X, [50])
y_train, y_test = np.array_split(y, [50])


# The indices which have the value -1 will be kept in train.
train_indices = np.full((35,), -1, dtype=int)

# The indices which have zero or positive values, will be kept in test
test_indices = np.full((15,), 0, dtype=int)
test_fold = np.append(train_indices, test_indices)

print(test_fold)
# OUTPUT: 
array([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
       -1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0])

from sklearn.model_selection import PredefinedSplit
ps = PredefinedSplit(test_fold)

# Check how many splits will be done, based on test_fold
ps.get_n_splits()
# OUTPUT: 1

for train_index, test_index in ps.split():
    print("TRAIN:", train_index, "TEST:", test_index)

# OUTPUT: 
('TRAIN:', array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
   17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
   34]), 
 'TEST:', array([35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49]))


# And now, send this `ps` to cv param in GridSearchCV
from sklearn.model_selection import GridSearchCV
grid_search = GridSearchCV(Ridge(random_state=444), param_grid, cv=ps)

# Here, send the X_train and y_train
grid_search.fit(X_train, y_train)

传递给fit()的X_train和y_train将使用我们定义的分割方式分成训练集和测试集(在您的情况下为val),因此Ridge将会在原始数据的索引[0:35]上进行训练并在[35:50]上进行测试。

希望这样可以清楚工作流程。


1
只是为了确保我理解正确 --你所说的“具有零或正值的索引将在测试集中保留”,意思是通常被称为验证集,对吗?也就是用来确定最佳参数的“测试”数据集。 - Brad Solomon
1
@BradSolomon 是的。网格搜索将在测试集上评估参数。你实际的测试集从索引50及以上是不变的。 - Vivek Kumar
2
你也可以在这里使用 test_fold = np.repeat([-1, 0], [35, 15]) 来节省几行代码。 - Brad Solomon
1
@Riley 我并没有完全理解这个问题。如果问题允许的话,就不需要拆分数据来保留另一个验证集。例如,有些人已经将数据拆分为训练集和测试集,他们只能使用训练数据进行拟合。在这种情况下,他们可以在网格搜索中使用整个训练数据,该过程将根据折叠来拆分数据。如果他们想要的话,他们可以事先拆分数据以将验证集保留在网格搜索之外。 - Vivek Kumar
1
@user23657 不好意思,GridSearchCV无法完成此任务。 - Vivek Kumar
显示剩余9条评论

6

你尝试过使用TimeSeriesSplit吗?

它是专门为分割时间序列数据而设计的。

tscv = TimeSeriesSplit(n_splits=3)
grid_search = GridSearchCV(clf, param_grid, cv=tscv.split(X))

3
在时间序列数据中,K折交叉验证不是正确的方法,因为K折交叉验证会打乱您的数据,导致您失去系列内的模式。这里提供一种方法。
import xgboost as xgb
from sklearn.model_selection import TimeSeriesSplit, GridSearchCV
import numpy as np
X = np.array([[4, 5, 6, 1, 0, 2], [3.1, 3.5, 1.0, 2.1, 8.3, 1.1]]).T
y = np.array([1, 6, 7, 1, 2, 3])
tscv = TimeSeriesSplit(n_splits=2)

model = xgb.XGBRegressor()
param_search = {'max_depth' : [3, 5]}

my_cv = TimeSeriesSplit(n_splits=2).split(X)
gsearch = GridSearchCV(estimator=model, cv=my_cv,
                        param_grid=param_search)
gsearch.fit(X, y)

参考 - 如何使用TimeSeriesSplit和GridSearchCV对象来调整scikit-learn中的模型?


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