我在使用GridSearchCV进行模型选择和超参数调优。从初步实验来看,使用rbf核的SVC表现最佳。但问题是它的速度太慢(样本量20万+)。使用OneVsRestClassifier可以并行化SVC(通过n_jobs参数)。然而,当我使用Pipeline同时测试多个估计器时,GridSearchCV对这种嵌套的估计器不起作用。
pipe = Pipeline([('clf', SVC())]) # 占位估计器# 候选学习算法及其超参数search_space = [{'clf': [OneVsRestClassifier(SVC(tol=0.1, gamma='scale', probability=True), n_jobs=-1)], 'clf__kernel': ['rbf', 'linear'], 'clf__C': [1, 10, 100]}, {'clf': [LogisticRegression(tol=0.1, penalty='l1', solver='saga', multi_class='multinomial', n_jobs=8)], 'clf__C': [1, 10, 100]}, {'clf': [RandomForestClassifier(n_jobs=8)], 'clf__n_estimators': [50, 100, 200, 300, 400], 'clf__max_depth': [10, 20, 30], 'clf__min_samples_leaf': [1, 2, 4], 'clf__min_samples_split': [2, 5, 10]}, {'clf': [MultinomialNB()], 'clf__alpha': [0.1, 0.5, 1]}]gs = GridSearchCV(pipe, search_space, cv=skf, scoring='accuracy', verbose=10)
我遇到了错误
Invalid Parameter __kernel
但根据GridSearch for an estimator inside a OneVsRestClassifier,这种方法应该可行。我认为是Pipeline搞乱了,因为它基本上在OneVsRestClassifier之上又加了一层。如何对这种嵌套的估计器进行gridsearchcv呢?
回答:
目前,Pipeline在OneVsRestClassifier
中寻找参数kernel
,找不到(不出所料,因为该模块没有此参数),并引发错误。由于您实际上想要的是SVC
的参数kernel
(以及后续的C
),您应该深入一层:将search_space
的前三个条目更改为:
{'clf': [OneVsRestClassifier(SVC(tol=0.1, gamma='scale', probability=True), n_jobs=-1], 'clf__estimator__kernel': ['rbf', 'linear'], 'clf__estimator__C': [1, 10, 100]}
这样应该就没问题了。
然而,无论错误如何,您使用这种方法的理由:
问题在于它速度太慢(样本量20万+)。使用OneVsRestClassifier可以并行化SVC(通过n_jobs参数)。
是不正确的。OneVsRestClassifier
会并行化拟合n_classes
个不同的SVC
估计器,而不是SVC
本身。实际上,您试图通过包装其他东西(这里是OneVsRestClassifier
)来避免一个瓶颈(SVC
),结果却发现自己又遇到了瓶颈(不出所料)。
我们可以通过一些虚拟数据的时间测量来证明这一点——让我们尝试一个相对现实的数据集,1万个样本,5个特征,3个类别:
from sklearn.multiclass import OneVsRestClassifierfrom sklearn.svm import SVCfrom sklearn.datasets import make_classificationX, y = make_classification(n_samples = 10000, n_features=5, n_redundant=0, n_informative=5, n_classes = 3, n_clusters_per_class=1, random_state=42)%timeit for x in range(10): SVC().fit(X,y)# 1 loop, best of 3: 7.72 s per loop%timeit for x in range(10): OneVsRestClassifier(SVC()).fit(X, y)# 1 loop, best of 3: 21.1 s per loop
这是您的基准差异;现在设置n_jobs=-1
会有所帮助:
%timeit for x in range(10): OneVsRestClassifier(SVC(), n_jobs=-1).fit(X, y)# 1 loop, best of 3: 19 s per loop
但不出所料,这只是相对于未并行化的OneVsRestClassifier
而言,而不是相对于SVC
本身。
随着特征和类别的增加,差异会变得更糟;在不达到您的完整案例的情况下,这里是10个特征和5个类别的情况(样本数相同,1万):
X1, y1 = make_classification(n_samples = 10000, n_features=10, n_redundant=0, n_informative=10, n_classes = 5, n_clusters_per_class=1, random_state=42)%timeit for x in range(10): SVC().fit(X1,y1)# 1 loop, best of 3: 10.3 s per loop%timeit for x in range(10): OneVsRestClassifier(SVC()).fit(X1, y1)# 1 loop, best of 3: 30.7 s per loop%timeit for x in range(10): OneVsRestClassifier(SVC(), n_jobs=-1).fit(X1, y1)# 1 loop, best of 3: 24.9 s per loop
因此,我强烈建议您重新考虑这里的方法(和目标)。
(所有时间测量都在Google Colab上进行)。