UserWarning:一个或多个测试分数是非有限的- GridSearchCV for MultiOutputClas



我第一次尝试scikit-learn,用于多输出多类文本分类问题。为此,我尝试使用GridSearchCV来优化MLPClassifier的参数。

下面是我到目前为止的代码:
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.multioutput import MultiOutputClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import accuracy_score
df = pd.read_csv('data.csv')
df.fillna('', inplace=True) #Replaces NaNs with "" in the DataFrame (which would be considered a viable choice in this multi-classification model)
x_features = df['input_text']
y_labels = df[['output_text_label_1', 'output_text_label_2']]
x_train, x_test, y_train, y_test = train_test_split(x_features, y_labels, test_size=0.3, random_state=7)

当我尝试在没有任何参数优化的情况下为MultiOutputClassifier(MLPClassifier())设置Pipeline时,pipe.score给出了~0.837的分数,这似乎表明上面的代码正在做一些事情。在一些测试字符串上运行pipe.predict()似乎产生了相对足够的输出结果。

pipe = Pipeline(steps=[('cv', CountVectorizer()),
('mlpc', MultiOutputClassifier(MLPClassifier()))])
pipe.fit(x_train, y_train)
pipe.score(x_test, y_test)

然而,当试图单独使用GridSearchCV使用下面的开始代码从另一个堆栈溢出答案,我遇到了一个问题:

mlpc = MLPClassifier(solver='adam',
learning_rate_init=0.01,
max_iter=300,
activation='relu',
early_stopping=True)
pipe = Pipeline(steps=[('cv', CountVectorizer(ngram_range=(1, 1))),
('scale', StandardScaler(with_mean=False)),
('mlpc', MultiOutputClassifier(mlpc))])
search_space = {
'cv__max_df': (0.9, 0.95, 0.99),
'cv__min_df': (0.01, 0.05, 0.1),
'mlpc__estimator__alpha': 10.0 ** -np.arange(1, 5),
'mlpc__estimator__hidden_layer_sizes': ((64, 32), (128, 64),
(64, 32, 16), (128, 64, 32)),
'mlpc__estimator__tol': (1e-3, 5e-3, 1e-4),
}
grid_search = GridSearchCV(pipe, search_space, scoring='accuracy', error_score='raise', n_jobs=-1)
grid_search.fit(x_train, y_train)

这个警告似乎与评分问题有关,UserWarning: One or more of the test scores are non-finite:

UserWarning: One or more of the test scores are non-finite: [nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan]
warnings.warn(

x_trainx_testy_trainy_test.shape分别为(3036,)(1302,)(3036, 2)(1302, 2)。内容都是字符串,正如您在上面的代码中看到的那样,我已经将NaN的许多出现替换为空字符串"",这应该是模型的一个可行选项(基本上让它选择None作为类):

df.fillna('', inplace=True) #Replaces NaNs with "" in the DataFrame (which would be considered a viable choice in this multi-classification model)

我在GridSearchCV中尝试了其他scoring=方法:roc_auc_ovrf1_macro,本质上是相同的问题。

如果我在GridSearchCV中包含以下选项:error_score='raise',我得到错误:ValueError: multiclass-multioutput is not supported.

ValueError                                Traceback (most recent call last)
<ipython-input-10-14bccd802d09> in <module>
21 #https://stackoverflow.com/questions/31265110/does-gridsearchcv-not-support-multi-class
22 
---> 23 grid_search.fit(x_train, y_train)
~/condaenv/lib/python3.9/site-packages/sklearn/model_selection/_search.py in fit(self, X, y, groups, **fit_params)
889                 return results
890 
--> 891             self._run_search(evaluate_candidates)
892 
893             # multimetric is determined here because in the case of a callable
~/condaenv/lib/python3.9/site-packages/sklearn/model_selection/_search.py in _run_search(self, evaluate_candidates)
1390     def _run_search(self, evaluate_candidates):
1391         """Search all candidates in param_grid"""
-> 1392         evaluate_candidates(ParameterGrid(self.param_grid))
1393 
1394 
~/condaenv/lib/python3.9/site-packages/sklearn/model_selection/_search.py in evaluate_candidates(candidate_params, cv, more_results)
836                     )
837 
--> 838                 out = parallel(
839                     delayed(_fit_and_score)(
840                         clone(base_estimator),
~/condaenv/lib/python3.9/site-packages/joblib/parallel.py in __call__(self, iterable)
1059 
1060             with self._backend.retrieval_context():
-> 1061                 self.retrieve()
1062             # Make sure that we get a last message telling us we are done
1063             elapsed_time = time.time() - self._start_time
~/condaenv/lib/python3.9/site-packages/joblib/parallel.py in retrieve(self)
938             try:
939                 if getattr(self._backend, 'supports_timeout', False):
--> 940                     self._output.extend(job.get(timeout=self.timeout))
941                 else:
942                     self._output.extend(job.get())
~/condaenv/lib/python3.9/site-packages/joblib/_parallel_backends.py in wrap_future_result(future, timeout)
540         AsyncResults.get from multiprocessing."""
541         try:
--> 542             return future.result(timeout=timeout)
543         except CfTimeoutError as e:
544             raise TimeoutError from e
~/condaenv/lib/python3.9/concurrent/futures/_base.py in result(self, timeout)
444                     raise CancelledError()
445                 elif self._state == FINISHED:
--> 446                     return self.__get_result()
447                 else:
448                     raise TimeoutError()
~/condaenv/lib/python3.9/concurrent/futures/_base.py in __get_result(self)
389         if self._exception:
390             try:
--> 391                 raise self._exception
392             finally:
393                 # Break a reference cycle with the exception in self._exception
ValueError: multiclass-multioutput is not supported

在寻找解决方案时,我发现了这个答案,这表明CountVectorizer期望1D输入。我的.shape中有两个功能用于多输出。我不知道这个答案是否正确,因为我是第一次评估scikit-learn。答案建议使用ColumnTransformer来解决这个问题,但我需要一个代码示例来理解如果这确实是问题。

您的y有多个列,而不是您的X,因此CountVectorizer不是问题。相反,问题在于你有两个目标,每个目标都有两个以上的可能结果。根据用户指南,MLPClassifier支持多标签,但不支持多输出multiclass。在同一页面的后面,有一个关于评分的额外警告:

警告:目前,sklearn.metrics中没有指标支持多类多输出分类任务。

所以即使你将分类器包装在例如MultiOutputClassifier中,网格搜索的评分也会失败。我认为你应该能够定义一个自定义的得分者来完成这个工作?您还可以考虑标签是否按照label_1和label_2正确排序,或者是否可以将其转换为多标签问题,对目标进行二值化?

最新更新