将 Keras 集成到 SKLearn 管道?

Posted

技术标签:

【中文标题】将 Keras 集成到 SKLearn 管道?【英文标题】:Integrate Keras to SKLearn Pipeline? 【发布时间】:2019-09-21 04:43:48 【问题描述】:

我有一个 sklearn 管道对异构数据类型(布尔、分类、数字、文本)执行特征工程,并想尝试使用神经网络作为我的学习算法来拟合模型。我在输入数据的形状方面遇到了一些问题。

我想知道我正在尝试做的事情是否可能,或者我是否应该尝试不同的方法?

我尝试了几种不同的方法,但收到以下错误:

    Error when checking input: expected dense_22_input to have shape (11,) but got array with shape (30513,) => 我有 11 个输入特征...所以我尝试将我的 X 和 y 转换为数组,现在出现此错误

    ValueError: Specifying the columns using strings is only supported for pandas DataFrames => 我认为这是因为 ColumnTransformer() 我在其中指定列名

print(X_train_OS.shape)
print(y_train_OS.shape)

(22354, 11)
(22354,)
from keras.models import Sequential
from keras.layers import Dense
from keras.wrappers.scikit_learn import KerasClassifier
from keras.utils import to_categorical # OHE

X_train_predictors = df_train_OS.drop("label", axis=1)
X_train_predictors = X_train_predictors.values
y_train_target = to_categorical(df_train_OS["label"])

y_test_predictors = test_set.drop("label", axis=1)
y_test_predictors = y_test_predictors.values
y_test_target = to_categorical(test_set["label"])

print(X_train_predictors.shape)
print(y_train_target.shape)

(22354, 11)
(22354, 2)
def keras_classifier_wrapper():
    clf = Sequential()
    clf.add(Dense(32, input_dim=11, activation='relu'))
    clf.add(Dense(2, activation='softmax'))
    clf.compile(loss='categorical_crossentropy', optimizer='adam', metrics=["accuracy"])
    return clf

TOKENS_ALPHANUMERIC_HYPHEN = "[A-Za-z0-9\-]+(?=\\s+)"

boolTransformer = Pipeline(steps=[
    ('bool', PandasDataFrameSelector(BOOL_FEATURES))])

catTransformer = Pipeline(steps=[
    ('cat_imputer', SimpleImputer(strategy='constant', fill_value='missing')),
    ('cat_ohe', OneHotEncoder(handle_unknown='ignore'))])

numTransformer = Pipeline(steps=[
    ('num_imputer', SimpleImputer(strategy='constant', fill_value=0)),
    ('num_scaler', StandardScaler())])

textTransformer_0 = Pipeline(steps=[
    ('text_bow', CountVectorizer(lowercase=True,\
                                 token_pattern=TOKENS_ALPHANUMERIC_HYPHEN,\
                                 stop_words=stopwords))])

textTransformer_1 = Pipeline(steps=[
    ('text_bow', CountVectorizer(lowercase=True,\
                                 token_pattern=TOKENS_ALPHANUMERIC_HYPHEN,\
                                 stop_words=stopwords))])

FE = ColumnTransformer(
    transformers=[
        ('bool', boolTransformer, BOOL_FEATURES),
        ('cat', catTransformer, CAT_FEATURES),
        ('num', numTransformer, NUM_FEATURES),
        ('text0', textTransformer_0, TEXT_FEATURES[0]),
        ('text1', textTransformer_1, TEXT_FEATURES[1])])

clf = KerasClassifier(keras_classifier_wrapper, epochs=100, batch_size=500, verbose=0)

PL = Pipeline(steps=[('feature_engineer', FE),
                     ('keras_clf', clf)])

PL.fit(X_train_predictors, y_train_target)
#PL.fit(X_train_OS, y_train_OS)

我想我理解这里的问题,但不知道如何解决它。如果无法将 sklearn ColumnTransformer+Pipeline 集成到 Keras 模型中,Keras 是否有处理固定数据类型给特征工程师的好方法?谢谢!

【问题讨论】:

问题现在解决了吗? 【参考方案1】:

我认为使用 Sklearn Pipelines 和 Keras sklearnWrappers 是处理您的问题的标准方法,ColumnDataTransformer 允许您以不同方式管理每个功能(无论是布尔值、数字还是分类),

要调试您的代码, 我建议对管道的每个步骤进行单元测试,尤其是 textTransformer_0 和 textTransformer_1

例如

textTransformer_0.fit_transform(X_train_predictors).shape # shape[1]
textTransformer_1.fit_transform(X_train_predictors).shape # shape[1]

所以一对一的热门编码器,以了解您的最终特征维度是什么。

因为 Sklearn Pipelines 的标准是处理 2D np.ndarray, 所以CountVectorizer 会根据数据创建一堆列, 并且这个值必须在keras.Dense层中引入input_dim

【讨论】:

【参考方案2】:

看起来您正在通过各种列转换器传递 11 列原始数据,并且维度的数量正在扩展到 30,513(在对文本进行计数矢量化、一种热编码等之后)。您的神经网络架构设置为仅接受 11 个输入特征,但正在传递您的(现在已转换的)30,513 个特征,这就是错误 1 ​​所解释的内容。

因此,您需要修改神经网络的 input_dim 以匹配特征提取管道中正在创建的特征数量。

您可以做的一件事是在它们之间添加一个中间步骤,例如 SelectKBest 并将其设置为 20,000 之类的值,以便您确切知道最终将有多少特征传递给分类器。

这是 Google 机器学习网站上的一个很好的指南和流程图 - link - look at the flow chart - 在这里您可以看到他们在训练模型之前有一个“选择前 k 个特征”步骤。

因此,请尝试将代码的这些部分更新为:

def keras_classifier_wrapper():
    clf = Sequential()
    clf.add(Dense(32, input_dim=20000, activation='relu'))
    clf.add(Dense(2, activation='softmax'))
    clf.compile(loss='categorical_crossentropy', optimizer='adam', metrics=["accuracy"])
    return clf

from sklearn.feature_selection import SelectKBest
select_best_features = SelectKBest(k=20000)

PL = Pipeline(steps=[('feature_engineer', FE),
                     ('select_k_best', select_best_features),
                     ('keras_clf', clf)])

【讨论】:

谢谢!这很有意义(不敢相信我错过了)......但是试图将input_dim映射到SelectKBest(k='all)......有什么想法吗?

以上是关于将 Keras 集成到 SKLearn 管道?的主要内容,如果未能解决你的问题,请参考以下文章

Keras Sklearn Tuner 模块“sklearn”没有属性“管道”

sklearn 管道 + keras 顺序模型 - 如何获取历史记录?

如何使用 sklearn 管道缩放 Keras 自动编码器模型的目标值?

如何使用 mlflow.pyfunc.log_model() 通过 Keras 步骤记录 sklearn 管道?类型错误:无法腌制 _thread.RLock 对象

keras开发成sklearn接口

管道中的 sklearn 函数转换器