はじめに
AutoGluonというAutoMLのライブラリを使おうとしたときに、使い方を結構調べたので自分がやりたかったことをまとめておきます。
AutoGluonとは
AWSが提供しているAutoMLのライブラリです。テーブルデータはもちろん、画像、物体検出、自然言語など様々なデータ型に対して使用できるようです。
AutoMLのライブラリと聞くと、lazypredictのようなデータを準備して、標準化などの前処理をした上でいろんなモデルを評価してくれるようなものを思い浮かべますが、AutoGluonはデータを準備するだけでよく、カテゴリ変数のエンコーディングなども勝手にやってくれる優れものです。
AutoGluon-Tabular: Robust and Accurate AutoML for Structured Data
AutoGluonの使い方
本記事ではsklearn.datasetのfetch_california_housingを使って、テーブルデータx回帰を扱います。基本的な使い方は抑えられると思います。
インストール
pipでインストールできます。kaggleノートブックではautogluon.tabularとしないと嫌な表示がされるようです。
!pip install autogluon
!pip install autogluon.tabular # Kaggle notebook
一番簡単な例
シンプルな例を示します。コード中ではあえて明示している部分があります。もっと削れば3行でいけます。
データの準備
sklearn.datasetのfetch_california_housingを使います。元のデータセットだとすべて数値ですが、前処理が必要ないことを示すためにあえてカテゴリ変数を作ります。
AutoGluonはpandasのデータフレーム型で渡さないといけません。polars等で読み込んだ場合、.to_pandasをする必要があります。
公式ドキュメントではTabularDataset(.\データ.csv)で読み込んでいますが、pandasでも大丈夫なのでなじみがあるpandasで書いていきます。
import numpy as np
import pandas as pd
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
california = fetch_california_housing()
df = pd.DataFrame(california.data, columns=california.feature_names)
df['PRICE'] = np.array(california.target)
def house_age(age):
if age <= 10:
age = 'new'
elif age > 10 and age <= 30:
age = 'mid'
else:
age = 'old'
return age
df['HouseAge'] = df['HouseAge'].map(house_age)
train, test = train_test_split(df, test_size=0.2, random_state=42)
print(f"TRAIN:{train.shape}, TEST:{test.shape}")
# 出力
TRAIN:(16512, 9), TEST:(4128, 9)
学習
学習の流れとしてはlightGBMなどと同じでmodelを定義して、fitで学習させます。
どういう風に学習しているのかはここでは言及しません。こちらの記事や論文に説明があります。
TabularPredictとfitのパラメータは下記のとおりです。
TabularPredict
- lable : 目的変数のカラム名を指定します。Autogluonでは説明変数, 目的変数に分けなくてもここで指定するだけで良きにしてくれます。
- problem_type : 解きたい問題の種類です。今回は回帰なのでregressionを指定します。2値分類ならbinary、マルチクラスならmulticlassを指定します。
- eval_metric : 評価関数です。思いつくものは大体使えると思います。
fit - train_data : 訓練データです。
- test_data : lightGBMのvalid_setみたいな感じだと思います。必須ではありません。
- presets : 自動で組んでくれるモデルの数とか重さを選べます。下表中で上に行くほど精度がよく重くなります。必須ではありません。
- time_limit : 1つのモデルで学習に使う時間です。単位は秒です。presetsと連動させる必要があり、例えばpresetsでbest_qualityを選んでいるのにtime_limitが小さすぎるとエラーが出ます。
コードを書いていきます。
学習から結果確認まで簡単にできます。
結果の確認はleaderboardとfit_summary()のどちらでも見れます。
- leaderboard : 簡単にどのモデルが一番良かったか確認できる。見やすい。
- fit_summary : 使った説明変数とかモデルのハイパーパラメータも表示される。ちょっとみにくい。
leaderboardやfit_summaryでの評価関数の表示がマイナスになっていますが、すべてx-1で表示される仕様のようです。
学習まで
import autogluon
from autogluon.tabular import TabularDataset, TabularPredictor
model = TabularPredictor(
label='PRICE',
problem_type='regression',
eval_metric='mape'
)
predictor = model.fit(
train_data=train,
test_data=test,
presets='optimize_for_deployment',
time_limit=5,
verbosity=0 # ログの表示
)
leaderboard
predictions = predictor.predict(test)
predictor.leaderboard(test)
# 出力
model score_test score_val eval_metric pred_time_test pred_time_val fit_time pred_time_test_marginal pred_time_val_marginal fit_time_marginal stack_level can_infer fit_order
0 KNeighborsDist -0.442327 -0.437835 mean_absolute_percentage_error 0.032931 0.029457 0.034693 0.032931 0.029457 0.034693 1 True 1
1 WeightedEnsemble_L2 -0.442327 -0.437835 mean_absolute_percentage_error 0.034929 0.029457 0.077695 0.001998 0.000000 0.043002 2 True 2
fit_summary()
predictor.fit_summary()
# 出力
*** Summary of fit() ***
Estimated performance of each model:
model score_val eval_metric pred_time_val fit_time pred_time_val_marginal fit_time_marginal stack_level can_infer fit_order
0 WeightedEnsemble_L2 -0.437835 mean_absolute_percentage_error 0.029457 0.077695 0.000000 0.043002 2 True 2
1 KNeighborsDist -0.437835 mean_absolute_percentage_error 0.029457 0.034693 0.029457 0.034693 1 True 1
Number of models trained: 2
Types of models trained:
{'KNNModel', 'WeightedEnsembleModel'}
Bagging used: False
Multi-layer stack-ensembling used: False
Feature Metadata (Processed):
(raw dtype, special dtypes):
('category', []) : 1 | ['HouseAge']
('float', []) : 7 | ['MedInc', 'AveRooms', 'AveBedrms', 'Population', 'AveOccup', ...]
{'model_types': {'KNeighborsDist': 'KNNModel',
'WeightedEnsemble_L2': 'WeightedEnsembleModel'},
'model_performance': {'KNeighborsDist': -0.4378350753111248,
'WeightedEnsemble_L2': -0.4378350753111248},
'model_best': 'WeightedEnsemble_L2',
'model_paths': {'KNeighborsDist': ['KNeighborsDist'],
'WeightedEnsemble_L2': ['WeightedEnsemble_L2']},
'model_fit_times': {'KNeighborsDist': 0.034693241119384766,
'WeightedEnsemble_L2': 0.04300189018249512},
'model_pred_times': {'KNeighborsDist': 0.02945685386657715,
'WeightedEnsemble_L2': 0.0},
'num_bag_folds': 0,
'max_stack_level': 2,
'model_hyperparams': {'KNeighborsDist': {'weights': 'distance'},
'WeightedEnsemble_L2': {'use_orig_features': False,
'valid_stacker': True,
'max_base_models': 0,
'max_base_models_per_type': 'auto',
'save_bag_folds': True}},
'leaderboard': model score_val eval_metric \
0 WeightedEnsemble_L2 -0.437835 mean_absolute_percentage_error
1 KNeighborsDist -0.437835 mean_absolute_percentage_error
いろいろ確認
学習に使われた説明変数を確認します。
説明変数が多いとfit_summary()ではすべて表示されないようなので、ちゃんと確認したいときはfeature_metadataで確認できます。
feature_metadata = predictor.feature_metadata
features_used = feature_metadata.get_features()
print(len(features_used))
print(features_used)
# 出力
8
['MedInc', 'AveRooms', 'AveBedrms', 'Population', 'AveOccup', 'Latitude', 'Longitude', 'HouseAge']
学習後に一番良い(評価関数の値が良かった)モデルを使うこともできます。
また、学習が終わると学習したすべてのモデルがフォルダに.pklの形で保存されるのでそれを使うこともできます。
best_model = predictor.leaderboard(test).iloc[0]['model']
print(f"Best model: {best_model}")
model = predictor._trainer.load_model(best_model)
model.predict(test)
# 出力
Best model: KNeighborsDist
array([0.7952446, 1.1951613, 2.374857 , ..., 3.0178776, 1.2840751,
1.758698 ], dtype=float32)
説明変数の重要度を確認します。表示される重要度はpermutation importanceです。
下記のコードのようにmodelにモデル名を渡すこともできますが、modelに何も書かない場合は、best_modelの時の重要度が表示されているようです。
predictor.feature_importance(test,
model='WeightedEnsemble_L2',
subsample_size=test.shape[0], # 5000以上推奨
num_shuffle_sets=10 # 10以上推奨
)
# 出力
importance stddev p_value n p99_high p99_low
Latitude 0.544581 0.007826 2.103220e-18 10 0.552624 0.536539
Longitude 0.431816 0.006095 1.789579e-18 10 0.438080 0.425552
MedInc 0.175098 0.006007 5.277614e-15 10 0.181271 0.168925
AveOccup 0.079705 0.001617 4.678775e-17 10 0.081367 0.078044
AveRooms 0.048541 0.003129 1.523938e-12 10 0.051756 0.045325
HouseAge 0.019866 0.000978 1.361002e-13 10 0.020871 0.018860
AveBedrms 0.008722 0.001166 1.031447e-09 10 0.009921 0.007524
Population 0.006633 0.001492 9.894354e-08 10 0.008166 0.005099
カスタムしてみる
上記までで基本的な部分はできました。ここではハイパーパラメータのチューニングや特徴量選択をやってもらう設定、モデルのスタッキングとかの制御の例を書いてみようと思います。
ハイパーパラメータチューニング
公式ではAutoGluonでハイパーパラメータのチューニングを行うことは推奨していないようです。そんなことするならpresetsでbest_qualityを指定していいモデルを探しましょうよという感じのようです。ですが、一応できるのでコードを示します。
チューニングするハイパーパラメータはモデルごとに指定できます。ここで指定しない場合はAutoGluonのデフォルト値になります。
- num_trials : ハイパーパラメータを範囲の中でいくつ試すか
- search_strategy : autoだとランダムサーチ?
from autogluon.common import space
nn_options = {
'num_epochs': 10,
'learning_rate': space.Real(1e-4, 1e-2, default=5e-4, log=True),
'activation': space.Categorical('relu', 'softrelu', 'tanh'),
'dropout_prob': space.Real(0.0, 0.5, default=0.1),
}
gbm_options = {
'num_boost_round': 100,
'num_leaves': space.Int(lower=26, upper=66, default=36),
}
hyperparameters = {
'GBM': gbm_options,
'NN_TORCH': nn_options,
}
time_limit = 2*60
num_trials = 5
search_strategy = 'auot'
hyperparameter_tune_kwargs = {
'num_trials': num_trials,
'scheduler' : 'local',
'searcher': search_strategy,
}
predictor = TabularPredictor(label='PRICE', eval_metric='mape').fit(
train_data=train,
time_limit=time_limit,
hyperparameters=hyperparameters,
hyperparameter_tune_kwargs=hyperparameter_tune_kwargs,
)
バギング/アンサンブル/スタッキング
AutoGluonではアンサンブルやスタッキングしたモデルもパラメータの指定だけで作れます。
バギングモデル : 交差検証のfoldごとにモデルを作ってアンサンブルする
スタッキングモデル : バギングモデルの結果を説明変数に追加してさらにバギングモデルを作ったもの
ピンとこないと思いますが、このページのBaggingとStacking Ensemblingの図を見るとなんとなくわかります。
実装としては、num_bag_folds、num_bag_sets、num_stack_levelsを指定するだけです。
- num_bag_folds : 何分割交差検証か。
- num_bag_sets : バギングを何回繰り返すか。バギングで作られるモデルの数は
num_bag_flods * num_bag_setsになります。 - num_stack_levels : いくつの層をつくるか。
predictor = model.fit(
train_data=train,
test_data=test,
num_bag_folds=5,
num_bag_sets=1,
num_stack_levels=1,
time_limit=60,
verbosity=0 # ログの表示
)
predictions = predictor.predict(test)
predictor.leaderboard(test)
# 出力
model score_test score_val eval_metric pred_time_test pred_time_val fit_time pred_time_test_marginal pred_time_val_marginal fit_time_marginal stack_level can_infer fit_order
0 LightGBM_BAG_L2 -0.159684 -0.166176 mean_absolute_percentage_error 2.222251 9.836669 27.618359 0.666541 3.194228 10.404011 2 True 6
1 WeightedEnsemble_L3 -0.161385 -0.164899 mean_absolute_percentage_error 2.222251 9.837948 27.697854 0.000000 0.001280 0.079495 3 True 7
2 LightGBMXT_BAG_L2 -0.174728 -0.175608 mean_absolute_percentage_error 1.822575 6.817423 21.743330 0.266864 0.174982 4.528982 2 True 5
3 LightGBMXT_BAG_L1 -0.176906 -0.176141 mean_absolute_percentage_error 1.505219 6.452528 17.107950 1.505219 6.452528 17.107950 1 True 3
4 WeightedEnsemble_L2 -0.176906 -0.176141 mean_absolute_percentage_error 1.516138 6.453916 17.154609 0.010919 0.001388 0.046659 2 True 4
5 KNeighborsDist_BAG_L1 -0.431942 -0.423039 mean_absolute_percentage_error 0.026125 0.080958 0.052552 0.026125 0.080958 0.052552 1 True 2
6 KNeighborsUnif_BAG_L1 -0.443944 -0.434590 mean_absolute_percentage_error 0.024366 0.108955 0.053846 0.024366 0.108955 0.053846 1 True 1
説明変数(特徴量)選択
特長量の選択もやってくれるみたいです。fitのパラメータにfeature_prune_kwargsを渡すと特徴量重要度を考慮して特徴量選択をしてくれます。
feature_prune_kwargsには辞書型を渡す必要があります。空の辞書だと(バギングやスタッキングの)各層でよき特徴量を使ってくれるようです。下のコードのように{'force_prune':True}とするとすべてのモデルで同じ特徴量を使うようです。
predictor = model.fit(
train_data=train,
test_data=test,
feature_prune_kwargs={'force_prune':True},
time_limit=60,
verbosity=0 # ログの表示
)
おわりに
強力なAutoMLであるAutoGluonを使ってみました。今回はテーブルデータでしたが、画像やテキスト、マルチモーダルなデータに対しても使えるようなのでいろいろ使ってみたいです。
間違っている箇所等あればご指摘ください。
参考記事・文献
https://qiita.com/DS27/items/032e89fe421c74b5027b
https://qiita.com/daikikatsuragawa/items/504be60a24e46c524318
https://qiita.com/matsu3365/items/ceea9160449c88eb8272
https://qiita.com/kirikei/items/f879eb2cfbaf3d37ee0f
https://qiita.com/hima2b4/items/e3c48f558f2a8d9ff8bf
https://auto.gluon.ai/stable/index.html
chrome-extension://efaidnbmnnnibpcajpcglclefindmkaj/https://arxiv.org/pdf/2003.06505
おまけ
TabularPredict
TabularPredictのproblem_typeやeval_metricで何が使えるわからないときはわざとエラーを出してみると使えるものを全部表示してくれます。
# problem_typeのエラーの例
ValueError: Invalid problem_type 'regresson'. Valid problem types: ['binary', 'multiclass', 'regression', 'quantile']
# eval_metricのエラーの例
ValueError: Unknown eval_metric 'maei'. Valid metrics for problem_type='regression':
['r2', 'mean_squared_error', 'mse', 'root_mean_squared_error', 'rmse', 'mean_absolute_error', 'mae', 'median_absolute_error', 'mean_absolute_percentage_error', 'mape', 'symmetric_mean_absolute_percentage_error', 'smape', 'spearmanr', 'pearsonr']
モデルの略称
ハイパーパラメータチューニングの際に指定するモデルの略称です。
いくつかわからないのもありますが、わかるのだけ書いておきます。
['RF':'Random Forest', 'XT':'Extra Trees model', 'KNN':'KNearestNeighbors model ',
'GBM':'LightGBM', 'CAT':'CatBoost', 'XGB':'XGBoost', 'NN_TORCH':'PyToorch NN',
'LR', 'FASTAI':'Class for fastai v1 neural network models that operate on tabular data',
'TRANSF', 'AG_TEXT_NN', 'AG_IMAGE_NN', 'AG_AUTOMM', 'FT_TRANSFORMER', 'TABPFN',
'TABPFNMIX', 'FASTTEXT', 'ENS_WEIGHTED', 'SIMPLE_ENS_WEIGHTED', 'IM_RULEFIT', 'IM_GREEDYTREE',
'IM_FIGS', 'IM_HSTREE', 'IM_BOOSTEDRULES', 'VW', 'DUMMY']