You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 03:50:47