概要
一昨年くらいに書いて社内サイトに放置していましたがせっかくなので公開します.
ほぼ以下のスライドの訳です (構成を結構変えているので翻訳というより翻案?)
HJvanVeen の "Feature Engineering"
https://www.slideshare.net/HJvanVeen/feature-engineering-72376750
このスライドは網羅性ならばちょっとした書籍よりも充実していると思います. しかしスライドなのでかなり簡素な記述でわかりにくい箇所も多いです. そこで補足説明を適宜補ったり, おかしいと思った点にはツッコミを入れたりしていますが, もとの発表を聞いていないため意図を誤解している可能性もあります. 1つ1つのトピックの詳細については, ググってすぐわかるレベルのことは基本的に書いていませんし補足もしません. あまり情報がないものやわかりにくいものだけに補足説明を入れています.
また, このスライドはいろんなトピックを挙げていますが具体的な活用場面やメリットデメリットについてはあまり書かれていません. 今回はそのままにしてますが, そのうちなんとかしたいです.
編注: 実装に関して
以下で紹介される特徴量エンジニアリングをPythonでやる場合は以下のモジュールが使える.
注: 書いたのが昔なのでモジュールの情報が少し古いかもしれません
pandas
表形式のデータの加工全般に使える. しかし, get_dummies()
は one-hot encoder に使えそうだが, 欠損値に対応できないので, scikit-learn の transformer クラスラッパーを書いたほうが良いと思います OneHotEncoder
のほうが使いやすい.
scikit-learn
preprocessing
の各種 transoformer
.
基本的に全て fit()
, transform()
メソッドで変換できるのでソースコードが読みやすくなる
Pipeline でまとめることも可能です. 理想はこの Pipeline でまとめることですが, 実装されてるクラスはあまり多くないです.
http://scikit-learn.org/stable/modules/preprocessing.html
たとえば,
- ~~カテゴリカル変数は直接扱えない (one-hot encoder は label-encoder を噛ませる必要)~~最新版では改善された
- 正規化処理は標準化, max-min-abs スケーリングなどがある.
- quantile transforemr, function-transformer, power transformer などの変換器もサポート
- 欠損値補完は単純平均で置き換えるなど簡単なものだけしかない.
この辺は私が以前書いた話も関連します.
Category Encoders
https://contrib.scikit-learn.org/categorical-encoding/
scikit-learn に連携できるような各種追加クラスが用意されている.
pandas.Series
にも対応しているが、一方で速度を考慮した実装でないため, サイズの大きなデータに対する処理が効率化されていない (むっちゃ遅い)
追記: 最近の更新でscikit-learn-contribでありながらパイプラインに使いづらい仕様になっています. scikit-learnネイティブのtransformerを使うか自分で実装したほうが簡単だと思います.
- Ordinal
- One-Hot
- Binary
- Helmert Contrast
- Sum Contrast
- Polynomial Contrast
- Backward Difference Contrast
- Hashing
- BaseN
- LeaveOneOut
- Target Encoding
Mlxtend
scikit-learn に連携できるように作られた、より発展的なモジュール
https://rasbt.github.io/mlxtend/
- 特徴量抽出のための前処理クラスも多い (ただしscikit-learnのパイプラインとの連携はあまり考慮されていない)
- stacking のscikit-learn準拠クラスが用意されている
- 相関ルールに基づく簡単な特徴量選択クラスも用意されている
- むしろ実装よりUser Guideでの解説が細かく有用. 今回紹介するスライドより詳しい.
追記: 最近の更新でscikit-learnも簡単なヘテロアンサンブル学習用のクラスがサポートされるようになりました.
使い勝手の比較は未調査
imbalance-learn
scikit-learn との連携を前提に作られたモジュール
不均衡データの処理
http://contrib.scikit-learn.org/imbalanced-learn/stable/
- たとえば, SMOTE が実装されている
- パイプラインにも対応している (scikit-learnのではなく
imblearn.pipeline.Pipeline
で置き換える) - 一方で, 事後確率の較正機能がない1
fancyimpute
欠損値補完用のモジュール
https://github.com/iskandr/fancyimpute
(使ったことはないです)
本編
本編の前に少しだけ補足しておきます
- encoding
- 変換のこと、特に1対1になるようなもの?
- collision
- 変換後の値が1対1にならない (衝突する) こと
カテゴリカル特徴量に対する変換
one-hot変換 one-hot encoding
- カテゴリカル変数のラベル1つ1つをダミー変数1つに変換する
- メモリを多く占有してしまう
- sparse-format ならましになる2
ハッシュ変換 hash encoding
hashing trick, feature hashingともいいます3.
- カテゴリ変数を指定した大きさのハッシュへ変換して、さらに one-hot encoding
- one-hot と違い列の増加を抑制する
- テストデータの新しいラベルにも対応しうる
ラベル変換 label encoding
- 各ラベルに対して、一意な数値で置き換える
- 当然、次元は増えない
- (非線形の) 決定木型のアルゴリズムを使うときに有効
- (線形) 回帰には不適当 (であることが多い)
カウント変換 count encoding
- 各ラベルを、訓練データ内でのそのラベルの発生頻度に置き換える
- 次元は増えない
- 線形・非線形どちらでも有効4
- 外れ値に敏感すぎるかもしれない
- (上記の問題は)対数変換すれば感度を弱められることもある
- 一度も現れないラベルは
"1"
にする - 異なるラベルでも変換後に同じ値になる可能性がある
ラベルカウント変換 label-count encoding
- count encoding をさらに、頻度の大きさの順位に変換する
- 線形・非線形どちらでも有効5
- 外れ値に敏感ではない
- コリジョンしない6
- (元の変数が異なっても衝突しやすいので)異なる変数を複数変換するときは注意
- (そのような場合はそれぞれの変数の)いいとこどりをしよう
ターゲット変換 target encoding
- count encoding のようにカテゴリごとに頻度にする代わりに、目的変数の相対頻度 (割合) を計算する
- オーバーフィットを回避するのが重要!
- オーバーフィット回避には交差検証などを使おう
- ゼロが多い場合は平滑化で補間することも検討
- オーバーフィットを防ぐためにノイズを加えることもある
- うまく使いさえすれば線形・非線形どちらでも有効
カテゴリ埋め込み category embedding
- dense embedding をニューラルネットで
- カテゴリカル変数を任意の次元のユークリッド空間上の近似関数に写す
- 学習が速い
- メモリのオーバーヘッドが少ない
- one-hot よりもパフォーマンスがよい7
NaN 変換 NaN encoding
- NaN という値じたいにも情報はある
- NaN値がある場合、その情報を保持したダミー変数を作る
- オーバーフィットしやすいので注意!
- 訓練データ・テストデータ両方に (CVで分割するならその中にも) NaN が現れるような場面でのみ使うように
多項式変換 Polynomial encoding
- カテゴリカル変数どうしの交互作用項を変換する方法
- 線形モデルで相互作用項がないと、XOR を表現できない
- 多項式カーネルなら XOR 問題を解決できる
- 特徴空間が爆発するので FS8, ハッシュ化、VW9などを併用する
訳注: 多項式変換とはカテゴリカル変数どうしの組み合わせを特徴量として使うことを意味しています. それは2つ以上の組でもいいですし, 3つ以上の組み合わせも考えられます. カテゴリカル変数をone-hot変換すれば次元が非常に大きくなることが多いので, さらにそれらの組み合わせも追加した場合「組み合わせ爆発」が起こります. カテゴリカル変数が多い場合はむしろ多項式変換を内包した学習アルゴリズムである Factorlization Machine (10.1109/ICDM.2010.127) を使えば良いでしょう
展開変換 expansion endoding
- 1つのカテゴリカル変数を複数のラベルに分解
- ブラウザのユーザーエージェント情報, OSのバージョン情報など、整頓されていない変数
連結変換 consolidation encoding
- むしろ1対1に変換せずに丸めてしまう
- スペルミスや表記のゆらぎ, 正式名称と略称の混在するテキストデータを扱うときに有効
- 現実のデータ, 自由記入欄は特に乱雑である
数値特徴量に対する変換
カテゴリカル変数よりはアルゴリズムにとって認識しやすい, 連続変数, カウントなどの数字情報の変換.
丸め rounding/ビンニング binning
- 特性をよりはっきり表現したいときのための不可逆な変換
- 桁数の多すぎる数字は, 時として単なるノイズかもしれない
- 丸めた変数はカテゴリカル変数として扱うこともできる
- 丸める前に対数変換という手もある
- 最適なビン幅を実用的なやりかたで決める必要がある. 例えば分位点とか
- ビンニングは訓練データに現れない範囲の値もうまく変換できることがある
スケーリング Scaling
数値変数を特定のレンジにおさまるように変換すること.
- Z-scaling (標準化)
- Min-Max scaling10
- root scaling
- log scaling
欠損値補間 Imputation
訳注: そもそも欠損値を扱えないアルゴリズムならば動くようになるだけましかもしれませんが, 上から3つのやり方が一般に有効かというと全くそういうことはないです. 4番目の「モデルで推定」では「推定値のバイアスが加わることに注意」と書いていますが, 平均値や中央値で補間するという方法にも特徴量の分布に恣意的なモデルを課していることも明らかで, これらの方法が有効だというのならば例えば欠損値を一律ゼロに変換するようなやり方も有効だと言えてしまいます.
交互作用 Interactions
- ためそう: 加減算、乗算、除算
- つかおう: feature importance などで選別する
- やめよう: 人間の勘13. 思いもよらない組み合わせが意味を成すこともある
線形に落とし込むための非線形アルゴリズム
非線形変換することで線形モデルへの当てはまりを良くする方法14.
- 多項式カーネル
- leafcoding
- 遺伝的アルゴリズム
- 局所線形埋め込み, スペクトル埋め込み, t-SNE
訳注: leafcoding について具体的な説明がないですが, おそらくランダムフォレストなど決定木系のアルゴリズムを当てはめ, その予測値を使う代わりにどのリーフノードに落ちたかの情報を特徴量として使う方法のことを指しています. ランダムフォレストは複雑な構造も学習できるため, リーフノードの情報は非線形な関数を線形の情報に変換できているかもしれないとされています. 最初に提案したのが誰なのか私は知りませんが, 例えば 10.1145/2648584.2648589 で使用例があります.
行ごとの統計量を特徴量に
- 欠損値の数
- ゼロの数
- 負値の数
- 平均最大最小歪度その他
時間変数 Temporal Variables
日付などを表す変数, しばしば確認が必要なもの. 間違いを犯しやすい箇所で, しかし大きく改善できる可能性もしばしば
- 循環的な変換: 週、月の日付のように循環する数値を, 円の座標のように循環する2つの変数に置き換える
- トレンドを取り出す: 例えば購入額の総計を, 週末の値, 月末の値などにすると, 総額が同じでも購入額が増えていく顧客と減っていく顧客を区別できる
- 大きなイベントからの距離
- 祝日の何日前/後かを表す特徴量を作る
訳注: 著者は触れていませんが, 「循環的な変換」について, 周期関数の近似に使われる有限フーリエ級数(三角多項式展開)を使うという古典的なやり方があります. 例えばprophetはこの方法で周期性を表現しています(prophetを知らない方は私の書いたものとそのリンク先を見てください). prophetはそれ以外にも簡単に自前で実装できるテクニックが多く, 時間変数を扱う際の参考になります. もちろんもっと単純に曜日, 1月の何日目か, 1年の何日目かといった情報をカテゴリカル変数としてone-hot変換しても表現できる場合もあるでしょう.
空間変数 Spatical Variables
空間上の位置: GPS座標、都市、国や地域、住所など
- クリギング15
- K-平均法でクラスタリング
- 経緯度の生データをそのまま使う
- 都市の位置を経緯度に変換する
- 郵便番号を住所のテキストに
- ハブ都市との距離
- 小さな町は近くの大都市の文化の影響下にある
- 電話番号は地理的な距離に関係があることも
- 位置情報のなかには疑わしいものもある
- ありえない移動速度
- 自宅や移動経路と異なる位置に滞在
- 同じ場所に二度と滞在していない
探索 Exploration
データの精査のこと. データの品質, 外れ値, ノイズ, 特徴量抽出のアイディアを見つける.
- コンソールなり Notebook なり pandas で操作
- 単純な記述統計を取って確認
- 目的変数との相関関係を見る
反復とデバッグ Iteration/Debugging
特徴量エンジニアリングは何度も繰り返す作業なので、すばやく繰り返せるような作業フローにする
- 「おおむね一直線のデバッグ」中間情報を擬似的なログとして、処理の中間で出力して確認
- より速い調査ができるようにツールを使う
- うまくいくアイディアよりも失敗するアイディアのほうが多いと心得よ
ラベルエンジニアリング
ラベル/ターゲット/目的変数自体を特徴量として使用できる. 逆も然り
- 対数/指数変換
- 2乗変換
- Box-Cox 変換
- 2値変数を回帰問題にするためにスコアリングする
- テストデータでは使えない未来の特徴量を予測するために目的変数で訓練する
訳注: この項目は記述が曖昧すぎてわかりにくいですが, 大きく分けて3つのテクニックに言及していると私は考えています.
第1のテクニックは, 目的変数を変換することで当てはまりを良くする方法です. 例えば単純な線形回帰モデルでは分布が非対称で歪んでいる場合当てはまりが悪い傾向があります. しかし対数変換や2乗変換などで目的変数を変換して対称になるよう調整すれば, 線形回帰モデルでも当てはまりがよくなることがあります. 典型例に一般化線形モデル(GLM)があります.
第2は, さらにそのようなモデルに対して別の方法で変換した目的変数を特徴量として加えるとさらに当てはまりがよくなる可能性に言及しています. 目的変数を特徴量に使っているため, もちろん実際の予測には使えませんが, これで当てはまりの悪さの原因がどこにあるのか, 言い換えるなら現在試しているモデルで捉えきれていない目的変数の傾向がどうなっているか, の確認には使うことができます. つまり, このようなモデルを残差ヒストグラムやq-qプロットで確認することで, 第1のテクニックで挙げた目的変数の変換方法をより改善できるヒントが得られます. 著者は統計学で残差診断 residual diagnosis と呼ばれる方法について言及しているのだと思います.
第3は, 最後から2つ「2値変数を~」「テストデータでは使えない~」で言及されている内容です. 2値変数は名前どおり2つしか値がないため情報が少ないです. しかし, この2値変数がどちらの値になるかが未知の変数によって決まるという**潜在変数モデルのように, 背後には見えない確率が存在すると考えられます. スコアリングとは2値変数の値に対応する確率を表す変数を作り, 分類問題をスコア変数に対する回帰問題に置き換えよう, という意味です. このスコア変数は何も考えずに作れるものではない(いつもどおりにデータに一番当てはまるモデルだけを選んでいては通常の分類問題を解いているのと変わらない)ため, 確率がどう決まるのかという情報をデータの外から得る, いわゆる「ドメイン知識」から導く必要があるでしょう. 「テストデータでは使えない~」は, 未来の特徴量の値を予測するために, 他の特徴量や目的変数を特徴量としてもう1つの予測モデルを作成するという意味です. これらは広義の欠損値補完**とも考えられます. この2つのテクニックは予測モデルをもう1つ作る手間はありますが, もちろんこのような方法も有効な場合があります.
自然言語処理の場合
訳注: このセクションはごく初歩的な話の羅列で具体的な説明がないのでちゃんとした教科書(例えばコロナ社の自然言語処理シリーズなど)を読んだほうがいいかもしれません.
- カテゴリカルな特徴量と同じアイディアが使える
- 深層学習(オートエンコーダ/自己符号化器) がこの分野を浸食しつつあるが, よくエンジニアリングされた特徴量による浅い学習にも未だに競争力がある
- データの高いスパース性によっていわゆる「次元の呪い」が引き起こされる
- 特徴量エンジニアリングの機会は多い (以下は英文の話であることに注意)
- クリーニング:
- lowercasing 小文字化
- unidecode アクセント記号つき文字を ASCII 文字に置換
- removing non-alphanumeric 英数字の除去 (あるいは約物などを除去する)
- repairing エンコード上の問題修正: 全角文字を半角に, 字間の変なスペースを除去する、など
- tokenizing トークナイズ
- encoding punctuation marks 約物の変換
- token-grams
- skip-grams
- char-grams 文字単位で n-gram を作成すること
- affixes char-grams の冒頭末尾だけを残すやつ
- 除去
- removing stopwords ストップワード除去
- removing rare/very common workds 希少語・頻出語の除去
- 語幹
- Chopping 単語の冒頭n文字だけを残す
- Speling correction 表記ゆれ補正
- Stemming 語幹だけを残す
- Lemmannization 意味上の語幹を見つける?
- 情報の付与
- Document features スペース/tabの数、行数、文字数、など文書全体の情報
- Entity insersion 単語補完 テキストの意味を明確にするために語を補う
- Parse trees 文の論理構造を付与: 主語、目的語、動詞...
- Reading level 文書の読解レベル(?)
- 類似度
- word2vec, glove/doc2vec
- token smilarity 文字列の類似度
- Levenstein?Hamming/Jaccard dist.
- nearest neighbor 最近傍法
- TF-IDF
- term frequency 全ての文書に対してある文書内に登場するトークンの頻度 文書の長さの違いによるバイアスを削減できる
- inverse document frequency 逆文書頻度: 文書ごとのトークン出現頻度の逆数。頻出トークンのバイアスを削減
- TF-IDF 重要なトークンを特定し、重要度の低いものを除外できる。
- 次元削減
- 主成分分析 (PCA)・特異値分解 (SVD): 50-100次元程度なら16
- 潜在ディリクレ配分法 (LDA): TF-IDFにSVD
- 潜在意味解析法 (LSA): トピックベクトルの作成
- 外部モデルに頼る
- 意味分析: 感情分析 テキストのポジティブ/ネガティブ情報
- トピックモデル
- クリーニング:
ニューラルネットワーク & 深層学習
- ニューラルネットは特徴量エンジニアリングをゆりかごから墓場まで全部自動でやってくれると主張する人がいる17
- では特徴量抽出はもう無意味?
- いいえ! アーキテクチャエンジニアリングへ視点を変えましょう18
- ニューラルネットの有無と無関係である証拠: 画像認識分野ではHOG, SIFT, ホワイトニング、摂動、イメージピラミッド、回転、z変換、対数変換、frame-grams, 外部の意味情報などなどを使って特徴量抽出している. (明らかに深層学習だけでこなせるタスクではないとわかる)
Leakage/Golden Features
- 特徴量抽出はleakageの活用の助けになる19
- リバースエンジニアリングをする
- rainbow table で MD5 ハッシュを復号
- TF-IDF から単語頻度を復元
- 標本データセットの並びを符号化
- ファイル作成日を符号化
- ルールマイニングをする
- 特徴量の単純なエンコーディングルールを見つけ出す
エピグラフ
各所に挿入されていますがここにまとめておきます.
Andrew Ng「『特徴量を捉えるのは難しく、時間を浪費し、専門知識もいる』機械学習の応用の本は特徴量エンジニアリングにあり。」
Domingos 「機械学習は成功例も失敗例もある。違いは何か? 単純に言えば、最も重要な要因は使用した特徴量」
Locklin: 「特徴量エンジニアリングは査読論文や教科書に載せるほどではない、何か別のものであるが、機械学習を成功させるに絶対不可欠だ。(中略) 機械学習の成功例の多くは実際特徴量エンジニアリングに帰する」
Miel: 「入力データをアルゴリズムが理解できるものにしよう」
Hal Daume III 「大抵の論文に書いてあること: 特徴量エンジニアリングは大変で時間がかかります。しかし我々が発見した、同様のことができるニューラルネットワークの新規的な方法はこのたった8ページの論文に書いてあります」
Francois Chollet 「良いモデルを開発するには当初のアイディアを締め切り近くまで何度も繰り返すことが要求される。モデルを改善する可能性は常にある。最終的なモデルはたいてい、問題に最初に取り組んだときの見通しとほとんど違っている。なぜならアプリオリな予定は原理的に実験による現実との対立に生き残れないからだ」
-
不均衡データのリサンプリングは事後確率を歪めるため, (当たり前だが)そのまま分類に使うと予測にバイアスを引き起こす. この問題と解決方法は理論的に示されている(10.1109/SSCI.2015.33)が, この論文のアイディアはそれ以前からも何度もあちこちで言及されている. 北澤拓也氏の個人ページではこの辺のアイディアがまとめられていて参考になる. ↩
-
訳注:
pandas.get_dummies
やsklearn.preprocessing.OneHotEncoder
などの実装がある. いずれもscipy
のスパース行列として返すことができる.: https://pandas.pydata.org/pandas-docs/stable/generated/pandas.get_dummies.html, http://contrib.scikit-learn.org/categorical-encoding/onehot.html, http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.htmlただし sklearn は numpy 準拠なので入力データは数値型のみ現在はobject型にも対応してかなり使いやすくなった. ↩ -
訳注: もともとの論文(DOI: 10.1145/1553374.1553516)では hash trick はハッシュ変換とカーネルトリックをあわせた手法のことを指している. 最近では単なるハッシュ変換までこう呼ぶ人が多い. ↩
-
訳注: 線形モデルの場合は目的変数とカウントがある程度線形相関していないと有効でないことに注意 ↩
-
訳注: 例によって「有効(な場合もある)」という意味 ↩
-
訳注: 順位タイの場合はどうするのか ↩
-
訳注: 例によって普遍的な性質ではなく, そういうケースがあるという話 (arXiv: 1604.06737). これによれば, 「次元よりも複雑な構造」の場合に有効とのこと. いわゆるスイスロール問題のような状況. ↩
-
訳注: feature selection(特徴量選択)と言いたいのでしょうか? ↩
-
訳注: これはよくわかりません. VowpalWabbitの特徴量選択アルゴリズムのこと? ↩
-
訳注: normalizeとも呼ばれる. レンジが0-1になる. ↩
-
訳注: ペアワイズ除去のこと? ↩
-
訳注: Tobitモデルとか多重代入法とか? ↩
-
勘というか「思い込み」みたいな ↩
-
ここで挙げられている方法はほとんどが計算量が多かったり, 次元に対して爆発的に計算量が増えたりします. よって変数が多い場合は実用的でないものが多いです. この辺の話は,例えば赤穂『カーネル多変量解析』などが参考になります. ↩
-
訳注: そこまでやるか? バリオグラムを特徴量に入れるとかから試すくらいで良さそうな気がする ↩
-
訳注: この数字の根拠は不明 ↩
-
訳注: HAHAHA 今日はエイプリルフールじゃないですぜ! ↩
-
訳注: そもそも特徴量抽出の一側面は悪条件問題を改善して最適化計算を安定させるための処理ではないか? ↩
-
訳注: leakageは本来学習時点に得られない情報を使ってテストデータの当てはまりを良くする上げる手法のこと. kaggleで出題者のミスでときどき発生するらしい. よって実用性はないが, 以下リバースエンジニアリング, ルールマイニングで挙げられるテクニックはどの場面でも使う機会があるだろう. ↩