十五、估计器流水线

优质
小牛编辑
129浏览
2023-12-01

在本节中,我们将研究如何链接不同的估计器。

简单示例:估计器之前的特征提取和选择

特征提取:向量化器

对于某些类型的数据,例如文本数据,必须应用特征提取步骤将其转换为数值特征。 为了说明,我们加载我们之前使用的 SMS 垃圾邮件数据集。

import os

with open(os.path.join("datasets", "smsspam", "SMSSpamCollection")) as f:
    lines = [line.strip().split("\t") for line in f.readlines()]
text = [x[1] for x in lines]
y = [x[0] == "ham" for x in lines]

from sklearn.model_selection import train_test_split

text_train, text_test, y_train, y_test = train_test_split(text, y)

以前,我们手动应用了特征提取,如下所示:

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression

vectorizer = TfidfVectorizer()
vectorizer.fit(text_train)

X_train = vectorizer.transform(text_train)
X_test = vectorizer.transform(text_test)

clf = LogisticRegression()
clf.fit(X_train, y_train)

clf.score(X_test, y_test)

我们学习转换然后将其应用于测试数据的情况,在机器学习中非常常见。 因此 scikit-learn 有一个快捷方式,称为流水线:

from sklearn.pipeline import make_pipeline

pipeline = make_pipeline(TfidfVectorizer(), LogisticRegression())
pipeline.fit(text_train, y_train)
pipeline.score(text_test, y_test)

如你所见,这使代码更短,更容易处理。 在背后,与上面完全相同。 当在水流上调用fit时,它将依次调用每个步骤的fit

在第一步的fit之后,它将使用第一步的transform方法来创建新的表示。 然后将其用于下一步的fit,依此类推。 最后,在最后一步,只调用fit

如果我们调用score,那么每一步都只会调用transform - 毕竟这可能是测试集! 然后,在最后一步,使用新的表示调用scorepredict也是如此。

流水线的构建不仅简化了代码,而且对于模型选择也很重要。 假设我们想要网格搜索C来调整上面的 Logistic 回归。

让我们假设我们这样做:

# This illustrates a common mistake. Don't use this code!
from sklearn.model_selection import GridSearchCV

vectorizer = TfidfVectorizer()
vectorizer.fit(text_train)

X_train = vectorizer.transform(text_train)
X_test = vectorizer.transform(text_test)

clf = LogisticRegression()
grid = GridSearchCV(clf, param_grid={'C': [.1, 1, 10, 100]}, cv=5)
grid.fit(X_train, y_train)

我们哪里做错了?

在这里,我们使用X_train上的交叉验证进行了网格搜索。 然而,当应用TfidfVectorizer时,它看到了所有的X_train,而不仅仅是训练折叠! 因此,它可以使用测试折叠中单词频率的知识。 这被称为测试集的“污染”,并且使泛化性能或错误选择的参数的估计过于乐观。 我们可以通过流水线解决这个问题:

from sklearn.model_selection import GridSearchCV

pipeline = make_pipeline(TfidfVectorizer(), 
                         LogisticRegression())

grid = GridSearchCV(pipeline,
                    param_grid={'logisticregression__C': [.1, 1, 10, 100]}, cv=5)

grid.fit(text_train, y_train)
grid.score(text_test, y_test)

请注意,我们需要告诉流水线我们要在哪一步设置参数C。我们可以使用特殊的__语法来完成此操作。 __之前的名称只是类的名称,__之后的部分是我们想要使用网格搜索设置的参数。

使用流水线的另一个好处是,我们现在还可以使用GridSearchCV搜索特征提取的参数:

from sklearn.model_selection import GridSearchCV

pipeline = make_pipeline(TfidfVectorizer(), LogisticRegression())

params = {'logisticregression__C': [.1, 1, 10, 100],
          "tfidfvectorizer__ngram_range": [(1, 1), (1, 2), (2, 2)]}
grid = GridSearchCV(pipeline, param_grid=params, cv=5)
grid.fit(text_train, y_train)
print(grid.best_params_)
grid.score(text_test, y_test)

练习

使用StandardScalerRidgeRegression创建流水线,并将其应用于波士顿住房数据集(使用sklearn.datasets.load_boston加载)。 尝试添加sklearn.preprocessing.PolynomialFeatures变换器作为第二个预处理步骤,并网格搜索多项式的次数(尝试 1,2 和 3)。

# %load solutions/15A_ridge_grid.py