{ "cells": [ { "cell_type": "markdown", "id": "682f0f6a", "metadata": {}, "source": [ "# Train Binary Classifier using AutoGluon\n", "\n", "AutoGluon은 `fit()` 함수 호출만으로 상당히 높은 정확도의 모델을 생성하며, 최신 기능들을 계속 업데이트되고 있습니다.
\n", "본 핸즈온에서는 개인 소득이 $50k를 초과하는지 여부를 예측하는 이진 분류 모델을 AutoGluon으로 훈련해 보겠습니다." ] }, { "cell_type": "code", "execution_count": 1, "id": "5668c59a", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'1.10.0'" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%load_ext autoreload\n", "%autoreload 2\n", "import torch\n", "torch.__version__" ] }, { "cell_type": "markdown", "id": "d05012f5", "metadata": {}, "source": [ "## 1. Quick Start\n", "\n", "Toy example로 AutoGluon의 저장소에 있는 CSV 데이터셋을 로드하고, 핸즈온을 위해 500건의 데이터만 샘플링합니다.\n", "\n", "참고로 TabularDataset은 pandas 데이터프레임과 호환되기 때문에, TabularDataset을 데이터프레임으로 변환하거나 데이터프레임을 TabularDataset로 자유롭게 변환할 수 있습니다." ] }, { "cell_type": "markdown", "id": "af006c68", "metadata": {}, "source": [ "### Data preparation" ] }, { "cell_type": "code", "execution_count": 2, "id": "4f197d4a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Summary of class variable: \n", " count 500\n", "unique 2\n", "top <=50K\n", "freq 365\n", "Name: class, dtype: object\n" ] } ], "source": [ "from autogluon.tabular import TabularDataset, TabularPredictor\n", "train_data = TabularDataset('https://autogluon.s3.amazonaws.com/datasets/Inc/train.csv')\n", "test_data = TabularDataset('https://autogluon.s3.amazonaws.com/datasets/Inc/test.csv')\n", "\n", "subsample_size = 500 # subsample subset of data for faster demo, try setting this to much larger values\n", "train_data = train_data.sample(n=subsample_size, random_state=0)\n", "\n", "label = 'class'\n", "print(\"Summary of class variable: \\n\", train_data[label].describe())\n", "\n", "y_test = test_data[label] # values to predict\n", "test_data_nolab = test_data.drop(columns=[label]) # delete label column to prove we're not cheating" ] }, { "cell_type": "code", "execution_count": 3, "id": "53d8ee81", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ageworkclassfnlwgteducationeducation-nummarital-statusoccupationrelationshipracesexcapital-gaincapital-losshours-per-weeknative-countryclass
611851Private39264Some-college10Married-civ-spouseExec-managerialWifeWhiteFemale0040United-States>50K
2320458Private5166210th6Married-civ-spouseOther-serviceWifeWhiteFemale008United-States<=50K
2959040Private326310Some-college10Married-civ-spouseCraft-repairHusbandWhiteMale0044United-States<=50K
1811637Private222450HS-grad9Never-marriedSalesNot-in-familyWhiteMale0233940El-Salvador<=50K
3396462Private109190Bachelors13Married-civ-spouseExec-managerialHusbandWhiteMale15024040United-States>50K
\n", "
" ], "text/plain": [ " age workclass fnlwgt education education-num \\\n", "6118 51 Private 39264 Some-college 10 \n", "23204 58 Private 51662 10th 6 \n", "29590 40 Private 326310 Some-college 10 \n", "18116 37 Private 222450 HS-grad 9 \n", "33964 62 Private 109190 Bachelors 13 \n", "\n", " marital-status occupation relationship race sex \\\n", "6118 Married-civ-spouse Exec-managerial Wife White Female \n", "23204 Married-civ-spouse Other-service Wife White Female \n", "29590 Married-civ-spouse Craft-repair Husband White Male \n", "18116 Never-married Sales Not-in-family White Male \n", "33964 Married-civ-spouse Exec-managerial Husband White Male \n", "\n", " capital-gain capital-loss hours-per-week native-country class \n", "6118 0 0 40 United-States >50K \n", "23204 0 0 8 United-States <=50K \n", "29590 0 0 44 United-States <=50K \n", "18116 0 2339 40 El-Salvador <=50K \n", "33964 15024 0 40 United-States >50K " ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "train_data.head(5)" ] }, { "cell_type": "markdown", "id": "e4071fb7", "metadata": {}, "source": [ "### Training\n", "\n", "여러분은 단 한 줄의 코드로 쉽게 AutoML을 수행할 수 있습니다.\n", "문제 유형을 지정하지 않아도 자동으로 문제 유형을 파악하며, 별도의 피쳐 인코딩/변환을 수행하지 않아도 되며 결측치 또한 자동으로 처리할 수 있습니다.\n", "이 때, 별도의 검증 데이터셋을 지정하지 않으면, AutoGluon은 데이터의 자동으로 훈련 데이터/검증 데이터를 분리합니다.\n", "\n", "AutoGluon은 검증 데이터에서 최고의 성능을 내기 위한 다양한 조합을 반복적으로 수행하며, `fit()`에서 다양한 인수를 지정하여 자유롭게 AutoML을 수행할 수 있습니다.\n", "\n", "참고로, Titanic 데이터셋에 아래 설정으로 훈련 수행 시 Kaggle Private LB 기준으로 Top 3%의 accuracy를 보입니다.\n", "(https://www.kaggle.com/innixma/top-3-in-1-line-of-code-w-autogluon)\n", "```python\n", "# Titanic Top 3% accuracy\n", "predictor = TabularPredictor(label=label).fit(train_data, time_limit=3600, num_bag_folds=8)\n", "```\n", "\n", "#### Tip \n", "GPU가 있다면, AutoGluon 0.1부터 LightGBM, CatBoost, XGBoost, NN, FastAI NN 모델 훈련 시 GPU로 훈련이 가능합니다.\n", "```python\n", "predictor = TabularPredictor(..).fit(..., ag_args_fit={'num_gpus': 1})\n", "```" ] }, { "cell_type": "code", "execution_count": 4, "id": "7d8d1265", "metadata": {}, "outputs": [], "source": [ "!rm -rf ag-01-binary ag-01-binary-hpo" ] }, { "cell_type": "code", "execution_count": 5, "id": "10724357", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Beginning AutoGluon training ... Time limit = 60s\n", "AutoGluon will save models to \"ag-01-binary/\"\n", "AutoGluon Version: 0.5.2\n", "Python Version: 3.8.12\n", "Operating System: Linux\n", "Train Data Rows: 500\n", "Train Data Columns: 14\n", "Label Column: class\n", "Preprocessing data ...\n", "AutoGluon infers your prediction problem is: 'binary' (because only two unique label-values observed).\n", "\t2 unique label values: [' >50K', ' <=50K']\n", "\tIf 'binary' is not the correct problem_type, please manually specify the problem_type parameter during predictor init (You may specify problem_type as one of: ['binary', 'multiclass', 'regression'])\n", "Selected class <--> label mapping: class 1 = >50K, class 0 = <=50K\n", "\tNote: For your binary classification, AutoGluon arbitrarily selected which label-value represents positive ( >50K) vs negative ( <=50K) class.\n", "\tTo explicitly set the positive_class, either rename classes to 1 and 0, or specify positive_class in Predictor init.\n", "Using Feature Generators to preprocess the data ...\n", "Fitting AutoMLPipelineFeatureGenerator...\n", "\tAvailable Memory: 13846.6 MB\n", "\tTrain Data (Original) Memory Usage: 0.29 MB (0.0% of available memory)\n", "\tInferring data type of each feature based on column values. Set feature_metadata_in to manually specify special dtypes of the features.\n", "\tStage 1 Generators:\n", "\t\tFitting AsTypeFeatureGenerator...\n", "\t\t\tNote: Converting 1 features to boolean dtype as they only contain 2 unique values.\n", "\tStage 2 Generators:\n", "\t\tFitting FillNaFeatureGenerator...\n", "\tStage 3 Generators:\n", "\t\tFitting IdentityFeatureGenerator...\n", "\t\tFitting CategoryFeatureGenerator...\n", "\t\t\tFitting CategoryMemoryMinimizeFeatureGenerator...\n", "\tStage 4 Generators:\n", "\t\tFitting DropUniqueFeatureGenerator...\n", "\tTypes of features in original data (raw dtype, special dtypes):\n", "\t\t('int', []) : 6 | ['age', 'fnlwgt', 'education-num', 'capital-gain', 'capital-loss', ...]\n", "\t\t('object', []) : 8 | ['workclass', 'education', 'marital-status', 'occupation', 'relationship', ...]\n", "\tTypes of features in processed data (raw dtype, special dtypes):\n", "\t\t('category', []) : 7 | ['workclass', 'education', 'marital-status', 'occupation', 'relationship', ...]\n", "\t\t('int', []) : 6 | ['age', 'fnlwgt', 'education-num', 'capital-gain', 'capital-loss', ...]\n", "\t\t('int', ['bool']) : 1 | ['sex']\n", "\t0.1s = Fit runtime\n", "\t14 features in original data used to generate 14 features in processed data.\n", "\tTrain Data (Processed) Memory Usage: 0.03 MB (0.0% of available memory)\n", "Data preprocessing and feature engineering runtime = 0.12s ...\n", "AutoGluon will gauge predictive performance using evaluation metric: 'accuracy'\n", "\tTo change this, specify the eval_metric parameter of Predictor()\n", "Automatically generating train/validation split with holdout_frac=0.2, Train Rows: 400, Val Rows: 100\n", "Fitting 13 L1 models ...\n", "Fitting model: KNeighborsUnif ... Training model for up to 59.88s of the 59.88s of remaining time.\n", "\t0.73\t = Validation score (accuracy)\n", "\t0.01s\t = Training runtime\n", "\t0.01s\t = Validation runtime\n", "Fitting model: KNeighborsDist ... Training model for up to 59.87s of the 59.87s of remaining time.\n", "\t0.65\t = Validation score (accuracy)\n", "\t0.0s\t = Training runtime\n", "\t0.0s\t = Validation runtime\n", "Fitting model: LightGBMXT ... Training model for up to 59.86s of the 59.85s of remaining time.\n", "\t0.83\t = Validation score (accuracy)\n", "\t0.89s\t = Training runtime\n", "\t0.02s\t = Validation runtime\n", "Fitting model: LightGBM ... Training model for up to 58.94s of the 58.94s of remaining time.\n", "\t0.85\t = Validation score (accuracy)\n", "\t0.27s\t = Training runtime\n", "\t0.01s\t = Validation runtime\n", "Fitting model: RandomForestGini ... Training model for up to 58.65s of the 58.65s of remaining time.\n", "\t0.84\t = Validation score (accuracy)\n", "\t0.63s\t = Training runtime\n", "\t0.07s\t = Validation runtime\n", "Fitting model: RandomForestEntr ... Training model for up to 57.94s of the 57.94s of remaining time.\n", "\t0.83\t = Validation score (accuracy)\n", "\t0.55s\t = Training runtime\n", "\t0.07s\t = Validation runtime\n", "Fitting model: CatBoost ... Training model for up to 57.3s of the 57.3s of remaining time.\n", "\t0.85\t = Validation score (accuracy)\n", "\t1.32s\t = Training runtime\n", "\t0.01s\t = Validation runtime\n", "Fitting model: ExtraTreesGini ... Training model for up to 55.97s of the 55.96s of remaining time.\n", "\t0.82\t = Validation score (accuracy)\n", "\t0.6s\t = Training runtime\n", "\t0.06s\t = Validation runtime\n", "Fitting model: ExtraTreesEntr ... Training model for up to 55.29s of the 55.29s of remaining time.\n", "\t0.81\t = Validation score (accuracy)\n", "\t0.52s\t = Training runtime\n", "\t0.06s\t = Validation runtime\n", "Fitting model: NeuralNetFastAI ... Training model for up to 54.69s of the 54.68s of remaining time.\n", "\t0.82\t = Validation score (accuracy)\n", "\t1.67s\t = Training runtime\n", "\t0.02s\t = Validation runtime\n", "Fitting model: XGBoost ... Training model for up to 52.98s of the 52.98s of remaining time.\n", "\t0.87\t = Validation score (accuracy)\n", "\t0.44s\t = Training runtime\n", "\t0.04s\t = Validation runtime\n", "Fitting model: NeuralNetTorch ... Training model for up to 52.48s of the 52.48s of remaining time.\n", "\t0.85\t = Validation score (accuracy)\n", "\t2.5s\t = Training runtime\n", "\t0.02s\t = Validation runtime\n", "Fitting model: LightGBMLarge ... Training model for up to 49.96s of the 49.96s of remaining time.\n", "\t0.83\t = Validation score (accuracy)\n", "\t0.55s\t = Training runtime\n", "\t0.02s\t = Validation runtime\n", "Fitting model: WeightedEnsemble_L2 ... Training model for up to 59.88s of the 49.37s of remaining time.\n", "\t0.87\t = Validation score (accuracy)\n", "\t0.33s\t = Training runtime\n", "\t0.0s\t = Validation runtime\n", "AutoGluon training complete, total runtime = 10.97s ... Best model: \"WeightedEnsemble_L2\"\n", "TabularPredictor saved. To load, use: predictor = TabularPredictor.load(\"ag-01-binary/\")\n" ] } ], "source": [ "# time_limit=60 -> 60sec = 1min\n", "save_path = 'ag-01-binary'\n", "predictor = TabularPredictor(label=label, path=save_path).fit(train_data, time_limit=60)" ] }, { "cell_type": "markdown", "id": "15ecfc25", "metadata": {}, "source": [ "### Evaluation" ] }, { "cell_type": "code", "execution_count": 6, "id": "12e236ee", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "AutoGluon infers problem type is: binary\n", "AutoGluon identified the following types of features:\n", "('category', []) : 7 | ['workclass', 'education', 'marital-status', 'occupation', 'relationship', ...]\n", "('int', []) : 6 | ['age', 'fnlwgt', 'education-num', 'capital-gain', 'capital-loss', ...]\n", "('int', ['bool']) : 1 | ['sex']\n" ] } ], "source": [ "print(\"AutoGluon infers problem type is: \", predictor.problem_type)\n", "print(\"AutoGluon identified the following types of features:\")\n", "print(predictor.feature_metadata)" ] }, { "cell_type": "code", "execution_count": 7, "id": "948fc6e3", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 <=50K\n", "1 <=50K\n", "2 <=50K\n", "3 <=50K\n", "4 <=50K\n", "Name: class, dtype: object" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y_pred = predictor.predict(test_data_nolab)\n", "y_pred.head()" ] }, { "cell_type": "code", "execution_count": 8, "id": "be545d84", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
<=50K>50K
00.9821070.017893
10.9883370.011663
20.5735050.426495
30.9982720.001728
40.9902990.009701
\n", "
" ], "text/plain": [ " <=50K >50K\n", "0 0.982107 0.017893\n", "1 0.988337 0.011663\n", "2 0.573505 0.426495\n", "3 0.998272 0.001728\n", "4 0.990299 0.009701" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y_prob = predictor.predict_proba(test_data_nolab)\n", "y_prob.head()" ] }, { "cell_type": "code", "execution_count": 9, "id": "3f578182", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Evaluation: accuracy on test data: 0.8374449790152523\n", "Evaluations on test data:\n", "{\n", " \"accuracy\": 0.8374449790152523,\n", " \"balanced_accuracy\": 0.7430558394221018,\n", " \"mcc\": 0.5243657567117436,\n", " \"f1\": 0.621904761904762,\n", " \"precision\": 0.69394261424017,\n", " \"recall\": 0.5634167385677308\n", "}\n" ] } ], "source": [ "perf = predictor.evaluate_predictions(y_true=y_test, y_pred=y_pred, auxiliary_metrics=True)" ] }, { "cell_type": "markdown", "id": "12590a1b", "metadata": {}, "source": [ "ROC 커브, Precision-Recall 커브, Confusion matrix를 확인합니다. " ] }, { "cell_type": "code", "execution_count": 10, "id": "c2650686", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "confusion matrix (cutoff=0.5)\n", " precision recall f1-score support\n", "\n", " 0 0.87 0.92 0.90 7451\n", " 1 0.69 0.56 0.62 2318\n", "\n", " accuracy 0.84 9769\n", " macro avg 0.78 0.74 0.76 9769\n", "weighted avg 0.83 0.84 0.83 9769\n", "\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "from src.utils import plot_all\n", "y_prob = predictor.predict_proba(test_data_nolab)\n", "y_prob_ = y_prob.iloc[:,-1]\n", "plot_all(y_test, y_prob_)" ] }, { "cell_type": "code", "execution_count": 11, "id": "2b79e95f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "*** Summary of fit() ***\n", "Estimated performance of each model:\n", " model score_val pred_time_val fit_time pred_time_val_marginal fit_time_marginal stack_level can_infer fit_order\n", "0 XGBoost 0.87 0.044890 0.442614 0.044890 0.442614 1 True 11\n", "1 WeightedEnsemble_L2 0.87 0.045413 0.770176 0.000522 0.327562 2 True 14\n", "2 CatBoost 0.85 0.010740 1.317234 0.010740 1.317234 1 True 7\n", "3 LightGBM 0.85 0.014454 0.265227 0.014454 0.265227 1 True 4\n", "4 NeuralNetTorch 0.85 0.018401 2.496524 0.018401 2.496524 1 True 12\n", "5 RandomForestGini 0.84 0.066613 0.629995 0.066613 0.629995 1 True 5\n", "6 LightGBMLarge 0.83 0.017587 0.553040 0.017587 0.553040 1 True 13\n", "7 LightGBMXT 0.83 0.018786 0.893909 0.018786 0.893909 1 True 3\n", "8 RandomForestEntr 0.83 0.071608 0.549277 0.071608 0.549277 1 True 6\n", "9 NeuralNetFastAI 0.82 0.017581 1.669663 0.017581 1.669663 1 True 10\n", "10 ExtraTreesGini 0.82 0.060402 0.597492 0.060402 0.597492 1 True 8\n", "11 ExtraTreesEntr 0.81 0.060960 0.519994 0.060960 0.519994 1 True 9\n", "12 KNeighborsUnif 0.73 0.005741 0.005677 0.005741 0.005677 1 True 1\n", "13 KNeighborsDist 0.65 0.004648 0.004866 0.004648 0.004866 1 True 2\n", "Number of models trained: 14\n", "Types of models trained:\n", "{'RFModel', 'WeightedEnsembleModel', 'TabularNeuralNetTorchModel', 'LGBModel', 'XTModel', 'CatBoostModel', 'KNNModel', 'XGBoostModel', 'NNFastAiTabularModel'}\n", "Bagging used: False \n", "Multi-layer stack-ensembling used: False \n", "Feature Metadata (Processed):\n", "(raw dtype, special dtypes):\n", "('category', []) : 7 | ['workclass', 'education', 'marital-status', 'occupation', 'relationship', ...]\n", "('int', []) : 6 | ['age', 'fnlwgt', 'education-num', 'capital-gain', 'capital-loss', ...]\n", "('int', ['bool']) : 1 | ['sex']\n", "Plot summary of models saved to file: ag-01-binary/SummaryOfModels.html\n", "*** End of fit() summary ***\n" ] } ], "source": [ "results = predictor.fit_summary()" ] }, { "cell_type": "code", "execution_count": 12, "id": "fdabe96f", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
modelscore_valpred_time_valfit_timepred_time_val_marginalfit_time_marginalstack_levelcan_inferfit_order
0XGBoost0.870.0448900.4426140.0448900.4426141True11
1WeightedEnsemble_L20.870.0454130.7701760.0005220.3275622True14
2CatBoost0.850.0107401.3172340.0107401.3172341True7
3LightGBM0.850.0144540.2652270.0144540.2652271True4
4NeuralNetTorch0.850.0184012.4965240.0184012.4965241True12
5RandomForestGini0.840.0666130.6299950.0666130.6299951True5
6LightGBMLarge0.830.0175870.5530400.0175870.5530401True13
7LightGBMXT0.830.0187860.8939090.0187860.8939091True3
8RandomForestEntr0.830.0716080.5492770.0716080.5492771True6
9NeuralNetFastAI0.820.0175811.6696630.0175811.6696631True10
10ExtraTreesGini0.820.0604020.5974920.0604020.5974921True8
11ExtraTreesEntr0.810.0609600.5199940.0609600.5199941True9
12KNeighborsUnif0.730.0057410.0056770.0057410.0056771True1
13KNeighborsDist0.650.0046480.0048660.0046480.0048661True2
\n", "
" ], "text/plain": [ " model score_val pred_time_val fit_time \\\n", "0 XGBoost 0.87 0.044890 0.442614 \n", "1 WeightedEnsemble_L2 0.87 0.045413 0.770176 \n", "2 CatBoost 0.85 0.010740 1.317234 \n", "3 LightGBM 0.85 0.014454 0.265227 \n", "4 NeuralNetTorch 0.85 0.018401 2.496524 \n", "5 RandomForestGini 0.84 0.066613 0.629995 \n", "6 LightGBMLarge 0.83 0.017587 0.553040 \n", "7 LightGBMXT 0.83 0.018786 0.893909 \n", "8 RandomForestEntr 0.83 0.071608 0.549277 \n", "9 NeuralNetFastAI 0.82 0.017581 1.669663 \n", "10 ExtraTreesGini 0.82 0.060402 0.597492 \n", "11 ExtraTreesEntr 0.81 0.060960 0.519994 \n", "12 KNeighborsUnif 0.73 0.005741 0.005677 \n", "13 KNeighborsDist 0.65 0.004648 0.004866 \n", "\n", " pred_time_val_marginal fit_time_marginal stack_level can_infer \\\n", "0 0.044890 0.442614 1 True \n", "1 0.000522 0.327562 2 True \n", "2 0.010740 1.317234 1 True \n", "3 0.014454 0.265227 1 True \n", "4 0.018401 2.496524 1 True \n", "5 0.066613 0.629995 1 True \n", "6 0.017587 0.553040 1 True \n", "7 0.018786 0.893909 1 True \n", "8 0.071608 0.549277 1 True \n", "9 0.017581 1.669663 1 True \n", "10 0.060402 0.597492 1 True \n", "11 0.060960 0.519994 1 True \n", "12 0.005741 0.005677 1 True \n", "13 0.004648 0.004866 1 True \n", "\n", " fit_order \n", "0 11 \n", "1 14 \n", "2 7 \n", "3 4 \n", "4 12 \n", "5 5 \n", "6 13 \n", "7 3 \n", "8 6 \n", "9 10 \n", "10 8 \n", "11 9 \n", "12 1 \n", "13 2 " ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "predictor.leaderboard(silent=True)" ] }, { "cell_type": "markdown", "id": "638fedf6", "metadata": {}, "source": [ "
\n", "\n", "## 2. Hyperparameter Tuning\n", "\n", "`fit()`은 기본적으로 신경망과 다양한 유형의 트리 앙상블 모델을 훈련합니다. 각 모델 유형에 대해 다양한 하이퍼파라메터를 지정할 수 있으며, 각 하이퍼 파라미터에 대해 단일 고정 값을 지정하거나 고려할 하이퍼파라메터의 검색 공간(search space)을 지정할 수 있습니다. 지정하지 않은 하이퍼 파라미터는 AutoGluon에서 자동으로 선택한 기본 설정으로 유지됩니다. 또한, 특정 모델을 훈련 시 제외할 수 있습니다.\n", "\n", "아래 코드 셀에서는 NN(Neural Network), GBM(Gradient Boosted Tree) 모델에 대해 아래의 하이퍼파라메터 조합을 설정하여 훈련을 수행하면서,\n", "RF(Random Forest), XT(ExtraTress)를 제외하는 예시를 보여주고 있습니다.\n", "\n", "\n", "### Pre-defined presets\n", "\n", "AutoGluon은 사전 정의된 6종류의 프리셋을 지원하고 있으며, 각 프리셋 설정에 적합한 하이퍼파라메터 튜닝이 자동으로 수행됩니다.\n", "- `best_quality`\n", "- `best_quality_with_high_quality_refit`\n", "- `high_quality_fast_inference_only_refit`\n", "- `good_quality_faster_inference_only_refit`\n", "- `medium_quality_faster_train`\n", "- `optimize_for_deployment`\n", "\n", "best_quality의 성능이 가장 높지만, 훈련 시간이 오래 걸리고 스태킹(stacking)을 위한 디스크 공간을 많이 차지하므로, 상황에 따라 적절한 프리셋을 선택하세요." ] }, { "cell_type": "code", "execution_count": 13, "id": "8328d705", "metadata": {}, "outputs": [], "source": [ "import autogluon.core as ag\n", "\n", "nn_options = { # specifies non-default hyperparameter values for neural network models\n", " 'num_epochs': 5, # number of training epochs (controls training time of NN models)\n", " 'learning_rate': ag.space.Real(1e-4, 1e-2, default=5e-4, log=True), # learning rate used in training (real-valued hyperparameter searched on log-scale)\n", " 'activation': ag.space.Categorical('relu', 'softrelu'), # activation function used in NN (categorical hyperparameter, default = first entry)\n", " 'dropout_prob': ag.space.Real(0.0, 0.5, default=0.1), # dropout probability (real-valued hyperparameter)\n", "}\n", "\n", "gbm_options = { # specifies non-default hyperparameter values for lightGBM gradient boosted trees\n", " 'num_boost_round': 100, # number of boosting rounds (controls training time of GBM models)\n", " 'num_leaves': ag.space.Int(lower=26, upper=66, default=36), # number of leaves in trees (integer hyperparameter)\n", "}\n", "\n", "hparams = { # hyperparameters of each model type\n", " 'GBM': gbm_options,\n", " 'NN_TORCH': nn_options, # NOTE: comment this line out if you get errors on Mac OSX\n", "} # When these keys are missing from hyperparameters dict, no models of that type are trained\n" ] }, { "cell_type": "code", "execution_count": 14, "id": "810fb118", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "NaN or Inf found in input tensor.\n", "2022-08-29 10:15:01,737\tINFO stopper.py:363 -- Reached timeout of 53.971181058883666 seconds. Stopping all trials.\n" ] } ], "source": [ "time_limit = 2*60 # train various models for ~2 min\n", "metric = 'accuracy'\n", "save_path = 'ag-01-binary-hpo'\n", "verbosity = 0\n", "\n", "predictor = TabularPredictor(label=label, eval_metric=metric, path=save_path, verbosity=verbosity).fit(\n", " train_data, \n", " #tuning_data=val_data, \n", " time_limit=time_limit,\n", " hyperparameters=hparams, \n", " hyperparameter_tune_kwargs='auto',\n", " excluded_model_types=['RF', 'XT']\n", ")" ] }, { "cell_type": "code", "execution_count": 15, "id": "09d17eec", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 <=50K\n", "1 <=50K\n", "2 >50K\n", "3 <=50K\n", "4 <=50K\n", "Name: class, dtype: object" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y_pred = predictor.predict(test_data_nolab)\n", "y_pred.head()" ] }, { "cell_type": "markdown", "id": "a6eb0e26", "metadata": {}, "source": [ "앞 섹션보다 예측 성능이 더 좋습니다. 다만, 하이퍼파라메터 튜닝을 너무 신봉하지는 마세요. 현업에서는 데이터 전처리가 훨씬 중요합니다!" ] }, { "cell_type": "code", "execution_count": 16, "id": "6ca04b71", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
modelscore_valpred_time_valfit_timepred_time_val_marginalfit_time_marginalstack_levelcan_inferfit_order
0LightGBM/T1880.880.0166680.1847020.0166680.1847021True188
1WeightedEnsemble_L20.880.0171950.4786970.0005280.2939952True540
2LightGBM/T900.870.0051220.1631310.0051220.1631311True90
3LightGBM/T1280.870.0052030.1980010.0052030.1980011True128
4LightGBM/T1180.870.0052400.1808230.0052400.1808231True118
..............................
535LightGBM/T1540.730.0051160.1658510.0051160.1658511True154
536LightGBM/T2450.730.0136020.1617250.0136020.1617251True245
537NeuralNetTorch/56a572200.730.0200640.3352740.0200640.3352741True301
538NeuralNetTorch/562f04dc0.730.0217450.3309550.0217450.3309551True297
539NeuralNetTorch/6e3424860.730.0341880.3702570.0341880.3702571True530
\n", "

540 rows × 9 columns

\n", "
" ], "text/plain": [ " model score_val pred_time_val fit_time \\\n", "0 LightGBM/T188 0.88 0.016668 0.184702 \n", "1 WeightedEnsemble_L2 0.88 0.017195 0.478697 \n", "2 LightGBM/T90 0.87 0.005122 0.163131 \n", "3 LightGBM/T128 0.87 0.005203 0.198001 \n", "4 LightGBM/T118 0.87 0.005240 0.180823 \n", ".. ... ... ... ... \n", "535 LightGBM/T154 0.73 0.005116 0.165851 \n", "536 LightGBM/T245 0.73 0.013602 0.161725 \n", "537 NeuralNetTorch/56a57220 0.73 0.020064 0.335274 \n", "538 NeuralNetTorch/562f04dc 0.73 0.021745 0.330955 \n", "539 NeuralNetTorch/6e342486 0.73 0.034188 0.370257 \n", "\n", " pred_time_val_marginal fit_time_marginal stack_level can_infer \\\n", "0 0.016668 0.184702 1 True \n", "1 0.000528 0.293995 2 True \n", "2 0.005122 0.163131 1 True \n", "3 0.005203 0.198001 1 True \n", "4 0.005240 0.180823 1 True \n", ".. ... ... ... ... \n", "535 0.005116 0.165851 1 True \n", "536 0.013602 0.161725 1 True \n", "537 0.020064 0.335274 1 True \n", "538 0.021745 0.330955 1 True \n", "539 0.034188 0.370257 1 True \n", "\n", " fit_order \n", "0 188 \n", "1 540 \n", "2 90 \n", "3 128 \n", "4 118 \n", ".. ... \n", "535 154 \n", "536 245 \n", "537 301 \n", "538 297 \n", "539 530 \n", "\n", "[540 rows x 9 columns]" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "predictor.leaderboard(silent=True)" ] }, { "cell_type": "code", "execution_count": 17, "id": "e36a12ec", "metadata": {}, "outputs": [], "source": [ "#predictor.delete_models(models_to_keep=[], dry_run=False)" ] }, { "cell_type": "markdown", "id": "da50d4cb", "metadata": {}, "source": [ "
\n", "\n", "## 3. Model Distillation\n", "\n", "단일 모델은 일반적으로 weighted/stacked/bagged 앙상블 모델보다 정확도가 낮습니다. 이 때, distillation(증류) 기법을 사용해서 단일 모델의 계산 이점을 유지하면서 앙상블의 성능을 유지할 수 있습니다. 아이디어는 매우 간단하며, 앙상블 모델을 teacher 모델로 하여 teacher 모델의 logit 예측값을 단일 student 모델의 예측값 분포와 유사하게 훈련합니다." ] }, { "cell_type": "code", "execution_count": 18, "id": "a19f5a1f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['LightGBM_DSTL', 'NeuralNetMXNet_DSTL', 'CatBoost_DSTL', 'RandomForestMSE_DSTL', 'WeightedEnsemble_L2_DSTL']\n" ] } ], "source": [ "student_models = predictor.distill(time_limit=30) # specify much longer time limit in real applications\n", "print(student_models)" ] }, { "cell_type": "code", "execution_count": 19, "id": "e0f50b04", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "predictions from LightGBM_DSTL: [' <=50K', ' <=50K', ' >50K', ' <=50K', ' <=50K']\n" ] } ], "source": [ "preds_student = predictor.predict(test_data_nolab, model=student_models[0])\n", "print(f\"predictions from {student_models[0]}:\", list(preds_student)[:5])" ] } ], "metadata": { "kernelspec": { "display_name": "conda_pytorch_p38", "language": "python", "name": "conda_pytorch_p38" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.12" } }, "nbformat": 4, "nbformat_minor": 5 }