pipeline模块是sklearn中一个可以让你链式操作系一列transformer和estimators的单元。当你需要做一系列数据提取、变换、规则化和训练的操作时往往是非常麻烦的。
pipeline就像一个线性的流水线,数据流入后经过每一步加工处理,最终达到我指定的分类器。
但是,有时候tansformer也需要被fit。看一下ModelTransformer.我用它来包裹sklearn的模型,使他的行为像一个transformer。如果你想用kmeans聚类模型为其他模型产生特征,那么这将会非常有用。
第一次参加一些数据竞赛,我会经常以以下的代码模式起手。
_file('data/train.tsv')
train_y = extract_targets(train)
train_essays = extract_essays(train)
train_tokens = get_tokens(train_essays)
train_features = extract_feactures(train)
classifier = MultinomialNB()
scores = []
train_idx, cv_idx in KFold():
classifier.fit(train_features[train_idx], train_y[train_idx])
scores.append(model.score(train_features[cv_idx], train_y[cv_idx]))
print("Score: {}".format(np.mean(scores)))
通常情况下,在第一次提交的时候会得到一个不错的分数,但是为了进一步提高分数,我通常会尝试提取更多的特征。
比如说文本n-gram计数,
我想用tf-idf特征。另外,我想增加文章的长度特征,最好能把拼写错误的字数也做为一个特征。这样的话,我干脆把这些都打包成一个extract_features函数,可以提取一篇文章中的三个特征矩阵,然后将这三个矩阵在数轴1上进行合并。
在提取完了特征以后,我发现我又做了很多normalize和scale的操作,我的代码会变的非常长,虽然我自己仍然能理解。
pipelines
使用pipeline来简化上述流程。下面的例子提取了文档,进行了分词统计,tfidf计算,最后输入了一个分类器。
pipeline = Pipeline([
('extract_essays', EssayExractor()),
('counts', CountVectorizer()),
('tf_idf', TfidfTransformer()),
('classifier', MultinomialNB())
])
train = read_file('data/train.tsv')
train_y = extract_targets(train)
scores = []
train_idx, cv_idx in KFold():
model.fit(train[train_idx], train_y[train_idx])
scores.append(model.score(train[cv_idx], train_y[cv_idx]))
print("Score: {}".format(np.mean(scores)))
pipeline就像一个线性的流水线,数据流入后经过每一步加工处理,最终达到我指定的分类器。
FeatureUnions
正如我之前所说,我想提取更多的特征,那就意味着并行处理进来的数据,最后对结果进行合并。
使用FeatureUnion可以对这些并行流程进行建模。
pipeline = Pipeline([
('extract_essays', EssayExractor()),
('features', FeatureUnion([
('ngram_tf_idf', Pipeline([
('counts', CountVectorizer()),
('tf_idf', TfidfTransformer())
])),
('essay_length', LengthTransformer()),
('misspellings', MispellingCountTransformer())
])),
('classifier', MultinomialNB())
])
在这个例子里,文章提取后进入了三个并行的处理流程:ngram_tf_idf、essay_length、misspellings,然后将三个合并起来输入到分类器。
通常情况下,我会用好几个pipeplines和featureunions来建立一个模型。例如下面就是一个很庞大的流水线
pipeline = Pipeline([
('features', FeatureUnion([
('continuous', Pipeline([
('extract', ColumnExtractor(CONTINUOUS_FIELDS)),
('scale', Normalizer())
])),
('factors', Pipeline([
('extract', ColumnExtractor(FACTOR_FIELDS)),
('one_hot', OneHotEncoder(n_values=5)),
('to_dense', DenseTransformer())
])),
('weekday', Pipeline([
('extract', DayOfWeekTransformer()),
('one_hot', OneHotEncoder()),
('to_dense', DenseTransformer())
])),
('hour_of_day', HourOfDayTransformer()),
('month', Pipeline([
('extract', ColumnExtractor(['datetime'])),
('to_month', DateTransformer()),
('one_hot', OneHotEncoder()),
('to_dense', DenseTransformer())
])),
('growth', Pipeline([
('datetime', ColumnExtractor(['datetime'])),
('to_numeric', MatrixConversion(int)),
('regression', ModelTransformer(LinearRegression()))
]))
])),
('estimators', FeatureUnion([
('knn', ModelTransformer(KNeighborsRegressor(n_neighbors=5))),
('gbr', ModelTransformer(GradientBoostingRegressor())),
('dtr', ModelTransformer(DecisionTreeRegressor())),
('etr', ModelTransformer(ExtraTreesRegressor())),
('rfr', ModelTransformer(RandomForestRegressor())),
('par', ModelTransformer(PassiveAggressiveRegressor())),
('en', ModelTransformer(ElasticNet())),
('cluster', ModelTransformer(KMeans(n_clusters=2)))
])),
('estimator', KNeighborsRegressor())
])
定制化的Transformers
在前面的例子中包含了很多tansformer并不是来自sklearn。所有的ColumnExtractor、DenseTransformer和ModelTransformer都是我自己写的。
一个Transformer就是一个包含fit、transform和fit_transform的object,包括内建的transformer(如MinMaxScaler),Pipelines,FeatureUnions。
当然,实现了上述方法的老python对象也是一种transformer。并不一定需要继承自TransformerMixin,但是那样比较方便。
一个transformer可以想象成是一个数据进入、数据输出的黑盒子。他会接受一个矩阵作为输入,然后返回一个相同尺寸的矩阵作为输出。这样就可以方便的记录和二次处理。但是,我通常使用pandas的DataFrames,希望输入一个dataframe到tansformer里来。例如,ColumnExtractor是一个从dataframe提取column的transformer.
有时tansformer可以非常简单,比如HourOfDay transformer,仅仅从datatime对象中提取出了小时的部分,这种事无状态的,他们不需要被fit,所以fit都是空操作。
class HourOfDayTransformer(TransformerMixin):
def transform(self, X, **transform_params):
hours = DataFrame(X['datetime'].apply(lambda x: x.hour))
return hours
def fit(self, X, y=None, **fit_params):
return self
class ModelTransformer(TransformerMixin):
def __init__(self, model):
self.model = model
def fit(self, *args, **kwargs):
self.model.fit(*args, **kwargs)
return self
def transform(self, X, **transform_params):
return DataFrame(self.model.predict(X))
pipeline会像内建的transformer一样对待这些自己写的transformer。
利用pipeline和featureunion,可以帮助对机器学习的建模流程进行统一化处理。当然目前的pipeline还有很多不足,比如没有办法对指定列进行处理,这在大型工程中是很伤的。期待pipeline会推出更强大的功能。