如何在Scikit-learn中调优含fit/predict额外参数的自定义分类器
问题描述
为复现问题,创建了以下虚拟数据集:
import numpy as np import pandas as pd cities = ['Berlin', 'Frankfurt', 'Hamburg', 'Nuremberg', 'Munich', 'Stuttgart', 'Hanover', 'Saarbruecken', 'Cologne', 'Constance', 'Freiburg', 'Karlsruhe' ] n= len(cities) data = pd.DataFrame({ 'City':cities, 'Temperature': np.random.normal(24, 3, n), 'Humidity': np.random.normal(78, 2.5, n), 'Wind': np.random.normal(15, 4, n), 'Target': np.random.randint(2, size=n) })
编写了一个自定义分类器,先将文本特征映射为连续值,再与其他特征结合分类:
from sklearn.base import BaseEstimator, ClassifierMixin from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.ensemble import GradientBoostingClassifier class CustomClassifier(ClassifierMixin, BaseEstimator): def __init__(self, n_estimators=100): self.n_estimators=100 self.NLP = Pipeline( [ ('preprocessor', TfidfVectorizer()), ('regressor', LogisticRegression()) ] ) self.Classifier = GradientBoostingClassifier(n_estimators=self.n_estimators) def fit(self, X, y, text_data, **kwargs): self.NLP.fit(text_data, y) text_feature = self.NLP.predict_proba(text_data) new_X = np.concatenate( (text_feature[:,1, np.newaxis], X), axis=1 ) self.Classifier.fit(new_X, y) return self def predict(self, X, text_data): text_feature = self.NLP.predict_proba(text_data) new_X = np.concatenate( (text_feature[:,1, np.newaxis],X), axis=1 ) y_pred = self.Classifier.predict(new_X) return y_pred
单独调用fit和predict方法正常,但尝试用交叉验证调优管道时:
from sklearn.pipeline import Pipeline from sklearn.preprocessing import MaxAbsScaler from sklearn.model_selection import RandomizedSearchCV custom_model = CustomClassifier() pipe = Pipeline([ ('scaling', MaxAbsScaler()), ('classifier', custom_model) ]) params = {'classifier__n_estimators':[100,200]} tuner = RandomizedSearchCV( pipe, param_distributions=params, cv=3, n_iter=2 ) tuner.fit(X=data.drop(['City', 'Target'], axis=1), y=data.loc[:,'Target'], classifier__text_data=data.loc[:,'City'])
出现错误:
predict() missing 1 required positional argument: 'text_data'
无法使用cross_val_score等工具,尝试自定义RandomizedSearchCV子类扩展predict方法未解决,询问如何调优该自定义管道。
解决方案
错误根源是自定义分类器不符合Scikit-learn的API规范:predict方法仅支持接收X和可选的**kwargs参数,不能额外要求text_data。交叉验证工具(如RandomizedSearchCV)执行时会自动调用predict(X),无法传递额外参数,因此触发报错。
要解决这个问题,需调整分类器设计,让它能从单一的X输入中同时获取数值特征和文本特征,具体步骤如下:
1. 修改自定义分类器代码
让分类器从输入的X中指定列提取文本数据,确保fit和predict符合sklearn标准API:
from sklearn.base import BaseEstimator, ClassifierMixin from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.ensemble import GradientBoostingClassifier from sklearn.pipeline import Pipeline import numpy as np class CustomClassifier(ClassifierMixin, BaseEstimator): def __init__(self, n_estimators=100, text_col='City'): self.n_estimators = n_estimators self.text_col = text_col # 指定存储文本的列名 self.NLP = Pipeline( [ ('preprocessor', TfidfVectorizer()), ('regressor', LogisticRegression()) ] ) self.Classifier = GradientBoostingClassifier(n_estimators=self.n_estimators) def fit(self, X, y, **kwargs): # 从X中提取文本数据与数值特征 text_data = X[self.text_col].values num_features = X.drop(self.text_col, axis=1).values self.NLP.fit(text_data, y) text_feature = self.NLP.predict_proba(text_data) new_X = np.concatenate( (text_feature[:,1, np.newaxis], num_features), axis=1 ) self.Classifier.fit(new_X, y) return self def predict(self, X): # 从X中提取文本数据与数值特征 text_data = X[self.text_col].values num_features = X.drop(self.text_col, axis=1).values text_feature = self.NLP.predict_proba(text_data) new_X = np.concatenate( (text_feature[:,1, np.newaxis], num_features), axis=1 ) y_pred = self.Classifier.predict(new_X) return y_pred
2. 调整管道与交叉验证代码
使用ColumnTransformer实现差异化预处理:仅对数值特征做缩放,文本特征直接保留传递给分类器。
from sklearn.pipeline import Pipeline from sklearn.preprocessing import MaxAbsScaler from sklearn.model_selection import RandomizedSearchCV from sklearn.compose import ColumnTransformer # 定义数值特征列与文本列 num_cols = ['Temperature', 'Humidity', 'Wind'] text_col = 'City' # 预处理管道:仅缩放数值特征,保留文本列 preprocessor = ColumnTransformer( transformers=[ ('num', MaxAbsScaler(), num_cols) ], remainder='passthrough' ) custom_model = CustomClassifier(text_col=text_col) pipe = Pipeline([ ('preprocessing', preprocessor), ('classifier', custom_model) ]) params = {'classifier__n_estimators':[100,200]} tuner = RandomizedSearchCV( pipe, param_distributions=params, cv=3, n_iter=2 ) # 传递包含文本列的完整数据集(排除Target) tuner.fit(X=data.drop('Target', axis=1), y=data['Target'])
核心要点
- 让自定义分类器严格遵循sklearn的API规范,避免
predict方法要求额外参数。 - 使用
ColumnTransformer实现多类型特征的差异化预处理,确保数值特征被正确缩放,文本特征不受影响。 - 调整后,交叉验证工具可正常调用
predict方法,不会再出现参数缺失的错误。
内容的提问来源于stack exchange,提问作者v44k3
相关产品推荐
相关产品推荐

