Japan
サイト内の現在位置
勾配ブースティング回帰木を使用した回帰分析
no.012 Frovedis機械学習 教師あり学習編2021.6.29 前回はFrovedisでのデータ処理を見てきました。これから数回に分けてFrovedis機械学習アルゴリズムを使った分析例を紹介していきます。
Frovedisに限らず機械学習のアルゴリズムを、教師あり学習と教師なし学習に大別する考え方が一般的です。教師あり学習は、何らかの入力に対する予測値を出力として得たいケースでの分析に使用されます。また、「教師あり」という名称が示す通り、入力に対する出力の組み合わせのデータがそろっている必要があります。この組み合わせをもとに何らかの学習を行い、その学習結果を使って入力データから出力データを予測します。
この教師あり学習は、さらに回帰分析とクラス分類の2種類に分類されます。回帰分析では入力xに対する出力yを予測するために、その関係性を決めるパラメータを機械学習で求めるものです。一方、クラス分類では出力yが回帰分析のように連続である必要はなく、離散的な形を持つデータセットに対する分析手段として利用されます。例えば、顧客情報(年収や家族構成、年齢、住所等)を基に何らかの商品の購入がありそうか、といった判定に使用するようなケースです。
回帰分析、クラス分類共に、様々な数学・統計的手法による機械学習アルゴリズムが存在します。データセットの性質や入力に対する出力の関係を決めるモデルの複雑さに応じて、どのアルゴリズムが最適な結果につながるか、分析者の判断が求められます。各機械学習アルゴリズムの長所短所について深く踏み込みませんが、個々のアルゴリズムについて特性を十分理解して選択することが重要です。
今回のコラムでは教師あり学習の中の回帰分析、また機械学習アルゴリズムは決定木から派生しアンサンブル法という考え方を取り入れた勾配ブースティング回帰木を取り上げます。決定木は木構造を用い、木の根から条件によって分岐しつつ葉に向かい、特定の葉に到達することで予測を行うものです。この決定木は階層が深くなりすぎるとテストデータに対して過剰に適合してしまい、過学習を起こす短所を持ち合わせています。この過学習を防ぐ手段として、決定木にアンサンブル法という考え方を導入し、単純に一つの決定木ではなく、複数の決定木の組み合わせを用いる手法が考え出されています。その代表的アルゴリズムとしてランダムフォレストと勾配ブースティングが一般的に知られています。
ランダムフォレストは、データセットを乱数に基づいて分割することで、複数の決定木を作ります。個々の決定木すべてについて予測を行い、その平均をとって回帰結果を得ることによって過学習を防ぐものです。
一方の勾配ブースティングでは一つ前の決定木の誤りを次の決定木が修正するように繰り返しながら決定木を追加していくものです。ランダムフォレストに比べ、学習に際して調整を必要とするパラメータ設定に結果が左右されやすくなりますが、正しいパラメータ設定においてはランダムフォレストより良いテスト結果が得られます。
これらランダムフォレスト、勾配ブースティングともにscikit-learnにおいて実装されています。Frovedisにおいても両方の学習アルゴリズムを利用することができます。scikit-learn版勾配ブースティングでは並列処理は行われませんが、Frovedis版では個々の決定木作成を並列処理するため、非常に大規模なデータセットにおいてscikit-learnと比べ学習時間短縮が期待できます。
Frovedis版勾配ブースティング回帰分析デモンストレーションではKaggleの中古車販売価格データセットを用いています。ブランド別にCSVに収められたデータを一つのテーブルに統合後、入力データとして中古車の製造年、走行距離、排気量を、出力データには中古車価格を選んでいます。そして、learning_rateとn_estimatorsのパラメータを変えながら学習データとテストデータ双方で予測結果を表示させています。最も予測結果が良好なパラメータで再度予測を実施します。learning_rateは個々の決定木がそれ以前の決定木の過ちの補正度合いを制御するものです。またn_estimatorsを指定することで決定木の数をコントロールします。
教師あり学習: 勾配ブースティング回帰モデルを使用した中古車価格の推定¶
100,000 UK Used Car Data set (100,000 scraped used car listings, cleaned and split into car make)¶
https://www.kaggle.com/adityadesai13/used-car-dataset-ford-and-mercedes¶
scikit-lear版の代わりにFrovedis 勾配ブースティング回帰をインポートしています¶
import pandas as pd
import numpy as np
from datetime import datetime
import os
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
from frovedis.mllib.ensemble import GradientBoostingRegressor
#from sklearn.ensemble import GradientBoostingRegressor
from frovedis.exrpc.server import FrovedisServer
import warnings
warnings.filterwarnings('ignore')
11のブランドに分けられた中古車データをDataFrameにロードします¶
vw = pd.read_table('../../data/datasets/used_car/vw.csv', sep=',', engine='python').dropna(how='any')
audi = pd.read_table('../../data/datasets/used_car/audi.csv', sep=',', engine='python').dropna(how='any')
bmw = pd.read_table('../../data/datasets/used_car/bmw.csv', sep=',', engine='python').dropna(how='any')
cclass = pd.read_table('../../data/datasets/used_car/cclass.csv', sep=',', engine='python').dropna(how='any')
focus = pd.read_table('../../data/datasets/used_car/focus.csv', sep=',', engine='python').dropna(how='any')
ford = pd.read_table('../../data/datasets/used_car/ford.csv', sep=',', engine='python').dropna(how='any')
hyundi= pd.read_table('../../data/datasets/used_car/hyundi.csv', sep=',', engine='python').dropna(how='any')
merc = pd.read_table('../../data/datasets/used_car/merc.csv', sep=',', engine='python').dropna(how='any')
skoda = pd.read_table('../../data/datasets/used_car/skoda.csv', sep=',', engine='python').dropna(how='any')
toyota = pd.read_table('../../data/datasets/used_car/toyota.csv', sep=',', engine='python').dropna(how='any')
vauxhall = pd.read_table('../../data/datasets/used_car/vauxhall.csv', sep=',', engine='python').dropna(how='any')
DataFrameにロードしたテーブルの中からAudiを選び内容を確認します¶
audi.head()
model | year | price | transmission | mileage | fuelType | tax | mpg | engineSize | |
---|---|---|---|---|---|---|---|---|---|
0 | A1 | 2017 | 12500 | Manual | 15735 | Petrol | 150 | 55.4 | 1.4 |
1 | A6 | 2016 | 16500 | Automatic | 36203 | Diesel | 20 | 64.2 | 2.0 |
2 | A1 | 2016 | 11000 | Manual | 29946 | Petrol | 30 | 55.4 | 1.4 |
3 | A4 | 2017 | 16800 | Automatic | 25952 | Diesel | 145 | 67.3 | 2.0 |
4 | A3 | 2019 | 17300 | Manual | 1998 | Petrol | 145 | 49.6 | 1.0 |
排気量の列に無効なレコードが確認されます(排気量=0)¶
audi.sort_values(by='engineSize')
model | year | price | transmission | mileage | fuelType | tax | mpg | engineSize | |
---|---|---|---|---|---|---|---|---|---|
7688 | S4 | 2019 | 39850 | Automatic | 4129 | Diesel | 145 | 40.4 | 0.0 |
7647 | Q3 | 2020 | 31990 | Automatic | 1500 | Petrol | 145 | 40.4 | 0.0 |
7649 | Q2 | 2020 | 24888 | Automatic | 1500 | Petrol | 145 | 42.2 | 0.0 |
7659 | Q3 | 2020 | 32444 | Automatic | 1500 | Petrol | 145 | 31.4 | 0.0 |
7662 | Q2 | 2020 | 24990 | Manual | 1500 | Petrol | 145 | 43.5 | 0.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
4391 | R8 | 2018 | 93950 | Semi-Auto | 3800 | Petrol | 145 | 23.0 | 5.2 |
4783 | R8 | 2020 | 145000 | Semi-Auto | 2000 | Petrol | 145 | 21.1 | 5.2 |
7475 | R8 | 2014 | 59990 | Automatic | 31930 | Petrol | 580 | 21.9 | 5.2 |
4742 | R8 | 2019 | 117990 | Automatic | 11936 | Petrol | 145 | 21.4 | 5.2 |
10455 | A8 | 2015 | 32000 | Automatic | 30306 | Petrol | 570 | 25.0 | 6.3 |
10668 rows × 9 columns
排気量と年式の列に対してフィルタリング実施後、全ブランドを一つのDataFrameに統合します。この際、各ブランドのテーブル間で非共通な列を取り除きます (join='inner')¶
vw = vw[vw["engineSize"] > 0]
audi = audi[audi["engineSize"] > 0]
bmw = bmw[bmw["engineSize"] > 0]
cclass = cclass[cclass["engineSize"] > 0]
focus = focus[focus["engineSize"] > 0]
ford = ford[ford["engineSize"] > 0]
hyundi = hyundi[hyundi["engineSize"] > 0]
merc = merc[merc["engineSize"] > 0]
skoda = skoda[skoda["engineSize"] > 0]
toyota = toyota[toyota["engineSize"] > 0]
vauxhall = vauxhall[vauxhall["engineSize"] > 0]
vw = vw[(vw["year"] < 2022) & (vw["year"] > 1990)]
audi = audi[(audi["year"] < 2022) & (audi["year"] > 1990)]
bmw = bmw[(bmw["year"] < 2022) & (bmw["year"] > 1990)]
cclass = cclass[(cclass["year"] < 2022) & (cclass["year"] > 1990)]
focus = focus[(focus["year"] < 2022) & (focus["year"] > 1990)]
ford = ford[(ford["year"] < 2022) & (ford["year"] > 1990)]
hyundi = hyundi[(hyundi["year"] < 2022) & (hyundi["year"] > 1990)]
merc = merc[(merc["year"] < 2022) & (merc["year"] > 1990)]
skoda = skoda[(skoda["year"] < 2022) & (skoda["year"] > 1990)]
toyota = toyota[(toyota["year"] < 2022) & (toyota["year"] > 1990)]
vauxhall = vauxhall[(vauxhall["year"] < 2022) & (vauxhall["year"] > 1990)]
all_brand = pd.concat([vw, audi, bmw, cclass, focus, ford, hyundi, merc, skoda, toyota, vauxhall],join='inner')
税金と燃費の列が削除されています¶
all_brand.head()
model | year | price | transmission | mileage | fuelType | engineSize | |
---|---|---|---|---|---|---|---|
0 | T-Roc | 2019 | 25000 | Automatic | 13904 | Diesel | 2.0 |
1 | T-Roc | 2019 | 26883 | Automatic | 4562 | Diesel | 2.0 |
2 | T-Roc | 2019 | 20000 | Manual | 7414 | Diesel | 2.0 |
3 | T-Roc | 2019 | 33492 | Automatic | 4825 | Petrol | 2.0 |
4 | T-Roc | 2019 | 22900 | Semi-Auto | 6500 | Petrol | 1.5 |
統合したDataFrameの統計情報を表示します¶
all_brand.describe()
year | price | mileage | engineSize | |
---|---|---|---|---|
count | 108252.000000 | 108252.000000 | 108252.000000 | 108252.000000 |
mean | 2017.098382 | 16890.472740 | 23030.811948 | 1.666039 |
std | 2.116347 | 9757.417443 | 21184.953118 | 0.551202 |
min | 1991.000000 | 450.000000 | 1.000000 | 0.600000 |
25% | 2016.000000 | 10230.750000 | 7491.000000 | 1.200000 |
50% | 2017.000000 | 14698.000000 | 17257.000000 | 1.600000 |
75% | 2019.000000 | 20945.000000 | 32245.000000 | 2.000000 |
max | 2020.000000 | 159999.000000 | 323000.000000 | 6.600000 |
学習を始める前にデータセット(排気量、走行距離、製造年に対する価格)の特性を確認します¶
fig = plt.figure(figsize=(16,8))
plt.subplots_adjust(wspace=0.4, hspace=0.4)
ax1 = fig.add_subplot(2,2,1)
ax1.scatter(all_brand['engineSize'], all_brand['price'])
ax1.set_title('All_Brand', size=15)
ax1.set_xlabel('Engine Size')
ax1.set_ylabel('price')
ax2 = fig.add_subplot(2,2,2)
ax2.scatter(all_brand['mileage'], all_brand['price'])
ax2.set_title('All_Brand', size=15)
ax2.set_xlabel('mileage')
ax2.set_ylabel('price')
ax3 = fig.add_subplot(2,2,3)
ax3.scatter(all_brand['year'], all_brand['price'])
ax3.set_title('All_brand', size=15)
ax3.set_xlabel('year')
ax3.set_ylabel('price')
plt.show()
3Dグラフによる可視化¶
fig = plt.figure(figsize=(16,8))
plt.subplots_adjust(wspace=0.4, hspace=0.4)
ax4 = fig.add_subplot(1,1,1, projection='3d')
ax4.set_xlabel("year")
ax4.set_ylabel("mileage")
ax4.set_zlabel("price")
ax4.scatter(all_brand['year'], all_brand['mileage'], all_brand['price'])
plt.show()
DataFrameから入力データxと出力データyを取り出します¶
y = all_brand[['price']]
x = all_brand[['year', 'mileage', 'engineSize']]
学習用、テスト用にデータを切り分ける作業を行います¶
x_train, x_test, y_train, y_test = train_test_split(
x, y, test_size=0.1, random_state=13)
np_y_train = y_train.to_numpy().ravel(); np_y_test = y_test.to_numpy().ravel()
print(x_train)
print(np_y_train)
year mileage engineSize 385 2016 29302 1.4 11592 2014 55861 1.2 10258 2019 6682 1.0 3094 2017 15116 2.0 1622 2017 59383 1.0 ... ... ... ... 6141 2013 46800 1.0 7914 2019 4250 2.0 3730 2017 23185 1.4 1453 2017 11627 1.0 3994 2017 36659 1.6 [97426 rows x 3 columns] [ 7000 5561 17991 ... 9220 12591 9990]
Frovedisサーバを起動します¶
FrovedisServer.initialize("mpirun -np 6 {}".format(os.environ['FROVEDIS_SERVER']))
'[ID: 1] FrovedisServer (Hostname: handson02, Port: 36630) has been initialized with 6 MPI processes.'
learning_rateとn_estimatorsのパラメータを変えながら学習を繰り返し、学習用データとテストデータそれぞれに対して推測精度を表示させます¶
for i in [0.1, 0.01, 0.001]:
for j in [100, 250, 500, 750]:
gbt = GradientBoostingRegressor(learning_rate=i, n_estimators=j)
gbt = gbt.fit(x_train, np_y_train)
print("predict output for GradientBoostingRegressor: learning_rate={}, n_estimators={}".format(i, j))
mse = mean_squared_error(np_y_test, gbt.predict(x_test))
print("The mean squared error (MSE) on test set: {:.4f}".format(mse))
pred2 = gbt.predict(x_test)
print("Accuracy on training set: %.3f" % gbt.score(x_train, np_y_train))
print("Accuracy on test set: %.3f" % gbt.score(x_test, np_y_test))
print("==============================================")
predict output for GradientBoostingRegressor: learning_rate=0.1, n_estimators=100 The mean squared error (MSE) on test set: 16958423.9092 Accuracy on training set: 0.824 Accuracy on test set: 0.823 ============================================== predict output for GradientBoostingRegressor: learning_rate=0.1, n_estimators=250 The mean squared error (MSE) on test set: 16773506.7649 Accuracy on training set: 0.828 Accuracy on test set: 0.825 ============================================== predict output for GradientBoostingRegressor: learning_rate=0.1, n_estimators=500 The mean squared error (MSE) on test set: 16731805.4272 Accuracy on training set: 0.830 Accuracy on test set: 0.826 ============================================== predict output for GradientBoostingRegressor: learning_rate=0.1, n_estimators=750 The mean squared error (MSE) on test set: 16756739.8292 Accuracy on training set: 0.831 Accuracy on test set: 0.825 ============================================== predict output for GradientBoostingRegressor: learning_rate=0.01, n_estimators=100 The mean squared error (MSE) on test set: 21688407.8670 Accuracy on training set: 0.771 Accuracy on test set: 0.774 ============================================== predict output for GradientBoostingRegressor: learning_rate=0.01, n_estimators=250 The mean squared error (MSE) on test set: 18676782.5213 Accuracy on training set: 0.803 Accuracy on test set: 0.805 ============================================== predict output for GradientBoostingRegressor: learning_rate=0.01, n_estimators=500 The mean squared error (MSE) on test set: 17588219.4446 Accuracy on training set: 0.815 Accuracy on test set: 0.817 ============================================== predict output for GradientBoostingRegressor: learning_rate=0.01, n_estimators=750 The mean squared error (MSE) on test set: 17225708.6052 Accuracy on training set: 0.820 Accuracy on test set: 0.820 ============================================== predict output for GradientBoostingRegressor: learning_rate=0.001, n_estimators=100 The mean squared error (MSE) on test set: 28317220.1012 Accuracy on training set: 0.703 Accuracy on test set: 0.705 ============================================== predict output for GradientBoostingRegressor: learning_rate=0.001, n_estimators=250 The mean squared error (MSE) on test set: 26177106.2856 Accuracy on training set: 0.725 Accuracy on test set: 0.727 ============================================== predict output for GradientBoostingRegressor: learning_rate=0.001, n_estimators=500 The mean squared error (MSE) on test set: 24060917.7513 Accuracy on training set: 0.747 Accuracy on test set: 0.749 ============================================== predict output for GradientBoostingRegressor: learning_rate=0.001, n_estimators=750 The mean squared error (MSE) on test set: 22547915.3948 Accuracy on training set: 0.762 Accuracy on test set: 0.765 ==============================================
x_test.loc[:, ['price']] = pred2
x_test.head()
year | mileage | engineSize | price | |
---|---|---|---|---|
10035 | 2019 | 3052 | 1.0 | 14715.109924 |
9238 | 2019 | 14310 | 1.0 | 14483.300719 |
8404 | 2019 | 13723 | 1.4 | 15100.060242 |
7033 | 2020 | 1500 | 2.0 | 29945.443639 |
16655 | 2017 | 19128 | 1.0 | 11491.146581 |
改めて最も正確な結果が得られるパラメータを指定し学習とテストを行います¶
gbt = GradientBoostingRegressor(learning_rate=0.1, n_estimators=500, random_state=0)
gbt = gbt.fit(x_train, np_y_train)
print("predict output for GradientBoostingRegressor: learning_rate={}, n_estimators={}".format(i, j))
mse = mean_squared_error(np_y_test, gbt.predict(x_test))
print("The mean squared error (MSE) on test set: {:.4f}".format(mse))
pred2 = gbt.predict(x_test)
print("The best accuracy on training set: %.3f" % gbt.score(x_train, np_y_train))
print("The best accuracy on test set: %.3f" % gbt.score(x_test, np_y_test))
predict output for GradientBoostingRegressor: learning_rate=0.001, n_estimators=750 The mean squared error (MSE) on test set: 16731805.4272 The best accuracy on training set: 0.830 The best accuracy on test set: 0.826
学習モデルのパラメータ一覧を表示します¶
print(gbt.get_params())
{'alpha': 0.9, 'ccp_alpha': 0.0, 'criterion': 'friedman_mse', 'init': None, 'learning_rate': 0.1, 'loss': 'ls', 'max_bins': 32, 'max_depth': 3, 'max_features': None, 'max_leaf_nodes': None, 'min_impurity_decrease': 0.0, 'min_impurity_split': None, 'min_samples_leaf': 1, 'min_samples_split': 2, 'min_weight_fraction_leaf': 0.0, 'n_estimators': 500, 'n_iter_no_change': None, 'presort': 'deprecated', 'random_state': 0, 'subsample': 1.0, 'tol': 0.0001, 'validation_fraction': 0.1, 'verbose': 0, 'warm_start': False}
FrovedisServer.shut_down()
今回行ったサンプルのように、個々のパラメータを変化させてテスト精度や過剰適合の程度を調べる作業はベストな学習モデルを得る一つの方法と言えます。大規模なデータセットに対して学習を繰り返し行うことは多くの時間消費につながります。SX-Aurora TSUBASAとFrovedisの処理並列化されたアルゴリズムを使用することで、高性能な学習モデルをより低コストで作り上げることができます。
関連リンク
AL/ML開発向けオープンソースプラットフォーム「Frovedis」
SX-Aurora TSUBASAテクニカルセミナー特設サイト
本コラム内容を踏まえたテクニカルセミナー動画公開中。第2回講演参照。
最新資料/カタログはこちらからダウンロード
ご紹介資料、各種カタログをダウンロードいただけます。
My NEC登録が必要です
My NEC登録が必要です
