2017年12月17日日曜日

XGBoost

はじめに

2年以上前に流行っていた[1]らしいXGBoost[2]をいまさら試してみました。勾配ブースティング決定木(Gradient boosting decision tree)と呼ばれている手法です。手法自体の説明は探すと色々出てきますので、そちらを参照ください。今回は、回帰問題と識別問題を簡単なデータで試してみます。

インストール

基本的には[2] を参考にインストールします。今回はWindows+VC2015のコマンドラインツールがインストールされている環境下で試しました。ソースコードの入手までは出来ている状態で、まず、VS2015 x64 Native Tools Command Prompt を立ち上げ、次のコマンドを実行すると、xgboostをビルドできます。

vcvarsall amd64
cd (path to xgboost)
mkdir build64
cd build64
cmake .. -G "Visual Studio 14 2015 Win64"
cmake --build . --config Release
Python環境下で利用するために、
cd (path to xgboost)
cd python-package
python setup.py install
を実行します。これで、import xgboostが使えるようになります。

回帰

回帰には、XGBRegressorを利用します。実験データは正弦波にノイズを加えたものです。

コード

コードは次の通りです。

%matplotlib inline
import xgboost as xgb
import numpy as np
import matplotlib.pyplot as plt
from IPython import display
from IPython.display import HTML
x = []
y = []
for i in range(2):
    xi = np.linspace(0,1,1000) + np.random.normal(0,0.1,1000)
    yi = np.cos(xi*10) + np.random.normal(0,0.1,1000)
    xi = xi.reshape(-1, 1)
    yi = yi.reshape(-1, 1)
    x.append(xi)
    y.append(yi)

mod = xgb.XGBRegressor(learning_rate=0.1, max_depth=2, n_estimators=100)
mod.fit(x[0], y[0])
y_train_pred = mod.predict(x[0])
y_test_pred = mod.predict(x[1])
from sklearn.metrics import mean_squared_error
print('MSE train : {0:.3f}, test : {1:.3f}'.
      format(mean_squared_error(y[0], y_train_pred),
             mean_squared_error(y[1], y_test_pred)))

x_pred = np.linspace(-0.5,1.5,10000).reshape(-1, 1)
y_pred = mod.predict(x_pred)
#plt.scatter(x[0], y[0], s=1)
plt.scatter(x[1], y[1], s=1)
plt.plot(x_pred, y_pred, 'C3')
plt.show()
plt.close()

結果

Jupyter notebookで実行すると、以下のような結果が得られます。

MSE train : 0.009, test : 0.013
テストデータに対する回帰折れ線(?)は次のようになりました。ギザギザではありますが、中心付近を通過するように学習できていることが分かります。

識別

識別にはXGBClassifierを使います。データは、1個のガウス分布から1000個サンプリングした点をクラス-1、2個のガウス分布から500個ずつサンプリングした点をクラス1として作成しています。

コード

コードは次の通りです。

%matplotlib inline
import xgboost as xgb
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
from IPython import display

# Gaussian distribution for class -1
mu = [0, 0]
sigma = [[30, 0], [0, 50]]
x1 = np.random.multivariate_normal(mu, sigma, 1000) # Train
x1t = np.random.multivariate_normal(mu, sigma, 1000) # Test

# 2 Gaussian distribution for class 1
mu = [5, 5]
sigma = [[5, 0], [0, 3]]
x2 = np.random.multivariate_normal(mu, sigma, 500) # Train
x2t = np.random.multivariate_normal(mu, sigma, 500) # Test

mu = [-10, 15]
sigma = [[3, 2], [2, 3]]
x2 = np.concatenate([x2, np.random.multivariate_normal(mu, sigma, 500)]) # Train
x2t = np.concatenate([x2t, np.random.multivariate_normal(mu, sigma, 500)]) # Test

# Make labels of train and test
y = np.concatenate([np.ones(1000)*-1, np.ones(1000)*1])

# classify by xgboost
mod = xgb.XGBClassifier(
        learning_rate=0.1,
        max_depth=2, # Maximum depth of each tree
        n_estimators=100) # The number of trees
x = np.concatenate([x1, x2])
xt = np.concatenate([x1t, x2t])
mod.fit(x, y)
y_pred = mod.predict(x)
yt_pred = mod.predict(xt)

from sklearn.metrics import accuracy_score
print('MSE train : {0:.3f}, test : {1:.3f}'.
      format(accuracy_score(y, y_pred), accuracy_score(y, yt_pred)))

# Show boundaries between class -1 and 1
gx1 = np.linspace(-20, 20, 1000)
gx2 = np.linspace(-20, 20, 1000)
gx1, gx2 = np.meshgrid(gx1, gx2)
y = mod.predict(np.array([gx1, gx2]).T.reshape(-1, 2))
plt.scatter(x1t[:,0], x1t[:,1], s=0.2)
plt.scatter(x2t[:,0], x2t[:,1], s=0.2)
cm = LinearSegmentedColormap.from_list('cm', [(0, 'blue'),(1, 'blue')])
plt.contour(gx1, gx2, y.reshape(1000, -1).T, [0], alpha=1, cmap=cm)
plt.show()

結果

Jupyter notebookで実行すると、以下のような結果が得られます。

MSE train : 0.926, test : 0.913
クラス間の識別境界(青線)と、テストデータのクラス-1(青点)と1(橙点)の分布は次の図のようになります。決定木ベースなのでギザギザですが、見た目では分割できているように見えます。

参考

[1] http://smrmkt.hatenablog.jp/entry/2015/04/28/210039
[2] http://xgboost.readthedocs.io/en/latest/build.html

0 件のコメント :