はじめに
経済系の分析などで、目的変数を対数変換して分析するというケースがあります。scikit-learnはそのようなケースもサポートしています。
どうやったらいいのかわからなくて、自分で変数を変換している人も中にはいるかと思いますが、モデル構築まではなんとかなっても、予測のことまで考えると不便になります。うまくPipelineなどで自動化できるといいのですが、普通のやり方では目的変数は処理してくれません。しかし、TransformedTargetRegressorなら大丈夫です。
目的変数の対数変換
sklearn.compose.TransformedTargetRegressorを使います。ほとんど紹介を見かけないのですが、実際これでできます。
>>> import numpy as np >>> from sklearn.linear_model import LinearRegression >>> from sklearn.compose import TransformedTargetRegressor >>> tt = TransformedTargetRegressor(regressor=LinearRegression(), ... func=np.log, inverse_func=np.exp) >>> X = np.arange(4).reshape(-1, 1) >>> y = np.exp(2 * X).ravel() >>> tt.fit(X, y) TransformedTargetRegressor(...) >>> tt.score(X, y) 1.0 >>> tt.regressor_.coef_ array([2.])sklearn.compose.TransformedTargetRegressor — scikit-learn 0.21.2 documentation
なかなか特殊な感じですね。モデルを作るときにfuncとinverse_funcを渡すのがミソで、学習はfuncで変換された目的変数に対して行われます。予測のときはinverse_funcで逆変換されて出てくるので、余計な手間をすべて省くことができます。
やってみる
ということで、対数変換した方がうまくいくようなデータを作って線形回帰してみます。
import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression from sklearn.compose import TransformedTargetRegressor def main(): np.random.seed(0) x = np.linspace(0, 5, 100) y = 20 + np.exp(x + np.random.normal(scale=0.5, size=x.shape)) X = x.reshape(-1, 1) lr = LinearRegression() lr_logy = TransformedTargetRegressor( regressor=lr, func=np.log, inverse_func=np.exp) plt.scatter(x, y, c="b", alpha=0.1) for name, model in [("Linear", lr), ("Linear-logy", lr_logy)]: model.fit(X, y) pred = model.predict(X) plt.plot(x, pred, label=name) plt.legend() plt.savefig("result.png") if __name__ == "__main__": main()
非等分散だったりして条件が悪いのですが、線形回帰で素直にやるのと比べると良いフィッティングを見せています。
おまけ:説明変数を対数変換したいとき
FunctionTransformerを使ってください。
sklearn.preprocessing.FunctionTransformer — scikit-learn 0.21.2 documentation
>>> import numpy as np >>> from sklearn.preprocessing import FunctionTransformer >>> transformer = FunctionTransformer(np.log1p, validate=True) >>> X = np.array([[0, 1], [2, 3]]) >>> transformer.transform(X) array([[0. , 0.69314718], [1.09861229, 1.38629436]])
Pipelineで組み合わせれば、説明変数・目的変数ともに対数変換でloglogというのも簡単です。
まとめ
対数変換した方がうまくいくようなデータのときは試してみましょう。もちろん対数以外の変換でもいけるので、目的変数を変換したいときはこれでいいと思います。