こんにちは、えんせきです。
冬場であまり運動が出来ていなくて3キロほど太りました。まずいですね。
すみません、嘘をつきました。寒くてラーメンばっかり食べてたからです。運動量は大して変わっていません。
つまりなにしたの?
前回XGBoostを使ってクラス分類ができることを確認した。今度は、アヤメのがく弁の長さをそれ以外の要素から予測する回帰問題として扱ってみる。
一応RMSEとして評価して寄与率の可視化も行った。
方針
- パッケージの読み込み
- データセットの読み込み
クラス分類の時と変わってアヤメの種類をOne Hot表現にエンコードした。
- データセットを学習と評価用に分ける
- ハイパーパラメータ探索しつつ学習する
- 評価する(RMSE)
- 予測と実測の可視化
- 変数の重要度を可視化
パッケージの読み込み
import xgboost as xgb from sklearn import datasets from sklearn import model_selection from sklearn.metrics import confusion_matrix, mean_squared_error import sklearn.preprocessing as sp import pandas as pd import numpy as np import matplotlib.pyplot as plt
ここは前回とほとんど同じ。グラフを可視化を行わないところとMSE、PreProcessing系が変わった。
データセットの読み込み
iris = datasets.load_iris() iris_df = pd.DataFrame(iris.data) iris_df = iris_df.rename(columns={ 0: 'sepal_length', 1: 'sepal_width', 2: 'petal_length', 3: 'petal_width'}) iris_df['target'] = iris.target # 数字のカテゴリをカテゴリ名に埋めなおしている for i, name in enumerate(iris.target_names): iris_df['target'] = iris_df['target'].where(iris_df['target'] != i, name) # ラベルエンコーダを使ってまた数字に戻している le = sp.LabelEncoder() le.fit(iris_df.target.unique()) iris_df.target = le.fit_transform(iris_df.target) # OneHotEncoderでtargetを3つのクラスのOneHot表現に変更している ohe = sp.OneHotEncoder() enced = ohe.fit_transform(iris_df.target.values.reshape(1, -1).transpose()) temp = pd.DataFrame(index=iris_df.target.index, columns="target-" + le.classes_, data=enced.toarray()) iris_df = pd.concat([iris_df, temp], axis=1) del iris_df['target'] iris_df.head()
データセットを読み込んでOne hot表現になおす。
One Hot表現にするところはこないだやった手法を使うために、一旦数字でラベリングされているものをカテゴリ名で埋めなおしている。
ensekitt.hatenablog.com
データセットを学習と評価用に分ける
train_df, test_df = model_selection.train_test_split(iris_df, test_size=0.3) train_df_y = train_df[['sepal_length']] train_df_x = train_df.copy().drop('sepal_length', axis=1) test_df_y = test_df[['sepal_length']] test_df_x = test_df.copy().drop('sepal_length', axis=1)
前回はTargetだったけど今回はSepal_length
ハイパーパラメータ探索しつつ学習する
clf = xgb.XGBRegressor() # ハイパーパラメータ探索 clf_cv = model_selection.GridSearchCV(clf, {'max_depth': [2,4,6], 'n_estimators': [50,100,200]}, verbose=1) clf_cv.fit(train_df_x, [i[0] for i in train_df_y.values]) print(clf_cv.best_params_, clf_cv.best_score_) # 改めて最適パラメータで学習 clf = xgb.XGBRegressor(**clf_cv.best_params_) clf.fit(train_df_x, [i[0] for i in train_df_y.values])
XGBRegressorを使う。使い方は全く同じ。便利過ぎる。
評価する(RMSE)
mean_pred = [train_df_y.mean() for i in range(len(test_df_y))] rmse_base = np.sqrt(mean_squared_error(test_df_y, mean_pred)) print("学習データの平均を予測としたやつをBaseLineとする\nBaseLineのrmse: " + str(rmse_base)) pred = clf.predict(test_df_x) rmse = np.sqrt(mean_squared_error(test_df_y, pred)) print("予測したやつのrmse: " + str(rmse))
学習データの平均を予測としたやつをBaseLineとする BaseLineのrmse: 0.825791175637 予測したやつのrmse: 0.357222034136
RMSEは一見良くなっているのかわからないので、ベースラインとして、学習データのSepal_lengthの平均値を予測とした場合のRMSEを先に表示した。
ちゃんと予測が機能していることがわかる(RMSEは値が小さいほど予実があっているということになる)
RMSEなどの誤差関数は前にまとめているので興味があればこっちも見てね
ensekitt.hatenablog.com
予測と実測の可視化
plt.ylabel("Predict") plt.xlabel("Actual") plt.scatter(test_df_y, pred) plt.plot([4.5, 8], [4.5, 8], c='r') plt.show()
plotの4.5と8は予測と実測が完全に一致していた場合に乗るはずの線を表示するために作った。
Plotなので、X、Yで同じ値をいれてあげれば直線になる。
なかなかあってそう。
ちなみにBaseLineとして採用した平均値を予測とした場合の結果はこちら。
当然だけど全然あってない。XGBoostが仕事をしていることがこの比較でよく分かる。
変数の重要度を可視化
xgb.plot_importance(clf) plt.show()
変数の重要度を可視化してみたら、意外なことにアヤメの種類は大して寄与していなくて、
結構がく片の幅に寄っていた。幅が決まれば長さもだいたい決まると思うとたしかにそのとおりだとは思うけど、
こうやって可視化してみると納得感があっていいですね。