u++の備忘録

ランダムフォレストなど木系のアンサンブルモデルの解釈性を高める「Feature Tweaking」

はじめに

今回は、ランダムフォレストなど木系のアンサンブルモデルの解釈性を高める「Feature Tweaking」について紹介します。Pythonによる実装や使い方をGitHubにて公開しています。

手法の概要

機械学習アルゴリズムは世間一般で「ブラックボックス」だと評されますが、解釈性に関する研究も盛んになっている印象があります。今回紹介する「Feature Tweaking」はKDDという著名な国際会議に2017年に採択された論文で提唱された手法で、著者はYahoo!Facebookに所属しています。

Tweakingは日本語で「微調整」の意です。「Feature Tweaking」は、木系のアンサンブルモデルの予測結果を変える方向に、入力 xを微調整します。「入力 xの中のどの変数をどう改善すれば良いかが分かる」といった意味で、解釈性が高まるといった具合です。

著者による紹介動画

www.youtube.com

日本語文献

今回の実装・記事執筆に当たって、下記2つの日本語の資料・記事を参照しました。

www.slideshare.net
setten-qb.hatenablog.com

特に後者の記事では既にPythonによる実装が公開されており、非常に参考になりました。上記で公開した私のGitHubは、こちらの記事に多少の修正と説明を加えたものになります。実装内容の理論的な概要については、こちらの記事をご参照ください。

I fixed some codes and added some explanations:

  • Fix load_iris() to datasets.load_iris() at In [2]
  • Fix rfc.fit(x, y) to rfc.fit(x_arr, y_arr) at In [3]
  • Fix aim_label = 3 to aim_label = 2 at In [7] and [22]
  • Add the usage of feature_tweaking()
  • Add featureTweakPy.py to extract functions

Quoted from featureTweakPy/README.md

「featureTweakPy」の使い方

今回、GitHubでは以下のような形で関数を読み込めるような形式にしました。

from featureTweakPy import feature_tweaking

以降、具体的な使い方を流れに沿って紹介します。

Requirements

Python 3系を使ってください。以下のパッケージを利用します。

  • numpy
  • pandas
  • scipy.stats

Download

git clone して、当該フォルダに移動します。上記で示したパッケージのインストールが必要な場合は適宜 pip install などをお願いします。

git clone git@github.com:upura/featureTweakPy.git
cd featureTweakPy

Package import

最初に、必要なパッケージをインポートします。今回は木系のアンサンブルモデルとしてランダムフォレストを、データセットとしては有名なアヤメを利用します。

import numpy as np
from sklearn import datasets
from sklearn.ensemble import RandomForestClassifier

from featureTweakPy import feature_tweaking

iris = datasets.load_iris()
x_arr = iris['data']
y_arr = iris['target']

Random Forest Prediction

「Feature Tweaking」は木系のアンサンブルモデルの予測結果を微調整する手法なので、まずは普通にモデルを学習させておく必要があります。

rfc = RandomForestClassifier()
rfc.fit(x_arr, y_arr)

Using function()

まずはハイパーパラメータの設定です。

Hyper Parameters Setting

アヤメのデータセットは3種類のラベルがあり、今回はそのうち2の付いたラベルに変化する方向に微調整させたいと思います。

class_labels = [0, 1, 2] 
aim_label = 2
epsilon = 0.1
Cost Function Setting

この手法では最も「コスト」が小さいような微調整方法を探索するので、何を「コスト」とするか定義する必要があります。コスト関数として今回は、単純にユークリッド距離を採用しました。他にもコサイン距離などが想定できます。

def cost_func(a, b):
    return np.linalg.norm(a-b)
Sample Data for Demonstration

いよいよ feature_tweaking() 関数を使います。

x = x_arr[0]
x
array([5.1, 3.5, 1.4, 0.2])
x_new = feature_tweaking(rfc, x, class_labels, aim_label, epsilon, cost_func)
x_new
array([5.1       , 2.9999999 , 4.75000038, 0.90000001])

微調整された x_new を元々の x と比べると、第一成分では変更がなく、それ以外の要素では値が変わっていると分かります。この方向に微調整すると定義した枠組みの中で最適なコストでランダムフォレストの予測結果を目的のラベル方向に変化させられるということです。

ここで、著者による紹介動画でも言及されていますが、今回の手法は「あくまでアンサンブルモデルの中の個々の決定木の結果を改善している」に過ぎず、必ずしも「ランダムフォレストとしての予測結果が目的のラベルに変わる」というわけではないことには注意が必要です。

f:id:upura:20180918193744p:plain

おわりに

ランダムフォレストをはじめとした木系のアンサンブルモデルは、手軽に高精度な予測結果を得られることから、特にテーブルデータを扱う際に頻繁に利用されています。

活躍の場はKaggleだけでなく広範に渡りつつあるかと思いますが、特にビジネスにおいては単に高精度を出すだけではなく、予測から得られた知見をどのように活用するかも同様に重要です。この手法は少し間接的な気もしますが、木系のアンサンブルモデルの解釈性を高められるという意味で、大きな価値があると思いました。