8.1 処理フローのコンポネント化22

さらに, 取り込んだデータの値を変換する前処理にはいろいろな種類がありますが, そのうちどれが有効なのかはデータだけでなく使用する学習器に依存します. 前処理のクラスもまた, 変換器 (transformer) と呼ばれるクラスとして定義され, 一貫性を持ちます. これらの部品を接続するのが Pipeline です23. 例えば, 数値特徴量を標準化する StandardScaler と主成分分析によって主成分に変換する PCA という変換器が用意されており, 以下のように組み合わせることができます.

from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

# standardizing + logisitic
logis_std = Pipeline([
  ('standardzize', StandardScaler()),
  ('logistic', logis)
])
logis_std.fit(X, y)
## Pipeline(steps=[('standardzize', StandardScaler()),
##                 ('logistic', LogisticRegression(random_state=42))])
logis_std.predict(X)[:5, ]
## array([0, 1, 0, 1, 0])
# pca + logisitic
logis_pca = Pipeline([
  ('PCA', PCA()),
  ('logistic', logis)
])
logis_pca.fit(X, y)
## Pipeline(steps=[('PCA', PCA()),
##                 ('logistic', LogisticRegression(random_state=42))])
logis_pca.predict(X)[:5, ]
## array([0, 1, 0, 1, 0])

変換器と学習器を与えた Pipeline() オブジェクトは, 再び学習器と同じように .fit().predict() メソッドを持ちます.

もちろん, もっと複雑なデータのフローを表現することも可能です. 例えば, PCA はその性質上, 多くの場合で入力データを標準化することが望ましいことが多いです24. すると, まず標準化の変換器を通してからPCAをやる必要が出てきます. パイプラインはこのような処理にも対応できます.

logis_std_pca = Pipeline([
  ('standardize', StandardScaler()),
  ('PCA', PCA()),
  ('logistic', logis) 
])

Pipeline() は入力から出力へと向かう「垂直方向」のフローを表現できます. 一方で, 特徴量ごとに異なる処理をするとか, 同じ特徴量に異なる複数の変換を並列して実行するといった, 「水平方向」のフローを表現する手段もあります. これは単なる特徴量変換ではなく, いわゆるアンサンブル学習を表現することにも使えます25. 新たに導入するクラスは, ColumnTransformerFeatureUnion です26. 前者は入力の特徴量行列ごとに異なる前処理を適用するためのもので, 後者は複数の変換器を同じ特徴量行列に適用するためのものです.

文で説明するより, 視覚的に見るほうがよいでしょう. そのために estimator_html_repr() が用意されています. この関数はフローを描画する html ソースを返します. jupyter で以下のようにしてそれぞれのパイプラインのフローを視覚化してみましょう.

from sklearn.utils import estimator_html_repr
from IPython.core.display import display, HTML

logis_col = Pipeline([
    ('colwise', ColumnTransformer([
        ('center', StandardScaler(with_std=False), [0, 1]),
        ('standardize', StandardScaler(), slice(2, 4))
    ]))
])
logis_union = Pipeline([
    ('union', FeatureUnion([
        ('standardize', StandardScaler()),
        ('PCA', PCA())
    ]))
])

display(HTML(estimator_html_repr(logis_std_pca)))
display(HTML(estimator_html_repr(logis_col)))
display(HTML(estimator_html_repr(logis_union)))

出力される html はクリックで展開できる詳細情報が折りたたまれています (図 8.1).

パイプラインの視覚化

図 8.1: パイプラインの視覚化

また, jupyter 上で以下のように設定することで学習器オブジェクトを呼び出したときに常にこの図を返すようになります. ただし, HTML であるため ipynb ファイルをHTML以外の媒体にエクスポートする際は表示されないことがあります.

from sklearn import set_config
set_config(display='diagram')

参考文献一覧

Géron, Aurélien. 2017. Hands-on Machine Learning with Scikit-Learn and TensorFlow: Concepts, Tools, and Techniques to Build Intelligent Systems. O’Reilly Media, Inc.

  1. このサブセクションでは, 以前『Python でのデータ分析作業をスマートにするために』(https://qiita.com/s_katagiri/items/a6c3914e7623b4d7b5a3) で書いたものとおおむね同じことを説明していますが, scikit-learn の更新により当時から環境の多少の変化があります. 公式ドキュメントでは “6.1. Pipelines and composite estimators” (https://scikit-learn.org/stable/modules/compose.html?highlight=columntransformer) で言及されている内容です.↩︎

  2. Pipeline() は変換器, 学習器にそれぞれ名称を指定する必要がありますが, make_pipeline() というバリエーションは自動で命名してくれます.↩︎

  3. Géron (2017) においても, PCAは入力データの中心化が必要と注意書きがされていますが, なぜ必要なのかには深く踏み込んでいません. さらに近年機械学習に関する書籍が多く出版されていますが, PCA を紹介していても PCA 必要な前処理に一切言及してないものも少なくありません. 本稿ではこういう実用上重要な話もなるべく余さず解説するようにしています.↩︎

  4. 例えば, バギングを行う baggingClassifier, スタッキングのための StackingClassifier/StackingRegressor などが sklearn.ensemble に用意されています.↩︎

  5. Pipeline() に対する make_pipeline() のように, FeatureUnion() に対して変換器の命名が不要な make_union() が存在します.↩︎