Wide & Deep Recommender model.
The following sections provide greater details of the dataset, running training, and the training results.
Scripts and sample code
These are the important scripts in this repository:
main.py- Python script for training the Wide & Deep recommender model.scripts/preproc.sh- Bash script for Outbrain dataset preparation for training, preprocessing, and saving into NVTabular format.data/outbrain/dataloader.py- Python file containing NVTabular data loaders for train and evaluation set.data/outbrain/features.py- Python file describing the request and item level features as well as embedding dimensions and hash buckets' sizes.trainer/model/widedeep.py- Python file with model definition.trainer/run.py- Python file with training and evaluation setup.
Parameters
These are model parameters in the main.py script:
| Scope | parameter | Comment | Default Value |
|---|---|---|---|
| location of datasets | --dataset_path | Dataset directory, the base for paths in feature spec | /outbrain/data |
| location of datasets | --fspec_file | Path to the feature spec file, relative to dataset_path | feature_spec.yaml |
| location of datasets | --embedding_sizes_file | Path to the file containing a dictionary of embedding sizes for categorical features | data/outbrain/embedding_sizes.json |
| location of datasets | --use_checkpoint | Use checkpoint stored in model_dir path | False |
| location of datasets | --model_dir MODEL_DIR | Destination where the model checkpoint will be saved | /outbrain/checkpoints |
| location of datasets | --results_dir RESULTS_DIR | Directory to store training results | /results |
| location of datasets | --log_filename LOG_FILENAME | Name of the file to store dlloger output | log.json |
| training parameters | --global_batch_size GLOBAL_BATCH_SIZE | Total size of training batch | 131072 |
| training parameters | --eval_batch_size EVAL_BATCH_SIZE | Total size of evaluation batch | 131072 |
| training parameters | --num_epochs NUM_EPOCHS | Number of training epochs | 20 |
| training parameters | --cpu | Run computations on the CPU | Currently not supported |
| training parameters | --amp | Enable automatic mixed precision conversion | False |
| training parameters | --xla | Enable XLA conversion | False |
| training parameters | --linear_learning_rate LINEAR_LEARNING_RATE | Learning rate for linear model | 0.02 |
| training parameters | --deep_learning_rate DEEP_LEARNING_RATE | Learning rate for deep model | 0.00012 |
| training parameters | --deep_warmup_epochs DEEP_WARMUP_EPOCHS | Number of learning rate warmup epochs for deep model | 6 |
| model construction | --deep_hidden_units DEEP_HIDDEN_UNITS [DEEP_HIDDEN_UNITS ...] | Hidden units per layer for deep model, separated by spaces | [1024, 1024, 1024, 1024, 1024] |
| model construction | --deep_dropout DEEP_DROPOUT | Dropout regularization for deep model | 0.1 |
| model construction | --combiner {mean,sum} | Type of aggregation used for multi hot categorical features | sum |
| run mode parameters | --evaluate | Only perform an evaluation on the validation dataset, don't train | False |
| run mode parameters | --benchmark | Run training or evaluation benchmark to collect performance metrics | False |
| run mode parameters | --benchmark_warmup_steps BENCHMARK_WARMUP_STEPS | Number of warmup steps before the start of the benchmark | 500 |
| run mode parameters | --benchmark_steps BENCHMARK_STEPS | Number of steps for performance benchmark | 1000 |
| run mode parameters | --affinity {all,single,single_unique, unique_interleaved,unique_contiguous,disabled} | Type of CPU affinity | unique_interleaved |
Command-line options
To display the full list of available options and their descriptions, use the -h or --help command-line option:
python main.py -h
Getting the data
The Outbrain dataset can be downloaded from Kaggle (requires a Kaggle account).
Dataset guidelines
The dataset contains a sample of users' page views and clicks, as observed on multiple publisher sites. Viewed pages and clicked recommendations have additional semantic attributes of the documents. The dataset contains sets of content recommendations served to a specific user in a specific context. Each context (i.e., a set of recommended ads) is given a display_id. In each such recommendation set, the user has clicked on exactly one of the ads.
The original data is stored in several separate files:
page_views.csv- log of users visiting documents (2B rows, ~100GB uncompressed)clicks_train.csv- data showing which ad was clicked in each recommendation set (87M rows)clicks_test.csv- used only for the submission in the original Kaggle contestevents.csv- metadata about the context of each recommendation set (23M rows)promoted_content.csv- metadata about the adsdocument_meta.csv,document_topics.csv,document_entities.csv,document_categories.csv- metadata about the documents
During the preprocessing stage, the data is transformed into 87M rows of tabular data of 29 features. The dataset is split into training and evaluation parts that have approx 60M and approx 27M rows, respectively. Splitting into train and eval is done in this way so that a random 80% of daily events for the first 10 days of the dataset form a training set and the remaining part (20% of events daily for the first 10 days and all events in the last two days) form an evaluation set. Eventually, the dataset is saved in NVTabular parquet format.
Dataset preprocessing
Dataset preprocessing aims to create a total of 29 features: 16 categorical and 13 numerical. These features are obtained from the original Outbrain dataset in NVTabular preprocessing.
NVTabular GPU preprocessing
The NVTabular dataset is preprocessed using the script provided in data/outbrain/nvtabular. The workflow consists of:
- separating out the validation set for cross-validation
- filling missing data with the mode, median, or imputed values most frequent value
- joining click data, ad metadata, and document category, topic, and entity tables to create an enriched table.joining the tables for the ad clicks data
- computing seven click-through rates (CTR) for ads grouped by seven features in different contexts
- computing attribute cosine similarity between the landing page and ad to be featured on the page features of the clicked ads and the viewed ads
- extracting multi-hot categorical values
- math transformations of the numeric features (logarithmic, normalization)
- categorifying data using hash-bucketing
- storing the result in a Parquet format
Most of the code describing operations in this workflow is in data/outbrain/nvtabular/utils/workflow.py and leverage NVTabular v0.7.1. As stated in its repository, NVTabular, a component of NVIDIA Merlin Open Beta, is a feature engineering and preprocessing library for tabular data that is designed to quickly and easily manipulate terabyte-scale datasets and train deep learning based recommender systems. It provides a high-level abstraction to simplify code and accelerates computation on the GPU using the RAPIDS Dask-cuDF library.
The NVTabular Outbrain workflow has been successfully tested on DGX-1 V100 and DGX A100 for single and multi GPU preprocessing.
For more information about NVTabular, refer to the NVTabular documentation.
BYO dataset
This implementation supports using other datasets thanks to BYO dataset functionality. The BYO dataset functionality allows users to plug in their dataset in a common fashion for all Recommender models that support this functionality. Using BYO dataset functionality, the user does not have to modify the source code of the model thanks to the Feature Specification file. For general information on how the BYO dataset works, refer to the BYO dataset overview section.
There are three ways to plug in the user's dataset:
1. Provide an unprocessed dataset in a format matching the one used by Outbrain, then use the preprocessing for Outbrain. Feature Specification file is then generated automatically.
The required format of the user's dataset is described [above](#dataset-guidelines)The correct processed dataset files, together with the Feature Specification yaml file, will be generated automatically by preprocessing script.
A configuration file specifying default embedding sizes for the outbrain dataset is available at data/outbrain/embedding_sizes.json.
For an example of using this process, refer to the Quick Start Guide
2. Provide a CSV containing preprocessed data and a simplified Feature Specification yaml file, then transcode the data with `transcode.py` script
This option should be used if the user has their own CSV file with a preprocessed dataset they want to train on.The required format of the user's dataset is:
- CSV files containing the data, already split into train and test sets.
- Feature Specification yaml file describing the layout of the CSV data
For an example of a feature specification file, refer to the tests/feature_specs folder.
The CSV containing the data:
- should be already split into train and test
- should contain no header
- should contain one column per feature, in the order specified by the list of features for that chunk in the source_spec section of the feature specification file
- one-hot categorical features should be non-negative integers in the range [0,cardinality-1] if cardinality is specified
- numerical features should be lists of non-negative integers in the range [0, cardinality-1] if cardinality is specified The Feature Specification yaml file:
- needs to describe the layout of data in CSV files
- must contain information about cardinalities for categorical features.
The following example shows how to use this way of plugging the user's dataset:
Prepare your data and save the path:
DATASET_PARENT_DIRECTORY=/raid/wd2
Build the Wide & Deep image with:
docker build . -t wd2
Launch the container with:
docker run --runtime=nvidia --gpus=all --rm -it --ipc=host -v ${DATASET_PARENT_DIRECTORY}:/outbrain wd2 bash
If you are just testing the process, you can create synthetic csv data:
select or write a feature specification file:
cp tests/feature_specs/fspec_csv.yaml /outbrain/feature_spec.yaml
python gen_csv.py --feature_spec_in /outbrain/feature_spec.yaml --output /outbrain --size 393216
Convert the data:
mkdir /outbrain/data
python transcode.py --input /outbrain --output /outbrain/data --chunk_size 16384 # to get 8 partitions out of 131072 rows
You may need to tune the --chunk_size parameter. Higher values speed up the conversion but require more RAM.
This will convert the data from /outbrain and save the output in /outbrain/data.
A feature specification file describing the new data will be automatically generated.
If you are using a different dataset than Outbrain, you need to specify the embedding sizes to use for each categorical feature. Please refer to data/outbrain/embedding_sizes.json for an example or generate random sizes with gen_embedding_sizes.py. Specify the location using the --embedding_sizes_file flag.
export EMBEDDING_SIZES_FILE="data/outbrain/embedding_sizes.json"
If your dataset does not contain a MAP aggregation channel (refer to details), use the --disable_map_calculation flag to enable compatibility. To run the training on one GPU:
horovodrun -np 1 sh hvd_wrapper.sh python main.py --dataset_path /outbrain/data --disable_map_calculation --embedding_sizes_file ${EMBEDDING_SIZES_FILE}
- multi-GPU:
horovodrun -np 8 sh hvd_wrapper.sh python main.py --dataset_path /outbrain/data --disable_map_calculation --embedding_sizes_file ${EMBEDDING_SIZES_FILE}
3. Provide a fully preprocessed dataset, saved in parquet files, and a Feature Specification yaml file
This is the option to choose if you want full control over preprocessing and/or want to preprocess data directly to the target format.Your final output must contain a Feature Specification yaml describing data and file layout. For an example feature specification file, refer to the file resulting from Outbrain preprocessing or the files in tests/feature_specs.
For details, refer to the BYO dataset overview section.
Channel definitions and requirements
This model defines three channels:
- multihot_categorical, accepting an arbitrary number of features
- onehot_categorical, accepting an arbitrary number of features
- numerical, accepting an arbitrary number of features
- label, accepting a single feature
- map, accepting zero or one feature
The training script expects two mappings:
- train
- test
For performance reasons:
- The only supported dataset type is parquet. CSV is supported through transcoding.
- Each chunk must contain at least as many parts as there are workers.
- Only integer types are supported for categorical features
- Only floating point types are supported for numerical features
The MAP channel is optional. If present, it is expected to be provided only for the test mapping. Rows with common values are aggregated into a list, and MAP@12 is calculated during evaluation. Please note that the MAP implementation used in this repository may not be correct for datasets other than Outbrain preprocessed with the included script.
BYO dataset constraints for the model
There are the following constraints of BYO dataset functionality for this model:
- The performance of the model depends on the dataset size. Generally, the model should scale better for datasets containing more data points. For a smaller dataset, you might experience smaller performance improvements than those reported for Outbrain
- Using other datasets might require tuning some hyperparameters (for example, learning rate, beta1, and beta2) to reach the desired accuracy.
Training process
The training can be started by running the main.py script. By default, the script is in train mode. Other training-related configs are also present in the trainer/utils/arguments.py and can be seen using the command python main.py -h. Training happens with a NVTabular data loader on a NVTabular training dataset files specified by feature spec. Training is run for --num_epochs epochs with a global batch size of --global_batch_size in strong scaling mode (i.e., the effective batch size per GPU equals global_batch_size/gpu_count).
The model:
tf.keras.experimental.WideDeepModel consists of a wide part and deep part with a sigmoid activation in the output layer (refer to Figure 1) for reference and trainer/model/widedeep.py for model definition).
During training (default configuration): Two separate optimizers are used to optimize the wide and the deep part of the network:
- FTLR (Follow the Regularized Leader) optimizer is used to optimize the wide part of the network.
- RMSProp optimizer is used to optimize the deep part of the network.
Checkpoint of the model:
- Can be loaded at the beginning of training when
--use_checkpointis set. - is saved into
--model_dirafter each training epoch. Only the last checkpoint is kept. - Contains information about the number of training epochs.
The model is evaluated on an evaluation dataset after every training epoch training log is displayed in the console and stored in --log_filename.
Every 100 batches with training metrics: bce loss
Every epoch after training, evaluation metrics are logged: bce loss and MAP@12 value.
Evaluation process
The evaluation can be started by running the main.py --evaluation script. Evaluation is done on the NVTabular parquet dataset specified by feature spec. Other evaluation-related configs are also present in the trainer/utils/arguments.py and can be seen using the command python main.py -h.
During evaluation (--evaluation flag):
- Model is restored from a checkpoint in
--model_dirif--use_checkpointis set. - Evaluation log is displayed in the console and stored in
--log_filename. - Every 100 batches, evaluation metrics are logged: bce loss.
After the whole evaluation, the total evaluation metrics are logged: bce loss and MAP@12 value.