我不知道sklearn.pipeline.Pipeline
是如何工作的。
文档中有一些解释。例如:
是什么意思?带最终估计量的变换管道。
为了使我的问题更清楚,steps
是什么?它们是如何工作的?
编辑
有了这些答案,我的问题就更清楚了:
当我调用pipeline并作为步骤传递两个变压器和一个估计器时,例如:
pipln = Pipeline([("trsfm1",transformer_1),
("trsfm2",transformer_2),
("estmtr",estimator)])
当我调用这个时会发生什么?
pipln.fit()
OR
pipln.fit_transform()
我不明白一个估计器怎么能成为变压器,变压器怎么能被安装。
变压器在scikit-learn - some类中有fit和transform方法,或者fit_transform方法。
预测-一些类有fit和predict方法,或者fit_predict方法。
管道只是一个抽象的概念,它不是一些现有的ml算法。通常在ML任务中,你需要在应用最终估计器之前对原始数据集执行一系列不同的转换(找到一组特征,生成新特征,只选择一些好的特征)。
这是一个使用Pipeline的好例子。管道为转换的所有3个步骤和结果估计器提供了一个单一的接口。它在内部封装了转换器和预测器,现在您可以做如下操作:
vect = CountVectorizer()
tfidf = TfidfTransformer()
clf = SGDClassifier()
vX = vect.fit_transform(Xtrain)
tfidfX = tfidf.fit_transform(vX)
predicted = clf.fit_predict(tfidfX)
# Now evaluate all steps on test set
vX = vect.fit_transform(Xtest)
tfidfX = tfidf.fit_transform(vX)
predicted = clf.fit_predict(tfidfX)
只有:
pipeline = Pipeline([
('vect', CountVectorizer()),
('tfidf', TfidfTransformer()),
('clf', SGDClassifier()),
])
predicted = pipeline.fit(Xtrain).predict(Xtrain)
# Now evaluate all steps on test set
predicted = pipeline.predict(Xtest)
使用管道,您可以轻松地对该元估计器的每个步骤的一组参数执行网格搜索。如上面的链接所述。除最后一步外的所有步骤都必须是转换,最后一步可以是变压器或预测器。编辑的答案:当你调用pipln.fit()
-管道内的每个变压器将安装在前一个变压器的输出上(第一个变压器是在原始数据集上学习的)。最后一个估计器可能是transformer或predictor,只有当您的最后一个估计器是transformer(它分别实现了fit_transform或transform和fit方法)时,您才能在pipeline上调用fit_transform(),只有当您的最后一个估计器是predictor时,您才能在pipeline上调用fit_predict()或predict()。所以你不能在管道上调用fit_transform或者transform,最后一步是predictor。
我认为M0rkHaV的想法是正确的。Scikit-learn的管道类是一个有用的工具,可以将多个不同的变压器与估算器一起封装到一个对象中,这样您只需要调用一次重要的方法(fit()
,predict()
等)。让我们分解两个主要组件:
-
变形金刚是实现
fit()
和transform()
的类。您可能熟悉一些sklearn预处理工具,如TfidfVectorizer
和Binarizer
。如果您查看这些预处理工具的文档,就会发现它们实现了这两种方法。我发现很酷的是一些估计器也可以用作转换步骤,例如LinearSVC
! -
估计是实现
fit()
和predict()
的类。您会发现许多分类器和回归模型都实现了这两种方法,因此您可以很容易地测试许多不同的模型。可以使用另一个变压器作为最终的估计器(即,它不一定实现predict()
,但肯定实现fit()
)。这意味着您将无法调用predict()
。
至于你的编辑:让我们看一个基于文本的例子。使用LabelBinarizer,我们希望将标签列表转换为二进制值列表。
bin = LabelBinarizer() #first we initialize
vec = ['cat', 'dog', 'dog', 'dog'] #we have our label list we want binarized
现在,当二进制化器在一些数据上进行拟合时,它将具有一个名为classes_
的结构,其中包含转换器"知道"的唯一类。如果不调用fit()
,二进制程序就不知道数据是什么样子,所以调用transform()
就没有任何意义。如果您在尝试拟合数据之前打印出类列表,则会出现这种情况。
print bin.classes_
当我尝试这个时,我得到以下错误:
AttributeError: 'LabelBinarizer' object has no attribute 'classes_'
但是当你把二进制化器放到vec
列表上时:
bin.fit(vec)
并重试
print bin.classes_
得到如下结果:
['cat' 'dog']
print bin.transform(vec)
现在,在vec
对象上调用transform之后,我们得到以下结果:
[[0]
[1]
[1]
[1]]
对于被用作变压器的估计器,让我们使用DecisionTree
分类器作为特征提取器的示例。决策树的优点有很多,但就我们的目的而言,重要的是它们有能力对树中的特征进行排序。发现对预测很有用。当你在决策树上调用transform()
时,它将获取你的输入数据并找到思考是最重要的特征。所以你可以想象它将你的数据矩阵(n行乘m列)转换成一个更小的矩阵(n行乘k列),其中k列是决策树发现的k个最重要的特征。
ML算法通常处理表格数据。您可能希望在ML算法之前和之后对这些数据进行预处理和后处理。管道是连接这些数据处理步骤的一种方法。
什么是ML管道,它们是如何工作的?
管道是转换数据的一系列步骤。它来自古老的"管道和过滤器"。设计模式(例如,您可以考虑使用管道"|"或重定向操作符">"的Unix bash命令)。然而,管道是代码中的对象。因此,您可能对每个过滤器(也就是每个管道步骤)都有一个类,然后另一个类将这些步骤组合到最终的管道中。有些管道可以串联或并联其他管道,具有多个输入或输出,等等。我们喜欢把流水线机器学习看作:
- 管道和过滤器。管道的步骤处理数据,并管理可以从数据中学习的内部状态。
- 复合材料。管道可以嵌套:例如,整个管道可以被视为另一个管道中的单个管道步骤。管道步骤不一定是管道,但管道本身至少是定义上的管道步骤。
- 有向无环图(DAG)。流水线步骤的输出可以发送到许多其他步骤,然后可以重新组合结果输出,依此类推。附注:尽管管道是无循环的,但它们可以一个接一个地处理多个项目,如果它们的状态发生变化(例如:每次使用fit_transform方法),那么它们可以被视为随时间循环展开,保持它们的状态(像RNN一样)。这是一种有趣的方式,可以看到管道在将其投入生产并对其进行更多数据的培训时进行在线学习。
Scikit-Learn管道的方法
管道(或管道中的步骤)必须具有这两个方法:
- "fit"对数据进行学习并获取状态(例如:神经网络的神经权值就是这样的状态)
- "transform"(或"predict")来实际处理数据并生成预测。
也可以调用这个方法来链接两者:
- " fit_transform "适合然后转换数据,但在一次传递中,当两个方法必须一个接一个直接完成时,这允许潜在的代码优化。
sklearn.pipeline.Pipeline类的问题Scikit-Learn的"管道和过滤器"设计模式非常漂亮。但是如何将它用于深度学习、自动化和复杂的生产级管道呢?
Scikit-Learn的"管道和过滤器"设计模式非常漂亮。但是如何将它用于深度学习、自动化和复杂的生产级管道呢?
Scikit-Learn在2007年发布了第一个版本,这是一个前深度学习时代。然而,它是最知名和采用的机器学习库之一,并且仍在增长。最重要的是,它使用Pipe和Filter设计模式作为一种软件架构风格——这就是Scikit-Learn如此出色的原因,再加上它提供了随时可用的算法。然而,在做以下事情时,它有很多问题,我们应该在2020年就能做到:
- 自动机器学习(AutoML),
- 深度学习管道,
- 更复杂的机器学习管道。
我们找到的解决Scikit-Learn问题的方法
当然,Scikit-Learn是非常方便和完善的。然而,它需要刷新。以下是我们与Neuraxle的解决方案,使Scikit-Learn在现代计算项目中新鲜和可用!
- 无法合理地进行自动机器学习(AutoML)
- 问题:定义搜索空间(超参数分布) 问题:在构造函数中定义超参数限制了
- 问题:训练和测试行为不同
- 问题:你训练了一个管道,你想要它的学习反馈。
- 问题:Scikit-Learn几乎不允许小批量梯度下降(增量拟合)
- 问题:初始化管道并释放资源
- 问题:在Scikit-Learn 中难以使用其他深度学习库
- 问题:能够转换输出标签
- 问题:在您的管道中处理3D, 4D或ND数据,并为低维数据制作步骤
- 问题:修改管道沿途,如预训练或微调
- 问题:从Scikit-Learn管道获取模型属性
- 问题:使用不能"按原样"序列化的步骤,您无法并行化或保存管道由Joblib
通过Neuraxle提供的其他管道方法和功能
注意:如果管道的某个步骤不需要fit或transform方法之一,它可以从NonFittableMixin或NonTransformableMixin继承,以提供其中一个方法的默认实现,而不做任何事情。
作为初学者,管道或其步骤也可以可选地定义这些方法:
- " setup ",它将在每一步调用" setup "方法。例如,如果一个步骤包含TensorFlow, PyTorch或Keras神经网络,这些步骤可以创建它们的神经图,并在拟合之前通过"setup"方法将它们注册到GPU。不鼓励在步骤的构造函数中直接创建图形,原因有几个,例如如果在自动机器学习算法中使用不同的超参数多次运行之前复制步骤,该算法会为您搜索最佳超参数。
- " teardown ",与" setup "方法相反:它清除资源。
下列方法是默认提供的允许管理超参数:
- " get_hyperparams "将返回一个超参数字典。如果你的管道包含更多的管道(嵌套管道),那么超参数的键用双下划线"__"分隔符链接。
- " set_hyperparams "将允许你以与获得它们时相同的格式设置新的超参数。
- " get_hyperparams_space "允许您获取超参数的空间,如果您定义了一个,该空间将不为空。因此,这里"get_hyperparams"的唯一区别是,您将获得作为值的统计分布,而不是精确值。例如,层数的一个超参数可以是
RandInt(1, 3)
,表示1到3层。你可以在这个字典上调用.rvs()
来随机选择一个值并将其发送给"set_hyperparams"来尝试对其进行训练。 - " set_hyperparams_space "可以用来设置一个新的空间,使用与" get_hyperparams_space "相同的超参数分布类。
有关我们建议的解决方案的更多信息,请阅读上面链接的大列表中的条目。
from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline, FeatureUnion
from sklearn.preprocessing import StandardScaler
from sklearn import metrics
import pandas as pd
class TextTransformer(BaseEstimator, TransformerMixin):
"""
Преобразование текстовых признаков
"""
def __init__(self, key):
self.key = key
def fit(self, X, y=None, *parg, **kwarg):
return self
def transform(self, X):
return X[self.key]
class NumberTransformer(BaseEstimator, TransformerMixin):
"""
Преобразование числовых признаков
"""
def __init__(self, key):
self.key = key
def fit(self, X, y=None):
return self
def transform(self, X):
return X[[self.key]]
def fit_predict(model, X_train, X_test, y_train, y_test):
vec_tdidf = TfidfVectorizer(ngram_range=(2,2), analyzer='word', norm='l2')
text = Pipeline([
('transformer', TextTransformer(key='clear_messages')),
('vectorizer', vec_tdidf)
])
word_numeric = Pipeline([
('transformer', NumberTransformer(key='word_count')),
('scalar', StandardScaler())
])
word_class = Pipeline([
('transformer', NumberTransformer(key='preds')),
('scalar', StandardScaler())
])
# Объединение всех признаков
features = FeatureUnion([('Text_Feature', text),
('Num1_Feature', word_numeric),
('Num2_Feature', word_class)
])
# Классификатор
clf = model
# Объединение классификатора и признаков
pipe = Pipeline([('features', features),
('clf',clf)
])
# Обучение модели
pipe_fit=pipe.fit(X_train, y_train)
# Предсказание данных
preds = pipe_fit.predict(X_test)
return preds, pipe_fit