前回の記事は「PythonでXgboost」を話しました。今回の記事はPartial dependence plotsを解説します。

Partial Dependence Plotは、各特徴量が予測にどのような影響を与えるかを知るのに役に立ちます。人間の知覚の限界のために、ターゲット特徴量が小さくなければならず(通常、1つか2つ)、ターゲット特徴量は通常最も重要な特徴の中から選ばれます。検証データのある点を元にして、影響を見たい1つの特徴量だけを変化させて、学習済みのモデルで予測を実施する。すると「1つの特徴量が変化すると予測はどう変わるのか?」が分かります。

この例では、カリフォルニア州の住宅データセットで学習したGradientBoostingRegressorからPartial Dependence Plotを計算方法を示します。

In [1]:
# ライブラリ
from __future__ import print_function
print(__doc__)

import numpy as np
import matplotlib.pyplot as plt

from mpl_toolkits.mplot3d import Axes3D
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.ensemble.partial_dependence import plot_partial_dependence
from sklearn.ensemble.partial_dependence import partial_dependence
from sklearn.datasets.california_housing import fetch_california_housing
from sklearn.metrics import mean_squared_error

%matplotlib inline
Automatically created module for IPython interactive environment
In [2]:
# データセットロード
cal_housing = fetch_california_housing()

# split 80/20 train-test
X_train, X_test, y_train, y_test = train_test_split(cal_housing.data,
                                                    cal_housing.target,
                                                    test_size=0.2,
                                                    random_state=1)
names = cal_housing.feature_names
cal_housing
Out[2]:
{'data': array([[   8.3252    ,   41.        ,    6.98412698, ...,    2.55555556,
           37.88      , -122.23      ],
        [   8.3014    ,   21.        ,    6.23813708, ...,    2.10984183,
           37.86      , -122.22      ],
        [   7.2574    ,   52.        ,    8.28813559, ...,    2.80225989,
           37.85      , -122.24      ],
        ...,
        [   1.7       ,   17.        ,    5.20554273, ...,    2.3256351 ,
           39.43      , -121.22      ],
        [   1.8672    ,   18.        ,    5.32951289, ...,    2.12320917,
           39.43      , -121.32      ],
        [   2.3886    ,   16.        ,    5.25471698, ...,    2.61698113,
           39.37      , -121.24      ]]),
 'target': array([4.526, 3.585, 3.521, ..., 0.923, 0.847, 0.894]),
 'feature_names': ['MedInc',
  'HouseAge',
  'AveRooms',
  'AveBedrms',
  'Population',
  'AveOccup',
  'Latitude',
  'Longitude'],
 'DESCR': '.. _california_housing_dataset:\n\nCalifornia Housing dataset\n--------------------------\n\n**Data Set Characteristics:**\n\n    :Number of Instances: 20640\n\n    :Number of Attributes: 8 numeric, predictive attributes and the target\n\n    :Attribute Information:\n        - MedInc        median income in block\n        - HouseAge      median house age in block\n        - AveRooms      average number of rooms\n        - AveBedrms     average number of bedrooms\n        - Population    block population\n        - AveOccup      average house occupancy\n        - Latitude      house block latitude\n        - Longitude     house block longitude\n\n    :Missing Attribute Values: None\n\nThis dataset was obtained from the StatLib repository.\nhttp://lib.stat.cmu.edu/datasets/\n\nThe target variable is the median house value for California districts.\n\nThis dataset was derived from the 1990 U.S. census, using one row per census\nblock group. A block group is the smallest geographical unit for which the U.S.\nCensus Bureau publishes sample data (a block group typically has a population\nof 600 to 3,000 people).\n\nIt can be downloaded/loaded using the\n:func:`sklearn.datasets.fetch_california_housing` function.\n\n.. topic:: References\n\n    - Pace, R. Kelley and Ronald Barry, Sparse Spatial Autoregressions,\n      Statistics and Probability Letters, 33 (1997) 291-297\n'}
In [3]:
print("Training GBRT...")
clf = GradientBoostingRegressor(n_estimators=100, max_depth=4,
                                learning_rate=0.1, loss='huber',
                                random_state=1)
clf.fit(X_train, y_train)

mse = mean_squared_error(y_test, clf.predict(X_test))
print("MSE: %.4f" % mse)
Training GBRT...
MSE: 0.2613
In [4]:
# Plot feature importance

cal_housing.feature_names = np.asarray(cal_housing.feature_names)
feature_importance = clf.feature_importances_
# make importances relative to max importance
feature_importance = 100.0 * (feature_importance / feature_importance.max())
sorted_idx = np.argsort(feature_importance)
pos = np.arange(sorted_idx.shape[0]) + .5
plt.barh(pos, feature_importance[sorted_idx], align='center')
plt.yticks(pos, cal_housing.feature_names[sorted_idx])
plt.xlabel('Relative Importance')
plt.title('Variable Importance')    
plt.show()

Partial dependence plots

one-way partial dependence plotsのターゲット変数は収入の中央値(MedInc)、平均。 1世帯当たりの居住者数(AvgOccup)、平均住宅年齢(HouseAge)、および1世帯当たりの平均部屋数(AveRooms)です。

明らかに、住宅価格の中央値は収入の中央値(左上)と線形の関係にあり、平均価格を下回ると住宅価格は下がることがわかります。 一世帯当たりの居住者数は増加している(上中央)。 右上のプロットは、地区内の住宅の築年数が(中央値)住宅価格に大きな影響を与えないことを示しています。 世帯当たりの平均部屋数もそうです。 x軸上の目盛りは、トレーニングデータ内の特徴値の十分位数を表します。

In [5]:
print('Convenience plot with ``partial_dependence_plots``')

features = [0, 5, 1, 2, (5, 1)]
fig, axs = plot_partial_dependence(clf, X_train, features,
                                   feature_names=names,
                                   n_jobs=3, grid_resolution=50)
fig.set_size_inches(12, 10)
plt.show()
Convenience plot with ``partial_dependence_plots``

Partial dependence plots with two ターゲット変数

2つのターゲットフィーチャーのPartial dependence plotsにより、住宅価格の中央値が住宅の年齢と平均のジョイント値に依存していることを示しています。 世帯ごとの居住者。 avgの2つの機能間の相互作用を明確に見ることができます。 2人以上の住居では、住宅価格は住宅年齢の影響をほとんど受けませんが、2人未満の値では年齢に強い影響があります。

In [6]:
fig.suptitle('Partial dependence of house value on nonlocation features\n'
             'for the California housing dataset')
plt.subplots_adjust(top=0.9)  # tight_layout causes overlap with suptitle

print('Custom 3d plot via ``partial_dependence``')
fig = plt.figure()

target_feature = (1, 5)
pdp, axes = partial_dependence(clf, target_feature,
                               X=X_train, grid_resolution=50)
XX, YY = np.meshgrid(axes[0], axes[1])
Z = pdp[0].reshape(list(map(np.size, axes))).T
ax = Axes3D(fig)
surf = ax.plot_surface(XX, YY, Z, rstride=1, cstride=1,
                       cmap=plt.cm.BuPu, edgecolor='k')
ax.set_xlabel(names[target_feature[0]])
ax.set_ylabel(names[target_feature[1]])
ax.set_zlabel('Partial dependence')
#  pretty init view
ax.view_init(elev=22, azim=122)
plt.colorbar(surf)
plt.suptitle('Partial dependence of house value on median\n'
             'age and average occupancy')
plt.subplots_adjust(top=0.9)

plt.show()
Custom 3d plot via ``partial_dependence``
<Figure size 432x288 with 0 Axes>