Start#
In this guide, we’ll show how you can use EvalML to automatically find the best pipeline for predicting whether or not a credit card transaction is fradulent. Along the way, we’ll highlight EvalML’s built-in tools and features for understanding and interacting with the search process.
[1]:
import evalml
from evalml import AutoMLSearch
from evalml.utils import infer_feature_types
First, we load in the features and outcomes we want to use to train our model.
[2]:
X, y = evalml.demos.load_fraud(n_rows=250)
Number of Features
Boolean 1
Categorical 6
Numeric 5
Number of training examples: 250
Targets
False 88.40%
True 11.60%
Name: count, dtype: object
/home/docs/checkouts/readthedocs.org/user_builds/feature-labs-inc-evalml/envs/stable/lib/python3.9/site-packages/woodwork/type_sys/utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
pd.to_datetime(
/home/docs/checkouts/readthedocs.org/user_builds/feature-labs-inc-evalml/envs/stable/lib/python3.9/site-packages/woodwork/type_sys/utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
pd.to_datetime(
/home/docs/checkouts/readthedocs.org/user_builds/feature-labs-inc-evalml/envs/stable/lib/python3.9/site-packages/woodwork/type_sys/utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
pd.to_datetime(
/home/docs/checkouts/readthedocs.org/user_builds/feature-labs-inc-evalml/envs/stable/lib/python3.9/site-packages/woodwork/type_sys/utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
pd.to_datetime(
/home/docs/checkouts/readthedocs.org/user_builds/feature-labs-inc-evalml/envs/stable/lib/python3.9/site-packages/woodwork/type_sys/utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
pd.to_datetime(
/home/docs/checkouts/readthedocs.org/user_builds/feature-labs-inc-evalml/envs/stable/lib/python3.9/site-packages/woodwork/type_sys/utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
pd.to_datetime(
/home/docs/checkouts/readthedocs.org/user_builds/feature-labs-inc-evalml/envs/stable/lib/python3.9/site-packages/woodwork/type_sys/utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
pd.to_datetime(
/home/docs/checkouts/readthedocs.org/user_builds/feature-labs-inc-evalml/envs/stable/lib/python3.9/site-packages/woodwork/type_sys/utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
pd.to_datetime(
/home/docs/checkouts/readthedocs.org/user_builds/feature-labs-inc-evalml/envs/stable/lib/python3.9/site-packages/woodwork/type_sys/utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
pd.to_datetime(
/home/docs/checkouts/readthedocs.org/user_builds/feature-labs-inc-evalml/envs/stable/lib/python3.9/site-packages/woodwork/type_sys/utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
pd.to_datetime(
First, we will clean the data. Since EvalML accepts a pandas input, it can run type inference on this data directly. Since we’d like to change the types inferred by EvalML, we can use the infer_feature_types
utility method. Here’s what we’re going to do with the following dataset:
Reformat the
expiration_date
column so it reflects a more familiar date format.Cast the
lat
andlng
columns from float to str.Use
infer_feature_types
to specify what types certain columns should be. For example, to avoid having theprovider
column be inferred as natural language text, we have specified it as a categorical column instead.
The infer_feature_types
utility method takes a pandas or numpy input and converts it to a pandas dataframe with a Woodwork accessor, providing us with flexibility to cast the data as necessary.
[3]:
X.ww["expiration_date"] = X["expiration_date"].apply(
lambda x: "20{}-01-{}".format(x.split("/")[1], x.split("/")[0])
)
X = infer_feature_types(
X,
feature_types={
"store_id": "categorical",
"expiration_date": "datetime",
"lat": "categorical",
"lng": "categorical",
"provider": "categorical",
},
)
X.ww
[3]:
Physical Type | Logical Type | Semantic Tag(s) | |
---|---|---|---|
Column | |||
card_id | int64 | Integer | ['numeric'] |
store_id | int64 | Integer | ['numeric'] |
datetime | datetime64[ns] | Datetime | [] |
amount | int64 | Integer | ['numeric'] |
currency | string | Unknown | [] |
customer_present | bool | Boolean | [] |
expiration_date | datetime64[ns] | Datetime | [] |
provider | category | Categorical | ['category'] |
lat | float64 | Double | ['numeric'] |
lng | float64 | Double | ['numeric'] |
region | category | Categorical | ['category'] |
country | category | Categorical | ['category'] |
In order to validate the results of the pipeline creation and optimization process, we will save some of our data as a holdout set.
[4]:
X_train, X_holdout, y_train, y_holdout = evalml.preprocessing.split_data(
X, y, problem_type="binary", test_size=0.2
)
Note: To provide data to EvalML, it is recommended that you initialize a woodwork accessor so that you control how EvalML will treat each feature, such as as a numeric feature, a categorical feature, a text feature or other type of feature. Consult the the Woodwork project for help on how to do this. Here, split_data()
returns dataframes with woodwork accessors.
EvalML has many options to configure the pipeline search. At the minimum, we need to define an objective function. For simplicity, we will use the F1 score in this example. However, the real power of EvalML is in using domain-specific objective functions or building your own.
Below EvalML utilizes Bayesian optimization (EvalML’s default optimizer) to search and find the best pipeline defined by the given objective.
EvalML provides a number of parameters to control the search process. max_batches
is one of the parameters which controls the stopping criterion for the AutoML search. It indicates the maximum number of rounds of AutoML to evaluate, where each round may train and score a variable number of pipelines. In this example, max_batches
is set to 1.
** Graphing methods, like AutoMLSearch, on Jupyter Notebook and Jupyter Lab require ipywidgets to be installed.
** If graphing on Jupyter Lab, jupyterlab-plotly required. To download this, make sure you have npm installed.
[5]:
automl = AutoMLSearch(
X_train=X_train,
y_train=y_train,
problem_type="binary",
objective="f1",
max_batches=2,
verbose=True,
)
AutoMLSearch will use mean CV score to rank pipelines.
Removing columns ['currency'] because they are of 'Unknown' type
When we call search()
, the search for the best pipeline will begin. There is no need to wrangle with missing data or categorical variables as EvalML includes various preprocessing steps (like imputation, one-hot encoding, feature selection) to ensure you’re getting the best results. As long as your data is in a single table, EvalML can handle it. If not, you can reduce your data to a single table by utilizing Featuretools and its Entity Sets.
You can find more information on pipeline components and how to integrate your own custom pipelines into EvalML here.
[6]:
automl.search(interactive_plot=False)
*****************************
* Beginning pipeline search *
*****************************
Optimizing for F1.
Greater score is better.
Using SequentialEngine to train and score pipelines.
Searching up to 2 batches for a total of None pipelines.
Allowed model families:
Evaluating Baseline Pipeline: Mode Baseline Binary Classification Pipeline
Mode Baseline Binary Classification Pipeline:
Starting cross validation
Finished cross validation - mean F1: 0.000
*****************************
* Evaluating Batch Number 1 *
*****************************
Random Forest Classifier w/ Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + One Hot Encoder + Oversampler + RF Classifier Select From Model:
Starting cross validation
Finished cross validation - mean F1: 0.731
*****************************
* Evaluating Batch Number 2 *
*****************************
LightGBM Classifier w/ Label Encoder + Select Columns By Type Transformer + Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + Select Columns Transformer + Select Columns Transformer + Label Encoder + Imputer + One Hot Encoder + Oversampler:
Starting cross validation
Finished cross validation - mean F1: 0.643
Extra Trees Classifier w/ Label Encoder + Select Columns By Type Transformer + Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + Select Columns Transformer + Select Columns Transformer + Label Encoder + Imputer + One Hot Encoder + Oversampler:
Starting cross validation
Finished cross validation - mean F1: 0.340
Elastic Net Classifier w/ Label Encoder + Select Columns By Type Transformer + Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + Standard Scaler + Select Columns Transformer + Select Columns Transformer + Label Encoder + Imputer + One Hot Encoder + Standard Scaler + Oversampler:
Starting cross validation
Finished cross validation - mean F1: 0.358
XGBoost Classifier w/ Label Encoder + Select Columns By Type Transformer + Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + Select Columns Transformer + Select Columns Transformer + Label Encoder + Imputer + One Hot Encoder + Oversampler:
Starting cross validation
Finished cross validation - mean F1: 0.580
Logistic Regression Classifier w/ Label Encoder + Select Columns By Type Transformer + Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + Standard Scaler + Select Columns Transformer + Select Columns Transformer + Label Encoder + Imputer + One Hot Encoder + Standard Scaler + Oversampler:
Starting cross validation
Finished cross validation - mean F1: 0.329
Search finished after 20.54 seconds
Best pipeline: Random Forest Classifier w/ Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + One Hot Encoder + Oversampler + RF Classifier Select From Model
Best pipeline F1: 0.730769
[6]:
{1: {'Random Forest Classifier w/ Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + One Hot Encoder + Oversampler + RF Classifier Select From Model': 3.441507577896118,
'Total time of batch': 3.571688652038574},
2: {'LightGBM Classifier w/ Label Encoder + Select Columns By Type Transformer + Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + Select Columns Transformer + Select Columns Transformer + Label Encoder + Imputer + One Hot Encoder + Oversampler': 2.4942121505737305,
'Extra Trees Classifier w/ Label Encoder + Select Columns By Type Transformer + Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + Select Columns Transformer + Select Columns Transformer + Label Encoder + Imputer + One Hot Encoder + Oversampler': 3.1688342094421387,
'Elastic Net Classifier w/ Label Encoder + Select Columns By Type Transformer + Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + Standard Scaler + Select Columns Transformer + Select Columns Transformer + Label Encoder + Imputer + One Hot Encoder + Standard Scaler + Oversampler': 3.0693318843841553,
'XGBoost Classifier w/ Label Encoder + Select Columns By Type Transformer + Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + Select Columns Transformer + Select Columns Transformer + Label Encoder + Imputer + One Hot Encoder + Oversampler': 2.312605619430542,
'Logistic Regression Classifier w/ Label Encoder + Select Columns By Type Transformer + Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + Standard Scaler + Select Columns Transformer + Select Columns Transformer + Label Encoder + Imputer + One Hot Encoder + Standard Scaler + Oversampler': 4.498785495758057,
'Total time of batch': 16.33537983894348}}
If you would like to suppress stdout output, set verbose=False
. This is also the default behavior for AutoMLSearch
if verbose
is not specified.
Also, if you would like to see the interactive plot update dynamically over time as the search progresses, either remove the parameter or set interactive_plot=True
. This is the default setting for search()
if interactive_plot
is not specified (it is set to False here due to documentation workaround).
[7]:
automl = AutoMLSearch(
X_train=X_train,
y_train=y_train,
problem_type="binary",
objective="f1",
max_batches=2,
verbose=False,
)
automl.search()
[7]:
{1: {'Random Forest Classifier w/ Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + One Hot Encoder + Oversampler + RF Classifier Select From Model': 3.6073479652404785,
'Total time of batch': 3.733790636062622},
2: {'LightGBM Classifier w/ Label Encoder + Select Columns By Type Transformer + Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + Select Columns Transformer + Select Columns Transformer + Label Encoder + Imputer + One Hot Encoder + Oversampler': 2.3472039699554443,
'Extra Trees Classifier w/ Label Encoder + Select Columns By Type Transformer + Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + Select Columns Transformer + Select Columns Transformer + Label Encoder + Imputer + One Hot Encoder + Oversampler': 3.327451467514038,
'Elastic Net Classifier w/ Label Encoder + Select Columns By Type Transformer + Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + Standard Scaler + Select Columns Transformer + Select Columns Transformer + Label Encoder + Imputer + One Hot Encoder + Standard Scaler + Oversampler': 3.0721278190612793,
'XGBoost Classifier w/ Label Encoder + Select Columns By Type Transformer + Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + Select Columns Transformer + Select Columns Transformer + Label Encoder + Imputer + One Hot Encoder + Oversampler': 2.17337703704834,
'Logistic Regression Classifier w/ Label Encoder + Select Columns By Type Transformer + Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + Standard Scaler + Select Columns Transformer + Select Columns Transformer + Label Encoder + Imputer + One Hot Encoder + Standard Scaler + Oversampler': 3.2876927852630615,
'Total time of batch': 14.998577356338501}}
We also provide a standalone search
method which does all of the above in a single line, and returns the AutoMLSearch
instance and data check results. If there were data check errors, AutoML will not be run and no AutoMLSearch
instance will be returned.
After the search is finished we can view all of the pipelines searched, ranked by score. Internally, EvalML performs cross validation to score the pipelines. If it notices a high variance across cross validation folds, it will warn you. EvalML also provides additional data checks to analyze your data to assist you in producing the best performing pipeline.
[8]:
automl.rankings
[8]:
id | pipeline_name | search_order | ranking_score | mean_cv_score | standard_deviation_cv_score | percent_better_than_baseline | high_variance_cv | parameters | |
---|---|---|---|---|---|---|---|---|---|
0 | 1 | Random Forest Classifier w/ Label Encoder + Dr... | 1 | 0.730769 | 0.730769 | 0.214145 | 73.076923 | False | {'Label Encoder': {'positive_label': None}, 'D... |
1 | 2 | LightGBM Classifier w/ Label Encoder + Select ... | 2 | 0.642735 | 0.642735 | 0.170495 | 64.273504 | False | {'Label Encoder': {'positive_label': None}, 'N... |
2 | 5 | XGBoost Classifier w/ Label Encoder + Select C... | 5 | 0.579616 | 0.579616 | 0.122378 | 57.961646 | False | {'Label Encoder': {'positive_label': None}, 'N... |
3 | 4 | Elastic Net Classifier w/ Label Encoder + Sele... | 4 | 0.358333 | 0.358333 | 0.150693 | 35.833333 | False | {'Label Encoder': {'positive_label': None}, 'N... |
4 | 3 | Extra Trees Classifier w/ Label Encoder + Sele... | 3 | 0.339683 | 0.339683 | 0.057407 | 33.968254 | False | {'Label Encoder': {'positive_label': None}, 'N... |
5 | 6 | Logistic Regression Classifier w/ Label Encode... | 6 | 0.328571 | 0.328571 | 0.154524 | 32.857143 | False | {'Label Encoder': {'positive_label': None}, 'N... |
6 | 0 | Mode Baseline Binary Classification Pipeline | 0 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | False | {'Label Encoder': {'positive_label': None}, 'B... |
If we are interested in see more details about the pipeline, we can view a summary description using the id
from the rankings table:
[9]:
automl.describe_pipeline(3)
*******************************************************************************************************************************************************************************************************************************************************************************
* Extra Trees Classifier w/ Label Encoder + Select Columns By Type Transformer + Label Encoder + Drop Columns Transformer + DateTime Featurizer + Imputer + Select Columns Transformer + Select Columns Transformer + Label Encoder + Imputer + One Hot Encoder + Oversampler *
*******************************************************************************************************************************************************************************************************************************************************************************
Problem Type: binary
Model Family: Extra Trees
Pipeline Steps
==============
1. Label Encoder
* positive_label : None
2. Select Columns By Type Transformer
* column_types : ['category', 'EmailAddress', 'URL']
* exclude : True
3. Label Encoder
* positive_label : None
4. Drop Columns Transformer
* columns : ['currency']
5. DateTime Featurizer
* features_to_extract : ['year', 'month', 'day_of_week', 'hour']
* encode_as_categories : False
* time_index : None
6. Imputer
* categorical_impute_strategy : most_frequent
* numeric_impute_strategy : mean
* boolean_impute_strategy : most_frequent
* categorical_fill_value : None
* numeric_fill_value : None
* boolean_fill_value : None
7. Select Columns Transformer
* columns : ['card_id', 'store_id', 'amount', 'customer_present', 'lat', 'lng', 'datetime_month', 'datetime_day_of_week', 'datetime_hour', 'expiration_date_year', 'expiration_date_day_of_week']
8. Select Columns Transformer
* columns : ['provider', 'region', 'country']
9. Label Encoder
* positive_label : None
10. Imputer
* categorical_impute_strategy : most_frequent
* numeric_impute_strategy : mean
* boolean_impute_strategy : most_frequent
* categorical_fill_value : None
* numeric_fill_value : None
* boolean_fill_value : None
11. One Hot Encoder
* top_n : 10
* features_to_encode : None
* categories : None
* drop : if_binary
* handle_unknown : ignore
* handle_missing : error
12. Oversampler
* sampling_ratio : 0.25
* k_neighbors_default : 5
* n_jobs : -1
* sampling_ratio_dict : None
* categorical_features : [3, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40]
* k_neighbors : 5
13. Extra Trees Classifier
* n_estimators : 100
* max_features : sqrt
* max_depth : 6
* min_samples_split : 2
* min_weight_fraction_leaf : 0.0
* n_jobs : -1
Training
========
Training for binary problems.
Objective to optimize binary classification pipeline thresholds for: <evalml.objectives.standard_metrics.F1 object at 0x7f1d29488700>
Total training time (including CV): 3.3 seconds
Cross Validation
----------------
F1 MCC Binary Log Loss Binary Gini AUC Precision Balanced Accuracy Binary Accuracy Binary # Training # Validation
0 0.400 0.308 0.336 0.445 0.722 0.333 0.682 0.821 133 67
1 0.333 0.226 0.342 0.364 0.682 0.250 0.648 0.761 133 67
2 0.286 0.201 0.355 0.361 0.680 0.286 0.600 0.848 134 66
mean 0.340 0.245 0.344 0.390 0.695 0.290 0.644 0.810 - -
std 0.057 0.056 0.010 0.048 0.024 0.042 0.041 0.045 - -
coef of var 0.169 0.229 0.029 0.122 0.034 0.144 0.064 0.055 - -
We can also view the pipeline parameters directly:
[10]:
pipeline = automl.get_pipeline(3)
print(pipeline.parameters)
{'Label Encoder': {'positive_label': None}, 'Numeric Pipeline - Select Columns By Type Transformer': {'column_types': ['category', 'EmailAddress', 'URL'], 'exclude': True}, 'Numeric Pipeline - Label Encoder': {'positive_label': None}, 'Numeric Pipeline - Drop Columns Transformer': {'columns': ['currency']}, 'Numeric Pipeline - DateTime Featurizer': {'features_to_extract': ['year', 'month', 'day_of_week', 'hour'], 'encode_as_categories': False, 'time_index': None}, 'Numeric Pipeline - Imputer': {'categorical_impute_strategy': 'most_frequent', 'numeric_impute_strategy': 'mean', 'boolean_impute_strategy': 'most_frequent', 'categorical_fill_value': None, 'numeric_fill_value': None, 'boolean_fill_value': None}, 'Numeric Pipeline - Select Columns Transformer': {'columns': ['card_id', 'store_id', 'amount', 'customer_present', 'lat', 'lng', 'datetime_month', 'datetime_day_of_week', 'datetime_hour', 'expiration_date_year', 'expiration_date_day_of_week']}, 'Categorical Pipeline - Select Columns Transformer': {'columns': ['provider', 'region', 'country']}, 'Categorical Pipeline - Label Encoder': {'positive_label': None}, 'Categorical Pipeline - Imputer': {'categorical_impute_strategy': 'most_frequent', 'numeric_impute_strategy': 'mean', 'boolean_impute_strategy': 'most_frequent', 'categorical_fill_value': None, 'numeric_fill_value': None, 'boolean_fill_value': None}, 'Categorical Pipeline - One Hot Encoder': {'top_n': 10, 'features_to_encode': None, 'categories': None, 'drop': 'if_binary', 'handle_unknown': 'ignore', 'handle_missing': 'error'}, 'Oversampler': {'sampling_ratio': 0.25, 'k_neighbors_default': 5, 'n_jobs': -1, 'sampling_ratio_dict': None, 'categorical_features': [3, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40], 'k_neighbors': 5}, 'Extra Trees Classifier': {'n_estimators': 100, 'max_features': 'sqrt', 'max_depth': 6, 'min_samples_split': 2, 'min_weight_fraction_leaf': 0.0, 'n_jobs': -1}}
We can now select the best pipeline and score it on our holdout data:
[11]:
pipeline = automl.best_pipeline
pipeline.score(X_holdout, y_holdout, ["f1"])
[11]:
OrderedDict([('F1', 0.6666666666666666)])
We can also visualize the structure of the components contained by the pipeline:
[12]:
pipeline.graph()
[12]: