Repository: NVIDIA-Merlin/NVTabular (language: python)

Issue:
Add preserve_files and output_files arguments to Dataset.to_parquet
The larger goal of this PR is to add the `preserve_files` and `output_files=` arguments to `Dataset.to_parquet`.  These options can be used to specify an explicit mapping between dataset partitions and output files.  This `output_files=` argument can accept the following input types:

- **`dict`**: The user may specify a dictionary with the keys corresponding to output file names, and the values corresponding to explicit partition indices.  If the dictionary leaves out any partition indices from the dataset, that data will **not** be written to disk.  This option provides the most control over the output-file names and contents.
- **`list`**: The user may specify a list of output file names.  In this case, the dataset partitions will be divided evenly among the specified output files (with contiguous partitions remaining contiguous).
- **`int`**: The user may specify the desired number of total output files as an integer value.  In this case, the output-file names will simply correspond to `part_*`.

In order to enable explicit file-mapping preservation during full dataset/format conversion, this PR also introduces a `Dataset.file_partition_map` property for "csv" and "parquet"-backed datasets, and a `base_dataset` attribute for **all** `Dataset` objects.  When accessing the `file_partition_map` property, an explicit mapping between input/origin file names and ddf-partition indices will be extracted.  The purpose of `base_dataset` is to keep track of an "origin" dataset that can be used to extract the original file-partition mapping when `to_parquet` is called.  If the user specifies `dataset.to_parquet(..., preserve_files=True)`, the result of `base_dataset.file_partition_map` property will be used to spefify the output-file mapping.

**Notes on the "old" Algorithm**

The current version of `to_parquet` only supports an `out_files_per_proc=` argument, because the algorithm for generating output files does not rely on any coordination between distinct worker processes.  In other words, each worker is assigned some stochastic number of `_write_output_partition` tasks by the scheduler, and that worker can split/shuffle whatever data it is assigned between `out_files_per_proc` different output files.  This is an **efficient** approach because (1) all processes can operate independently with minimal synchronization overhead, and (2) there is no need to aggregate output data in memory before persisting to disk, which typically increases memory pressure.

The drawback of the existing approach is that there is no way to preserve data ordering, or to provide any form of deterministic behavior/output.  This PR is designed to tackle this limitation.  These changes do not attempt to match the raw performance of the `out_files_per_proc`-based approach (although they should still be quite performant).


<!--

Thank you for contributing to NVTabular :)

Here are some guidelines to help the review process go smoothly.

1. Please write a description in this text box of the changes that are being
   made.

2. Please ensure that you have written units tests for the changes made/features
   added.

3. If you are closing an issue please use one of the automatic closing words as
   noted here: https://help.github.com/articles/closing-issues-using-keywords/

4. If your pull request is not ready for review but you want to make use of the
   continuous integration testing facilities please label it with `[WIP]`.

5. If your pull request is ready to be reviewed without requiring additional
   work on top of it, then remove the `[WIP]` label (if present) and replace
   it with `[REVIEW]`. If assistance is required to complete the functionality,
   for example when the C/C++ code of a feature is complete but Python bindings
   are still required, then add the label `[HELP-REQ]` so that others can triage
   and assist. The additional changes then can be implemented on top of the
   same PR. If the assistance is done by members of the rapidsAI team, then no
   additional actions are required by the creator of the original PR for this,
   otherwise the original author of the PR needs to give permission to the
   person(s) assisting to commit to their personal fork of the project. If that
   doesn't happen then a new PR based on the code of the original PR can be
   opened by the person assisting, which then will be the PR that will be
   merged.

6. Once all work has been done and review has taken place please do not add
   features or make changes out of the scope of those requested by the reviewer
   (doing this just add delays as already reviewed code ends up having to be
   re-reviewed/it is hard to tell what is new etc!). Further, please do not
   rebase your branch on master/force push/rewrite history, doing any of these
   causes the context of any comments made by reviewers to be lost. If
   conflicts occur against master they should be resolved by merging master
   into the branch used for making the pull request.

Many thanks in advance for your cooperation!

-->



Discussion / hints:
<details><summary>Click to view CI Results</summary><pre>GitHub pull request #660 of commit 6c4061d49b651df9ba9c41eb3b8ac0bb5568e61e, no merge conflicts.
Running as SYSTEM
Setting status of 6c4061d49b651df9ba9c41eb3b8ac0bb5568e61e to PENDING with url http://10.20.13.93:8080/job/nvtabular_tests/1903/ and message: 'Pending'
Using context: Jenkins Unit Test Run
Building in workspace /var/jenkins_home/workspace/nvtabular_tests
using credential ghub_token
Cloning the remote Git repository
Cloning repository https://github.com/NVIDIA/NVTabular.git
 > git init /var/jenkins_home/workspace/nvtabular_tests/nvtabular # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
 > git --version # timeout=10
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
 > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/pull/660/*:refs/remotes/origin/pr/660/* # timeout=10
 > git rev-parse 6c4061d49b651df9ba9c41eb3b8ac0bb5568e61e^{commit} # timeout=10
Checking out Revision 6c4061d49b651df9ba9c41eb3b8ac0bb5568e61e (detached)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f 6c4061d49b651df9ba9c41eb3b8ac0bb5568e61e # timeout=10
Commit message: "some test coverage (more needed)"
 > git rev-list --no-walk 7d1c2f6397075a0a8358242823cb7077fe298e79 # timeout=10
First time build. Skipping changelog.
[nvtabular_tests] $ /bin/bash /tmp/jenkins824836507213313438.sh
Obtaining file:///var/jenkins_home/workspace/nvtabular_tests/nvtabular
  Installing build dependencies: started
  Installing build dependencies: finished with status 'done'
  Getting requirements to build wheel: started
  Getting requirements to build wheel: finished with status 'done'
    Preparing wheel metadata: started
    Preparing wheel metadata: finished with status 'done'
Installing collected packages: nvtabular
  Running setup.py develop for nvtabular
Successfully installed nvtabular
All done! ✨ 🍰 ✨
92 files would be left unchanged.
/conda/envs/rapids/lib/python3.8/site-packages/isort/files.py:30: UserWarning: Likely recursive symlink detected to /var/jenkins_home/workspace/nvtabular_tests/nvtabular/images
  warn(f"Likely recursive symlink detected to {resolved_path}")
============================= test session starts ==============================
platform linux -- Python 3.8.8, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /var/jenkins_home/workspace/nvtabular_tests/nvtabular, configfile: setup.cfg
plugins: cov-2.11.1, xdist-2.2.1, forked-1.3.0
collected 719 items / 2 skipped / 717 selected

tests/unit/test_column_group.py .                                        [  0%]
tests/unit/test_column_similarity.py ......                              [  0%]
tests/unit/test_dask_nvt.py ............................................ [  7%]
.....................................................................    [ 16%]
tests/unit/test_io.py .................................................. [ 23%]
..................................................................ssssss [ 33%]
ss....                                                                   [ 34%]
tests/unit/test_notebooks.py .Fs..                                       [ 35%]
tests/unit/test_ops.py ................................................. [ 42%]
........................................................................ [ 52%]
........................................................................ [ 62%]
..................................                                       [ 66%]
tests/unit/test_tf_dataloader.py ...................                     [ 69%]
tests/unit/test_tf_layers.py ........................................... [ 75%]
...................................                                      [ 80%]
tests/unit/test_tools.py ......................                          [ 83%]
tests/unit/test_torch_dataloader.py .............................s       [ 87%]
tests/unit/test_workflow.py ............................................ [ 93%]
..............................................                           [100%]

=================================== FAILURES ===================================
_____________________________ test_optimize_criteo _____________________________

tmpdir = local('/tmp/pytest-of-jenkins/pytest-3/test_optimize_criteo0')

    def test_optimize_criteo(tmpdir):
        _get_random_criteo_data(1000).to_csv(os.path.join(tmpdir, "day_0"), sep="\t", header=False)
        os.environ["INPUT_DATA_DIR"] = str(tmpdir)
        os.environ["OUTPUT_DATA_DIR"] = str(tmpdir)
    
        notebook_path = os.path.join(
            dirname(TEST_PATH),
            "examples/scaling-criteo/",
            "01-Download-Convert.ipynb",
        )
>       _run_notebook(tmpdir, notebook_path)

tests/unit/test_notebooks.py:64: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/unit/test_notebooks.py:171: in _run_notebook
    subprocess.check_output([sys.executable, script_path])
/conda/envs/rapids/lib/python3.8/subprocess.py:415: in check_output
    return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

input = None, capture_output = False, timeout = None, check = True
popenargs = (['/conda/envs/rapids/bin/python3.8', '/tmp/pytest-of-jenkins/pytest-3/test_optimize_criteo0/notebook.py'],)
kwargs = {'stdout': -1}, process = <subprocess.Popen object at 0x7fae5e1bb5b0>
stdout = b'', stderr = None, retcode = 1

    def run(*popenargs,
            input=None, capture_output=False, timeout=None, check=False, **kwargs):
        """Run command with arguments and return a CompletedProcess instance.
    
        The returned instance will have attributes args, returncode, stdout and
        stderr. By default, stdout and stderr are not captured, and those attributes
        will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them.
    
        If check is True and the exit code was non-zero, it raises a
        CalledProcessError. The CalledProcessError object will have the return code
        in the returncode attribute, and output & stderr attributes if those streams
        were captured.
    
        If timeout is given, and the process takes too long, a TimeoutExpired
        exception will be raised.
    
        There is an optional argument "input", allowing you to
        pass bytes or a string to the subprocess's stdin.  If you use this argument
        you may not also use the Popen constructor's "stdin" argument, as
        it will be used internally.
    
        By default, all communication is in bytes, and therefore any "input" should
        be bytes, and the stdout and stderr will be bytes. If in text mode, any
        "input" should be a string, and stdout and stderr will be strings decoded
        according to locale encoding, or by "encoding" if set. Text mode is
        triggered by setting any of text, encoding, errors or universal_newlines.
    
        The other arguments are the same as for the Popen constructor.
        """
        if input is not None:
            if kwargs.get('stdin') is not None:
                raise ValueError('stdin and input arguments may not both be used.')
            kwargs['stdin'] = PIPE
    
        if capture_output:
            if kwargs.get('stdout') is not None or kwargs.get('stderr') is not None:
                raise ValueError('stdout and stderr arguments may not be used '
                                 'with capture_output.')
            kwargs['stdout'] = PIPE
            kwargs['stderr'] = PIPE
    
        with Popen(*popenargs, **kwargs) as process:
            try:
                stdout, stderr = process.communicate(input, timeout=timeout)
            except TimeoutExpired as exc:
                process.kill()
                if _mswindows:
                    # Windows accumulates the output in a single blocking
                    # read() call run on child threads, with the timeout
                    # being done in a join() on those threads.  communicate()
                    # _after_ kill() is required to collect that and add it
                    # to the exception.
                    exc.stdout, exc.stderr = process.communicate()
                else:
                    # POSIX _communicate already populated the output so
                    # far into the TimeoutExpired exception.
                    process.wait()
                raise
            except:  # Including KeyboardInterrupt, communicate handled that.
                process.kill()
                # We don't call process.wait() as .__exit__ does that for us.
                raise
            retcode = process.poll()
            if check and retcode:
>               raise CalledProcessError(retcode, process.args,
                                         output=stdout, stderr=stderr)
E               subprocess.CalledProcessError: Command '['/conda/envs/rapids/bin/python3.8', '/tmp/pytest-of-jenkins/pytest-3/test_optimize_criteo0/notebook.py']' returned non-zero exit status 1.

/conda/envs/rapids/lib/python3.8/subprocess.py:516: CalledProcessError
----------------------------- Captured stderr call -----------------------------
Traceback (most recent call last):
  File "/tmp/pytest-of-jenkins/pytest-3/test_optimize_criteo0/notebook.py", line 249, in <module>
    conversion_delayed.compute(scheduler="synchronous")
  File "/conda/envs/rapids/lib/python3.8/site-packages/dask/base.py", line 281, in compute
    (result,) = compute(self, traverse=False, **kwargs)
  File "/conda/envs/rapids/lib/python3.8/site-packages/dask/base.py", line 563, in compute
    results = schedule(dsk, keys, **kwargs)
  File "/conda/envs/rapids/lib/python3.8/site-packages/dask/local.py", line 528, in get_sync
    return get_async(apply_sync, 1, dsk, keys, **kwargs)
  File "/conda/envs/rapids/lib/python3.8/site-packages/dask/local.py", line 472, in get_async
    fire_task()
  File "/conda/envs/rapids/lib/python3.8/site-packages/dask/local.py", line 457, in fire_task
    apply_async(
  File "/conda/envs/rapids/lib/python3.8/site-packages/dask/local.py", line 517, in apply_sync
    res = func(*args, **kwds)
  File "/conda/envs/rapids/lib/python3.8/site-packages/dask/local.py", line 227, in execute_task
    result = pack_exception(e, dumps)
  File "/conda/envs/rapids/lib/python3.8/site-packages/dask/local.py", line 222, in execute_task
    result = _execute_task(task, data)
  File "/conda/envs/rapids/lib/python3.8/site-packages/dask/core.py", line 121, in _execute_task
    return func(*(_execute_task(a, cache) for a in args))
  File "/tmp/pytest-of-jenkins/pytest-3/test_optimize_criteo0/notebook.py", line 98, in _convert_file
    for gdf in nvt.Dataset(
  File "/var/jenkins_home/workspace/nvtabular_tests/nvtabular/nvtabular/io/dataset.py", line 396, in to_iter
    self.to_ddf(columns=columns, shuffle=shuffle, seed=seed), indices=indices
  File "/var/jenkins_home/workspace/nvtabular_tests/nvtabular/nvtabular/io/dataset.py", line 346, in to_ddf
    _meta = _set_dtypes(ddf._meta, self.dtypes)
  File "/var/jenkins_home/workspace/nvtabular_tests/nvtabular/nvtabular/io/dataset.py", line 707, in _set_dtypes
    chunk[col] = chunk[col].astype(dtype)
  File "/conda/envs/rapids/lib/python3.8/site-packages/cudf/core/series.py", line 2190, in astype
    raise e
  File "/conda/envs/rapids/lib/python3.8/site-packages/cudf/core/series.py", line 2182, in astype
    data = self._column.astype(dtype)
  File "/conda/envs/rapids/lib/python3.8/site-packages/cudf/core/column/column.py", line 992, in astype
    elif pd.api.types.pandas_dtype(dtype).type in {
  File "/conda/envs/rapids/lib/python3.8/site-packages/pandas/core/dtypes/common.py", line 1763, in pandas_dtype
    npdtype = np.dtype(dtype)
TypeError: data type 'hex' not understood
=============================== warnings summary ===============================
tests/unit/test_dask_nvt.py: 110 warnings
tests/unit/test_io.py: 10 warnings
tests/unit/test_ops.py: 127 warnings
  /var/jenkins_home/workspace/nvtabular_tests/nvtabular/nvtabular/io/dataset.py:205: UserWarning: Initializing an NVTabular Dataset in CPU mode.This is an experimental feature with extremely limited support!
    warnings.warn(

tests/unit/test_io.py: 20 warnings
  /var/jenkins_home/workspace/nvtabular_tests/nvtabular/nvtabular/io/dataset.py:355: UserWarning: Changing an NVTabular Dataset to CPU mode.This is an experimental feature with extremely limited support!
    warnings.warn(

tests/unit/test_io.py::test_validate_dataset_bad_schema
  /var/jenkins_home/workspace/nvtabular_tests/nvtabular/nvtabular/io/parquet.py:297: UserWarning: Schema mismatch detected in column: 'a'.
    warnings.warn(msg)

tests/unit/test_io.py::test_validate_dataset_bad_schema
  /var/jenkins_home/workspace/nvtabular_tests/nvtabular/nvtabular/io/parquet.py:300: UserWarning: [/tmp/pytest-of-jenkins/pytest-3/test_validate_dataset_bad_sche0/part.1.parquet] Expected int64, got null.
    warnings.warn(msg)

tests/unit/test_io.py::test_validate_dataset_bad_schema
  /var/jenkins_home/workspace/nvtabular_tests/nvtabular/nvtabular/io/parquet.py:334: UserWarning: 
  Please pass add_metadata_file=True to add a global _metadata file, or use the regenerate_dataset utility to rewrite your dataset. Without a _metadata file, the schema mismatch may cause errors at read time.
    warnings.warn(msg)

tests/unit/test_io.py::test_validate_dataset_bad_schema
  /var/jenkins_home/workspace/nvtabular_tests/nvtabular/nvtabular/io/parquet.py:373: UserWarning: File part.0.parquet is smaller than the desired dataset partition size (2133663744). Consider using the regenerate_dataset utility to rewrite your dataset with a smaller number of (larger) files.
    warnings.warn(msg)

tests/unit/test_io.py::test_validate_dataset_bad_schema
  /var/jenkins_home/workspace/nvtabular_tests/nvtabular/nvtabular/io/parquet.py:373: UserWarning: File part.1.parquet is smaller than the desired dataset partition size (2133663744). Consider using the regenerate_dataset utility to rewrite your dataset with a smaller number of (larger) files.
    warnings.warn(msg)

tests/unit/test_io.py::test_validate_dataset_bad_schema
  /var/jenkins_home/workspace/nvtabular_tests/nvtabular/nvtabular/io/parquet.py:394: UserWarning: For best performance with NVTabular, there should be a global _metadata file located in the root directory of the dataset. Please pass add_metadata_file=True to add the missing file.
    warnings.warn(msg)

tests/unit/test_io.py::test_validate_and_regenerate_dataset
  /var/jenkins_home/workspace/nvtabular_tests/nvtabular/nvtabular/io/parquet.py:475: UserWarning: General-metadata information not detected! Please pass lists for `cats`, `conts`, and `labels` asarguments to `regenerate_dataset` to ensure a complete and correct _metadata.json file.
    warnings.warn(

tests/unit/test_ops.py: 16 warnings
  /var/jenkins_home/workspace/nvtabular_tests/nvtabular/nvtabular/ops/categorify.py:266: UserWarning: Performing a hash-based transformation. Do not expect Categorify to be consistent on GPU and CPU with this num_buckets setting!
    warnings.warn(

tests/unit/test_ops.py::test_categorify_freq_limit[True-True-None-None]
tests/unit/test_ops.py::test_categorify_freq_limit[True-True-None-0]
tests/unit/test_ops.py::test_categorify_freq_limit[True-True-10-None]
tests/unit/test_ops.py::test_categorify_freq_limit[True-True-10-0]
tests/unit/test_ops.py::test_categorify_freq_limit[True-True-buckets2-None]
tests/unit/test_ops.py::test_categorify_freq_limit[True-True-buckets2-0]
  /var/jenkins_home/workspace/nvtabular_tests/nvtabular/nvtabular/ops/categorify.py:304: UserWarning: Cannot use `search_sorted=True` for pandas-backed data.
    warnings.warn("Cannot use `search_sorted=True` for pandas-backed data.")

-- Docs: https://docs.pytest.org/en/stable/warnings.html

----------- coverage: platform linux, python 3.8.8-final-0 -----------
Name                                                           Stmts   Miss Branch BrPart  Cover   Missing
----------------------------------------------------------------------------------------------------------
nvtabular/__init__.py                                             12      0      0      0   100%
nvtabular/column_group.py                                        149     19     80      7    85%   54, 87, 98, 101->103, 128, 151-164, 191, 278
nvtabular/dispatch.py                                             69      8     30      4    86%   51, 76, 93, 100, 117-120, 152
nvtabular/framework_utils/__init__.py                              0      0      0      0   100%
nvtabular/framework_utils/tensorflow/__init__.py                   1      0      0      0   100%
nvtabular/framework_utils/tensorflow/feature_column_utils.py     146    137     96      0     4%   13-17, 54-288
nvtabular/framework_utils/tensorflow/layers/__init__.py            4      0      0      0   100%
nvtabular/framework_utils/tensorflow/layers/embedding.py         153     14     89      7    87%   56, 64->45, 100, 108, 188, 240-248, 251, 344->352, 366->369, 372-373, 376
nvtabular/framework_utils/tensorflow/layers/interaction.py        47     25     20      1    43%   49, 74-103, 106-110, 113
nvtabular/framework_utils/tensorflow/layers/outer_product.py      30     24     10      0    15%   22-23, 26-45, 56-69, 72
nvtabular/framework_utils/torch/__init__.py                        0      0      0      0   100%
nvtabular/framework_utils/torch/layers/__init__.py                 2      0      0      0   100%
nvtabular/framework_utils/torch/layers/embeddings.py              27     12     12      1    51%   47, 71-79, 82-88
nvtabular/framework_utils/torch/models.py                         41      6     22      7    76%   56, 59, 64, 85, 87-88, 91->94, 98->100
nvtabular/framework_utils/torch/utils.py                          31      7     10      3    76%   52, 56-58, 67-69
nvtabular/inference/__init__.py                                    0      0      0      0   100%
nvtabular/inference/triton/__init__.py                           119    113     58      0     3%   8-203
nvtabular/inference/triton/model.py                               34     34     12      0     0%   27-97
nvtabular/io/__init__.py                                           4      0      0      0   100%
nvtabular/io/avro.py                                              88     88     30      0     0%   16-189
nvtabular/io/csv.py                                               54     22     20      1    50%   44, 61-72, 94-122
nvtabular/io/dask.py                                             127      5     54      9    92%   106, 130, 240->238, 268->271, 279, 283->285, 285->281, 290, 292
nvtabular/io/dataframe_engine.py                                  58      2     30      6    91%   44, 82->86, 86->91, 88->91, 91->110, 119
nvtabular/io/dataset.py                                          187     29     82     15    81%   243, 245, 258, 267, 285-299, 352, 393, 472->474, 474->482, 483, 566-567, 676-681, 687, 692-694, 698-705, 724
nvtabular/io/dataset_engine.py                                    23      1      0      0    96%   45
nvtabular/io/hugectr.py                                           44      2     24      2    91%   32, 72->95, 99
nvtabular/io/parquet.py                                          469     40    146     11    90%   82-90, 115-144, 183-185, 310-315, 353-358, 474->481, 544->549, 550-551, 670, 674, 678, 716, 733, 737, 744->746, 863->868, 873->883, 910
nvtabular/io/shuffle.py                                           30      7     12      2    69%   41-48
nvtabular/io/writer.py                                           156     10     56      3    93%   31, 48, 75, 120, 123, 196, 239, 260-262
nvtabular/io/writer_factory.py                                    18      2      8      2    85%   34, 58
nvtabular/loader/__init__.py                                       0      0      0      0   100%
nvtabular/loader/backend.py                                      269     15    112      9    94%   77-78, 107, 124, 131-132, 143, 203->205, 220, 243-244, 283->287, 358, 362-363, 457, 464
nvtabular/loader/tensorflow.py                                   120      8     52      7    90%   52, 60-63, 73, 83, 282, 309->313, 342
nvtabular/loader/tf_utils.py                                      55     10     20      5    80%   29->32, 32->34, 39->41, 43, 50-51, 58-60, 66-70
nvtabular/loader/torch.py                                         43     10      8      0    69%   25-27, 30-36
nvtabular/ops/__init__.py                                         19      0      0      0   100%
nvtabular/ops/bucketize.py                                        25      4     16      2    76%   46, 49-52
nvtabular/ops/categorify.py                                      513     67    298     46    84%   223, 240, 244, 252, 260, 262, 284, 336-337, 399-401, 459-461, 466->468, 541, 579, 608->611, 612-614, 621-622, 635-637, 638->606, 654, 664, 666, 672, 688-689, 694, 697->700, 710, 734-736, 739, 741->743, 755-758, 784, 788, 790, 802-805, 920, 922, 964->985, 970->985, 986-991, 1028, 1044->1049, 1048, 1058->1055, 1063->1055, 1071, 1079-1089
nvtabular/ops/clip.py                                             19      2      6      3    80%   45, 53->55, 56
nvtabular/ops/column_similarity.py                                88     22     32      5    69%   84, 154-155, 164-166, 174-190, 205->215, 207->210, 211, 221
nvtabular/ops/data_stats.py                                       57      1     24      4    94%   88->90, 90->92, 93->84, 96->84, 101
nvtabular/ops/difference_lag.py                                   26      0      8      1    97%   67->69
nvtabular/ops/dropna.py                                            9      0      0      0   100%
nvtabular/ops/fill.py                                             58      2     20      1    96%   99, 125
nvtabular/ops/filter.py                                           21      1      6      1    93%   44
nvtabular/ops/hash_bucket.py                                      32      2     18      2    88%   74, 103
nvtabular/ops/hashed_cross.py                                     29      3     13      4    83%   52, 67, 81->exit, 82
nvtabular/ops/join_external.py                                    69      5     28      6    89%   96, 98, 116, 129->133, 153, 168
nvtabular/ops/join_groupby.py                                     78      5     28      2    93%   102, 105->112, 179-180, 183-184
nvtabular/ops/lambdaop.py                                         27      3     10      3    84%   61, 65, 78
nvtabular/ops/logop.py                                             9      0      0      0   100%
nvtabular/ops/moments.py                                          65      0     20      0   100%
nvtabular/ops/normalize.py                                        64      6     14      2    87%   64->63, 70-71, 101-102, 124-125
nvtabular/ops/operator.py                                         15      1      2      1    88%   24
nvtabular/ops/rename.py                                           18      3     10      3    71%   41, 54, 58
nvtabular/ops/stat_operator.py                                    11      0      0      0   100%
nvtabular/ops/target_encoding.py                                 151     11     66      5    91%   142, 162->166, 169->178, 221-222, 225-226, 235-241, 334->337
nvtabular/tools/__init__.py                                        0      0      0      0   100%
nvtabular/tools/data_gen.py                                      235      1     62      4    98%   168->170, 215->219, 305->304, 307
nvtabular/tools/dataset_inspector.py                              49      9     18      0    75%   29-38
nvtabular/tools/inspector_script.py                               46     46      0      0     0%   17-168
nvtabular/utils.py                                                42     16     18      7    55%   20-21, 25-26, 35, 39, 48-51, 53-55, 58, 61, 67, 73
nvtabular/worker.py                                               68      1     30      2    97%   73, 83->98
nvtabular/workflow.py                                            140      9     62      4    93%   39, 120, 132-134, 236, 264-265, 335
----------------------------------------------------------------------------------------------------------
TOTAL                                                           4593    870   1902    210    78%
Coverage XML written to file coverage.xml

Required test coverage of 70% reached. Total coverage: 78.04%
=========================== short test summary info ============================
FAILED tests/unit/test_notebooks.py::test_optimize_criteo - subprocess.Called...
===== 1 failed, 708 passed, 12 skipped, 296 warnings in 531.65s (0:08:51) ======
Build step 'Execute shell' marked build as failure
Performing Post build task...
Match found for : : True
Logical operation result is TRUE
Running script  : #!/bin/bash
source activate rapids
cd /var/jenkins_home/
CUDA_VISIBLE_DEVICES=1 python test_res_push.py "https://api.GitHub.com/repos/NVIDIA/NVTabular/issues/$ghprbPullId/comments" "/var/jenkins_home/jobs/$JOB_NAME/builds/$BUILD_NUMBER/log" 
[nvtabular_tests] $ /bin/bash /tmp/jenkins862462854312732203.sh
</pre></details>
<details><summary>Click to view CI Results</summary><pre>GitHub pull request #660 of commit 93f801a7974da91060f9a1c9dc25bfa8a20bd079, no merge conflicts.
Running as SYSTEM
Setting status of 93f801a7974da91060f9a1c9dc25bfa8a20bd079 to PENDING with url http://10.20.13.93:8080/job/nvtabular_tests/1904/ and message: 'Pending'
Using context: Jenkins Unit Test Run
Building in workspace /var/jenkins_home/workspace/nvtabular_tests
using credential ghub_token
Cloning the remote Git repository
Cloning repository https://github.com/NVIDIA/NVTabular.git
 > git init /var/jenkins_home/workspace/nvtabular_tests/nvtabular # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
 > git --version # timeout=10
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
 > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/pull/660/*:refs/remotes/origin/pr/660/* # timeout=10
 > git rev-parse 93f801a7974da91060f9a1c9dc25bfa8a20bd079^{commit} # timeout=10
Checking out Revision 93f801a7974da91060f9a1c9dc25bfa8a20bd079 (detached)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f 93f801a7974da91060f9a1c9dc25bfa8a20bd079 # timeout=10
Commit message: "Merge remote-tracking branch 'upstream/main' into to_parquet_improvements"
 > git rev-list --no-walk 6c4061d49b651df9ba9c41eb3b8ac0bb5568e61e # timeout=10
[nvtabular_tests] $ /bin/bash /tmp/jenkins6858570012317202470.sh
Obtaining file:///var/jenkins_home/workspace/nvtabular_tests/nvtabular
  Installing build dependencies: started
  Installing build dependencies: finished with status 'done'
  Getting requirements to build wheel: started
  Getting requirements to build wheel: finished with status 'done'
    Preparing wheel metadata: started
    Preparing wheel metadata: finished with status 'done'
Installing collected packages: nvtabular
  Running setup.py develop for nvtabular
Successfully installed nvtabular
All done! ✨ 🍰 ✨
93 files would be left unchanged.
/conda/envs/rapids/lib/python3.8/site-packages/isort/files.py:30: UserWarning: Likely recursive symlink detected to /var/jenkins_home/workspace/nvtabular_tests/nvtabular/images
  warn(f"Likely recursive symlink detected to {resolved_path}")
============================= test session starts ==============================
platform linux -- Python 3.8.8, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /var/jenkins_home/workspace/nvtabular_tests/nvtabular, configfile: pyproject.toml
plugins: cov-2.11.1, xdist-2.2.1, forked-1.3.0
collected 720 items / 2 skipped / 718 selected

tests/unit/test_column_group.py .                                        [  0%]
tests/unit/test_column_similarity.py ......                              [  0%]
tests/unit/test_dask_nvt.py ............................................ [  7%]
.....................................................................    [ 16%]
tests/unit/test_io.py .................................................. [ 23%]
..................................................................ssssss [ 33%]
ss....                                                                   [ 34%]
tests/unit/test_notebooks.py .Fs..                                       [ 35%]
tests/unit/test_ops.py ................................................. [ 41%]
........................................................................ [ 51%]
........................................................................ [ 61%]
..................................                                       [ 66%]
tests/unit/test_tf_dataloader.py ...................                     [ 69%]
tests/unit/test_tf_layers.py ........................................... [ 75%]
...................................                                      [ 80%]
tests/unit/test_tools.py ......................                          [ 83%]
tests/unit/test_torch_dataloader.py .............................s       [ 87%]
tests/unit/test_workflow.py ............................................ [ 93%]
...............................................                          [100%]

=================================== FAILURES ===================================
_____________________________ test_optimize_criteo _____________________________

tmpdir = local('/tmp/pytest-of-jenkins/pytest-4/test_optimize_criteo0')

    def test_optimize_criteo(tmpdir):
        _get_random_criteo_data(1000).to_csv(os.path.join(tmpdir, "day_0"), sep="\t", header=False)
        os.environ["INPUT_DATA_DIR"] = str(tmpdir)
        os.environ["OUTPUT_DATA_DIR"] = str(tmpdir)
    
        notebook_path = os.path.join(
            dirname(TEST_PATH),
            "examples/scaling-criteo/",
            "01-Download-Convert.ipynb",
        )
>       _run_notebook(tmpdir, notebook_path)

tests/unit/test_notebooks.py:64: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/unit/test_notebooks.py:171: in _run_notebook
    subprocess.check_output([sys.executable, script_path])
/conda/envs/rapids/lib/python3.8/subprocess.py:415: in check_output
    return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

input = None, capture_output = False, timeout = None, check = True
popenargs = (['/conda/envs/rapids/bin/python3.8', '/tmp/pytest-of-jenkins/pytest-4/test_optimize_criteo0/notebook.py'],)
kwargs = {'stdout': -1}, process = <subprocess.Popen object at 0x7fae1e6511c0>
stdout = b'', stderr = None, retcode = 1

    def run(*popenargs,
            input=None, capture_output=False, timeout=None, check=False, **kwargs):
        """Run command with arguments and return a CompletedProcess instance.
    
        The returned instance will have attributes args, returncode, stdout and
        stderr. By default, stdout and stderr are not captured, and those attributes
        will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them.
    
        If check is True and the exit code was non-zero, it raises a
        CalledProcessError. The CalledProcessError object will have the return code
        in the returncode attribute, and output & stderr attributes if those streams
        were captured.
    
        If timeout is given, and the process takes too long, a TimeoutExpired
        exception will be raised.
    
        There is an optional argument "input", allowing you to
        pass bytes or a string to the subprocess's stdin.  If you use this argument
        you may not also use the Popen constructor's "stdin" argument, as
        it will be used internally.
    
        By default, all communication is in bytes, and therefore any "input" should
        be bytes, and the stdout and stderr will be bytes. If in text mode, any
        "input" should be a string, and stdout and stderr will be strings decoded
        according to locale encoding, or by "encoding" if set. Text mode is
        triggered by setting any of text, encoding, errors or universal_newlines.
    
        The other arguments are the same as for the Popen constructor.
        """
        if input is not None:
            if kwargs.get('stdin') is not None:
                raise ValueError('stdin and input arguments may not both be used.')
            kwargs['stdin'] = PIPE
    
        if capture_output:
            if kwargs.get('stdout') is not None or kwargs.get('stderr') is not None:
                raise ValueError('stdout and stderr arguments may not be used '
                                 'with capture_output.')
            kwargs['stdout'] = PIPE
            kwargs['stderr'] = PIPE
    
        with Popen(*popenargs, **kwargs) as process:
            try:
                stdout, stderr = process.communicate(input, timeout=timeout)
            except TimeoutExpired as exc:
                process.kill()
                if _mswindows:
                    # Windows accumulates the output in a single blocking
                    # read() call run on child threads, with the timeout
                    # being done in a join() on those threads.  communicate()
                    # _after_ kill() is required to collect that and add it
                    # to the exception.
                    exc.stdout, exc.stderr = process.communicate()
                else:
                    # POSIX _communicate already populated the output so
                    # far into the TimeoutExpired exception.
                    process.wait()
                raise
            except:  # Including KeyboardInterrupt, communicate handled that.
                process.kill()
                # We don't call process.wait() as .__exit__ does that for us.
                raise
            retcode = process.poll()
            if check and retcode:
>               raise CalledProcessError(retcode, process.args,
                                         output=stdout, stderr=stderr)
E               subprocess.CalledProcessError: Command '['/conda/envs/rapids/bin/python3.8', '/tmp/pytest-of-jenkins/pytest-4/test_optimize_criteo0/notebook.py']' returned non-zero exit status 1.

/conda/envs/rapids/lib/python3.8/subprocess.py:516: CalledProcessError
----------------------------- Captured stderr call -----------------------------
Traceback (most recent call last):
  File "/tmp/pytest-of-jenkins/pytest-4/test_optimize_criteo0/notebook.py", line 249, in <module>
    conversion_delayed.compute(scheduler="synchronous")
  File "/conda/envs/rapids/lib/python3.8/site-packages/dask/base.py", line 281, in compute
    (result,) = compute(self, traverse=False, **kwargs)
  File "/conda/envs/rapids/lib/python3.8/site-packages/dask/base.py", line 563, in compute
    results = schedule(dsk, keys, **kwargs)
  File "/conda/envs/rapids/lib/python3.8/site-packages/dask/local.py", line 528, in get_sync
    return get_async(apply_sync, 1, dsk, keys, **kwargs)
  File "/conda/envs/rapids/lib/python3.8/site-packages/dask/local.py", line 472, in get_async
    fire_task()
  File "/conda/envs/rapids/lib/python3.8/site-packages/dask/local.py", line 457, in fire_task
    apply_async(
  File "/conda/envs/rapids/lib/python3.8/site-packages/dask/local.py", line 517, in apply_sync
    res = func(*args, **kwds)
  File "/conda/envs/rapids/lib/python3.8/site-packages/dask/local.py", line 227, in execute_task
    result = pack_exception(e, dumps)
  File "/conda/envs/rapids/lib/python3.8/site-packages/dask/local.py", line 222, in execute_task
    result = _execute_task(task, data)
  File "/conda/envs/rapids/lib/python3.8/site-packages/dask/core.py", line 121, in _execute_task
    return func(*(_execute_task(a, cache) for a in args))
  File "/tmp/pytest-of-jenkins/pytest-4/test_optimize_criteo0/notebook.py", line 98, in _convert_file
    for gdf in nvt.Dataset(
  File "/var/jenkins_home/workspace/nvtabular_tests/nvtabular/nvtabular/io/dataset.py", line 397, in to_iter
    self.to_ddf(columns=columns, shuffle=shuffle, seed=seed), indices=indices
  File "/var/jenkins_home/workspace/nvtabular_tests/nvtabular/nvtabular/io/dataset.py", line 347, in to_ddf
    _meta = _set_dtypes(ddf._meta, self.dtypes)
  File "/var/jenkins_home/workspace/nvtabular_tests/nvtabular/nvtabular/io/dataset.py", line 711, in _set_dtypes
    chunk[col] = chunk[col].astype(dtype)
  File "/conda/envs/rapids/lib/python3.8/site-packages/cudf/core/series.py", line 2190, in astype
    raise e
  File "/conda/envs/rapids/lib/python3.8/site-packages/cudf/core/series.py", line 2182, in astype
    data = self._column.astype(dtype)
  File "/conda/envs/rapids/lib/python3.8/site-packages/cudf/core/column/column.py", line 992, in astype
    elif pd.api.types.pandas_dtype(dtype).type in {
  File "/conda/envs/rapids/lib/python3.8/site-packages/pandas/core/dtypes/common.py", line 1763, in pandas_dtype
    npdtype = np.dtype(dtype)
TypeError: data type 'hex' not understood

----------- coverage: platform linux, python 3.8.8-final-0 -----------
Name                                                           Stmts   Miss Branch BrPart  Cover   Missing
----------------------------------------------------------------------------------------------------------
nvtabular/__init__.py                                             12      0      0      0   100%
nvtabular/column_group.py                                        149     19     80      7    85%   54, 87, 98, 101->103, 128, 151-164, 191, 278
nvtabular/dispatch.py                                             69      8     30      4    86%   51, 76, 93, 100, 117-120, 152
nvtabular/framework_utils/__init__.py                              0      0      0      0   100%
nvtabular/framework_utils/tensorflow/__init__.py                   1      0      0      0   100%
nvtabular/framework_utils/tensorflow/feature_column_utils.py     146    137     96      0     4%   28-32, 69-303
nvtabular/framework_utils/tensorflow/layers/__init__.py            4      0      0      0   100%
nvtabular/framework_utils/tensorflow/layers/embedding.py         153     14     89      7    87%   56, 64->45, 100, 108, 188, 240-248, 251, 344->352, 366->369, 372-373, 376
nvtabular/framework_utils/tensorflow/layers/interaction.py        47     25     20      1    43%   49, 74-103, 106-110, 113
nvtabular/framework_utils/tensorflow/layers/outer_product.py      30     24     10      0    15%   37-38, 41-60, 71-84, 87
nvtabular/framework_utils/torch/__init__.py                        0      0      0      0   100%
nvtabular/framework_utils/torch/layers/__init__.py                 2      0      0      0   100%
nvtabular/framework_utils/torch/layers/embeddings.py              27     12     12      1    51%   47, 71-79, 82-88
nvtabular/framework_utils/torch/models.py                         41      6     22      7    76%   56, 59, 64, 85, 87-88, 91->94, 98->100
nvtabular/framework_utils/torch/utils.py                          31      7     10      3    76%   52, 56-58, 67-69
nvtabular/inference/__init__.py                                    0      0      0      0   100%
nvtabular/inference/triton/__init__.py                           218    211     90      0     2%   24-494
nvtabular/inference/triton/model.py                               56     56     22      0     0%   27-142
nvtabular/inference/triton/model_hugectr.py                       44     44     14      0     0%   27-120
nvtabular/io/__init__.py                                           4      0      0      0   100%
nvtabular/io/avro.py                                              88     88     30      0     0%   16-189
nvtabular/io/csv.py                                               54     22     20      1    50%   44, 61-72, 94-122
nvtabular/io/dask.py                                             127      5     54      9    92%   106, 130, 240->238, 268->271, 279, 283->285, 285->281, 290, 292
nvtabular/io/dataframe_engine.py                                  58      2     30      6    91%   44, 82->86, 86->91, 88->91, 91->110, 119
nvtabular/io/dataset.py                                          187     29     82     15    81%   244, 246, 259, 268, 286-300, 353, 394, 473->475, 475->483, 484, 567-568, 680-685, 691, 696-698, 702-709, 728
nvtabular/io/dataset_engine.py                                    23      1      0      0    96%   45
nvtabular/io/hugectr.py                                           44      2     24      2    91%   32, 72->95, 99
nvtabular/io/parquet.py                                          470     40    146     11    90%   83-91, 116-145, 184-186, 311-316, 354-359, 475->482, 545->550, 551-552, 671, 675, 679, 717, 734, 738, 745->747, 864->869, 874->884, 911
nvtabular/io/shuffle.py                                           30      7     12      2    69%   41-48
nvtabular/io/writer.py                                           156     10     56      3    93%   31, 48, 75, 120, 123, 196, 239, 260-262
nvtabular/io/writer_factory.py                                    18      2      8      2    85%   34, 58
nvtabular/loader/__init__.py                                       0      0      0      0   100%
nvtabular/loader/backend.py                                      269     15    112      9    94%   77-78, 107, 124, 131-132, 143, 203->205, 220, 243-244, 283->287, 358, 362-363, 457, 464
nvtabular/loader/tensorflow.py                                   120      8     52      7    90%   52, 60-63, 73, 83, 282, 309->313, 342
nvtabular/loader/tf_utils.py                                      55     10     20      5    80%   29->32, 32->34, 39->41, 43, 50-51, 58-60, 66-70
nvtabular/loader/torch.py                                         43     10      8      0    69%   25-27, 30-36
nvtabular/ops/__init__.py                                         19      0      0      0   100%
nvtabular/ops/bucketize.py                                        25      4     16      2    76%   46, 49-52
nvtabular/ops/categorify.py                                      513     69    298     47    83%   223, 240, 244, 252, 260, 262, 284, 303-304, 336-337, 399-401, 459-461, 466->468, 541, 579, 608->611, 612-614, 621-622, 635-637, 638->606, 654, 664, 666, 672, 688-689, 694, 697->700, 710, 734-736, 739, 741->743, 755-758, 784, 788, 790, 802-805, 920, 922, 964->985, 970->985, 986-991, 1028, 1044->1049, 1048, 1058->1055, 1063->1055, 1071, 1079-1089
nvtabular/ops/clip.py                                             19      2      6      3    80%   45, 53->55, 56
nvtabular/ops/column_similarity.py                                88     22     32      5    69%   84, 154-155, 164-166, 174-190, 205->215, 207->210, 211, 221
nvtabular/ops/data_stats.py                                       57      1     24      4    94%   88->90, 90->92, 93->84, 96->84, 101
nvtabular/ops/difference_lag.py                                   26      0      8      1    97%   67->69
nvtabular/ops/dropna.py                                            9      0      0      0   100%
nvtabular/ops/fill.py                                             58      2     20      1    96%   99, 125
nvtabular/ops/filter.py                                           21      1      6      1    93%   44
nvtabular/ops/hash_bucket.py                                      32      2     18      2    88%   73, 102
nvtabular/ops/hashed_cross.py                                     29      3     13      4    83%   51, 66, 80->exit, 81
nvtabular/ops/join_external.py                                    69      5     28      6    89%   96, 98, 116, 129->133, 153, 168
nvtabular/ops/join_groupby.py                                     78      5     28      2    93%   102, 105->112, 179-180, 183-184
nvtabular/ops/lambdaop.py                                         27      3     10      3    84%   61, 65, 78
nvtabular/ops/logop.py                                             9      0      0      0   100%
nvtabular/ops/moments.py                                          65      0     20      0   100%
nvtabular/ops/normalize.py                                        64      6     14      2    87%   61->60, 67-68, 100-101, 123-124
nvtabular/ops/operator.py                                         15      1      2      1    88%   24
nvtabular/ops/rename.py                                           18      3     10      3    71%   41, 54, 58
nvtabular/ops/stat_operator.py                                    11      0      0      0   100%
nvtabular/ops/target_encoding.py                                 151     11     66      5    91%   143, 163->167, 170->179, 222-223, 226-227, 236-242, 335->338
nvtabular/tools/__init__.py                                        0      0      0      0   100%
nvtabular/tools/data_gen.py                                      235      1     62      4    98%   183->185, 230->234, 320->319, 322
nvtabular/tools/dataset_inspector.py                              49      9     18      0    75%   29-38
nvtabular/tools/inspector_script.py                               46     46      0      0     0%   17-168
nvtabular/utils.py                                                42     16     18      7    55%   20-21, 25-26, 35, 39, 48-51, 53-55, 58, 61, 67, 73
nvtabular/worker.py                                               68      1     30      2    97%   73, 83->98
nvtabular/workflow.py                                            140      9     62      4    93%   39, 120, 132-134, 237, 265-266, 336
----------------------------------------------------------------------------------------------------------
TOTAL                                                           4759   1036   1958    211    75%
Coverage XML written to file coverage.xml

Required test coverage of 70% reached. Total coverage: 75.45%
=========================== short test summary info ============================
FAILED tests/unit/test_notebooks.py::test_optimize_criteo - subprocess.Called...
============ 1 failed, 709 passed, 12 skipped in 530.20s (0:08:50) =============
Build step 'Execute shell' marked build as failure
Performing Post build task...
Match found for : : True
Logical operation result is TRUE
Running script  : #!/bin/bash
source activate rapids
cd /var/jenkins_home/
CUDA_VISIBLE_DEVICES=1 python test_res_push.py "https://api.GitHub.com/repos/NVIDIA/NVTabular/issues/$ghprbPullId/comments" "/var/jenkins_home/jobs/$JOB_NAME/builds/$BUILD_NUMBER/log" 
[nvtabular_tests] $ /bin/bash /tmp/jenkins4896357901685158918.sh
</pre></details>
<details><summary>Click to view CI Results</summary><pre>GitHub pull request #660 of commit 821d993525bf3cfbc68aa31ef6dd061a404e1511, no merge conflicts.
Running as SYSTEM
Setting status of 821d993525bf3cfbc68aa31ef6dd061a404e1511 to PENDING with url http://10.20.13.93:8080/job/nvtabular_tests/1905/ and message: 'Pending'
Using context: Jenkins Unit Test Run
Building in workspace /var/jenkins_home/workspace/nvtabular_tests
using credential ghub_token
Cloning the remote Git repository
Cloning repository https://github.com/NVIDIA/NVTabular.git
 > git init /var/jenkins_home/workspace/nvtabular_tests/nvtabular # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
 > git --version # timeout=10
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
 > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/pull/660/*:refs/remotes/origin/pr/660/* # timeout=10
 > git rev-parse 821d993525bf3cfbc68aa31ef6dd061a404e1511^{commit} # timeout=10
Checking out Revision 821d993525bf3cfbc68aa31ef6dd061a404e1511 (detached)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f 821d993525bf3cfbc68aa31ef6dd061a404e1511 # timeout=10
Commit message: "add test_dataset_conversion to cover dataset conversion similar to criteo"
 > git rev-list --no-walk 93f801a7974da91060f9a1c9dc25bfa8a20bd079 # timeout=10
[nvtabular_tests] $ /bin/bash /tmp/jenkins2226515148164556597.sh
Obtaining file:///var/jenkins_home/workspace/nvtabular_tests/nvtabular
  Installing build dependencies: started
  Installing build dependencies: finished with status 'done'
  Getting requirements to build wheel: started
  Getting requirements to build wheel: finished with status 'done'
    Preparing wheel metadata: started
    Preparing wheel metadata: finished with status 'done'
Installing collected packages: nvtabular
  Running setup.py develop for nvtabular
Successfully installed nvtabular
All done! ✨ 🍰 ✨
93 files would be left unchanged.
/conda/envs/rapids/lib/python3.8/site-packages/isort/files.py:30: UserWarning: Likely recursive symlink detected to /var/jenkins_home/workspace/nvtabular_tests/nvtabular/images
  warn(f"Likely recursive symlink detected to {resolved_path}")
============================= test session starts ==============================
platform linux -- Python 3.8.8, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /var/jenkins_home/workspace/nvtabular_tests/nvtabular, configfile: pyproject.toml
plugins: cov-2.11.1, xdist-2.2.1, forked-1.3.0
collected 724 items / 2 skipped / 722 selected

tests/unit/test_column_group.py .                                        [  0%]
tests/unit/test_column_similarity.py ......                              [  0%]
tests/unit/test_dask_nvt.py ............................................ [  7%]
.....................................................................    [ 16%]
tests/unit/test_io.py .................................................. [ 23%]
..................................................................ssssss [ 33%]
ss........                                                               [ 34%]
tests/unit/test_notebooks.py ..s..                                       [ 35%]
tests/unit/test_ops.py ................................................. [ 42%]
........................................................................ [ 52%]
........................................................................ [ 62%]
..................................                                       [ 66%]
tests/unit/test_tf_dataloader.py ...................                     [ 69%]
tests/unit/test_tf_layers.py ........................................... [ 75%]
...................................                                      [ 80%]
tests/unit/test_tools.py ......................                          [ 83%]
tests/unit/test_torch_dataloader.py .............................s       [ 87%]
tests/unit/test_workflow.py ............................................ [ 93%]
...............................................                          [100%]

----------- coverage: platform linux, python 3.8.8-final-0 -----------
Name                                                           Stmts   Miss Branch BrPart  Cover   Missing
----------------------------------------------------------------------------------------------------------
nvtabular/__init__.py                                             12      0      0      0   100%
nvtabular/column_group.py                                        149     19     80      7    85%   54, 87, 98, 101->103, 128, 151-164, 191, 278
nvtabular/dispatch.py                                             81      9     38      6    86%   35, 45->47, 69, 94, 111, 118, 135-138, 170
nvtabular/framework_utils/__init__.py                              0      0      0      0   100%
nvtabular/framework_utils/tensorflow/__init__.py                   1      0      0      0   100%
nvtabular/framework_utils/tensorflow/feature_column_utils.py     146    137     96      0     4%   28-32, 69-303
nvtabular/framework_utils/tensorflow/layers/__init__.py            4      0      0      0   100%
nvtabular/framework_utils/tensorflow/layers/embedding.py         153     14     89      7    87%   56, 64->45, 100, 108, 188, 240-248, 251, 344->352, 366->369, 372-373, 376
nvtabular/framework_utils/tensorflow/layers/interaction.py        47     25     20      1    43%   49, 74-103, 106-110, 113
nvtabular/framework_utils/tensorflow/layers/outer_product.py      30     24     10      0    15%   37-38, 41-60, 71-84, 87
nvtabular/framework_utils/torch/__init__.py                        0      0      0      0   100%
nvtabular/framework_utils/torch/layers/__init__.py                 2      0      0      0   100%
nvtabular/framework_utils/torch/layers/embeddings.py              27     12     12      1    51%   47, 71-79, 82-88
nvtabular/framework_utils/torch/models.py                         41      6     22      7    76%   56, 59, 64, 85, 87-88, 91->94, 98->100
nvtabular/framework_utils/torch/utils.py                          31      7     10      3    76%   52, 56-58, 67-69
nvtabular/inference/__init__.py                                    0      0      0      0   100%
nvtabular/inference/triton/__init__.py                           218    211     90      0     2%   24-494
nvtabular/inference/triton/model.py                               56     56     22      0     0%   27-142
nvtabular/inference/triton/model_hugectr.py                       44     44     14      0     0%   27-120
nvtabular/io/__init__.py                                           4      0      0      0   100%
nvtabular/io/avro.py                                              88     88     30      0     0%   16-189
nvtabular/io/csv.py                                               54      4     20      5    88%   95, 99->103, 104, 106, 120
nvtabular/io/dask.py                                             127      5     54      9    92%   106, 130, 240->238, 268->271, 279, 283->285, 285->281, 290, 292
nvtabular/io/dataframe_engine.py                                  58      2     30      6    91%   44, 82->86, 86->91, 88->91, 91->110, 119
nvtabular/io/dataset.py                                          180     20     78     12    87%   245, 247, 260, 269, 287-301, 395, 485, 568-569, 681-686, 692, 717
nvtabular/io/dataset_engine.py                                    23      1      0      0    96%   45
nvtabular/io/hugectr.py                                           44      2     24      2    91%   32, 72->95, 99
nvtabular/io/parquet.py                                          470     40    146     11    90%   83-91, 116-145, 184-186, 311-316, 354-359, 475->482, 545->550, 551-552, 671, 675, 679, 717, 734, 738, 745->747, 864->869, 874->884, 911
nvtabular/io/shuffle.py                                           30      7     12      2    69%   41-48
nvtabular/io/writer.py                                           156     10     56      3    93%   31, 48, 75, 120, 123, 196, 239, 260-262
nvtabular/io/writer_factory.py                                    18      2      8      2    85%   34, 58
nvtabular/loader/__init__.py                                       0      0      0      0   100%
nvtabular/loader/backend.py                                      269     15    112      9    94%   77-78, 107, 124, 131-132, 143, 203->205, 220, 243-244, 283->287, 358, 362-363, 457, 464
nvtabular/loader/tensorflow.py                                   120      8     52      7    90%   52, 60-63, 73, 83, 282, 309->313, 342
nvtabular/loader/tf_utils.py                                      55     10     20      5    80%   29->32, 32->34, 39->41, 43, 50-51, 58-60, 66-70
nvtabular/loader/torch.py                                         43     10      8      0    69%   25-27, 30-36
nvtabular/ops/__init__.py                                         19      0      0      0   100%
nvtabular/ops/bucketize.py                                        25      4     16      2    76%   46, 49-52
nvtabular/ops/categorify.py                                      513     69    298     47    83%   223, 240, 244, 252, 260, 262, 284, 303-304, 336-337, 399-401, 459-461, 466->468, 541, 579, 608->611, 612-614, 621-622, 635-637, 638->606, 654, 664, 666, 672, 688-689, 694, 697->700, 710, 734-736, 739, 741->743, 755-758, 784, 788, 790, 802-805, 920, 922, 964->985, 970->985, 986-991, 1028, 1044->1049, 1048, 1058->1055, 1063->1055, 1071, 1079-1089
nvtabular/ops/clip.py                                             19      2      6      3    80%   45, 53->55, 56
nvtabular/ops/column_similarity.py                                88     22     32      5    69%   84, 154-155, 164-166, 174-190, 205->215, 207->210, 211, 221
nvtabular/ops/data_stats.py                                       57      1     24      4    94%   88->90, 90->92, 93->84, 96->84, 101
nvtabular/ops/difference_lag.py                                   26      0      8      1    97%   67->69
nvtabular/ops/dropna.py                                            9      0      0      0   100%
nvtabular/ops/fill.py                                             58      2     20      1    96%   99, 125
nvtabular/ops/filter.py                                           21      1      6      1    93%   44
nvtabular/ops/hash_bucket.py                                      32      2     18      2    88%   73, 102
nvtabular/ops/hashed_cross.py                                     29      3     13      4    83%   51, 66, 80->exit, 81
nvtabular/ops/join_external.py                                    69      5     28      6    89%   96, 98, 116, 129->133, 153, 168
nvtabular/ops/join_groupby.py                                     78      5     28      2    93%   102, 105->112, 179-180, 183-184
nvtabular/ops/lambdaop.py                                         27      3     10      3    84%   61, 65, 78
nvtabular/ops/logop.py                                             9      0      0      0   100%
nvtabular/ops/moments.py                                          65      0     20      0   100%
nvtabular/ops/normalize.py                                        64      6     14      2    87%   61->60, 67-68, 100-101, 123-124
nvtabular/ops/operator.py                                         15      1      2      1    88%   24
nvtabular/ops/rename.py                                           18      3     10      3    71%   41, 54, 58
nvtabular/ops/stat_operator.py                                    11      0      0      0   100%
nvtabular/ops/target_encoding.py                                 151     11     66      5    91%   143, 163->167, 170->179, 222-223, 226-227, 236-242, 335->338
nvtabular/tools/__init__.py                                        0      0      0      0   100%
nvtabular/tools/data_gen.py                                      235      1     62      4    98%   183->185, 230->234, 320->319, 322
nvtabular/tools/dataset_inspector.py                              49      9     18      0    75%   29-38
nvtabular/tools/inspector_script.py                               46     46      0      0     0%   17-168
nvtabular/utils.py                                                42     16     18      7    55%   20-21, 25-26, 35, 39, 48-51, 53-55, 58, 61, 67, 73
nvtabular/worker.py                                               68      1     30      2    97%   73, 83->98
nvtabular/workflow.py                                            140      9     62      4    93%   39, 120, 132-134, 237, 265-266, 336
----------------------------------------------------------------------------------------------------------
TOTAL                                                           4764   1010   1962    214    76%
Coverage XML written to file coverage.xml

Required test coverage of 70% reached. Total coverage: 76.09%

================= 714 passed, 12 skipped in 535.32s (0:08:55) ==================
Performing Post build task...
Match found for : : True
Logical operation result is TRUE
Running script  : #!/bin/bash
source activate rapids
cd /var/jenkins_home/
CUDA_VISIBLE_DEVICES=1 python test_res_push.py "https://api.GitHub.com/repos/NVIDIA/NVTabular/issues/$ghprbPullId/comments" "/var/jenkins_home/jobs/$JOB_NAME/builds/$BUILD_NUMBER/log" 
[nvtabular_tests] $ /bin/bash /tmp/jenkins7028813468789679603.sh
</pre></details>
**Update**: As I am finding the Criteo conversion time to be >20% slower than the custom script, I would like to come up with a way to avoid the `to_arrow` step (the cause of the regression) before this PR is integrated.
<details><summary>Click to view CI Results</summary><pre>GitHub pull request #660 of commit a8b9813c427de9c8690b7e411a3b760e8574d565, no merge conflicts.
Running as SYSTEM
Setting status of a8b9813c427de9c8690b7e411a3b760e8574d565 to PENDING with url http://10.20.13.93:8080/job/nvtabular_tests/1910/ and message: 'Pending'
Using context: Jenkins Unit Test Run
Building in workspace /var/jenkins_home/workspace/nvtabular_tests
using credential ghub_token
Cloning the remote Git repository
Cloning repository https://github.com/NVIDIA/NVTabular.git
 > git init /var/jenkins_home/workspace/nvtabular_tests/nvtabular # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
 > git --version # timeout=10
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
 > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/pull/660/*:refs/remotes/origin/pr/660/* # timeout=10
 > git rev-parse a8b9813c427de9c8690b7e411a3b760e8574d565^{commit} # timeout=10
Checking out Revision a8b9813c427de9c8690b7e411a3b760e8574d565 (detached)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f a8b9813c427de9c8690b7e411a3b760e8574d565 # timeout=10
Commit message: "add preserve_files option to to_parquet"
 > git rev-list --no-walk f1dc60f5ee16a43d12cb34b241917361618479bd # timeout=10
First time build. Skipping changelog.
[nvtabular_tests] $ /bin/bash /tmp/jenkins2845688012836608399.sh
Obtaining file:///var/jenkins_home/workspace/nvtabular_tests/nvtabular
  Installing build dependencies: started
  Installing build dependencies: finished with status 'done'
  Getting requirements to build wheel: started
  Getting requirements to build wheel: finished with status 'done'
    Preparing wheel metadata: started
    Preparing wheel metadata: finished with status 'done'
Installing collected packages: nvtabular
  Running setup.py develop for nvtabular
Successfully installed nvtabular
All done! ✨ 🍰 ✨
93 files would be left unchanged.
/conda/envs/rapids/lib/python3.8/site-packages/isort/files.py:30: UserWarning: Likely recursive symlink detected to /var/jenkins_home/workspace/nvtabular_tests/nvtabular/images
  warn(f"Likely recursive symlink detected to {resolved_path}")
============================= test session starts ==============================
platform linux -- Python 3.8.8, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /var/jenkins_home/workspace/nvtabular_tests/nvtabular, configfile: pyproject.toml
plugins: cov-2.11.1, xdist-2.2.1, forked-1.3.0
collected 724 items / 2 skipped / 722 selected

tests/unit/test_column_group.py .                                        [  0%]
tests/unit/test_column_similarity.py ......                              [  0%]
tests/unit/test_dask_nvt.py ............................................ [  7%]
.....................................................................    [ 16%]
tests/unit/test_io.py .................................................. [ 23%]
..................................................................ssssss [ 33%]
ss........                                                               [ 34%]
tests/unit/test_notebooks.py ..s..                                       [ 35%]
tests/unit/test_ops.py ................................................. [ 42%]
........................................................................ [ 52%]
........................................................................ [ 62%]
..................................                                       [ 66%]
tests/unit/test_tf_dataloader.py ...................                     [ 69%]
tests/unit/test_tf_layers.py ........................................... [ 75%]
...................................                                      [ 80%]
tests/unit/test_tools.py ......................                          [ 83%]
tests/unit/test_torch_dataloader.py ..........................Build timed out (after 20 minutes). Marking the build as failed.
Build was aborted
Performing Post build task...
Match found for : : True
Logical operation result is TRUE
Running script  : #!/bin/bash
source activate rapids
cd /var/jenkins_home/
CUDA_VISIBLE_DEVICES=1 python test_res_push.py "https://api.GitHub.com/repos/NVIDIA/NVTabular/issues/$ghprbPullId/comments" "/var/jenkins_home/jobs/$JOB_NAME/builds/$BUILD_NUMBER/log" 
[nvtabular_tests] $ /bin/bash /tmp/jenkins8300188418615438646.sh
</pre></details>
<details><summary>Click to view CI Results</summary><pre>GitHub pull request #660 of commit 06ebc5aeb1947e4c5a02797d1e11c9c140d1cbae, no merge conflicts.
Running as SYSTEM
Setting status of 06ebc5aeb1947e4c5a02797d1e11c9c140d1cbae to PENDING with url http://10.20.13.93:8080/job/nvtabular_tests/1911/ and message: 'Pending'
Using context: Jenkins Unit Test Run
Building in workspace /var/jenkins_home/workspace/nvtabular_tests
using credential ghub_token
Cloning the remote Git repository
Cloning repository https://github.com/NVIDIA/NVTabular.git
 > git init /var/jenkins_home/workspace/nvtabular_tests/nvtabular # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
 > git --version # timeout=10
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
 > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/pull/660/*:refs/remotes/origin/pr/660/* # timeout=10
 > git rev-parse 06ebc5aeb1947e4c5a02797d1e11c9c140d1cbae^{commit} # timeout=10
Checking out Revision 06ebc5aeb1947e4c5a02797d1e11c9c140d1cbae (detached)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f 06ebc5aeb1947e4c5a02797d1e11c9c140d1cbae # timeout=10
Commit message: "avoid aggregation by executing full subgraphs within each file-based task"
 > git rev-list --no-walk a8b9813c427de9c8690b7e411a3b760e8574d565 # timeout=10
[nvtabular_tests] $ /bin/bash /tmp/jenkins7682324231028873830.sh
Obtaining file:///var/jenkins_home/workspace/nvtabular_tests/nvtabular
  Installing build dependencies: started
  Installing build dependencies: finished with status 'done'
  Getting requirements to build wheel: started
  Getting requirements to build wheel: finished with status 'done'
    Preparing wheel metadata: started
    Preparing wheel metadata: finished with status 'done'
Installing collected packages: nvtabular
  Running setup.py develop for nvtabular
Successfully installed nvtabular
All done! ✨ 🍰 ✨
93 files would be left unchanged.
/conda/envs/rapids/lib/python3.8/site-packages/isort/files.py:30: UserWarning: Likely recursive symlink detected to /var/jenkins_home/workspace/nvtabular_tests/nvtabular/images
  warn(f"Likely recursive symlink detected to {resolved_path}")
============================= test session starts ==============================
platform linux -- Python 3.8.8, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /var/jenkins_home/workspace/nvtabular_tests/nvtabular, configfile: pyproject.toml
plugins: cov-2.11.1, xdist-2.2.1, forked-1.3.0
collected 724 items / 2 skipped / 722 selected

tests/unit/test_column_group.py .                                        [  0%]
tests/unit/test_column_similarity.py ......                              [  0%]
tests/unit/test_dask_nvt.py ............................................ [  7%]
.....................................................................    [ 16%]
tests/unit/test_io.py .................................................. [ 23%]
..................................................................ssssss [ 33%]
ss........                                                               [ 34%]
tests/unit/test_notebooks.py ..s..                                       [ 35%]
tests/unit/test_ops.py ................................................. [ 42%]
........................................................................ [ 52%]
........................................................................ [ 62%]
..................................                                       [ 66%]
tests/unit/test_tf_dataloader.py ...................                     [ 69%]
tests/unit/test_tf_layers.py ........................................... [ 75%]
...................................                                      [ 80%]
tests/unit/test_tools.py ......................                          [ 83%]
tests/unit/test_torch_dataloader.py .............................s       [ 87%]
tests/unit/test_workflow.py ............................................ [ 93%]
...............................................                          [100%]

----------- coverage: platform linux, python 3.8.8-final-0 -----------
Name                                                           Stmts   Miss Branch BrPart  Cover   Missing
----------------------------------------------------------------------------------------------------------
nvtabular/__init__.py                                             12      0      0      0   100%
nvtabular/column_group.py                                        149     19     80      7    85%   54, 87, 98, 101->103, 128, 151-164, 191, 278
nvtabular/dispatch.py                                             81     11     38      5    83%   35, 45->47, 69, 94, 111, 118, 135-138, 167-170
nvtabular/framework_utils/__init__.py                              0      0      0      0   100%
nvtabular/framework_utils/tensorflow/__init__.py                   1      0      0      0   100%
nvtabular/framework_utils/tensorflow/feature_column_utils.py     146    137     96      0     4%   28-32, 69-303
nvtabular/framework_utils/tensorflow/layers/__init__.py            4      0      0      0   100%
nvtabular/framework_utils/tensorflow/layers/embedding.py         153     14     89      7    87%   56, 64->45, 100, 108, 188, 240-248, 251, 344->352, 366->369, 372-373, 376
nvtabular/framework_utils/tensorflow/layers/interaction.py        47     25     20      1    43%   49, 74-103, 106-110, 113
nvtabular/framework_utils/tensorflow/layers/outer_product.py      30     24     10      0    15%   37-38, 41-60, 71-84, 87
nvtabular/framework_utils/torch/__init__.py                        0      0      0      0   100%
nvtabular/framework_utils/torch/layers/__init__.py                 2      0      0      0   100%
nvtabular/framework_utils/torch/layers/embeddings.py              27     12     12      1    51%   47, 71-79, 82-88
nvtabular/framework_utils/torch/models.py                         41      6     22      7    76%   56, 59, 64, 85, 87-88, 91->94, 98->100
nvtabular/framework_utils/torch/utils.py                          31      7     10      3    76%   52, 56-58, 67-69
nvtabular/inference/__init__.py                                    0      0      0      0   100%
nvtabular/inference/triton/__init__.py                           218    211     90      0     2%   24-494
nvtabular/inference/triton/model.py                               56     56     22      0     0%   27-142
nvtabular/inference/triton/model_hugectr.py                       44     44     14      0     0%   27-120
nvtabular/io/__init__.py                                           4      0      0      0   100%
nvtabular/io/avro.py                                              88     88     30      0     0%   16-189
nvtabular/io/csv.py                                               54      4     20      5    88%   95, 99->103, 104, 106, 120
nvtabular/io/dask.py                                             125      4     48      8    93%   128, 237->235, 265->268, 276, 280->282, 282->278, 287, 289
nvtabular/io/dataframe_engine.py                                  58      2     30      6    91%   44, 82->86, 86->91, 88->91, 91->110, 119
nvtabular/io/dataset.py                                          186     22     80     14    86%   253, 255, 268, 277, 295-309, 403, 489->491, 491->499, 500, 507-508, 595-596, 708-713, 719, 744
nvtabular/io/dataset_engine.py                                    23      1      0      0    96%   45
nvtabular/io/hugectr.py                                           44      2     24      2    91%   32, 72->95, 99
nvtabular/io/parquet.py                                          470     40    146     11    90%   83-91, 116-145, 184-186, 311-316, 354-359, 475->482, 545->550, 551-552, 671, 675, 679, 717, 734, 738, 745->747, 864->869, 874->884, 911
nvtabular/io/shuffle.py                                           30      7     12      2    69%   41-48
nvtabular/io/writer.py                                           156     10     56      3    93%   31, 48, 75, 120, 123, 196, 239, 260-262
nvtabular/io/writer_factory.py                                    18      2      8      2    85%   34, 58
nvtabular/loader/__init__.py                                       0      0      0      0   100%
nvtabular/loader/backend.py                                      269     15    112      9    94%   77-78, 107, 124, 131-132, 143, 203->205, 220, 243-244, 283->287, 358, 362-363, 457, 464
nvtabular/loader/tensorflow.py                                   120      8     52      7    90%   52, 60-63, 73, 83, 282, 309->313, 342
nvtabular/loader/tf_utils.py                                      55     10     20      5    80%   29->32, 32->34, 39->41, 43, 50-51, 58-60, 66-70
nvtabular/loader/torch.py                                         43     10      8      0    69%   25-27, 30-36
nvtabular/ops/__init__.py                                         19      0      0      0   100%
nvtabular/ops/bucketize.py                                        25      4     16      2    76%   46, 49-52
nvtabular/ops/categorify.py                                      513     69    298     47    83%   223, 240, 244, 252, 260, 262, 284, 303-304, 336-337, 399-401, 459-461, 466->468, 541, 579, 608->611, 612-614, 621-622, 635-637, 638->606, 654, 664, 666, 672, 688-689, 694, 697->700, 710, 734-736, 739, 741->743, 755-758, 784, 788, 790, 802-805, 920, 922, 964->985, 970->985, 986-991, 1028, 1044->1049, 1048, 1058->1055, 1063->1055, 1071, 1079-1089
nvtabular/ops/clip.py                                             19      2      6      3    80%   45, 53->55, 56
nvtabular/ops/column_similarity.py                                88     22     32      5    69%   84, 154-155, 164-166, 174-190, 205->215, 207->210, 211, 221
nvtabular/ops/data_stats.py                                       57      1     24      4    94%   88->90, 90->92, 93->84, 96->84, 101
nvtabular/ops/difference_lag.py                                   26      0      8      1    97%   67->69
nvtabular/ops/dropna.py                                            9      0      0      0   100%
nvtabular/ops/fill.py                                             58      2     20      1    96%   99, 125
nvtabular/ops/filter.py                                           21      1      6      1    93%   44
nvtabular/ops/hash_bucket.py                                      32      2     18      2    88%   73, 102
nvtabular/ops/hashed_cross.py                                     29      3     13      4    83%   51, 66, 80->exit, 81
nvtabular/ops/join_external.py                                    69      5     28      6    89%   96, 98, 116, 129->133, 153, 168
nvtabular/ops/join_groupby.py                                     78      5     28      2    93%   102, 105->112, 179-180, 183-184
nvtabular/ops/lambdaop.py                                         27      3     10      3    84%   61, 65, 78
nvtabular/ops/logop.py                                             9      0      0      0   100%
nvtabular/ops/moments.py                                          65      0     20      0   100%
nvtabular/ops/normalize.py                                        64      6     14      2    87%   61->60, 67-68, 100-101, 123-124
nvtabular/ops/operator.py                                         15      1      2      1    88%   24
nvtabular/ops/rename.py                                           18      3     10      3    71%   41, 54, 58
nvtabular/ops/stat_operator.py                                    11      0      0      0   100%
nvtabular/ops/target_encoding.py                                 151     11     66      5    91%   143, 163->167, 170->179, 222-223, 226-227, 236-242, 335->338
nvtabular/tools/__init__.py                                        0      0      0      0   100%
nvtabular/tools/data_gen.py                                      235      1     62      4    98%   183->185, 230->234, 320->319, 322
nvtabular/tools/dataset_inspector.py                              49      9     18      0    75%   29-38
nvtabular/tools/inspector_script.py                               46     46      0      0     0%   17-168
nvtabular/utils.py                                                42     16     18      7    55%   20-21, 25-26, 35, 39, 48-51, 53-55, 58, 61, 67, 73
nvtabular/worker.py                                               68      1     30      2    97%   73, 83->98
nvtabular/workflow.py                                            140      9     62      4    93%   39, 125, 137-139, 242, 270-271, 341
----------------------------------------------------------------------------------------------------------
TOTAL                                                           4768   1013   1958    214    76%
Coverage XML written to file coverage.xml

Required test coverage of 70% reached. Total coverage: 76.02%

================= 714 passed, 12 skipped in 529.84s (0:08:49) ==================
Performing Post build task...
Match found for : : True
Logical operation result is TRUE
Running script  : #!/bin/bash
source activate rapids
cd /var/jenkins_home/
CUDA_VISIBLE_DEVICES=1 python test_res_push.py "https://api.GitHub.com/repos/NVIDIA/NVTabular/issues/$ghprbPullId/comments" "/var/jenkins_home/jobs/$JOB_NAME/builds/$BUILD_NUMBER/log" 
[nvtabular_tests] $ /bin/bash /tmp/jenkins250737810177183113.sh
</pre></details>
**Update 2**: Latest changes avoid the need for in-memory aggregation of partitions for each output file.  Instead, we are essentially dividing the graph of the underlying `ddf` into a distinct subgraph for each output file.  Each subgraph is then executed, and the resulting data written to disk, within a single task.  So, we are passing the subgraphs as inputs to the tasks that are actually assigned to each worker (Dask-ception).  This approach results in great performance, ~but it also requires the use of `cloudpickle` to reliably package the subgraphs as task arguments.~
<details><summary>Click to view CI Results</summary><pre>GitHub pull request #660 of commit 6f04daa82fb83da938c7b581d813ca9e1b229239, no merge conflicts.
Running as SYSTEM
Setting status of 6f04daa82fb83da938c7b581d813ca9e1b229239 to PENDING with url http://10.20.13.93:8080/job/nvtabular_tests/1912/ and message: 'Pending'
Using context: Jenkins Unit Test Run
Building in workspace /var/jenkins_home/workspace/nvtabular_tests
using credential ghub_token
Cloning the remote Git repository
Cloning repository https://github.com/NVIDIA/NVTabular.git
 > git init /var/jenkins_home/workspace/nvtabular_tests/nvtabular # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
 > git --version # timeout=10
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
 > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/pull/660/*:refs/remotes/origin/pr/660/* # timeout=10
 > git rev-parse 6f04daa82fb83da938c7b581d813ca9e1b229239^{commit} # timeout=10
Checking out Revision 6f04daa82fb83da938c7b581d813ca9e1b229239 (detached)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f 6f04daa82fb83da938c7b581d813ca9e1b229239 # timeout=10
Commit message: "Merge remote-tracking branch 'upstream/main' into to_parquet_improvements"
 > git rev-list --no-walk 06ebc5aeb1947e4c5a02797d1e11c9c140d1cbae # timeout=10
[nvtabular_tests] $ /bin/bash /tmp/jenkins5782662807632167199.sh
Obtaining file:///var/jenkins_home/workspace/nvtabular_tests/nvtabular
  Installing build dependencies: started
  Installing build dependencies: finished with status 'done'
  Getting requirements to build wheel: started
  Getting requirements to build wheel: finished with status 'done'
    Preparing wheel metadata: started
    Preparing wheel metadata: finished with status 'done'
Installing collected packages: nvtabular
  Attempting uninstall: nvtabular
    Found existing installation: nvtabular 0.3.0+85.g42d1a13
    Uninstalling nvtabular-0.3.0+85.g42d1a13:
      Successfully uninstalled nvtabular-0.3.0+85.g42d1a13
  Running setup.py develop for nvtabular
Successfully installed nvtabular
All done! ✨ 🍰 ✨
93 files would be left unchanged.
/conda/envs/rapids/lib/python3.8/site-packages/isort/files.py:30: UserWarning: Likely recursive symlink detected to /var/jenkins_home/workspace/nvtabular_tests/nvtabular/images
  warn(f"Likely recursive symlink detected to {resolved_path}")
============================= test session starts ==============================
platform linux -- Python 3.8.8, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /var/jenkins_home/workspace/nvtabular_tests/nvtabular, configfile: pyproject.toml
plugins: cov-2.11.1, xdist-2.2.1, forked-1.3.0
collected 724 items / 2 skipped / 722 selected

tests/unit/test_column_group.py .                                        [  0%]
tests/unit/test_column_similarity.py ......                              [  0%]
tests/unit/test_dask_nvt.py ............................................ [  7%]
.....................................................................    [ 16%]
tests/unit/test_io.py .................................................. [ 23%]
..................................................................ssssss [ 33%]
ss........                                                               [ 34%]
tests/unit/test_notebooks.py ..s..                                       [ 35%]
tests/unit/test_ops.py ................................................. [ 42%]
........................................................................ [ 52%]
........................................................................ [ 62%]
..................................                                       [ 66%]
tests/unit/test_tf_dataloader.py ...................                     [ 69%]
tests/unit/test_tf_layers.py ........................................... [ 75%]
...................................                                      [ 80%]
tests/unit/test_tools.py ......................                          [ 83%]
tests/unit/test_torch_dataloader.py .............................s       [ 87%]
tests/unit/test_workflow.py ............................................ [ 93%]
...............................................                          [100%]

----------- coverage: platform linux, python 3.8.8-final-0 -----------
Name                                                           Stmts   Miss Branch BrPart  Cover   Missing
----------------------------------------------------------------------------------------------------------
nvtabular/__init__.py                                             12      0      0      0   100%
nvtabular/column_group.py                                        149     19     80      7    85%   54, 87, 98, 101->103, 128, 151-164, 191, 278
nvtabular/dispatch.py                                             81     11     38      5    83%   35, 45->47, 69, 94, 111, 118, 135-138, 167-170
nvtabular/framework_utils/__init__.py                              0      0      0      0   100%
nvtabular/framework_utils/tensorflow/__init__.py                   1      0      0      0   100%
nvtabular/framework_utils/tensorflow/feature_column_utils.py     146    137     96      0     4%   28-32, 69-303
nvtabular/framework_utils/tensorflow/layers/__init__.py            4      0      0      0   100%
nvtabular/framework_utils/tensorflow/layers/embedding.py         153     14     89      7    87%   56, 64->45, 100, 108, 188, 240-248, 251, 344->352, 366->369, 372-373, 376
nvtabular/framework_utils/tensorflow/layers/interaction.py        47     25     20      1    43%   49, 74-103, 106-110, 113
nvtabular/framework_utils/tensorflow/layers/outer_product.py      30     24     10      0    15%   37-38, 41-60, 71-84, 87
nvtabular/framework_utils/torch/__init__.py                        0      0      0      0   100%
nvtabular/framework_utils/torch/layers/__init__.py                 2      0      0      0   100%
nvtabular/framework_utils/torch/layers/embeddings.py              27     12     12      1    51%   47, 71-79, 82-88
nvtabular/framework_utils/torch/models.py                         41      6     22      7    76%   56, 59, 64, 85, 87-88, 91->94, 98->100
nvtabular/framework_utils/torch/utils.py                          31      7     10      3    76%   52, 56-58, 67-69
nvtabular/inference/__init__.py                                    0      0      0      0   100%
nvtabular/inference/triton/__init__.py                           218    211     90      0     2%   24-494
nvtabular/inference/triton/model.py                               56     56     22      0     0%   27-142
nvtabular/inference/triton/model_hugectr.py                       44     44     14      0     0%   27-120
nvtabular/io/__init__.py                                           4      0      0      0   100%
nvtabular/io/avro.py                                              88     88     30      0     0%   16-189
nvtabular/io/csv.py                                               54      4     20      5    88%   95, 99->103, 104, 106, 120
nvtabular/io/dask.py                                             128      4     48      8    93%   145, 253->251, 281->284, 292, 296->298, 298->294, 303, 305
nvtabular/io/dataframe_engine.py                                  58      2     30      6    91%   44, 82->86, 86->91, 88->91, 91->110, 119
nvtabular/io/dataset.py                                          186     22     80     14    86%   253, 255, 268, 277, 295-309, 403, 489->491, 491->499, 500, 507-508, 595-596, 708-713, 719, 744
nvtabular/io/dataset_engine.py                                    23      1      0      0    96%   45
nvtabular/io/hugectr.py                                           44      2     24      2    91%   32, 72->95, 99
nvtabular/io/parquet.py                                          470     40    146     11    90%   83-91, 116-145, 184-186, 311-316, 354-359, 475->482, 545->550, 551-552, 671, 675, 679, 717, 734, 738, 745->747, 864->869, 874->884, 911
nvtabular/io/shuffle.py                                           30      7     12      2    69%   41-48
nvtabular/io/writer.py                                           156     10     56      3    93%   31, 48, 75, 120, 123, 196, 239, 260-262
nvtabular/io/writer_factory.py                                    18      2      8      2    85%   34, 58
nvtabular/loader/__init__.py                                       0      0      0      0   100%
nvtabular/loader/backend.py                                      269     15    112      9    94%   77-78, 107, 124, 131-132, 143, 203->205, 220, 243-244, 283->287, 358, 362-363, 457, 464
nvtabular/loader/tensorflow.py                                   120      8     52      7    90%   52, 60-63, 73, 83, 282, 309->313, 342
nvtabular/loader/tf_utils.py                                      55     10     20      5    80%   29->32, 32->34, 39->41, 43, 50-51, 58-60, 66-70
nvtabular/loader/torch.py                                         43     10      8      0    69%   25-27, 30-36
nvtabular/ops/__init__.py                                         19      0      0      0   100%
nvtabular/ops/bucketize.py                                        25      4     16      2    76%   46, 49-52
nvtabular/ops/categorify.py                                      513     69    298     47    83%   223, 240, 244, 252, 260, 262, 284, 303-304, 336-337, 399-401, 459-461, 466->468, 541, 579, 608->611, 612-614, 621-622, 635-637, 638->606, 654, 664, 666, 672, 688-689, 694, 697->700, 710, 734-736, 739, 741->743, 755-758, 784, 788, 790, 802-805, 920, 922, 964->985, 970->985, 986-991, 1028, 1044->1049, 1048, 1058->1055, 1063->1055, 1071, 1079-1089
nvtabular/ops/clip.py                                             19      2      6      3    80%   45, 53->55, 56
nvtabular/ops/column_similarity.py                                88     22     32      5    69%   84, 154-155, 164-166, 174-190, 205->215, 207->210, 211, 221
nvtabular/ops/data_stats.py                                       57      1     24      4    94%   88->90, 90->92, 93->84, 96->84, 101
nvtabular/ops/difference_lag.py                                   26      0      8      1    97%   67->69
nvtabular/ops/dropna.py                                            9      0      0      0   100%
nvtabular/ops/fill.py                                             58      2     20      1    96%   99, 125
nvtabular/ops/filter.py                                           21      1      6      1    93%   44
nvtabular/ops/hash_bucket.py                                      32      2     18      2    88%   73, 102
nvtabular/ops/hashed_cross.py                                     29      3     13      4    83%   51, 66, 80->exit, 81
nvtabular/ops/join_external.py                                    69      5     28      6    89%   96, 98, 116, 129->133, 153, 168
nvtabular/ops/join_groupby.py                                     78      5     28      2    93%   102, 105->112, 179-180, 183-184
nvtabular/ops/lambdaop.py                                         27      3     10      3    84%   61, 65, 78
nvtabular/ops/logop.py                                             9      0      0      0   100%
nvtabular/ops/moments.py                                          65      0     20      0   100%
nvtabular/ops/normalize.py                                        64      6     14      2    87%   61->60, 67-68, 100-101, 123-124
nvtabular/ops/operator.py                                         15      1      2      1    88%   24
nvtabular/ops/rename.py                                           18      3     10      3    71%   41, 54, 58
nvtabular/ops/stat_operator.py                                    11      0      0      0   100%
nvtabular/ops/target_encoding.py                                 151     11     66      5    91%   143, 163->167, 170->179, 222-223, 226-227, 236-242, 335->338
nvtabular/tools/__init__.py                                        0      0      0      0   100%
nvtabular/tools/data_gen.py                                      235      1     62      4    98%   183->185, 230->234, 320->319, 322
nvtabular/tools/dataset_inspector.py                              49      9     18      0    75%   29-38
nvtabular/tools/inspector_script.py                               46     46      0      0     0%   17-168
nvtabular/utils.py                                                42     16     18      7    55%   20-21, 25-26, 35, 39, 48-51, 53-55, 58, 61, 67, 73
nvtabular/worker.py                                               68      1     30      2    97%   73, 83->98
nvtabular/workflow.py                                            140      9     62      4    93%   39, 125, 137-139, 242, 270-271, 341
----------------------------------------------------------------------------------------------------------
TOTAL                                                           4771   1013   1958    214    76%
Coverage XML written to file coverage.xml

Required test coverage of 70% reached. Total coverage: 76.03%

================= 714 passed, 12 skipped in 531.00s (0:08:50) ==================
Performing Post build task...
Match found for : : True
Logical operation result is TRUE
Running script  : #!/bin/bash
source activate rapids
cd /var/jenkins_home/
CUDA_VISIBLE_DEVICES=1 python test_res_push.py "https://api.GitHub.com/repos/NVIDIA/NVTabular/issues/$ghprbPullId/comments" "/var/jenkins_home/jobs/$JOB_NAME/builds/$BUILD_NUMBER/log" 
[nvtabular_tests] $ /bin/bash /tmp/jenkins9177300832110328631.sh
</pre></details>
<details><summary>Click to view CI Results</summary><pre>GitHub pull request #660 of commit a5c809cae0fa97e463fbb02b047e2e4d75f4b1cd, no merge conflicts.
Running as SYSTEM
Setting status of a5c809cae0fa97e463fbb02b047e2e4d75f4b1cd to PENDING with url http://10.20.13.93:8080/job/nvtabular_tests/1913/ and message: 'Pending'
Using context: Jenkins Unit Test Run
Building in workspace /var/jenkins_home/workspace/nvtabular_tests
using credential ghub_token
Cloning the remote Git repository
Cloning repository https://github.com/NVIDIA/NVTabular.git
 > git init /var/jenkins_home/workspace/nvtabular_tests/nvtabular # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
 > git --version # timeout=10
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
 > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/pull/660/*:refs/remotes/origin/pr/660/* # timeout=10
 > git rev-parse a5c809cae0fa97e463fbb02b047e2e4d75f4b1cd^{commit} # timeout=10
Checking out Revision a5c809cae0fa97e463fbb02b047e2e4d75f4b1cd (detached)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f a5c809cae0fa97e463fbb02b047e2e4d75f4b1cd # timeout=10
Commit message: "fix order-preservation bug"
 > git rev-list --no-walk 6f04daa82fb83da938c7b581d813ca9e1b229239 # timeout=10
[nvtabular_tests] $ /bin/bash /tmp/jenkins5913159709959711868.sh
Obtaining file:///var/jenkins_home/workspace/nvtabular_tests/nvtabular
  Installing build dependencies: started
  Installing build dependencies: finished with status 'done'
  Getting requirements to build wheel: started
  Getting requirements to build wheel: finished with status 'done'
    Preparing wheel metadata: started
    Preparing wheel metadata: finished with status 'done'
Installing collected packages: nvtabular
  Running setup.py develop for nvtabular
Successfully installed nvtabular
All done! ✨ 🍰 ✨
93 files would be left unchanged.
/conda/envs/rapids/lib/python3.8/site-packages/isort/files.py:30: UserWarning: Likely recursive symlink detected to /var/jenkins_home/workspace/nvtabular_tests/nvtabular/images
  warn(f"Likely recursive symlink detected to {resolved_path}")
============================= test session starts ==============================
platform linux -- Python 3.8.8, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /var/jenkins_home/workspace/nvtabular_tests/nvtabular, configfile: pyproject.toml
plugins: cov-2.11.1, xdist-2.2.1, forked-1.3.0
collected 724 items / 2 skipped / 722 selected

tests/unit/test_column_group.py .                                        [  0%]
tests/unit/test_column_similarity.py ......                              [  0%]
tests/unit/test_dask_nvt.py ............................................ [  7%]
.....................................................................    [ 16%]
tests/unit/test_io.py .................................................. [ 23%]
..................................................................ssssss [ 33%]
ss........                                                               [ 34%]
tests/unit/test_notebooks.py ..s..                                       [ 35%]
tests/unit/test_ops.py ................................................. [ 42%]
........................................................................ [ 52%]
........................................................................ [ 62%]
..................................                                       [ 66%]
tests/unit/test_tf_dataloader.py ...................                     [ 69%]
tests/unit/test_tf_layers.py ........................................... [ 75%]
...................................                                      [ 80%]
tests/unit/test_tools.py ......................                          [ 83%]
tests/unit/test_torch_dataloader.py .............................s       [ 87%]
tests/unit/test_workflow.py ............................................ [ 93%]
...............................................                          [100%]

----------- coverage: platform linux, python 3.8.8-final-0 -----------
Name                                                           Stmts   Miss Branch BrPart  Cover   Missing
----------------------------------------------------------------------------------------------------------
nvtabular/__init__.py                                             12      0      0      0   100%
nvtabular/column_group.py                                        149     19     80      7    85%   54, 87, 98, 101->103, 128, 151-164, 191, 278
nvtabular/dispatch.py                                             81     11     38      5    83%   35, 45->47, 69, 94, 111, 118, 135-138, 167-170
nvtabular/framework_utils/__init__.py                              0      0      0      0   100%
nvtabular/framework_utils/tensorflow/__init__.py                   1      0      0      0   100%
nvtabular/framework_utils/tensorflow/feature_column_utils.py     146    137     96      0     4%   28-32, 69-303
nvtabular/framework_utils/tensorflow/layers/__init__.py            4      0      0      0   100%
nvtabular/framework_utils/tensorflow/layers/embedding.py         153     14     89      7    87%   56, 64->45, 100, 108, 188, 240-248, 251, 344->352, 366->369, 372-373, 376
nvtabular/framework_utils/tensorflow/layers/interaction.py        47     25     20      1    43%   49, 74-103, 106-110, 113
nvtabular/framework_utils/tensorflow/layers/outer_product.py      30     24     10      0    15%   37-38, 41-60, 71-84, 87
nvtabular/framework_utils/torch/__init__.py                        0      0      0      0   100%
nvtabular/framework_utils/torch/layers/__init__.py                 2      0      0      0   100%
nvtabular/framework_utils/torch/layers/embeddings.py              27     12     12      1    51%   47, 71-79, 82-88
nvtabular/framework_utils/torch/models.py                         41      6     22      7    76%   56, 59, 64, 85, 87-88, 91->94, 98->100
nvtabular/framework_utils/torch/utils.py                          31      7     10      3    76%   52, 56-58, 67-69
nvtabular/inference/__init__.py                                    0      0      0      0   100%
nvtabular/inference/triton/__init__.py                           218    211     90      0     2%   24-494
nvtabular/inference/triton/model.py                               56     56     22      0     0%   27-142
nvtabular/inference/triton/model_hugectr.py                       44     44     14      0     0%   27-120
nvtabular/io/__init__.py                                           4      0      0      0   100%
nvtabular/io/avro.py                                              88     88     30      0     0%   16-189
nvtabular/io/csv.py                                               54      4     20      5    88%   95, 99->103, 104, 106, 120
nvtabular/io/dask.py                                             129      4     48      8    93%   147, 254->252, 282->285, 293, 297->299, 299->295, 304, 306
nvtabular/io/dataframe_engine.py                                  58      2     30      6    91%   44, 82->86, 86->91, 88->91, 91->110, 119
nvtabular/io/dataset.py                                          186     22     80     14    86%   253, 255, 268, 277, 295-309, 403, 489->491, 491->499, 500, 507-508, 595-596, 708-713, 719, 744
nvtabular/io/dataset_engine.py                                    23      1      0      0    96%   45
nvtabular/io/hugectr.py                                           44      2     24      2    91%   32, 72->95, 99
nvtabular/io/parquet.py                                          470     40    146     11    90%   83-91, 116-145, 184-186, 311-316, 354-359, 475->482, 545->550, 551-552, 671, 675, 679, 717, 734, 738, 745->747, 864->869, 874->884, 911
nvtabular/io/shuffle.py                                           30      7     12      2    69%   41-48
nvtabular/io/writer.py                                           156     10     56      3    93%   31, 48, 75, 120, 123, 196, 239, 260-262
nvtabular/io/writer_factory.py                                    18      2      8      2    85%   34, 58
nvtabular/loader/__init__.py                                       0      0      0      0   100%
nvtabular/loader/backend.py                                      269     15    112      9    94%   77-78, 107, 124, 131-132, 143, 203->205, 220, 243-244, 283->287, 358, 362-363, 457, 464
nvtabular/loader/tensorflow.py                                   120      8     52      7    90%   52, 60-63, 73, 83, 282, 309->313, 342
nvtabular/loader/tf_utils.py                                      55     10     20      5    80%   29->32, 32->34, 39->41, 43, 50-51, 58-60, 66-70
nvtabular/loader/torch.py                                         43     10      8      0    69%   25-27, 30-36
nvtabular/ops/__init__.py                                         19      0      0      0   100%
nvtabular/ops/bucketize.py                                        25      4     16      2    76%   46, 49-52
nvtabular/ops/categorify.py                                      513     69    298     47    83%   223, 240, 244, 252, 260, 262, 284, 303-304, 336-337, 399-401, 459-461, 466->468, 541, 579, 608->611, 612-614, 621-622, 635-637, 638->606, 654, 664, 666, 672, 688-689, 694, 697->700, 710, 734-736, 739, 741->743, 755-758, 784, 788, 790, 802-805, 920, 922, 964->985, 970->985, 986-991, 1028, 1044->1049, 1048, 1058->1055, 1063->1055, 1071, 1079-1089
nvtabular/ops/clip.py                                             19      2      6      3    80%   45, 53->55, 56
nvtabular/ops/column_similarity.py                                88     22     32      5    69%   84, 154-155, 164-166, 174-190, 205->215, 207->210, 211, 221
nvtabular/ops/data_stats.py                                       57      1     24      4    94%   88->90, 90->92, 93->84, 96->84, 101
nvtabular/ops/difference_lag.py                                   26      0      8      1    97%   67->69
nvtabular/ops/dropna.py                                            9      0      0      0   100%
nvtabular/ops/fill.py                                             58      2     20      1    96%   99, 125
nvtabular/ops/filter.py                                           21      1      6      1    93%   44
nvtabular/ops/hash_bucket.py                                      32      2     18      2    88%   73, 102
nvtabular/ops/hashed_cross.py                                     29      3     13      4    83%   51, 66, 80->exit, 81
nvtabular/ops/join_external.py                                    69      5     28      6    89%   96, 98, 116, 129->133, 153, 168
nvtabular/ops/join_groupby.py                                     78      5     28      2    93%   102, 105->112, 179-180, 183-184
nvtabular/ops/lambdaop.py                                         27      3     10      3    84%   61, 65, 78
nvtabular/ops/logop.py                                             9      0      0      0   100%
nvtabular/ops/moments.py                                          65      0     20      0   100%
nvtabular/ops/normalize.py                                        64      6     14      2    87%   61->60, 67-68, 100-101, 123-124
nvtabular/ops/operator.py                                         15      1      2      1    88%   24
nvtabular/ops/rename.py                                           18      3     10      3    71%   41, 54, 58
nvtabular/ops/stat_operator.py                                    11      0      0      0   100%
nvtabular/ops/target_encoding.py                                 151     11     66      5    91%   143, 163->167, 170->179, 222-223, 226-227, 236-242, 335->338
nvtabular/tools/__init__.py                                        0      0      0      0   100%
nvtabular/tools/data_gen.py                                      235      1     62      4    98%   183->185, 230->234, 320->319, 322
nvtabular/tools/dataset_inspector.py                              49      9     18      0    75%   29-38
nvtabular/tools/inspector_script.py                               46     46      0      0     0%   17-168
nvtabular/utils.py                                                42     16     18      7    55%   20-21, 25-26, 35, 39, 48-51, 53-55, 58, 61, 67, 73
nvtabular/worker.py                                               68      1     30      2    97%   73, 83->98
nvtabular/workflow.py                                            140      9     62      4    93%   39, 125, 137-139, 242, 270-271, 341
----------------------------------------------------------------------------------------------------------
TOTAL                                                           4772   1013   1958    214    76%
Coverage XML written to file coverage.xml

Required test coverage of 70% reached. Total coverage: 76.03%

================= 714 passed, 12 skipped in 532.65s (0:08:52) ==================
Performing Post build task...
Match found for : : True
Logical operation result is TRUE
Running script  : #!/bin/bash
source activate rapids
cd /var/jenkins_home/
CUDA_VISIBLE_DEVICES=1 python test_res_push.py "https://api.GitHub.com/repos/NVIDIA/NVTabular/issues/$ghprbPullId/comments" "/var/jenkins_home/jobs/$JOB_NAME/builds/$BUILD_NUMBER/log" 
[nvtabular_tests] $ /bin/bash /tmp/jenkins1459691061645382234.sh
</pre></details>
<details><summary>Click to view CI Results</summary><pre>GitHub pull request #660 of commit b5f3914cb09ea2f7c0f68e1b73ef17590c6581b3, no merge conflicts.
Running as SYSTEM
Setting status of b5f3914cb09ea2f7c0f68e1b73ef17590c6581b3 to PENDING with url http://10.20.13.93:8080/job/nvtabular_tests/1919/ and message: 'Pending'
Using context: Jenkins Unit Test Run
Building in workspace /var/jenkins_home/workspace/nvtabular_tests
using credential ghub_token
Cloning the remote Git repository
Cloning repository https://github.com/NVIDIA/NVTabular.git
 > git init /var/jenkins_home/workspace/nvtabular_tests/nvtabular # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
 > git --version # timeout=10
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
 > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/pull/660/*:refs/remotes/origin/pr/660/* # timeout=10
 > git rev-parse b5f3914cb09ea2f7c0f68e1b73ef17590c6581b3^{commit} # timeout=10
Checking out Revision b5f3914cb09ea2f7c0f68e1b73ef17590c6581b3 (detached)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f b5f3914cb09ea2f7c0f68e1b73ef17590c6581b3 # timeout=10
Commit message: "Merge remote-tracking branch 'upstream/main' into to_parquet_improvements"
 > git rev-list --no-walk 88aa4119163e5406dd9affcc18a0b007f42f1592 # timeout=10
First time build. Skipping changelog.
[nvtabular_tests] $ /bin/bash /tmp/jenkins7029281984781249780.sh
Obtaining file:///var/jenkins_home/workspace/nvtabular_tests/nvtabular
  Installing build dependencies: started
  Installing build dependencies: finished with status 'done'
  Getting requirements to build wheel: started
  Getting requirements to build wheel: finished with status 'done'
    Preparing wheel metadata: started
    Preparing wheel metadata: finished with status 'done'
Installing collected packages: nvtabular
  Running setup.py develop for nvtabular
Successfully installed nvtabular
All done! ✨ 🍰 ✨
93 files would be left unchanged.
/conda/envs/rapids/lib/python3.8/site-packages/isort/files.py:30: UserWarning: Likely recursive symlink detected to /var/jenkins_home/workspace/nvtabular_tests/nvtabular/images
  warn(f"Likely recursive symlink detected to {resolved_path}")
============================= test session starts ==============================
platform linux -- Python 3.8.8, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /var/jenkins_home/workspace/nvtabular_tests/nvtabular, configfile: pyproject.toml
plugins: cov-2.11.1, xdist-2.2.1, forked-1.3.0
collected 724 items / 2 skipped / 722 selected

tests/unit/test_column_group.py .                                        [  0%]
tests/unit/test_column_similarity.py ......                              [  0%]
tests/unit/test_dask_nvt.py ............................................ [  7%]
.....................................................................    [ 16%]
tests/unit/test_io.py .................................................. [ 23%]
..................................................................ssssss [ 33%]
ss........                                                               [ 34%]
tests/unit/test_notebooks.py ..s..                                       [ 35%]
tests/unit/test_ops.py ................................................. [ 42%]
........................................................................ [ 52%]
........................................................................ [ 62%]
..................................                                       [ 66%]
tests/unit/test_tf_dataloader.py ...................                     [ 69%]
tests/unit/test_tf_layers.py ........................................... [ 75%]
...................................                                      [ 80%]
tests/unit/test_tools.py ......................                          [ 83%]
tests/unit/test_torch_dataloader.py .............................s       [ 87%]
tests/unit/test_workflow.py ............................................ [ 93%]
...............................................                          [100%]

----------- coverage: platform linux, python 3.8.8-final-0 -----------
Name                                                           Stmts   Miss Branch BrPart  Cover   Missing
----------------------------------------------------------------------------------------------------------
nvtabular/__init__.py                                             12      0      0      0   100%
nvtabular/column_group.py                                        149     19     80      7    85%   54, 87, 98, 101->103, 128, 151-164, 191, 278
nvtabular/dispatch.py                                             81     11     38      5    83%   35, 45->47, 69, 94, 111, 118, 135-138, 167-170
nvtabular/framework_utils/__init__.py                              0      0      0      0   100%
nvtabular/framework_utils/tensorflow/__init__.py                   1      0      0      0   100%
nvtabular/framework_utils/tensorflow/feature_column_utils.py     146    137     96      0     4%   28-32, 69-303
nvtabular/framework_utils/tensorflow/layers/__init__.py            4      0      0      0   100%
nvtabular/framework_utils/tensorflow/layers/embedding.py         153     14     89      7    87%   56, 64->45, 100, 108, 188, 240-248, 251, 344->352, 366->369, 372-373, 376
nvtabular/framework_utils/tensorflow/layers/interaction.py        47     25     20      1    43%   49, 74-103, 106-110, 113
nvtabular/framework_utils/tensorflow/layers/outer_product.py      30     24     10      0    15%   37-38, 41-60, 71-84, 87
nvtabular/framework_utils/torch/__init__.py                        0      0      0      0   100%
nvtabular/framework_utils/torch/layers/__init__.py                 2      0      0      0   100%
nvtabular/framework_utils/torch/layers/embeddings.py              27     12     12      1    51%   47, 71-79, 82-88
nvtabular/framework_utils/torch/models.py                         41      6     22      7    76%   56, 59, 64, 85, 87-88, 91->94, 98->100
nvtabular/framework_utils/torch/utils.py                          31      7     10      3    76%   52, 56-58, 67-69
nvtabular/inference/__init__.py                                    0      0      0      0   100%
nvtabular/inference/triton/__init__.py                           218    211     90      0     2%   24-494
nvtabular/inference/triton/model.py                               56     56     22      0     0%   27-142
nvtabular/inference/triton/model_hugectr.py                       44     44     14      0     0%   27-120
nvtabular/io/__init__.py                                           4      0      0      0   100%
nvtabular/io/avro.py                                              88     88     30      0     0%   16-189
nvtabular/io/csv.py                                               54      4     20      5    88%   95, 99->103, 104, 106, 120
nvtabular/io/dask.py                                             129      4     48      8    93%   147, 254->252, 282->285, 293, 297->299, 299->295, 304, 306
nvtabular/io/dataframe_engine.py                                  58      2     30      6    91%   44, 82->86, 86->91, 88->91, 91->110, 119
nvtabular/io/dataset.py                                          186     22     80     14    86%   253, 255, 268, 277, 295-309, 403, 489->491, 491->499, 500, 507-508, 595-596, 708-713, 719, 744
nvtabular/io/dataset_engine.py                                    23      1      0      0    96%   45
nvtabular/io/hugectr.py                                           44      2     24      2    91%   32, 72->95, 99
nvtabular/io/parquet.py                                          470     40    146     11    90%   83-91, 116-145, 184-186, 311-316, 354-359, 475->482, 545->550, 551-552, 671, 675, 679, 717, 734, 738, 745->747, 864->869, 874->884, 911
nvtabular/io/shuffle.py                                           30      7     12      2    69%   41-48
nvtabular/io/writer.py                                           156     10     56      3    93%   31, 48, 75, 120, 123, 196, 239, 260-262
nvtabular/io/writer_factory.py                                    18      2      8      2    85%   34, 58
nvtabular/loader/__init__.py                                       0      0      0      0   100%
nvtabular/loader/backend.py                                      269     15    112      9    94%   77-78, 107, 124, 131-132, 143, 203->205, 220, 243-244, 283->287, 358, 362-363, 457, 464
nvtabular/loader/tensorflow.py                                   120      8     52      7    90%   52, 60-63, 73, 83, 282, 309->313, 342
nvtabular/loader/tf_utils.py                                      55     10     20      5    80%   29->32, 32->34, 39->41, 43, 50-51, 58-60, 66-70
nvtabular/loader/torch.py                                         43     10      8      0    69%   25-27, 30-36
nvtabular/ops/__init__.py                                         19      0      0      0   100%
nvtabular/ops/bucketize.py                                        25      4     16      2    76%   46, 49-52
nvtabular/ops/categorify.py                                      513     69    298     47    83%   223, 240, 244, 252, 260, 262, 284, 303-304, 336-337, 399-401, 459-461, 466->468, 541, 579, 608->611, 612-614, 621-622, 635-637, 638->606, 654, 664, 666, 672, 688-689, 694, 697->700, 710, 734-736, 739, 741->743, 755-758, 784, 788, 790, 802-805, 920, 922, 964->985, 970->985, 986-991, 1028, 1044->1049, 1048, 1058->1055, 1063->1055, 1071, 1079-1089
nvtabular/ops/clip.py                                             19      2      6      3    80%   45, 53->55, 56
nvtabular/ops/column_similarity.py                                88     22     32      5    69%   84, 154-155, 164-166, 174-190, 205->215, 207->210, 211, 221
nvtabular/ops/data_stats.py                                       57      1     24      4    94%   88->90, 90->92, 93->84, 96->84, 101
nvtabular/ops/difference_lag.py                                   26      0      8      1    97%   67->69
nvtabular/ops/dropna.py                                            9      0      0      0   100%
nvtabular/ops/fill.py                                             58      2     20      1    96%   99, 125
nvtabular/ops/filter.py                                           21      1      6      1    93%   44
nvtabular/ops/hash_bucket.py                                      32      2     18      2    88%   73, 102
nvtabular/ops/hashed_cross.py                                     29      3     13      4    83%   51, 66, 80->exit, 81
nvtabular/ops/join_external.py                                    69      5     28      6    89%   96, 98, 116, 129->133, 153, 168
nvtabular/ops/join_groupby.py                                     78      5     28      2    93%   102, 105->112, 179-180, 183-184
nvtabular/ops/lambdaop.py                                         27      3     10      3    84%   61, 65, 78
nvtabular/ops/logop.py                                             9      0      0      0   100%
nvtabular/ops/moments.py                                          65      0     20      0   100%
nvtabular/ops/normalize.py                                        64      6     14      2    87%   61->60, 67-68, 100-101, 123-124
nvtabular/ops/operator.py                                         15      1      2      1    88%   24
nvtabular/ops/rename.py                                           18      3     10      3    71%   41, 54, 58
nvtabular/ops/stat_operator.py                                    11      0      0      0   100%
nvtabular/ops/target_encoding.py                                 151     11     66      5    91%   143, 163->167, 170->179, 222-223, 226-227, 236-242, 335->338
nvtabular/tools/__init__.py                                        0      0      0      0   100%
nvtabular/tools/data_gen.py                                      235      1     62      4    98%   183->185, 230->234, 320->319, 322
nvtabular/tools/dataset_inspector.py                              49      9     18      0    75%   29-38
nvtabular/tools/inspector_script.py                               46     46      0      0     0%   17-168
nvtabular/utils.py                                                42     16     18      7    55%   20-21, 25-26, 35, 39, 48-51, 53-55, 58, 61, 67, 73
nvtabular/worker.py                                               68      1     30      2    97%   73, 83->98
nvtabular/workflow.py                                            140      9     62      4    93%   39, 125, 137-139, 242, 270-271, 341
----------------------------------------------------------------------------------------------------------
TOTAL                                                           4772   1013   1958    214    76%
Coverage XML written to file coverage.xml

Required test coverage of 70% reached. Total coverage: 76.03%

================= 714 passed, 12 skipped in 532.59s (0:08:52) ==================
Performing Post build task...
Match found for : : True
Logical operation result is TRUE
Running script  : #!/bin/bash
source activate rapids
cd /var/jenkins_home/
CUDA_VISIBLE_DEVICES=1 python test_res_push.py "https://api.GitHub.com/repos/NVIDIA/NVTabular/issues/$ghprbPullId/comments" "/var/jenkins_home/jobs/$JOB_NAME/builds/$BUILD_NUMBER/log" 
[nvtabular_tests] $ /bin/bash /tmp/jenkins5742080576668310853.sh
</pre></details>
<details><summary>Click to view CI Results</summary><pre>GitHub pull request #660 of commit 8d4b200c91fcd67cb2311fc6b7420a9a913019c9, no merge conflicts.
Running as SYSTEM
Setting status of 8d4b200c91fcd67cb2311fc6b7420a9a913019c9 to PENDING with url http://10.20.13.93:8080/job/nvtabular_tests/1920/ and message: 'Pending'
Using context: Jenkins Unit Test Run
Building in workspace /var/jenkins_home/workspace/nvtabular_tests
using credential ghub_token
Cloning the remote Git repository
Cloning repository https://github.com/NVIDIA/NVTabular.git
 > git init /var/jenkins_home/workspace/nvtabular_tests/nvtabular # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
 > git --version # timeout=10
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
 > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/pull/660/*:refs/remotes/origin/pr/660/* # timeout=10
 > git rev-parse 8d4b200c91fcd67cb2311fc6b7420a9a913019c9^{commit} # timeout=10
Checking out Revision 8d4b200c91fcd67cb2311fc6b7420a9a913019c9 (detached)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f 8d4b200c91fcd67cb2311fc6b7420a9a913019c9 # timeout=10
Commit message: "avoid partition shuffle when output-files are already mapped"
 > git rev-list --no-walk b5f3914cb09ea2f7c0f68e1b73ef17590c6581b3 # timeout=10
[nvtabular_tests] $ /bin/bash /tmp/jenkins2900514547066955301.sh
Obtaining file:///var/jenkins_home/workspace/nvtabular_tests/nvtabular
  Installing build dependencies: started
  Installing build dependencies: finished with status 'done'
  Getting requirements to build wheel: started
  Getting requirements to build wheel: finished with status 'done'
    Preparing wheel metadata: started
    Preparing wheel metadata: finished with status 'done'
Installing collected packages: nvtabular
  Running setup.py develop for nvtabular
Successfully installed nvtabular
All done! ✨ 🍰 ✨
93 files would be left unchanged.
/conda/envs/rapids/lib/python3.8/site-packages/isort/files.py:30: UserWarning: Likely recursive symlink detected to /var/jenkins_home/workspace/nvtabular_tests/nvtabular/images
  warn(f"Likely recursive symlink detected to {resolved_path}")
============================= test session starts ==============================
platform linux -- Python 3.8.8, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /var/jenkins_home/workspace/nvtabular_tests/nvtabular, configfile: pyproject.toml
plugins: cov-2.11.1, xdist-2.2.1, forked-1.3.0
collected 724 items / 2 skipped / 722 selected

tests/unit/test_column_group.py .                                        [  0%]
tests/unit/test_column_similarity.py ......                              [  0%]
tests/unit/test_dask_nvt.py ............................................ [  7%]
.....................................................................    [ 16%]
tests/unit/test_io.py .................................................. [ 23%]
..................................................................ssssss [ 33%]
ss........                                                               [ 34%]
tests/unit/test_notebooks.py ..s..                                       [ 35%]
tests/unit/test_ops.py ................................................. [ 42%]
........................................................................ [ 52%]
........................................................................ [ 62%]
..................................                                       [ 66%]
tests/unit/test_tf_dataloader.py ...................                     [ 69%]
tests/unit/test_tf_layers.py ........................................... [ 75%]
...................................                                      [ 80%]
tests/unit/test_tools.py ......................                          [ 83%]
tests/unit/test_torch_dataloader.py .............................s       [ 87%]
tests/unit/test_workflow.py ............................................ [ 93%]
...............................................                          [100%]

----------- coverage: platform linux, python 3.8.8-final-0 -----------
Name                                                           Stmts   Miss Branch BrPart  Cover   Missing
----------------------------------------------------------------------------------------------------------
nvtabular/__init__.py                                             12      0      0      0   100%
nvtabular/column_group.py                                        149     19     80      7    85%   54, 87, 98, 101->103, 128, 151-164, 191, 278
nvtabular/dispatch.py                                             81     11     38      5    83%   35, 45->47, 69, 94, 111, 118, 135-138, 167-170
nvtabular/framework_utils/__init__.py                              0      0      0      0   100%
nvtabular/framework_utils/tensorflow/__init__.py                   1      0      0      0   100%
nvtabular/framework_utils/tensorflow/feature_column_utils.py     146    137     96      0     4%   28-32, 69-303
nvtabular/framework_utils/tensorflow/layers/__init__.py            4      0      0      0   100%
nvtabular/framework_utils/tensorflow/layers/embedding.py         153     14     89      7    87%   56, 64->45, 100, 108, 188, 240-248, 251, 344->352, 366->369, 372-373, 376
nvtabular/framework_utils/tensorflow/layers/interaction.py        47     25     20      1    43%   49, 74-103, 106-110, 113
nvtabular/framework_utils/tensorflow/layers/outer_product.py      30     24     10      0    15%   37-38, 41-60, 71-84, 87
nvtabular/framework_utils/torch/__init__.py                        0      0      0      0   100%
nvtabular/framework_utils/torch/layers/__init__.py                 2      0      0      0   100%
nvtabular/framework_utils/torch/layers/embeddings.py              27     12     12      1    51%   47, 71-79, 82-88
nvtabular/framework_utils/torch/models.py                         41      6     22      7    76%   56, 59, 64, 85, 87-88, 91->94, 98->100
nvtabular/framework_utils/torch/utils.py                          31      7     10      3    76%   52, 56-58, 67-69
nvtabular/inference/__init__.py                                    0      0      0      0   100%
nvtabular/inference/triton/__init__.py                           218    211     90      0     2%   24-494
nvtabular/inference/triton/model.py                               56     56     22      0     0%   27-142
nvtabular/inference/triton/model_hugectr.py                       44     44     14      0     0%   27-120
nvtabular/io/__init__.py                                           4      0      0      0   100%
nvtabular/io/avro.py                                              88     88     30      0     0%   16-189
nvtabular/io/csv.py                                               54      4     20      5    88%   95, 99->103, 104, 106, 120
nvtabular/io/dask.py                                             129      4     48      8    93%   147, 254->252, 282->285, 293, 297->299, 299->295, 304, 306
nvtabular/io/dataframe_engine.py                                  58      2     30      6    91%   44, 82->86, 86->91, 88->91, 91->110, 119
nvtabular/io/dataset.py                                          188     22     82     14    86%   253, 255, 268, 277, 295-309, 403, 488->490, 490->498, 499, 506-507, 601-602, 714-719, 725, 750
nvtabular/io/dataset_engine.py                                    23      1      0      0    96%   45
nvtabular/io/hugectr.py                                           44      2     24      2    91%   32, 72->95, 99
nvtabular/io/parquet.py                                          470     40    146     11    90%   83-91, 116-145, 184-186, 311-316, 354-359, 475->482, 545->550, 551-552, 671, 675, 679, 717, 734, 738, 745->747, 864->869, 874->884, 911
nvtabular/io/shuffle.py                                           30      7     12      2    69%   41-48
nvtabular/io/writer.py                                           156     10     56      3    93%   31, 48, 75, 120, 123, 196, 239, 260-262
nvtabular/io/writer_factory.py                                    18      2      8      2    85%   34, 58
nvtabular/loader/__init__.py                                       0      0      0      0   100%
nvtabular/loader/backend.py                                      269     15    112      9    94%   77-78, 107, 124, 131-132, 143, 203->205, 220, 243-244, 283->287, 358, 362-363, 457, 464
nvtabular/loader/tensorflow.py                                   120      8     52      7    90%   52, 60-63, 73, 83, 282, 309->313, 342
nvtabular/loader/tf_utils.py                                      55     10     20      5    80%   29->32, 32->34, 39->41, 43, 50-51, 58-60, 66-70
nvtabular/loader/torch.py                                         43     10      8      0    69%   25-27, 30-36
nvtabular/ops/__init__.py                                         19      0      0      0   100%
nvtabular/ops/bucketize.py                                        25      4     16      2    76%   46, 49-52
nvtabular/ops/categorify.py                                      513     69    298     47    83%   223, 240, 244, 252, 260, 262, 284, 303-304, 336-337, 399-401, 459-461, 466->468, 541, 579, 608->611, 612-614, 621-622, 635-637, 638->606, 654, 664, 666, 672, 688-689, 694, 697->700, 710, 734-736, 739, 741->743, 755-758, 784, 788, 790, 802-805, 920, 922, 964->985, 970->985, 986-991, 1028, 1044->1049, 1048, 1058->1055, 1063->1055, 1071, 1079-1089
nvtabular/ops/clip.py                                             19      2      6      3    80%   45, 53->55, 56
nvtabular/ops/column_similarity.py                                88     22     32      5    69%   84, 154-155, 164-166, 174-190, 205->215, 207->210, 211, 221
nvtabular/ops/data_stats.py                                       57      1     24      4    94%   88->90, 90->92, 93->84, 96->84, 101
nvtabular/ops/difference_lag.py                                   26      0      8      1    97%   67->69
nvtabular/ops/dropna.py                                            9      0      0      0   100%
nvtabular/ops/fill.py                                             58      2     20      1    96%   99, 125
nvtabular/ops/filter.py                                           21      1      6      1    93%   44
nvtabular/ops/hash_bucket.py                                      32      2     18      2    88%   73, 102
nvtabular/ops/hashed_cross.py                                     29      3     13      4    83%   51, 66, 80->exit, 81
nvtabular/ops/join_external.py                                    69      5     28      6    89%   96, 98, 116, 129->133, 153, 168
nvtabular/ops/join_groupby.py                                     78      5     28      2    93%   102, 105->112, 179-180, 183-184
nvtabular/ops/lambdaop.py                                         27      3     10      3    84%   61, 65, 78
nvtabular/ops/logop.py                                             9      0      0      0   100%
nvtabular/ops/moments.py                                          65      0     20      0   100%
nvtabular/ops/normalize.py                                        64      6     14      2    87%   61->60, 67-68, 100-101, 123-124
nvtabular/ops/operator.py                                         15      1      2      1    88%   24
nvtabular/ops/rename.py                                           18      3     10      3    71%   41, 54, 58
nvtabular/ops/stat_operator.py                                    11      0      0      0   100%
nvtabular/ops/target_encoding.py                                 151     11     66      5    91%   143, 163->167, 170->179, 222-223, 226-227, 236-242, 335->338
nvtabular/tools/__init__.py                                        0      0      0      0   100%
nvtabular/tools/data_gen.py                                      235      1     62      4    98%   183->185, 230->234, 320->319, 322
nvtabular/tools/dataset_inspector.py                              49      9     18      0    75%   29-38
nvtabular/tools/inspector_script.py                               46     46      0      0     0%   17-168
nvtabular/utils.py                                                42     16     18      7    55%   20-21, 25-26, 35, 39, 48-51, 53-55, 58, 61, 67, 73
nvtabular/worker.py                                               68      1     30      2    97%   73, 83->98
nvtabular/workflow.py                                            140      9     62      4    93%   39, 125, 137-139, 242, 270-271, 341
----------------------------------------------------------------------------------------------------------
TOTAL                                                           4774   1013   1960    214    76%
Coverage XML written to file coverage.xml

Required test coverage of 70% reached. Total coverage: 76.05%

================= 714 passed, 12 skipped in 533.35s (0:08:53) ==================
Performing Post build task...
Match found for : : True
Logical operation result is TRUE
Running script  : #!/bin/bash
source activate rapids
cd /var/jenkins_home/
CUDA_VISIBLE_DEVICES=1 python test_res_push.py "https://api.GitHub.com/repos/NVIDIA/NVTabular/issues/$ghprbPullId/comments" "/var/jenkins_home/jobs/$JOB_NAME/builds/$BUILD_NUMBER/log" 
[nvtabular_tests] $ /bin/bash /tmp/jenkins4906028607440309800.sh
</pre></details>
<details><summary>Click to view CI Results</summary><pre>GitHub pull request #660 of commit 3c9f04faa6cb322f0a7511fd04163a7deca32960, no merge conflicts.
Running as SYSTEM
Setting status of 3c9f04faa6cb322f0a7511fd04163a7deca32960 to PENDING with url http://10.20.13.93:8080/job/nvtabular_tests/1921/ and message: 'Pending'
Using context: Jenkins Unit Test Run
Building in workspace /var/jenkins_home/workspace/nvtabular_tests
using credential ghub_token
Cloning the remote Git repository
Cloning repository https://github.com/NVIDIA/NVTabular.git
 > git init /var/jenkins_home/workspace/nvtabular_tests/nvtabular # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
 > git --version # timeout=10
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
 > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/pull/660/*:refs/remotes/origin/pr/660/* # timeout=10
 > git rev-parse 3c9f04faa6cb322f0a7511fd04163a7deca32960^{commit} # timeout=10
Checking out Revision 3c9f04faa6cb322f0a7511fd04163a7deca32960 (detached)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f 3c9f04faa6cb322f0a7511fd04163a7deca32960 # timeout=10
Commit message: "Apply suggestions from code review"
 > git rev-list --no-walk 8d4b200c91fcd67cb2311fc6b7420a9a913019c9 # timeout=10
[nvtabular_tests] $ /bin/bash /tmp/jenkins984740764052572800.sh
Obtaining file:///var/jenkins_home/workspace/nvtabular_tests/nvtabular
  Installing build dependencies: started
  Installing build dependencies: finished with status 'done'
  Getting requirements to build wheel: started
  Getting requirements to build wheel: finished with status 'done'
    Preparing wheel metadata: started
    Preparing wheel metadata: finished with status 'done'
Installing collected packages: nvtabular
  Attempting uninstall: nvtabular
    Found existing installation: nvtabular 0.3.0+85.g42d1a13
    Uninstalling nvtabular-0.3.0+85.g42d1a13:
      Successfully uninstalled nvtabular-0.3.0+85.g42d1a13
  Running setup.py develop for nvtabular
Successfully installed nvtabular
All done! ✨ 🍰 ✨
93 files would be left unchanged.
/conda/envs/rapids/lib/python3.8/site-packages/isort/files.py:30: UserWarning: Likely recursive symlink detected to /var/jenkins_home/workspace/nvtabular_tests/nvtabular/images
  warn(f"Likely recursive symlink detected to {resolved_path}")
============================= test session starts ==============================
platform linux -- Python 3.8.8, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /var/jenkins_home/workspace/nvtabular_tests/nvtabular, configfile: pyproject.toml
plugins: cov-2.11.1, xdist-2.2.1, forked-1.3.0
collected 724 items / 2 skipped / 722 selected

tests/unit/test_column_group.py .                                        [  0%]
tests/unit/test_column_similarity.py ......                              [  0%]
tests/unit/test_dask_nvt.py ............................................ [  7%]
.....................................................................    [ 16%]
tests/unit/test_io.py .................................................. [ 23%]
..................................................................ssssss [ 33%]
ss........                                                               [ 34%]
tests/unit/test_notebooks.py ..s..                                       [ 35%]
tests/unit/test_ops.py ................................................. [ 42%]
........................................................................ [ 52%]
........................................................................ [ 62%]
..................................                                       [ 66%]
tests/unit/test_tf_dataloader.py ..FFFFF..FFFF......                     [ 69%]
tests/unit/test_tf_layers.py ........................................... [ 75%]
...................................                                      [ 80%]
tests/unit/test_tools.py ......................                          [ 83%]
tests/unit/test_torch_dataloader.py .............................s       [ 87%]
tests/unit/test_workflow.py ............................................ [ 93%]
...............................................                          [100%]

=================================== FAILURES ===================================
_____________________ test_tf_gpu_dl[True-1-parquet-0.06] ______________________

tmpdir = local('/tmp/pytest-of-jenkins/pytest-0/test_tf_gpu_dl_True_1_parquet_1')
paths = ['/tmp/pytest-of-jenkins/pytest-0/parquet0/dataset-0.parquet', '/tmp/pytest-of-jenkins/pytest-0/parquet0/dataset-1.parquet']
use_paths = True
dataset = <nvtabular.io.dataset.Dataset object at 0x7f31c8e763d0>
batch_size = 1, gpu_memory_frac = 0.06, engine = 'parquet'

    @pytest.mark.parametrize("gpu_memory_frac", [0.01, 0.06])
    @pytest.mark.parametrize("engine", ["parquet"])
    @pytest.mark.parametrize("batch_size", [1, 10, 100])
    @pytest.mark.parametrize("use_paths", [True, False])
    def test_tf_gpu_dl(tmpdir, paths, use_paths, dataset, batch_size, gpu_memory_frac, engine):
        cont_names = ["x", "y", "id"]
        cat_names = ["name-string"]
        label_name = ["label"]
        if engine == "parquet":
            cat_names.append("name-cat")
    
        columns = cont_names + cat_names
    
        conts = cont_names >> ops.FillMedian() >> ops.Normalize()
        cats = cat_names >> ops.Categorify()
    
        workflow = nvt.Workflow(conts + cats + label_name)
>       workflow.fit(dataset)

tests/unit/test_tf_dataloader.py:88: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
nvtabular/workflow.py:147: in fit
    op.fit_finalize(computed_stats)
/conda/envs/rapids/lib/python3.8/contextlib.py:75: in inner
    return func(*args, **kwds)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <nvtabular.ops.fill.FillMedian object at 0x7f31c8ec2460>
dask_stats = x            <NA>
y     0.030652915
id         1000.0
Name: 0.5, dtype: float64

    @annotate("FillMedian_finalize", color="green", domain="nvt_python")
    def fit_finalize(self, dask_stats):
        index = dask_stats.index
        vals = index.values_host if hasattr(index, "values_host") else index.values
        for col in vals:
>           self.medians[col] = float(dask_stats[col])
E           TypeError: float() argument must be a string or a number, not '_NAType'

nvtabular/ops/fill.py:118: TypeError
_____________________ test_tf_gpu_dl[True-10-parquet-0.01] _____________________

tmpdir = local('/tmp/pytest-of-jenkins/pytest-0/test_tf_gpu_dl_True_10_parquet0')
paths = ['/tmp/pytest-of-jenkins/pytest-0/parquet0/dataset-0.parquet', '/tmp/pytest-of-jenkins/pytest-0/parquet0/dataset-1.parquet']
use_paths = True
dataset = <nvtabular.io.dataset.Dataset object at 0x7f3134f3dc10>
batch_size = 10, gpu_memory_frac = 0.01, engine = 'parquet'

    @pytest.mark.parametrize("gpu_memory_frac", [0.01, 0.06])
    @pytest.mark.parametrize("engine", ["parquet"])
    @pytest.mark.parametrize("batch_size", [1, 10, 100])
    @pytest.mark.parametrize("use_paths", [True, False])
    def test_tf_gpu_dl(tmpdir, paths, use_paths, dataset, batch_size, gpu_memory_frac, engine):
        cont_names = ["x", "y", "id"]
        cat_names = ["name-string"]
        label_name = ["label"]
        if engine == "parquet":
            cat_names.append("name-cat")
    
        columns = cont_names + cat_names
    
        conts = cont_names >> ops.FillMedian() >> ops.Normalize()
        cats = cat_names >> ops.Categorify()
    
        workflow = nvt.Workflow(conts + cats + label_name)
>       workflow.fit(dataset)

tests/unit/test_tf_dataloader.py:88: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
nvtabular/workflow.py:147: in fit
    op.fit_finalize(computed_stats)
/conda/envs/rapids/lib/python3.8/contextlib.py:75: in inner
    return func(*args, **kwds)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <nvtabular.ops.fill.FillMedian object at 0x7f3134f3db80>
dask_stats = x            <NA>
y     0.030652915
id         1000.0
Name: 0.5, dtype: float64

    @annotate("FillMedian_finalize", color="green", domain="nvt_python")
    def fit_finalize(self, dask_stats):
        index = dask_stats.index
        vals = index.values_host if hasattr(index, "values_host") else index.values
        for col in vals:
>           self.medians[col] = float(dask_stats[col])
E           TypeError: float() argument must be a string or a number, not '_NAType'

nvtabular/ops/fill.py:118: TypeError
_____________________ test_tf_gpu_dl[True-10-parquet-0.06] _____________________

tmpdir = local('/tmp/pytest-of-jenkins/pytest-0/test_tf_gpu_dl_True_10_parquet1')
paths = ['/tmp/pytest-of-jenkins/pytest-0/parquet0/dataset-0.parquet', '/tmp/pytest-of-jenkins/pytest-0/parquet0/dataset-1.parquet']
use_paths = True
dataset = <nvtabular.io.dataset.Dataset object at 0x7f319ed092b0>
batch_size = 10, gpu_memory_frac = 0.06, engine = 'parquet'

    @pytest.mark.parametrize("gpu_memory_frac", [0.01, 0.06])
    @pytest.mark.parametrize("engine", ["parquet"])
    @pytest.mark.parametrize("batch_size", [1, 10, 100])
    @pytest.mark.parametrize("use_paths", [True, False])
    def test_tf_gpu_dl(tmpdir, paths, use_paths, dataset, batch_size, gpu_memory_frac, engine):
        cont_names = ["x", "y", "id"]
        cat_names = ["name-string"]
        label_name = ["label"]
        if engine == "parquet":
            cat_names.append("name-cat")
    
        columns = cont_names + cat_names
    
        conts = cont_names >> ops.FillMedian() >> ops.Normalize()
        cats = cat_names >> ops.Categorify()
    
        workflow = nvt.Workflow(conts + cats + label_name)
>       workflow.fit(dataset)

tests/unit/test_tf_dataloader.py:88: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
nvtabular/workflow.py:147: in fit
    op.fit_finalize(computed_stats)
/conda/envs/rapids/lib/python3.8/contextlib.py:75: in inner
    return func(*args, **kwds)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <nvtabular.ops.fill.FillMedian object at 0x7f319ed09fa0>
dask_stats = x            <NA>
y     0.030652915
id         1000.0
Name: 0.5, dtype: float64

    @annotate("FillMedian_finalize", color="green", domain="nvt_python")
    def fit_finalize(self, dask_stats):
        index = dask_stats.index
        vals = index.values_host if hasattr(index, "values_host") else index.values
        for col in vals:
>           self.medians[col] = float(dask_stats[col])
E           TypeError: float() argument must be a string or a number, not '_NAType'

nvtabular/ops/fill.py:118: TypeError
____________________ test_tf_gpu_dl[True-100-parquet-0.01] _____________________

tmpdir = local('/tmp/pytest-of-jenkins/pytest-0/test_tf_gpu_dl_True_100_parque0')
paths = ['/tmp/pytest-of-jenkins/pytest-0/parquet0/dataset-0.parquet', '/tmp/pytest-of-jenkins/pytest-0/parquet0/dataset-1.parquet']
use_paths = True
dataset = <nvtabular.io.dataset.Dataset object at 0x7f31c8edd0a0>
batch_size = 100, gpu_memory_frac = 0.01, engine = 'parquet'

    @pytest.mark.parametrize("gpu_memory_frac", [0.01, 0.06])
    @pytest.mark.parametrize("engine", ["parquet"])
    @pytest.mark.parametrize("batch_size", [1, 10, 100])
    @pytest.mark.parametrize("use_paths", [True, False])
    def test_tf_gpu_dl(tmpdir, paths, use_paths, dataset, batch_size, gpu_memory_frac, engine):
        cont_names = ["x", "y", "id"]
        cat_names = ["name-string"]
        label_name = ["label"]
        if engine == "parquet":
            cat_names.append("name-cat")
    
        columns = cont_names + cat_names
    
        conts = cont_names >> ops.FillMedian() >> ops.Normalize()
        cats = cat_names >> ops.Categorify()
    
        workflow = nvt.Workflow(conts + cats + label_name)
>       workflow.fit(dataset)

tests/unit/test_tf_dataloader.py:88: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
nvtabular/workflow.py:147: in fit
    op.fit_finalize(computed_stats)
/conda/envs/rapids/lib/python3.8/contextlib.py:75: in inner
    return func(*args, **kwds)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <nvtabular.ops.fill.FillMedian object at 0x7f31c8edd250>
dask_stats = x            <NA>
y     0.030652915
id         1000.0
Name: 0.5, dtype: float64

    @annotate("FillMedian_finalize", color="green", domain="nvt_python")
    def fit_finalize(self, dask_stats):
        index = dask_stats.index
        vals = index.values_host if hasattr(index, "values_host") else index.values
        for col in vals:
>           self.medians[col] = float(dask_stats[col])
E           TypeError: float() argument must be a string or a number, not '_NAType'

nvtabular/ops/fill.py:118: TypeError
____________________ test_tf_gpu_dl[True-100-parquet-0.06] _____________________

tmpdir = local('/tmp/pytest-of-jenkins/pytest-0/test_tf_gpu_dl_True_100_parque1')
paths = ['/tmp/pytest-of-jenkins/pytest-0/parquet0/dataset-0.parquet', '/tmp/pytest-of-jenkins/pytest-0/parquet0/dataset-1.parquet']
use_paths = True
dataset = <nvtabular.io.dataset.Dataset object at 0x7f31c8ee7940>
batch_size = 100, gpu_memory_frac = 0.06, engine = 'parquet'

    @pytest.mark.parametrize("gpu_memory_frac", [0.01, 0.06])
    @pytest.mark.parametrize("engine", ["parquet"])
    @pytest.mark.parametrize("batch_size", [1, 10, 100])
    @pytest.mark.parametrize("use_paths", [True, False])
    def test_tf_gpu_dl(tmpdir, paths, use_paths, dataset, batch_size, gpu_memory_frac, engine):
        cont_names = ["x", "y", "id"]
        cat_names = ["name-string"]
        label_name = ["label"]
        if engine == "parquet":
            cat_names.append("name-cat")
    
        columns = cont_names + cat_names
    
        conts = cont_names >> ops.FillMedian() >> ops.Normalize()
        cats = cat_names >> ops.Categorify()
    
        workflow = nvt.Workflow(conts + cats + label_name)
>       workflow.fit(dataset)

tests/unit/test_tf_dataloader.py:88: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
nvtabular/workflow.py:147: in fit
    op.fit_finalize(computed_stats)
/conda/envs/rapids/lib/python3.8/contextlib.py:75: in inner
    return func(*args, **kwds)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <nvtabular.ops.fill.FillMedian object at 0x7f31c8ee7670>
dask_stats = x            <NA>
y     0.030652915
id         1000.0
Name: 0.5, dtype: float64

    @annotate("FillMedian_finalize", color="green", domain="nvt_python")
    def fit_finalize(self, dask_stats):
        index = dask_stats.index
        vals = index.values_host if hasattr(index, "values_host") else index.values
        for col in vals:
>           self.medians[col] = float(dask_stats[col])
E           TypeError: float() argument must be a string or a number, not '_NAType'

nvtabular/ops/fill.py:118: TypeError
____________________ test_tf_gpu_dl[False-10-parquet-0.01] _____________________

tmpdir = local('/tmp/pytest-of-jenkins/pytest-0/test_tf_gpu_dl_False_10_parque0')
paths = ['/tmp/pytest-of-jenkins/pytest-0/parquet0/dataset-0.parquet', '/tmp/pytest-of-jenkins/pytest-0/parquet0/dataset-1.parquet']
use_paths = False
dataset = <nvtabular.io.dataset.Dataset object at 0x7f31c8ff8340>
batch_size = 10, gpu_memory_frac = 0.01, engine = 'parquet'

    @pytest.mark.parametrize("gpu_memory_frac", [0.01, 0.06])
    @pytest.mark.parametrize("engine", ["parquet"])
    @pytest.mark.parametrize("batch_size", [1, 10, 100])
    @pytest.mark.parametrize("use_paths", [True, False])
    def test_tf_gpu_dl(tmpdir, paths, use_paths, dataset, batch_size, gpu_memory_frac, engine):
        cont_names = ["x", "y", "id"]
        cat_names = ["name-string"]
        label_name = ["label"]
        if engine == "parquet":
            cat_names.append("name-cat")
    
        columns = cont_names + cat_names
    
        conts = cont_names >> ops.FillMedian() >> ops.Normalize()
        cats = cat_names >> ops.Categorify()
    
        workflow = nvt.Workflow(conts + cats + label_name)
>       workflow.fit(dataset)

tests/unit/test_tf_dataloader.py:88: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
nvtabular/workflow.py:147: in fit
    op.fit_finalize(computed_stats)
/conda/envs/rapids/lib/python3.8/contextlib.py:75: in inner
    return func(*args, **kwds)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <nvtabular.ops.fill.FillMedian object at 0x7f31dc1841f0>
dask_stats = x            <NA>
y     0.030652915
id         1000.0
Name: 0.5, dtype: float64

    @annotate("FillMedian_finalize", color="green", domain="nvt_python")
    def fit_finalize(self, dask_stats):
        index = dask_stats.index
        vals = index.values_host if hasattr(index, "values_host") else index.values
        for col in vals:
>           self.medians[col] = float(dask_stats[col])
E           TypeError: float() argument must be a string or a number, not '_NAType'

nvtabular/ops/fill.py:118: TypeError
____________________ test_tf_gpu_dl[False-10-parquet-0.06] _____________________

tmpdir = local('/tmp/pytest-of-jenkins/pytest-0/test_tf_gpu_dl_False_10_parque1')
paths = ['/tmp/pytest-of-jenkins/pytest-0/parquet0/dataset-0.parquet', '/tmp/pytest-of-jenkins/pytest-0/parquet0/dataset-1.parquet']
use_paths = False
dataset = <nvtabular.io.dataset.Dataset object at 0x7f31c8fbee20>
batch_size = 10, gpu_memory_frac = 0.06, engine = 'parquet'

    @pytest.mark.parametrize("gpu_memory_frac", [0.01, 0.06])
    @pytest.mark.parametrize("engine", ["parquet"])
    @pytest.mark.parametrize("batch_size", [1, 10, 100])
    @pytest.mark.parametrize("use_paths", [True, False])
    def test_tf_gpu_dl(tmpdir, paths, use_paths, dataset, batch_size, gpu_memory_frac, engine):
        cont_names = ["x", "y", "id"]
        cat_names = ["name-string"]
        label_name = ["label"]
        if engine == "parquet":
            cat_names.append("name-cat")
    
        columns = cont_names + cat_names
    
        conts = cont_names >> ops.FillMedian() >> ops.Normalize()
        cats = cat_names >> ops.Categorify()
    
        workflow = nvt.Workflow(conts + cats + label_name)
>       workflow.fit(dataset)

tests/unit/test_tf_dataloader.py:88: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
nvtabular/workflow.py:147: in fit
    op.fit_finalize(computed_stats)
/conda/envs/rapids/lib/python3.8/contextlib.py:75: in inner
    return func(*args, **kwds)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <nvtabular.ops.fill.FillMedian object at 0x7f31c8fbeb20>
dask_stats = x            <NA>
y     0.030652915
id         1000.0
Name: 0.5, dtype: float64

    @annotate("FillMedian_finalize", color="green", domain="nvt_python")
    def fit_finalize(self, dask_stats):
        index = dask_stats.index
        vals = index.values_host if hasattr(index, "values_host") else index.values
        for col in vals:
>           self.medians[col] = float(dask_stats[col])
E           TypeError: float() argument must be a string or a number, not '_NAType'

nvtabular/ops/fill.py:118: TypeError
____________________ test_tf_gpu_dl[False-100-parquet-0.01] ____________________

tmpdir = local('/tmp/pytest-of-jenkins/pytest-0/test_tf_gpu_dl_False_100_parqu0')
paths = ['/tmp/pytest-of-jenkins/pytest-0/parquet0/dataset-0.parquet', '/tmp/pytest-of-jenkins/pytest-0/parquet0/dataset-1.parquet']
use_paths = False
dataset = <nvtabular.io.dataset.Dataset object at 0x7f31c8fb0760>
batch_size = 100, gpu_memory_frac = 0.01, engine = 'parquet'

    @pytest.mark.parametrize("gpu_memory_frac", [0.01, 0.06])
    @pytest.mark.parametrize("engine", ["parquet"])
    @pytest.mark.parametrize("batch_size", [1, 10, 100])
    @pytest.mark.parametrize("use_paths", [True, False])
    def test_tf_gpu_dl(tmpdir, paths, use_paths, dataset, batch_size, gpu_memory_frac, engine):
        cont_names = ["x", "y", "id"]
        cat_names = ["name-string"]
        label_name = ["label"]
        if engine == "parquet":
            cat_names.append("name-cat")
    
        columns = cont_names + cat_names
    
        conts = cont_names >> ops.FillMedian() >> ops.Normalize()
        cats = cat_names >> ops.Categorify()
    
        workflow = nvt.Workflow(conts + cats + label_name)
>       workflow.fit(dataset)

tests/unit/test_tf_dataloader.py:88: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
nvtabular/workflow.py:147: in fit
    op.fit_finalize(computed_stats)
/conda/envs/rapids/lib/python3.8/contextlib.py:75: in inner
    return func(*args, **kwds)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <nvtabular.ops.fill.FillMedian object at 0x7f31c8fb00a0>
dask_stats = x            <NA>
y     0.030652915
id         1000.0
Name: 0.5, dtype: float64

    @annotate("FillMedian_finalize", color="green", domain="nvt_python")
    def fit_finalize(self, dask_stats):
        index = dask_stats.index
        vals = index.values_host if hasattr(index, "values_host") else index.values
        for col in vals:
>           self.medians[col] = float(dask_stats[col])
E           TypeError: float() argument must be a string or a number, not '_NAType'

nvtabular/ops/fill.py:118: TypeError
____________________ test_tf_gpu_dl[False-100-parquet-0.06] ____________________

tmpdir = local('/tmp/pytest-of-jenkins/pytest-0/test_tf_gpu_dl_False_100_parqu1')
paths = ['/tmp/pytest-of-jenkins/pytest-0/parquet0/dataset-0.parquet', '/tmp/pytest-of-jenkins/pytest-0/parquet0/dataset-1.parquet']
use_paths = False
dataset = <nvtabular.io.dataset.Dataset object at 0x7f31c8fb8670>
batch_size = 100, gpu_memory_frac = 0.06, engine = 'parquet'

    @pytest.mark.parametrize("gpu_memory_frac", [0.01, 0.06])
    @pytest.mark.parametrize("engine", ["parquet"])
    @pytest.mark.parametrize("batch_size", [1, 10, 100])
    @pytest.mark.parametrize("use_paths", [True, False])
    def test_tf_gpu_dl(tmpdir, paths, use_paths, dataset, batch_size, gpu_memory_frac, engine):
        cont_names = ["x", "y", "id"]
        cat_names = ["name-string"]
        label_name = ["label"]
        if engine == "parquet":
            cat_names.append("name-cat")
    
        columns = cont_names + cat_names
    
        conts = cont_names >> ops.FillMedian() >> ops.Normalize()
        cats = cat_names >> ops.Categorify()
    
        workflow = nvt.Workflow(conts + cats + label_name)
>       workflow.fit(dataset)

tests/unit/test_tf_dataloader.py:88: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
nvtabular/workflow.py:147: in fit
    op.fit_finalize(computed_stats)
/conda/envs/rapids/lib/python3.8/contextlib.py:75: in inner
    return func(*args, **kwds)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <nvtabular.ops.fill.FillMedian object at 0x7f31c8fb80d0>
dask_stats = x            <NA>
y     0.030652915
id         1000.0
Name: 0.5, dtype: float64

    @annotate("FillMedian_finalize", color="green", domain="nvt_python")
    def fit_finalize(self, dask_stats):
        index = dask_stats.index
        vals = index.values_host if hasattr(index, "values_host") else index.values
        for col in vals:
>           self.medians[col] = float(dask_stats[col])
E           TypeError: float() argument must be a string or a number, not '_NAType'

nvtabular/ops/fill.py:118: TypeError

----------- coverage: platform linux, python 3.8.8-final-0 -----------
Name                                                           Stmts   Miss Branch BrPart  Cover   Missing
----------------------------------------------------------------------------------------------------------
nvtabular/__init__.py                                             12      0      0      0   100%
nvtabular/column_group.py                                        149     19     80      7    85%   54, 87, 98, 101->103, 128, 151-164, 191, 278
nvtabular/dispatch.py                                             81     11     38      5    83%   35, 45->47, 69, 94, 111, 118, 135-138, 167-170
nvtabular/framework_utils/__init__.py                              0      0      0      0   100%
nvtabular/framework_utils/tensorflow/__init__.py                   1      0      0      0   100%
nvtabular/framework_utils/tensorflow/feature_column_utils.py     146    137     96      0     4%   28-32, 69-303
nvtabular/framework_utils/tensorflow/layers/__init__.py            4      0      0      0   100%
nvtabular/framework_utils/tensorflow/layers/embedding.py         153     14     89      7    87%   56, 64->45, 100, 108, 188, 240-248, 251, 344->352, 366->369, 372-373, 376
nvtabular/framework_utils/tensorflow/layers/interaction.py        47     25     20      1    43%   49, 74-103, 106-110, 113
nvtabular/framework_utils/tensorflow/layers/outer_product.py      30     24     10      0    15%   37-38, 41-60, 71-84, 87
nvtabular/framework_utils/torch/__init__.py                        0      0      0      0   100%
nvtabular/framework_utils/torch/layers/__init__.py                 2      0      0      0   100%
nvtabular/framework_utils/torch/layers/embeddings.py              27     12     12      1    51%   47, 71-79, 82-88
nvtabular/framework_utils/torch/models.py                         41      6     22      7    76%   56, 59, 64, 85, 87-88, 91->94, 98->100
nvtabular/framework_utils/torch/utils.py                          31      7     10      3    76%   52, 56-58, 67-69
nvtabular/inference/__init__.py                                    0      0      0      0   100%
nvtabular/inference/triton/__init__.py                           218    211     90      0     2%   24-494
nvtabular/inference/triton/model.py                               56     56     22      0     0%   27-142
nvtabular/inference/triton/model_hugectr.py                       44     44     14      0     0%   27-120
nvtabular/io/__init__.py                                           4      0      0      0   100%
nvtabular/io/avro.py                                              88     88     30      0     0%   16-189
nvtabular/io/csv.py                                               54      4     20      5    88%   95, 99->103, 104, 106, 120
nvtabular/io/dask.py                                             129      4     48      8    93%   147, 254->252, 282->285, 293, 297->299, 299->295, 304, 306
nvtabular/io/dataframe_engine.py                                  58      2     30      6    91%   44, 82->86, 86->91, 88->91, 91->110, 119
nvtabular/io/dataset.py                                          188     22     82     14    86%   253, 255, 268, 277, 295-309, 403, 488->490, 490->498, 499, 506-507, 601-602, 714-719, 725, 750
nvtabular/io/dataset_engine.py                                    23      1      0      0    96%   45
nvtabular/io/hugectr.py                                           44      2     24      2    91%   32, 72->95, 99
nvtabular/io/parquet.py                                          470     40    146     11    90%   83-91, 116-145, 184-186, 311-316, 354-359, 475->482, 545->550, 551-552, 671, 675, 679, 717, 734, 738, 745->747, 864->869, 874->884, 911
nvtabular/io/shuffle.py                                           30      7     12      2    69%   41-48
nvtabular/io/writer.py                                           156     10     56      3    93%   31, 48, 75, 120, 123, 196, 239, 260-262
nvtabular/io/writer_factory.py                                    18      2      8      2    85%   34, 58
nvtabular/loader/__init__.py                                       0      0      0      0   100%
nvtabular/loader/backend.py                                      269     23    112     10    90%   72, 77-78, 107, 124, 131-132, 136-140, 143, 203-207, 220, 243-244, 283->287, 358, 362-363, 457, 464
nvtabular/loader/tensorflow.py                                   120      9     52      8    89%   52, 60-63, 73, 83, 282, 289, 309->313, 342
nvtabular/loader/tf_utils.py                                      55     10     20      5    80%   29->32, 32->34, 39->41, 43, 50-51, 58-60, 66-70
nvtabular/loader/torch.py                                         43     10      8      0    69%   25-27, 30-36
nvtabular/ops/__init__.py                                         19      0      0      0   100%
nvtabular/ops/bucketize.py                                        25      4     16      2    76%   46, 49-52
nvtabular/ops/categorify.py                                      513     69    298     47    83%   223, 240, 244, 252, 260, 262, 284, 303-304, 336-337, 399-401, 459-461, 466->468, 541, 579, 608->611, 612-614, 621-622, 635-637, 638->606, 654, 664, 666, 672, 688-689, 694, 697->700, 710, 734-736, 739, 741->743, 755-758, 784, 788, 790, 802-805, 920, 922, 964->985, 970->985, 986-991, 1028, 1044->1049, 1048, 1058->1055, 1063->1055, 1071, 1079-1089
nvtabular/ops/clip.py                                             19      2      6      3    80%   45, 53->55, 56
nvtabular/ops/column_similarity.py                                88     22     32      5    69%   84, 154-155, 164-166, 174-190, 205->215, 207->210, 211, 221
nvtabular/ops/data_stats.py                                       57      1     24      4    94%   88->90, 90->92, 93->84, 96->84, 101
nvtabular/ops/difference_lag.py                                   26      0      8      1    97%   67->69
nvtabular/ops/dropna.py                                            9      0      0      0   100%
nvtabular/ops/fill.py                                             58      2     20      1    96%   99, 125
nvtabular/ops/filter.py                                           21      1      6      1    93%   44
nvtabular/ops/hash_bucket.py                                      32      2     18      2    88%   73, 102
nvtabular/ops/hashed_cross.py                                     29      3     13      4    83%   51, 66, 80->exit, 81
nvtabular/ops/join_external.py                                    69      5     28      6    89%   96, 98, 116, 129->133, 153, 168
nvtabular/ops/join_groupby.py                                     78      5     28      2    93%   102, 105->112, 179-180, 183-184
nvtabular/ops/lambdaop.py                                         27      3     10      3    84%   61, 65, 78
nvtabular/ops/logop.py                                             9      0      0      0   100%
nvtabular/ops/moments.py                                          65      0     20      0   100%
nvtabular/ops/normalize.py                                        64      6     14      2    87%   61->60, 67-68, 100-101, 123-124
nvtabular/ops/operator.py                                         15      1      2      1    88%   24
nvtabular/ops/rename.py                                           18      3     10      3    71%   41, 54, 58
nvtabular/ops/stat_operator.py                                    11      0      0      0   100%
nvtabular/ops/target_encoding.py                                 151     11     66      5    91%   143, 163->167, 170->179, 222-223, 226-227, 236-242, 335->338
nvtabular/tools/__init__.py                                        0      0      0      0   100%
nvtabular/tools/data_gen.py                                      235      1     62      4    98%   183->185, 230->234, 320->319, 322
nvtabular/tools/dataset_inspector.py                              49      9     18      0    75%   29-38
nvtabular/tools/inspector_script.py                               46     46      0      0     0%   17-168
nvtabular/utils.py                                                42     16     18      7    55%   20-21, 25-26, 35, 39, 48-51, 53-55, 58, 61, 67, 73
nvtabular/worker.py                                               68      1     30      2    97%   73, 83->98
nvtabular/workflow.py                                            140      9     62      4    93%   39, 125, 137-139, 242, 270-271, 341
----------------------------------------------------------------------------------------------------------
TOTAL                                                           4774   1022   1960    216    76%
Coverage XML written to file coverage.xml

Required test coverage of 70% reached. Total coverage: 75.82%
=========================== short test summary info ============================
FAILED tests/unit/test_tf_dataloader.py::test_tf_gpu_dl[True-1-parquet-0.06]
FAILED tests/unit/test_tf_dataloader.py::test_tf_gpu_dl[True-10-parquet-0.01]
FAILED tests/unit/test_tf_dataloader.py::test_tf_gpu_dl[True-10-parquet-0.06]
FAILED tests/unit/test_tf_dataloader.py::test_tf_gpu_dl[True-100-parquet-0.01]
FAILED tests/unit/test_tf_dataloader.py::test_tf_gpu_dl[True-100-parquet-0.06]
FAILED tests/unit/test_tf_dataloader.py::test_tf_gpu_dl[False-10-parquet-0.01]
FAILED tests/unit/test_tf_dataloader.py::test_tf_gpu_dl[False-10-parquet-0.06]
FAILED tests/unit/test_tf_dataloader.py::test_tf_gpu_dl[False-100-parquet-0.01]
FAILED tests/unit/test_tf_dataloader.py::test_tf_gpu_dl[False-100-parquet-0.06]
============ 9 failed, 705 passed, 12 skipped in 537.41s (0:08:57) =============
Build step 'Execute shell' marked build as failure
Performing Post build task...
Match found for : : True
Logical operation result is TRUE
Running script  : #!/bin/bash
source activate rapids
cd /var/jenkins_home/
CUDA_VISIBLE_DEVICES=1 python test_res_push.py "https://api.GitHub.com/repos/NVIDIA/NVTabular/issues/$ghprbPullId/comments" "/var/jenkins_home/jobs/$JOB_NAME/builds/$BUILD_NUMBER/log" 
[nvtabular_tests] $ /bin/bash /tmp/jenkins6532938778459755502.sh
</pre></details>
<details><summary>Click to view CI Results</summary><pre>GitHub pull request #660 of commit 2024d43f7d3008a122ae0df45b693795a29823c0, no merge conflicts.
Running as SYSTEM
Setting status of 2024d43f7d3008a122ae0df45b693795a29823c0 to PENDING with url http://10.20.13.93:8080/job/nvtabular_tests/1922/ and message: 'Pending'
Using context: Jenkins Unit Test Run
Building in workspace /var/jenkins_home/workspace/nvtabular_tests
using credential ghub_token
Cloning the remote Git repository
Cloning repository https://github.com/NVIDIA/NVTabular.git
 > git init /var/jenkins_home/workspace/nvtabular_tests/nvtabular # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
 > git --version # timeout=10
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
 > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/pull/660/*:refs/remotes/origin/pr/660/* # timeout=10
 > git rev-parse 2024d43f7d3008a122ae0df45b693795a29823c0^{commit} # timeout=10
Checking out Revision 2024d43f7d3008a122ae0df45b693795a29823c0 (detached)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f 2024d43f7d3008a122ae0df45b693795a29823c0 # timeout=10
Commit message: "Merge branch 'main' into to_parquet_improvements"
 > git rev-list --no-walk 3c9f04faa6cb322f0a7511fd04163a7deca32960 # timeout=10
[nvtabular_tests] $ /bin/bash /tmp/jenkins5845110907344164424.sh
Obtaining file:///var/jenkins_home/workspace/nvtabular_tests/nvtabular
  Installing build dependencies: started
  Installing build dependencies: finished with status 'done'
  Getting requirements to build wheel: started
  Getting requirements to build wheel: finished with status 'done'
    Preparing wheel metadata: started
    Preparing wheel metadata: finished with status 'done'
Installing collected packages: nvtabular
  Running setup.py develop for nvtabular
Successfully installed nvtabular
All done! ✨ 🍰 ✨
93 files would be left unchanged.
/conda/envs/rapids/lib/python3.8/site-packages/isort/files.py:30: UserWarning: Likely recursive symlink detected to /var/jenkins_home/workspace/nvtabular_tests/nvtabular/images
  warn(f"Likely recursive symlink detected to {resolved_path}")
============================= test session starts ==============================
platform linux -- Python 3.8.8, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /var/jenkins_home/workspace/nvtabular_tests/nvtabular, configfile: pyproject.toml
plugins: cov-2.11.1, xdist-2.2.1, forked-1.3.0
collected 724 items / 2 skipped / 722 selected

tests/unit/test_column_group.py .                                        [  0%]
tests/unit/test_column_similarity.py ......                              [  0%]
tests/unit/test_dask_nvt.py ............................................ [  7%]
.....................................................................    [ 16%]
tests/unit/test_io.py .................................................. [ 23%]
..................................................................ssssss [ 33%]
ss........                                                               [ 34%]
tests/unit/test_notebooks.py ..s..                                       [ 35%]
tests/unit/test_ops.py ................................................. [ 42%]
........................................................................ [ 52%]
........................................................................ [ 62%]
..................................                                       [ 66%]
tests/unit/test_tf_dataloader.py ...................                     [ 69%]
tests/unit/test_tf_layers.py ........................................... [ 75%]
...............F...................                                      [ 80%]
tests/unit/test_tools.py ......................                          [ 83%]
tests/unit/test_torch_dataloader.py .............................s       [ 87%]
tests/unit/test_workflow.py ............................................ [ 93%]
...............................................                          [100%]

=================================== FAILURES ===================================
__________ test_dot_product_interaction_layer[False-field_all-64-16] ___________

embedding_dim = 16, num_features = 64, interaction_type = 'field_all'
self_interaction = False

    @pytest.mark.parametrize("embedding_dim", [1, 4, 16])
    @pytest.mark.parametrize("num_features", [1, 16, 64])
    @pytest.mark.parametrize("interaction_type", [None, "field_all", "field_each", "field_interaction"])
    @pytest.mark.parametrize("self_interaction", [True, False])
    def test_dot_product_interaction_layer(
        embedding_dim, num_features, interaction_type, self_interaction
    ):
        if num_features == 1 and not self_interaction:
            return
    
        input = tf.keras.Input(name="x", shape=(num_features, embedding_dim), dtype=tf.float32)
        interaction_layer = layers.DotProductInteraction(interaction_type, self_interaction)
        output = interaction_layer(input)
        model = tf.keras.Model(inputs=input, outputs=output)
        model.compile("sgd", "mse")
    
        x = np.random.randn(8, num_features, embedding_dim).astype(np.float32)
        y_hat = model.predict(x)
    
        if self_interaction:
            expected_dim = num_features * (num_features + 1) // 2
        else:
            expected_dim = num_features * (num_features - 1) // 2
        assert y_hat.shape[1] == expected_dim
    
        if interaction_type is not None:
            W = interaction_layer.kernel.numpy()
        expected_outputs = []
        for i in range(num_features):
            j_start = i if self_interaction else i + 1
            for j in range(j_start, num_features):
                x_i = x[:, i]
                x_j = x[:, j]
                if interaction_type == "field_all":
                    W_ij = W
                elif interaction_type == "field_each":
                    W_ij = W[i].T
                elif interaction_type == "field_interaction":
                    W_ij = W[i, j]
    
                if interaction_type is not None:
                    x_i = x_i @ W_ij
                expected_outputs.append((x_i * x_j).sum(axis=1))
        expected_output = np.stack(expected_outputs).T
    
        rtol = 1e-3
        atol = 1e-6
        match = np.isclose(expected_output, y_hat, rtol=rtol, atol=atol)
>       assert match.all()
E       assert False
E        +  where False = <built-in method all of numpy.ndarray object at 0x7efbe83ec710>()
E        +    where <built-in method all of numpy.ndarray object at 0x7efbe83ec710> = array([[ True,  True,  True, ...,  True,  True,  True],\n       [ True,  True,  True, ...,  True,  True,  True],\n      ...True],\n       [ True,  True,  True, ...,  True,  True,  True],\n       [ True,  True,  True, ...,  True,  True,  True]]).all

tests/unit/test_tf_layers.py:290: AssertionError

----------- coverage: platform linux, python 3.8.8-final-0 -----------
Name                                                           Stmts   Miss Branch BrPart  Cover   Missing
----------------------------------------------------------------------------------------------------------
nvtabular/__init__.py                                             12      0      0      0   100%
nvtabular/column_group.py                                        149     19     80      7    85%   54, 87, 98, 101->103, 128, 151-164, 191, 278
nvtabular/dispatch.py                                             81     11     38      5    83%   35, 45->47, 69, 94, 111, 118, 135-138, 167-170
nvtabular/framework_utils/__init__.py                              0      0      0      0   100%
nvtabular/framework_utils/tensorflow/__init__.py                   1      0      0      0   100%
nvtabular/framework_utils/tensorflow/feature_column_utils.py     146    137     96      0     4%   28-32, 69-303
nvtabular/framework_utils/tensorflow/layers/__init__.py            4      0      0      0   100%
nvtabular/framework_utils/tensorflow/layers/embedding.py         153     14     89      7    87%   56, 64->45, 100, 108, 188, 240-248, 251, 344->352, 366->369, 372-373, 376
nvtabular/framework_utils/tensorflow/layers/interaction.py        47     25     20      1    43%   49, 74-103, 106-110, 113
nvtabular/framework_utils/tensorflow/layers/outer_product.py      30     24     10      0    15%   37-38, 41-60, 71-84, 87
nvtabular/framework_utils/torch/__init__.py                        0      0      0      0   100%
nvtabular/framework_utils/torch/layers/__init__.py                 2      0      0      0   100%
nvtabular/framework_utils/torch/layers/embeddings.py              27     12     12      1    51%   47, 71-79, 82-88
nvtabular/framework_utils/torch/models.py                         41      6     22      7    76%   56, 59, 64, 85, 87-88, 91->94, 98->100
nvtabular/framework_utils/torch/utils.py                          31      7     10      3    76%   52, 56-58, 67-69
nvtabular/inference/__init__.py                                    0      0      0      0   100%
nvtabular/inference/triton/__init__.py                           218    211     90      0     2%   24-494
nvtabular/inference/triton/model.py                               56     56     22      0     0%   27-142
nvtabular/inference/triton/model_hugectr.py                       44     44     14      0     0%   27-120
nvtabular/io/__init__.py                                           4      0      0      0   100%
nvtabular/io/avro.py                                              88     88     30      0     0%   16-189
nvtabular/io/csv.py                                               54      4     20      5    88%   95, 99->103, 104, 106, 120
nvtabular/io/dask.py                                             129      4     48      8    93%   147, 254->252, 282->285, 293, 297->299, 299->295, 304, 306
nvtabular/io/dataframe_engine.py                                  58      2     30      6    91%   44, 82->86, 86->91, 88->91, 91->110, 119
nvtabular/io/dataset.py                                          188     22     82     14    86%   253, 255, 268, 277, 295-309, 403, 488->490, 490->498, 499, 506-507, 601-602, 714-719, 725, 750
nvtabular/io/dataset_engine.py                                    23      1      0      0    96%   45
nvtabular/io/hugectr.py                                           44      2     24      2    91%   32, 72->95, 99
nvtabular/io/parquet.py                                          470     40    146     11    90%   83-91, 116-145, 184-186, 311-316, 354-359, 475->482, 545->550, 551-552, 671, 675, 679, 717, 734, 738, 745->747, 864->869, 874->884, 911
nvtabular/io/shuffle.py                                           30      7     12      2    69%   41-48
nvtabular/io/writer.py                                           156     10     56      3    93%   31, 48, 75, 120, 123, 196, 239, 260-262
nvtabular/io/writer_factory.py                                    18      2      8      2    85%   34, 58
nvtabular/loader/__init__.py                                       0      0      0      0   100%
nvtabular/loader/backend.py                                      269     15    112      9    94%   77-78, 107, 124, 131-132, 143, 203->205, 220, 243-244, 283->287, 358, 362-363, 457, 464
nvtabular/loader/tensorflow.py                                   120      8     52      7    90%   52, 60-63, 73, 83, 282, 309->313, 342
nvtabular/loader/tf_utils.py                                      55     10     20      5    80%   29->32, 32->34, 39->41, 43, 50-51, 58-60, 66-70
nvtabular/loader/torch.py                                         43     10      8      0    69%   25-27, 30-36
nvtabular/ops/__init__.py                                         19      0      0      0   100%
nvtabular/ops/bucketize.py                                        25      4     16      2    76%   46, 49-52
nvtabular/ops/categorify.py                                      513     69    298     47    83%   223, 240, 244, 252, 260, 262, 284, 303-304, 336-337, 399-401, 459-461, 466->468, 541, 579, 608->611, 612-614, 621-622, 635-637, 638->606, 654, 664, 666, 672, 688-689, 694, 697->700, 710, 734-736, 739, 741->743, 755-758, 784, 788, 790, 802-805, 920, 922, 964->985, 970->985, 986-991, 1028, 1044->1049, 1048, 1058->1055, 1063->1055, 1071, 1079-1089
nvtabular/ops/clip.py                                             19      2      6      3    80%   45, 53->55, 56
nvtabular/ops/column_similarity.py                                88     22     32      5    69%   84, 154-155, 164-166, 174-190, 205->215, 207->210, 211, 221
nvtabular/ops/data_stats.py                                       57      1     24      4    94%   88->90, 90->92, 93->84, 96->84, 101
nvtabular/ops/difference_lag.py                                   26      0      8      1    97%   67->69
nvtabular/ops/dropna.py                                            9      0      0      0   100%
nvtabular/ops/fill.py                                             58      2     20      1    96%   99, 125
nvtabular/ops/filter.py                                           21      1      6      1    93%   44
nvtabular/ops/hash_bucket.py                                      32      2     18      2    88%   73, 102
nvtabular/ops/hashed_cross.py                                     29      3     13      4    83%   51, 66, 80->exit, 81
nvtabular/ops/join_external.py                                    69      5     28      6    89%   96, 98, 116, 129->133, 153, 168
nvtabular/ops/join_groupby.py                                     78      5     28      2    93%   102, 105->112, 179-180, 183-184
nvtabular/ops/lambdaop.py                                         27      3     10      3    84%   61, 65, 78
nvtabular/ops/logop.py                                             9      0      0      0   100%
nvtabular/ops/moments.py                                          65      0     20      0   100%
nvtabular/ops/normalize.py                                        64      6     14      2    87%   61->60, 67-68, 100-101, 123-124
nvtabular/ops/operator.py                                         15      1      2      1    88%   24
nvtabular/ops/rename.py                                           18      3     10      3    71%   41, 54, 58
nvtabular/ops/stat_operator.py                                    11      0      0      0   100%
nvtabular/ops/target_encoding.py                                 151     11     66      5    91%   143, 163->167, 170->179, 222-223, 226-227, 236-242, 335->338
nvtabular/tools/__init__.py                                        0      0      0      0   100%
nvtabular/tools/data_gen.py                                      235      1     62      4    98%   183->185, 230->234, 320->319, 322
nvtabular/tools/dataset_inspector.py                              49      9     18      0    75%   29-38
nvtabular/tools/inspector_script.py                               46     46      0      0     0%   17-168
nvtabular/utils.py                                                42     16     18      7    55%   20-21, 25-26, 35, 39, 48-51, 53-55, 58, 61, 67, 73
nvtabular/worker.py                                               68      1     30      2    97%   73, 83->98
nvtabular/workflow.py                                            140      9     62      4    93%   39, 125, 137-139, 242, 270-271, 341
----------------------------------------------------------------------------------------------------------
TOTAL                                                           4774   1013   1960    214    76%
Coverage XML written to file coverage.xml

Required test coverage of 70% reached. Total coverage: 76.05%
=========================== short test summary info ============================
FAILED tests/unit/test_tf_layers.py::test_dot_product_interaction_layer[False-field_all-64-16]
============ 1 failed, 713 passed, 12 skipped in 535.74s (0:08:55) =============
Build step 'Execute shell' marked build as failure
Performing Post build task...
Match found for : : True
Logical operation result is TRUE
Running script  : #!/bin/bash
source activate rapids
cd /var/jenkins_home/
CUDA_VISIBLE_DEVICES=1 python test_res_push.py "https://api.GitHub.com/repos/NVIDIA/NVTabular/issues/$ghprbPullId/comments" "/var/jenkins_home/jobs/$JOB_NAME/builds/$BUILD_NUMBER/log" 
[nvtabular_tests] $ /bin/bash /tmp/jenkins9153655365753585672.sh
</pre></details>
<details><summary>Click to view CI Results</summary><pre>GitHub pull request #660 of commit 85e927a497a1342b38ef90ebf8646a34492f491a, no merge conflicts.
Running as SYSTEM
Setting status of 85e927a497a1342b38ef90ebf8646a34492f491a to PENDING with url http://10.20.13.93:8080/job/nvtabular_tests/1924/ and message: 'Pending'
Using context: Jenkins Unit Test Run
Building in workspace /var/jenkins_home/workspace/nvtabular_tests
using credential ghub_token
Cloning the remote Git repository
Cloning repository https://github.com/NVIDIA/NVTabular.git
 > git init /var/jenkins_home/workspace/nvtabular_tests/nvtabular # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
 > git --version # timeout=10
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
 > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/NVIDIA/NVTabular.git # timeout=10
Fetching upstream changes from https://github.com/NVIDIA/NVTabular.git
using GIT_ASKPASS to set credentials github token setup
 > git fetch --tags --force --progress -- https://github.com/NVIDIA/NVTabular.git +refs/pull/660/*:refs/remotes/origin/pr/660/* # timeout=10
 > git rev-parse 85e927a497a1342b38ef90ebf8646a34492f491a^{commit} # timeout=10
Checking out Revision 85e927a497a1342b38ef90ebf8646a34492f491a (detached)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f 85e927a497a1342b38ef90ebf8646a34492f491a # timeout=10
Commit message: "Merge branch 'to_parquet_improvements' of https://github.com/rjzamora/NVTabular into to_parquet_improvements"
 > git rev-list --no-walk b4e6c1304ac8a78956256f82828f7a5f174f02a4 # timeout=10
First time build. Skipping changelog.
[nvtabular_tests] $ /bin/bash /tmp/jenkins8991119164808813761.sh
Obtaining file:///var/jenkins_home/workspace/nvtabular_tests/nvtabular
  Installing build dependencies: started
  Installing build dependencies: finished with status 'done'
  Getting requirements to build wheel: started
  Getting requirements to build wheel: finished with status 'done'
    Preparing wheel metadata: started
    Preparing wheel metadata: finished with status 'done'
Installing collected packages: nvtabular
  Attempting uninstall: nvtabular
    Found existing installation: nvtabular 0.3.0+85.g42d1a13
    Uninstalling nvtabular-0.3.0+85.g42d1a13:
      Successfully uninstalled nvtabular-0.3.0+85.g42d1a13
  Running setup.py develop for nvtabular
Successfully installed nvtabular
All done! ✨ 🍰 ✨
93 files would be left unchanged.
/conda/envs/rapids/lib/python3.8/site-packages/isort/files.py:30: UserWarning: Likely recursive symlink detected to /var/jenkins_home/workspace/nvtabular_tests/nvtabular/images
  warn(f"Likely recursive symlink detected to {resolved_path}")
============================= test session starts ==============================
platform linux -- Python 3.8.8, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /var/jenkins_home/workspace/nvtabular_tests/nvtabular, configfile: pyproject.toml
plugins: cov-2.11.1, xdist-2.2.1, forked-1.3.0
collected 724 items / 2 skipped / 722 selected

tests/unit/test_column_group.py .                                        [  0%]
tests/unit/test_column_similarity.py ......                              [  0%]
tests/unit/test_dask_nvt.py ............................................ [  7%]
.....................................................................    [ 16%]
tests/unit/test_io.py .................................................. [ 23%]
..................................................................ssssss [ 33%]
ss........                                                               [ 34%]
tests/unit/test_notebooks.py ..s..                                       [ 35%]
tests/unit/test_ops.py ................................................. [ 42%]
........................................................................ [ 52%]
........................................................................ [ 62%]
..................................                                       [ 66%]
tests/unit/test_tf_dataloader.py ...................                     [ 69%]
tests/unit/test_tf_layers.py ........................................... [ 75%]
...................................                                      [ 80%]
tests/unit/test_tools.py ......................                          [ 83%]
tests/unit/test_torch_dataloader.py .............................s       [ 87%]
tests/unit/test_workflow.py ............................................ [ 93%]
...............................................                          [100%]

----------- coverage: platform linux, python 3.8.8-final-0 -----------
Name                                                           Stmts   Miss Branch BrPart  Cover   Missing
----------------------------------------------------------------------------------------------------------
nvtabular/__init__.py                                             12      0      0      0   100%
nvtabular/column_group.py                                        149     19     80      7    85%   54, 87, 98, 101->103, 128, 151-164, 191, 278
nvtabular/dispatch.py                                             81     11     38      5    83%   35, 45->47, 69, 94, 111, 118, 135-138, 167-170
nvtabular/framework_utils/__init__.py                              0      0      0      0   100%
nvtabular/framework_utils/tensorflow/__init__.py                   1      0      0      0   100%
nvtabular/framework_utils/tensorflow/feature_column_utils.py     146    137     96      0     4%   28-32, 69-303
nvtabular/framework_utils/tensorflow/layers/__init__.py            4      0      0      0   100%
nvtabular/framework_utils/tensorflow/layers/embedding.py         153     14     89      7    87%   56, 64->45, 100, 108, 188, 240-248, 251, 344->352, 366->369, 372-373, 376
nvtabular/framework_utils/tensorflow/layers/interaction.py        47     25     20      1    43%   49, 74-103, 106-110, 113
nvtabular/framework_utils/tensorflow/layers/outer_product.py      30     24     10      0    15%   37-38, 41-60, 71-84, 87
nvtabular/framework_utils/torch/__init__.py                        0      0      0      0   100%
nvtabular/framework_utils/torch/layers/__init__.py                 2      0      0      0   100%
nvtabular/framework_utils/torch/layers/embeddings.py              27     12     12      1    51%   47, 71-79, 82-88
nvtabular/framework_utils/torch/models.py                         41      6     22      7    76%   56, 59, 64, 85, 87-88, 91->94, 98->100
nvtabular/framework_utils/torch/utils.py                          31      7     10      3    76%   52, 56-58, 67-69
nvtabular/inference/__init__.py                                    0      0      0      0   100%
nvtabular/inference/triton/__init__.py                           218    211     90      0     2%   24-494
nvtabular/inference/triton/model.py                               56     56     22      0     0%   27-142
nvtabular/inference/triton/model_hugectr.py                       44     44     14      0     0%   27-120
nvtabular/io/__init__.py                                           4      0      0      0   100%
nvtabular/io/avro.py                                              88     88     30      0     0%   16-189
nvtabular/io/csv.py                                               54      4     20      5    88%   95, 99->103, 104, 106, 120
nvtabular/io/dask.py                                             129      4     48      8    93%   147, 254->252, 282->285, 293, 297->299, 299->295, 304, 306
nvtabular/io/dataframe_engine.py                                  58      2     30      6    91%   44, 82->86, 86->91, 88->91, 91->110, 119
nvtabular/io/dataset.py                                          188     22     82     14    86%   253, 255, 268, 277, 295-309, 403, 488->490, 490->498, 499, 506-507, 601-602, 714-719, 725, 750
nvtabular/io/dataset_engine.py                                    23      1      0      0    96%   45
nvtabular/io/hugectr.py                                           44      2     24      2    91%   32, 72->95, 99
nvtabular/io/parquet.py                                          470     40    146     11    90%   83-91, 116-145, 184-186, 311-316, 354-359, 475->482, 545->550, 551-552, 671, 675, 679, 717, 734, 738, 745->747, 864->869, 874->884, 911
nvtabular/io/shuffle.py                                           30      7     12      2    69%   41-48
nvtabular/io/writer.py                                           156     10     56      3    93%   31, 48, 75, 120, 123, 196, 239, 260-262
nvtabular/io/writer_factory.py                                    18      2      8      2    85%   34, 58
nvtabular/loader/__init__.py                                       0      0      0      0   100%
nvtabular/loader/backend.py                                      269     13    112      9    94%   107, 124, 131-132, 143, 203->205, 220, 243-244, 283->287, 358, 362-363, 457, 464
nvtabular/loader/tensorflow.py                                   120      8     52      7    90%   52, 60-63, 73, 83, 282, 309->313, 342
nvtabular/loader/tf_utils.py                                      55     10     20      5    80%   29->32, 32->34, 39->41, 43, 50-51, 58-60, 66-70
nvtabular/loader/torch.py                                         43     10      8      0    69%   25-27, 30-36
nvtabular/ops/__init__.py                                         19      0      0      0   100%
nvtabular/ops/bucketize.py                                        25      4     16      2    76%   46, 49-52
nvtabular/ops/categorify.py                                      513     69    298     47    83%   223, 240, 244, 252, 260, 262, 284, 303-304, 336-337, 399-401, 459-461, 466->468, 541, 579, 608->611, 612-614, 621-622, 635-637, 638->606, 654, 664, 666, 672, 688-689, 694, 697->700, 710, 734-736, 739, 741->743, 755-758, 784, 788, 790, 802-805, 920, 922, 964->985, 970->985, 986-991, 1028, 1044->1049, 1048, 1058->1055, 1063->1055, 1071, 1079-1089
nvtabular/ops/clip.py                                             19      2      6      3    80%   45, 53->55, 56
nvtabular/ops/column_similarity.py                                88     22     32      5    69%   84, 154-155, 164-166, 174-190, 205->215, 207->210, 211, 221
nvtabular/ops/data_stats.py                                       57      1     24      4    94%   88->90, 90->92, 93->84, 96->84, 101
nvtabular/ops/difference_lag.py                                   26      0      8      1    97%   67->69
nvtabular/ops/dropna.py                                            9      0      0      0   100%
nvtabular/ops/fill.py                                             58      2     20      1    96%   99, 125
nvtabular/ops/filter.py                                           21      1      6      1    93%   44
nvtabular/ops/hash_bucket.py                                      32      2     18      2    88%   73, 102
nvtabular/ops/hashed_cross.py                                     29      3     13      4    83%   51, 66, 80->exit, 81
nvtabular/ops/join_external.py                                    69      5     28      6    89%   96, 98, 116, 129->133, 153, 168
nvtabular/ops/join_groupby.py                                     78      5     28      2    93%   102, 105->112, 179-180, 183-184
nvtabular/ops/lambdaop.py                                         27      3     10      3    84%   61, 65, 78
nvtabular/ops/logop.py                                             9      0      0      0   100%
nvtabular/ops/moments.py                                          65      0     20      0   100%
nvtabular/ops/normalize.py                                        64      6     14      2    87%   61->60, 67-68, 100-101, 123-124
nvtabular/ops/operator.py                                         15      1      2      1    88%   24
nvtabular/ops/rename.py                                           18      3     10      3    71%   41, 54, 58
nvtabular/ops/stat_operator.py                                    11      0      0      0   100%
nvtabular/ops/target_encoding.py                                 151     11     66      5    91%   143, 163->167, 170->179, 222-223, 226-227, 236-242, 335->338
nvtabular/tools/__init__.py                                        0      0      0      0   100%
nvtabular/tools/data_gen.py                                      235      1     62      4    98%   183->185, 230->234, 320->319, 322
nvtabular/tools/dataset_inspector.py                              49      9     18      0    75%   29-38
nvtabular/tools/inspector_script.py                               46     46      0      0     0%   17-168
nvtabular/utils.py                                                42     16     18      7    55%   20-21, 25-26, 35, 39, 48-51, 53-55, 58, 61, 67, 73
nvtabular/worker.py                                               68      1     30      2    97%   73, 83->98
nvtabular/workflow.py                                            140      9     62      4    93%   39, 125, 137-139, 242, 270-271, 341
----------------------------------------------------------------------------------------------------------
TOTAL                                                           4774   1011   1960    214    76%
Coverage XML written to file coverage.xml

Required test coverage of 70% reached. Total coverage: 76.08%

================= 714 passed, 12 skipped in 837.42s (0:13:57) ==================
Performing Post build task...
Match found for : : True
Logical operation result is TRUE
Running script  : #!/bin/bash
source activate rapids
cd /var/jenkins_home/
CUDA_VISIBLE_DEVICES=1 python test_res_push.py "https://api.GitHub.com/repos/NVIDIA/NVTabular/issues/$ghprbPullId/comments" "/var/jenkins_home/jobs/$JOB_NAME/builds/$BUILD_NUMBER/log" 
[nvtabular_tests] $ /bin/bash /tmp/jenkins5823311971895017829.sh
</pre></details>

Fix patch:
diff --git a/examples/scaling-criteo/01-Download-Convert.ipynb b/examples/scaling-criteo/01-Download-Convert.ipynb
index 0fbb222eb4a..99ce2a15c0f 100644
--- a/examples/scaling-criteo/01-Download-Convert.ipynb
+++ b/examples/scaling-criteo/01-Download-Convert.ipynb
@@ -2,7 +2,7 @@
  "cells": [
   {
    "cell_type": "code",
-   "execution_count": 1,
+   "execution_count": 2,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -26,166 +26,224 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### Conversion Script for Criteo Dataset (CSV-to-Parquet) \n",
+    "<img src=\"http://developer.download.nvidia.com/compute/machine-learning/frameworks/nvidia_logo.png\" style=\"width: 90px; float: right;\">\n",
     "\n",
-    "__Step 1__: Import libraries"
+    "# Scaling Criteo: Download and Convert\n",
+    "\n",
+    "## Criteo 1TB Click Logs dataset\n",
+    "\n",
+    "The [Criteo 1TB Click Logs dataset](https://ailab.criteo.com/download-criteo-1tb-click-logs-dataset/) is the largest public available dataset for recommender system. It contains ~1.3 TB of uncompressed click logs containing over four billion samples spanning 24 days. Each record contains 40 features: one label indicating a click or no click, 13 numerical figures, and 26 categorical features. The dataset is provided by CriteoLabs. A subset of 7 days was used in this [Kaggle Competition](https://www.kaggle.com/c/criteo-display-ad-challenge/overview). We will use the dataset as an example how to scale ETL, Training and Inference."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Getting Started"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "First, we will download the data and extract it. We define the base directory for the dataset and the numbers of day. Criteo provides 24 days. We will use the last day as validation dataset and the remaining days as training. \n",
+    "\n",
+    "**Each day has a size of ~15GB compressed `.gz` and uncompressed ~XXXGB. You can define a smaller subset of days, if you like. Each day takes ~20-30min to download and extract it.**"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 2,
+   "execution_count": 3,
    "metadata": {},
    "outputs": [],
    "source": [
     "import os\n",
-    "import rmm\n",
-    "import cudf\n",
-    "from cudf.io.parquet import ParquetWriter\n",
-    "from fsspec.core import get_fs_token_paths\n",
-    "import numpy as np\n",
-    "import pyarrow.parquet as pq\n",
     "\n",
-    "from dask.dataframe.io.parquet.utils import _analyze_paths\n",
-    "from dask.base import tokenize\n",
-    "from dask.utils import natural_sort_key\n",
-    "from dask.highlevelgraph import HighLevelGraph\n",
-    "from dask.delayed import Delayed\n",
-    "from dask.distributed import Client\n",
-    "from dask_cuda import LocalCUDACluster\n",
+    "download_criteo = True\n",
+    "BASE_DIR = os.environ.get('BASE_DIR', '/raid/data/criteo')\n",
+    "input_path = os.path.join(BASE_DIR, \"crit_orig\")\n",
+    "NUMBER_DAYS = os.environ.get('NUMBER_DAYS', 2)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We create the folder structure and download and extract the files. If the file already exist, it will be skipped."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Downloading day 0 of 1\n",
+      "Downloading day 1 of 1\n",
+      "CPU times: user 44 ms, sys: 11.2 ms, total: 55.2 ms\n",
+      "Wall time: 29min 36s\n"
+     ]
+    }
+   ],
+   "source": [
+    "%%time\n",
+    "if download_criteo:\n",
     "\n",
-    "import nvtabular as nvt\n",
-    "from nvtabular.utils import device_mem_size, get_rmm_size"
+    "    # Test if NUMBER_DAYS in valid range\n",
+    "    if NUMBER_DAYS<2 or NUMBER_DAYS>23:\n",
+    "        raise ValueError(str(NUMBER_DAYS) + ' is not supported. A minimum of 2 days are ' + \\\n",
+    "              'required and a maximum of 24 (0-23 days) are available')\n",
+    "\n",
+    "    # Create BASE_DIR if not exists\n",
+    "    if not os.path.exists(BASE_DIR):\n",
+    "        os.system(\"mkdir -p \" + BASE_DIR)\n",
+    "\n",
+    "    # Create input dir if not exists\n",
+    "    if not os.path.exists(input_path):\n",
+    "        os.system(\"mkdir -p \" + input_path)\n",
+    "\n",
+    "    # Iterate over days\n",
+    "    for i in range(0, NUMBER_DAYS):\n",
+    "        file = os.path.join(input_path, \"day_\" + str(i) + \".gz\")\n",
+    "        # Download file, if there is no .gz, .csv or .parquet file\n",
+    "        if not(\n",
+    "            os.path.exists(file) or \n",
+    "            os.path.exists(file.replace(\".gz\", \".parquet\").replace(\"crit_orig\", \"converted/criteo/\")) or\n",
+    "            os.path.exists(file.replace(\".gz\", \"\"))\n",
+    "        ):\n",
+    "            print(\"Downloading day \" + str(i) + \" of \" + str(NUMBER_DAYS-1))\n",
+    "            os.system(\"wget -O \" + file + \" http://azuremlsampleexperiments.blob.core.windows.net/criteo/day_\" + str(i) + \".gz\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Extracting day 0 of 1\n",
+      "Extracting day 1 of 1\n"
+     ]
+    }
+   ],
+   "source": [
+    "# Iterate over days\n",
+    "if download_criteo:\n",
+    "\n",
+    "    for i in range(0, NUMBER_DAYS):\n",
+    "        file = os.path.join(input_path, \"day_\" + str(i) + \".gz\")\n",
+    "        # Extract file, if there is no .parquet but .gz file\n",
+    "        if (\n",
+    "            not os.path.exists(file.replace(\".gz\", \".parquet\").replace(\"crit_orig\", \"converted\"))\n",
+    "        ) and os.path.exists(file):\n",
+    "            print(\"Extracting day \" + str(i) + \" of \" + str(NUMBER_DAYS-1))\n",
+    "            os.system(\"gzip -d \" + file)"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "__Step 2__: Specify options\n",
+    "The original dataset is in text format. We will convert the dataset into `.parquet` format. Parquet is a compressed, column-oriented file structure and requires less disk space."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Conversion Script for Criteo Dataset (CSV-to-Parquet) \n",
     "\n",
-    "Specify the input and output paths, unless the `INPUT_DATA_DIR` and `OUTPUT_DATA_DIR` environment variables are already set. In order to utilize a multi-GPU system, be sure to specify `allow_multi_gpu=True` (and check the setting of your `CUDA_VISIBLE_DEVICES` environment variable)."
+    "__Step 1__: Import libraries"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 3,
+   "execution_count": 7,
    "metadata": {},
    "outputs": [],
    "source": [
-    "INPUT_PATH = os.environ.get('INPUT_DATA_DIR', '/datasets/criteo/crit_orig')\n",
-    "OUTPUT_PATH = os.environ.get('OUTPUT_DATA_DIR', '/raid/criteo/tests/demo_out')\n",
-    "CUDA_VISIBLE_DEVICES = os.environ.get(\"CUDA_VISIBLE_DEVICES\", \"0\")\n",
-    "n_workers = len(CUDA_VISIBLE_DEVICES.split(\",\"))\n",
-    "frac_size = 0.15\n",
-    "allow_multi_gpu = False\n",
-    "use_rmm_pool = False\n",
-    "max_day = None  # (Optional) -- Limit the dataset to day 0-max_day for debugging"
+    "import os\n",
+    "import glob\n",
+    "\n",
+    "import numpy as np\n",
+    "from dask.distributed import Client\n",
+    "from dask_cuda import LocalCUDACluster\n",
+    "\n",
+    "import nvtabular as nvt\n",
+    "from nvtabular.utils import device_mem_size, get_rmm_size"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "__Step 3__: Define helper/task functions"
+    "__Step 2__: Specify options\n",
+    "\n",
+    "Specify the input and output paths, unless the `INPUT_DATA_DIR` and `OUTPUT_DATA_DIR` environment variables are already set. For multi-GPU systems, check that the `CUDA_VISIBLE_DEVICES` environment variable includes all desired device IDs."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 8,
    "metadata": {},
    "outputs": [],
    "source": [
-    "def _pool(frac=0.8):\n",
-    "    rmm.reinitialize(\n",
-    "        pool_allocator=True,\n",
-    "        initial_pool_size=get_rmm_size(frac * device_mem_size()),\n",
-    "    )\n",
-    "    \n",
-    "def _convert_file(path, name, out_dir, frac_size, fs, cols, dtypes):\n",
-    "    fn = f\"{name}.parquet\"\n",
-    "    out_path = fs.sep.join([out_dir, f\"{name}.parquet\"])\n",
-    "    writer = ParquetWriter(out_path, compression=None)\n",
-    "    for gdf in nvt.Dataset(\n",
-    "        path,\n",
-    "        engine=\"csv\",\n",
-    "        names=cols,\n",
-    "        part_memory_fraction=frac_size,\n",
-    "        sep='\\t',\n",
-    "        dtypes=dtypes,\n",
-    "    ).to_iter():\n",
-    "        writer.write_table(gdf)\n",
-    "        del gdf\n",
-    "    md = writer.close(metadata_file_path=fn)\n",
-    "    return md\n",
-    "\n",
-    "def _write_metadata(md_list, fs, path):\n",
-    "    rg_sizes = []\n",
-    "    if md_list:\n",
-    "        metadata_path = fs.sep.join([path, \"_metadata\"])\n",
-    "        _meta = (\n",
-    "            cudf.io.merge_parquet_filemetadata(md_list)\n",
-    "            if len(md_list) > 1\n",
-    "            else md_list[0]\n",
-    "        )\n",
-    "        with fs.open(metadata_path, \"wb\") as fil:\n",
-    "            _meta.tofile(fil)\n",
-    "    return True"
+    "INPUT_PATH = os.environ.get('INPUT_DATA_DIR', input_path)\n",
+    "OUTPUT_PATH = os.environ.get('OUTPUT_DATA_DIR', os.path.join(BASE_DIR, \"converted\")) \n",
+    "CUDA_VISIBLE_DEVICES = os.environ.get(\"CUDA_VISIBLE_DEVICES\", \"0\")\n",
+    "frac_size = 0.10"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "__Step 4__: (Optionally) Start a Dask cluster"
+    "__Step 3__: (Optionally) Start a Dask cluster"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": 9,
    "metadata": {},
    "outputs": [],
    "source": [
-    "# Start up cluster if we have multiple devices\n",
-    "# (and `allow_multi_gpu == True`)\n",
-    "client = None\n",
-    "if n_workers > 1 and allow_multi_gpu:\n",
+    "cluster = None  # Connect to existing cluster if desired\n",
+    "if cluster == None:\n",
     "    cluster = LocalCUDACluster(\n",
-    "        n_workers=n_workers,\n",
     "        CUDA_VISIBLE_DEVICES=CUDA_VISIBLE_DEVICES,\n",
+    "        rmm_pool_size=get_rmm_size(0.8 * device_mem_size()),\n",
+    "        local_directory=os.path.join(OUTPUT_PATH, \"dask-space\"),\n",
     "    )\n",
-    "    client = Client(cluster)\n",
-    "    if use_rmm_pool:\n",
-    "        client.run(_pool)\n",
-    "elif use_rmm_pool:\n",
-    "    _pool()"
+    "client = Client(cluster)"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "__Step 5__: Main conversion script (build Dask task graph)"
+    "__Step 5__: Convert original data to an NVTabular Dataset"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": 10,
    "metadata": {},
    "outputs": [],
    "source": [
-    "fs = get_fs_token_paths(INPUT_PATH, mode=\"rb\")[0]\n",
-    "file_list = [\n",
-    "    x for x in fs.glob(fs.sep.join([INPUT_PATH, \"day_*\"]))\n",
-    "    if not x.endswith(\"parquet\")\n",
-    "]\n",
-    "file_list = sorted(file_list, key=natural_sort_key)\n",
-    "file_list = file_list[:max_day] if max_day else file_list\n",
-    "name_list = _analyze_paths(file_list, fs)[1]\n",
-    "\n",
+    "# Specify column names\n",
     "cont_names = [\"I\" + str(x) for x in range(1, 14)]\n",
     "cat_names = [\"C\" + str(x) for x in range(1, 27)]\n",
     "cols = [\"label\"] + cont_names + cat_names\n",
     "\n",
+    "# Specify column dtypes. Note that \"hex\" means that\n",
+    "# the values will be hexadecimal strings that should\n",
+    "# be converted to int32\n",
     "dtypes = {}\n",
     "dtypes[\"label\"] = np.int32\n",
     "for x in cont_names:\n",
@@ -193,51 +251,54 @@
     "for x in cat_names:\n",
     "    dtypes[x] = \"hex\"\n",
     "\n",
-    "dsk = {}\n",
-    "token = tokenize(file_list, name_list, OUTPUT_PATH, frac_size, fs, cols, dtypes)\n",
-    "convert_file_name = \"convert_file-\" + token\n",
-    "for i, (path, name) in enumerate(zip(file_list, name_list)):\n",
-    "    key = (convert_file_name, i)\n",
-    "    dsk[key] = (_convert_file, path, name, OUTPUT_PATH, frac_size, fs, cols, dtypes)\n",
-    "\n",
-    "write_meta_name = \"write-metadata-\" + token\n",
-    "dsk[write_meta_name] = (\n",
-    "    _write_metadata,\n",
-    "    [(convert_file_name, i) for i in range(len(file_list))],\n",
-    "    fs,\n",
-    "    OUTPUT_PATH,\n",
-    ")\n",
-    "graph = HighLevelGraph.from_collections(write_meta_name, dsk, dependencies=[])\n",
-    "conversion_delayed = Delayed(write_meta_name, graph)"
+    "# Create an NVTabular Dataset from a CSV-file glob\n",
+    "file_list = glob.glob(os.path.join(INPUT_PATH, \"day_*\"))\n",
+    "dataset = nvt.Dataset(\n",
+    "    file_list,\n",
+    "    engine=\"csv\",\n",
+    "    names=cols,\n",
+    "    part_mem_fraction=frac_size,\n",
+    "    sep='\\t',\n",
+    "    dtypes=dtypes,\n",
+    "    client=client,\n",
+    ")"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "**__Step 6__**: Execute conversion"
+    "**__Step 6__**: Write Dataset to Parquet"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": 11,
    "metadata": {},
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "CPU times: user 8min 48s, sys: 12min 53s, total: 21min 41s\n",
-      "Wall time: 30min 51s\n"
+      "CPU times: user 8.59 s, sys: 2.83 s, total: 11.4 s\n",
+      "Wall time: 5min 55s\n"
      ]
     }
    ],
    "source": [
-    "%%time\n",
-    "if client:\n",
-    "    conversion_delayed.compute()\n",
-    "else:\n",
-    "    conversion_delayed.compute(scheduler=\"synchronous\")"
+    "#%%time \n",
+    "\n",
+    "dataset.to_parquet(\n",
+    "   os.path.join(OUTPUT_PATH, \"criteo\"),\n",
+    "   preserve_files=True,\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "You can delete the original criteo files as they require a lot of disk space."
    ]
   }
  ],
@@ -257,7 +318,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.7.8"
+   "version": "3.8.8"
   }
  },
  "nbformat": 4,
diff --git a/examples/scaling-criteo/02-03b-ETL-with-NVTabular-Training-with-PyTorch.ipynb b/examples/scaling-criteo/02-03b-ETL-with-NVTabular-Training-with-PyTorch.ipynb
deleted file mode 100644
index 144a5e93615..00000000000
--- a/examples/scaling-criteo/02-03b-ETL-with-NVTabular-Training-with-PyTorch.ipynb
+++ /dev/null
@@ -1,688 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "code",
-   "execution_count": 1,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "# Copyright 2021 NVIDIA Corporation. All Rights Reserved.\n",
-    "#\n",
-    "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
-    "# you may not use this file except in compliance with the License.\n",
-    "# You may obtain a copy of the License at\n",
-    "#|\n",
-    "#     http://www.apache.org/licenses/LICENSE-2.0\n",
-    "#\n",
-    "# Unless required by applicable law or agreed to in writing, software\n",
-    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
-    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
-    "# See the License for the specific language governing permissions and\n",
-    "# limitations under the License.\n",
-    "# =============================================================================="
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "# Criteo Example \n",
-    "Here we'll show how to use NVTabular first as a preprocessing library to prepare the [Criteo Display Advertising Challenge](https://www.kaggle.com/c/criteo-display-ad-challenge) dataset, and then as a dataloader to train a FastAI model on the prepared data. The large memory footprint of the Criteo dataset presents a great opportunity to highlight the advantages of the online fashion in which NVTabular loads and transforms data.\n",
-    "\n",
-    "### Data Prep\n",
-    "Before we get started, make sure you've run the [optimize_criteo](https://github.com/NVIDIA/NVTabular/blob/main/examples/optimize_criteo.ipynb) notebook, which will convert the tsv data published by Criteo into the parquet format that our accelerated readers prefer. It's fair to mention at this point that that notebook will take around 30 minutes to run. While we're hoping to release accelerated csv readers in the near future, we also believe that inefficiencies in existing data representations like csv are in no small part a consequence of inefficiencies in the existing hardware/software stack. Accelerating these pipelines on new hardware like GPUs may require us to make new choices about the representations we use to store that data, and parquet represents a strong alternative."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 2,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import os\n",
-    "from time import time\n",
-    "import re\n",
-    "import glob\n",
-    "import warnings\n",
-    "\n",
-    "# tools for data preproc/loading\n",
-    "import torch\n",
-    "import rmm\n",
-    "import nvtabular as nvt\n",
-    "from nvtabular.ops import Normalize,  Categorify,  LogOp, FillMissing, Clip, get_embedding_sizes\n",
-    "from nvtabular.loader.torch import TorchAsyncItr, DLDataLoader\n",
-    "from nvtabular.utils import device_mem_size, get_rmm_size\n",
-    "\n",
-    "# tools for training\n",
-    "from fastai.basics import Learner\n",
-    "from fastai.tabular.model import TabularModel\n",
-    "from fastai.tabular.data import TabularDataLoaders\n",
-    "from fastai.metrics import RocAucBinary, APScoreBinary\n",
-    "from fastai.callback.progress import ProgressCallback"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### Initializing the Memory Pool\n",
-    "For applications like the one that follows where RAPIDS will be the only workhorse user of GPU memory and resource, a best practice is to use the RAPIDS Memory Manager library `rmm` to allocate a dedicated pool of GPU memory that allows for fast, asynchronous memory management. Here, we'll dedicate 80% of free GPU memory to this pool to make sure we get the most utilization possible."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 3,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "rmm.reinitialize(pool_allocator=True, initial_pool_size=get_rmm_size(0.8 * device_mem_size(kind='total')))"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### Dataset and Dataset Schema\n",
-    "Once our data is ready, we'll define some high level parameters to describe where our data is and what it \"looks like\" at a high level."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 4,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "# define some information about where to get our data\n",
-    "INPUT_DATA_DIR = os.environ.get('INPUT_DATA_DIR', '/raid/criteo/tests/crit_int_pq')\n",
-    "OUTPUT_DATA_DIR = os.environ.get('OUTPUT_DATA_DIR', '/raid/criteo/tests/test_dask') # where we'll save our procesed data to\n",
-    "BATCH_SIZE = int(os.environ.get('BATCH_SIZE', 800000))\n",
-    "PARTS_PER_CHUNK = int(os.environ.get('PARTS_PER_CHUNK', 2))\n",
-    "NUM_TRAIN_DAYS = 23 # number of days worth of data to use for training, the rest will be used for validation\n",
-    "\n",
-    "output_train_dir = os.path.join(OUTPUT_DATA_DIR, 'train/')\n",
-    "output_valid_dir = os.path.join(OUTPUT_DATA_DIR, 'valid/')\n",
-    "! mkdir -p $output_train_dir\n",
-    "! mkdir -p $output_valid_dir"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 5,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "_metadata\tday_12.parquet\tday_17.parquet\tday_21.parquet\tday_5.parquet\n",
-      "day_0.parquet\tday_13.parquet\tday_18.parquet\tday_22.parquet\tday_6.parquet\n",
-      "day_1.parquet\tday_14.parquet\tday_19.parquet\tday_23.parquet\tday_7.parquet\n",
-      "day_10.parquet\tday_15.parquet\tday_2.parquet\tday_3.parquet\tday_8.parquet\n",
-      "day_11.parquet\tday_16.parquet\tday_20.parquet\tday_4.parquet\tday_9.parquet\n"
-     ]
-    }
-   ],
-   "source": [
-    "! ls $INPUT_DATA_DIR"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 6,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "fname = 'day_{}.parquet'\n",
-    "num_days = len([i for i in os.listdir(INPUT_DATA_DIR) if re.match(fname.format('[0-9]{1,2}'), i) is not None])\n",
-    "train_paths = [os.path.join(INPUT_DATA_DIR, fname.format(day)) for day in range(NUM_TRAIN_DAYS)]\n",
-    "valid_paths = [os.path.join(INPUT_DATA_DIR, fname.format(day)) for day in range(NUM_TRAIN_DAYS, num_days)]"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### Preprocessing\n",
-    "At this point, our data still isn't in a form that's ideal for consumption by neural networks. The most pressing issues are missing values and the fact that our categorical variables are still represented by random, discrete identifiers, and need to be transformed into contiguous indices that can be leveraged by a learned embedding. Less pressing, but still important for learning dynamics, are the distributions of our continuous variables, which are distributed across multiple orders of magnitude and are uncentered (i.e. E[x] != 0).\n",
-    "\n",
-    "We can fix these issues in a conscise and GPU-accelerated manner with an NVTabular `Workflow`. We define our operation pipelines on `ColumnGroups` (list of column names). Then, we initialize the NVTabular `Workflow` and collect train dataset statistics with `.fit()` and apply the transformation to the train and valid dataset with `.transform()`. NVTabular `ops` can be chained with the overloaded `>>` operator.\n",
-    "\n",
-    "#### Frequency Thresholding\n",
-    "One interesting thing worth pointing out is that we're using _frequency thresholding_ in our `Categorify` op. This handy functionality will map all categories which occur in the dataset with some threshold level of infrequency (which we've set here to be 15 occurrences throughout the dataset) to the _same_ index, keeping the model from overfitting to sparse signals."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "#### Defining the operation pipelines\n",
-    "1. Categorical input features (`CATEGORICAL_COLUMNS`) are `Categorify` with frequency treshold of 15\n",
-    "2. Continuous input features (`CONTINUOUS_COLUMNS`) filled in missing values, clipped, applied logarithmn and normalized"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 7,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "# define our dataset schema\n",
-    "CONTINUOUS_COLUMNS = ['I' + str(x) for x in range(1,14)]\n",
-    "CATEGORICAL_COLUMNS = ['C' + str(x) for x in range(1,27)]\n",
-    "LABEL_COLUMNS = [\"label\"]\n",
-    "\n",
-    "cat_features = CATEGORICAL_COLUMNS >> Categorify(freq_threshold=15, out_path=OUTPUT_DATA_DIR)\n",
-    "cont_features = CONTINUOUS_COLUMNS >> FillMissing() >> Clip(min_value=0) >> LogOp() >> Normalize()\n",
-    "features = cat_features + cont_features + LABEL_COLUMNS"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "We can visualize the pipeline with `graphviz`."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 8,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "image/svg+xml": [
-       "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
-       "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
-       " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
-       "<!-- Generated by graphviz version 2.43.0 (0)\n",
-       " -->\n",
-       "<!-- Title: %3 Pages: 1 -->\n",
-       "<svg width=\"574pt\" height=\"476pt\"\n",
-       " viewBox=\"0.00 0.00 574.28 476.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
-       "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 472)\">\n",
-       "<title>%3</title>\n",
-       "<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-472 570.28,-472 570.28,4 -4,4\"/>\n",
-       "<!-- 0 -->\n",
-       "<g id=\"node1\" class=\"node\">\n",
-       "<title>0</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"304.34\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"304.34\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">+</text>\n",
-       "</g>\n",
-       "<!-- 9 -->\n",
-       "<g id=\"node10\" class=\"node\">\n",
-       "<title>9</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"304.34\" cy=\"-18\" rx=\"135.68\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"304.34\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">output cols=[C1, C2, C3...]</text>\n",
-       "</g>\n",
-       "<!-- 0&#45;&gt;9 -->\n",
-       "<g id=\"edge9\" class=\"edge\">\n",
-       "<title>0&#45;&gt;9</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M304.34,-71.7C304.34,-63.98 304.34,-54.71 304.34,-46.11\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"307.84,-46.1 304.34,-36.1 300.84,-46.1 307.84,-46.1\"/>\n",
-       "</g>\n",
-       "<!-- 7 -->\n",
-       "<g id=\"node2\" class=\"node\">\n",
-       "<title>7</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"155.34\" cy=\"-162\" rx=\"59.59\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"155.34\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">Categorify</text>\n",
-       "</g>\n",
-       "<!-- 7&#45;&gt;0 -->\n",
-       "<g id=\"edge1\" class=\"edge\">\n",
-       "<title>7&#45;&gt;0</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M186.21,-146.5C211.76,-134.49 247.87,-117.53 273.43,-105.52\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"275.1,-108.6 282.67,-101.18 272.13,-102.27 275.1,-108.6\"/>\n",
-       "</g>\n",
-       "<!-- 5 -->\n",
-       "<g id=\"node3\" class=\"node\">\n",
-       "<title>5</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"304.34\" cy=\"-162\" rx=\"58.49\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"304.34\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">Normalize</text>\n",
-       "</g>\n",
-       "<!-- 5&#45;&gt;0 -->\n",
-       "<g id=\"edge2\" class=\"edge\">\n",
-       "<title>5&#45;&gt;0</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M304.34,-143.7C304.34,-135.98 304.34,-126.71 304.34,-118.11\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"307.84,-118.1 304.34,-108.1 300.84,-118.1 307.84,-118.1\"/>\n",
-       "</g>\n",
-       "<!-- 4 -->\n",
-       "<g id=\"node4\" class=\"node\">\n",
-       "<title>4</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"473.34\" cy=\"-162\" rx=\"92.88\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"473.34\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">input cols=[label]</text>\n",
-       "</g>\n",
-       "<!-- 4&#45;&gt;0 -->\n",
-       "<g id=\"edge3\" class=\"edge\">\n",
-       "<title>4&#45;&gt;0</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M435.83,-145.46C405.91,-133.07 364.46,-115.9 336.14,-104.17\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"337.29,-100.86 326.71,-100.27 334.61,-107.33 337.29,-100.86\"/>\n",
-       "</g>\n",
-       "<!-- 1 -->\n",
-       "<g id=\"node5\" class=\"node\">\n",
-       "<title>1</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"129.34\" cy=\"-234\" rx=\"129.18\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"129.34\" y=\"-230.3\" font-family=\"Times,serif\" font-size=\"14.00\">input cols=[C1, C2, C3...]</text>\n",
-       "</g>\n",
-       "<!-- 1&#45;&gt;7 -->\n",
-       "<g id=\"edge7\" class=\"edge\">\n",
-       "<title>1&#45;&gt;7</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M135.77,-215.7C138.69,-207.81 142.23,-198.3 145.48,-189.55\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"148.78,-190.7 148.99,-180.1 142.22,-188.26 148.78,-190.7\"/>\n",
-       "</g>\n",
-       "<!-- 2 -->\n",
-       "<g id=\"node6\" class=\"node\">\n",
-       "<title>2</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"317.34\" cy=\"-234\" rx=\"40.89\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"317.34\" y=\"-230.3\" font-family=\"Times,serif\" font-size=\"14.00\">LogOp</text>\n",
-       "</g>\n",
-       "<!-- 2&#45;&gt;5 -->\n",
-       "<g id=\"edge6\" class=\"edge\">\n",
-       "<title>2&#45;&gt;5</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M314.13,-215.7C312.69,-207.98 310.97,-198.71 309.37,-190.11\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"312.78,-189.3 307.52,-180.1 305.9,-190.58 312.78,-189.3\"/>\n",
-       "</g>\n",
-       "<!-- 8 -->\n",
-       "<g id=\"node7\" class=\"node\">\n",
-       "<title>8</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"317.34\" cy=\"-306\" rx=\"29.5\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"317.34\" y=\"-302.3\" font-family=\"Times,serif\" font-size=\"14.00\">Clip</text>\n",
-       "</g>\n",
-       "<!-- 8&#45;&gt;2 -->\n",
-       "<g id=\"edge4\" class=\"edge\">\n",
-       "<title>8&#45;&gt;2</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M317.34,-287.7C317.34,-279.98 317.34,-270.71 317.34,-262.11\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"320.84,-262.1 317.34,-252.1 313.84,-262.1 320.84,-262.1\"/>\n",
-       "</g>\n",
-       "<!-- 3 -->\n",
-       "<g id=\"node8\" class=\"node\">\n",
-       "<title>3</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"317.34\" cy=\"-378\" rx=\"62.29\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"317.34\" y=\"-374.3\" font-family=\"Times,serif\" font-size=\"14.00\">FillMissing</text>\n",
-       "</g>\n",
-       "<!-- 3&#45;&gt;8 -->\n",
-       "<g id=\"edge8\" class=\"edge\">\n",
-       "<title>3&#45;&gt;8</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M317.34,-359.7C317.34,-351.98 317.34,-342.71 317.34,-334.11\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"320.84,-334.1 317.34,-324.1 313.84,-334.1 320.84,-334.1\"/>\n",
-       "</g>\n",
-       "<!-- 6 -->\n",
-       "<g id=\"node9\" class=\"node\">\n",
-       "<title>6</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"317.34\" cy=\"-450\" rx=\"118.88\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"317.34\" y=\"-446.3\" font-family=\"Times,serif\" font-size=\"14.00\">input cols=[I1, I2, I3...]</text>\n",
-       "</g>\n",
-       "<!-- 6&#45;&gt;3 -->\n",
-       "<g id=\"edge5\" class=\"edge\">\n",
-       "<title>6&#45;&gt;3</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M317.34,-431.7C317.34,-423.98 317.34,-414.71 317.34,-406.11\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"320.84,-406.1 317.34,-396.1 313.84,-406.1 320.84,-406.1\"/>\n",
-       "</g>\n",
-       "</g>\n",
-       "</svg>\n"
-      ],
-      "text/plain": [
-       "<graphviz.dot.Digraph at 0x7f1b1c209700>"
-      ]
-     },
-     "execution_count": 8,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "features.graph"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "We initialize a NVTabular `Workflow` with our pipelines."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 9,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "workflow = nvt.Workflow(features)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "Now instantiate dataset's to partition our dataset (which we couldn't fit into GPU memory)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 10,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "train_dataset = nvt.Dataset(train_paths, part_mem_fraction=0.2)\n",
-    "valid_dataset = nvt.Dataset(valid_paths, part_mem_fraction=0.2)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "Now run them through our workflows to collect statistics on the train set, then transform and save to parquet files."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 11,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "CPU times: user 3min 40s, sys: 1min 14s, total: 4min 54s\n",
-      "Wall time: 4min 51s\n"
-     ]
-    }
-   ],
-   "source": [
-    "%%time\n",
-    "workflow.fit(train_dataset)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "Next, we apply the transformation to the train and valid dataset and persist it to disk."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 12,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "CPU times: user 3min 59s, sys: 4min 35s, total: 8min 35s\n",
-      "Wall time: 8min 38s\n"
-     ]
-    }
-   ],
-   "source": [
-    "%%time\n",
-    "workflow.transform(train_dataset).to_parquet(output_path=output_train_dir,\n",
-    "                                         shuffle=nvt.io.Shuffle.PER_PARTITION, \n",
-    "                                         out_files_per_proc=5)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 13,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "CPU times: user 9.25 s, sys: 11.7 s, total: 21 s\n",
-      "Wall time: 27.6 s\n"
-     ]
-    }
-   ],
-   "source": [
-    "%%time\n",
-    "workflow.transform(valid_dataset).to_parquet(output_path=output_valid_dir)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "And just like that, we have training and validation sets ready to feed to a model!"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Deep Learning\n",
-    "### Data Loading\n",
-    "We'll start by using the parquet files we just created to feed an NVTabular `TorchAsyncItr`, which will loop through the files in chunks. First, we'll reinitialize our memory pool from earlier to free up some memory so that we can share it with PyTorch."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 14,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "rmm.reinitialize(pool_allocator=True, initial_pool_size=get_rmm_size(0.3 * device_mem_size(kind='total')))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 15,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "train_paths = glob.glob(os.path.join(output_train_dir, \"*.parquet\"))\n",
-    "valid_paths = glob.glob(os.path.join(output_valid_dir, \"*.parquet\"))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 16,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "train_data = nvt.Dataset(train_paths, engine=\"parquet\", part_mem_fraction=0.04/PARTS_PER_CHUNK)\n",
-    "valid_data = nvt.Dataset(valid_paths, engine=\"parquet\", part_mem_fraction=0.04/PARTS_PER_CHUNK)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 17,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "train_data_itrs = TorchAsyncItr(\n",
-    "    train_data,\n",
-    "    batch_size=BATCH_SIZE,\n",
-    "    cats=CATEGORICAL_COLUMNS,\n",
-    "    conts=CONTINUOUS_COLUMNS,\n",
-    "    labels=LABEL_COLUMNS,\n",
-    "    parts_per_chunk=PARTS_PER_CHUNK\n",
-    ")\n",
-    "valid_data_itrs = TorchAsyncItr(\n",
-    "    valid_data,\n",
-    "    batch_size=BATCH_SIZE,\n",
-    "    cats=CATEGORICAL_COLUMNS,\n",
-    "    conts=CONTINUOUS_COLUMNS,\n",
-    "    labels=LABEL_COLUMNS,\n",
-    "    parts_per_chunk=PARTS_PER_CHUNK\n",
-    ")"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 18,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def gen_col(batch):\n",
-    "    return (batch[0], batch[1], batch[2].long())"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 19,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "train_dataloader = DLDataLoader(train_data_itrs, collate_fn=gen_col, batch_size=None, pin_memory=False, num_workers=0)\n",
-    "valid_dataloader = DLDataLoader(valid_data_itrs, collate_fn=gen_col, batch_size=None, pin_memory=False, num_workers=0)\n",
-    "databunch = TabularDataLoaders(train_dataloader, valid_dataloader)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "Now we have data ready to be fed to our model online!"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### Training\n",
-    "One extra handy functionality of NVTabular is the ability to use the stats collected by the `Categorify` op to define embedding dictionary sizes (i.e. the number of rows of your embedding table). It even includes a heuristic for computing a good embedding size (i.e. the number of columns of your embedding table) based off of the number of categories."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 20,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[[7599500, 16],\n",
-       " [5345303, 16],\n",
-       " [561810, 16],\n",
-       " [242827, 16],\n",
-       " [11, 16],\n",
-       " [2209, 16],\n",
-       " [10616, 16],\n",
-       " [100, 16],\n",
-       " [4, 16],\n",
-       " [968, 16],\n",
-       " [15, 16],\n",
-       " [33521, 16],\n",
-       " [7838519, 16],\n",
-       " [2580502, 16],\n",
-       " [6878028, 16],\n",
-       " [298771, 16],\n",
-       " [11951, 16],\n",
-       " [97, 16],\n",
-       " [35, 16],\n",
-       " [17022, 16],\n",
-       " [7339, 16],\n",
-       " [20046, 16],\n",
-       " [4, 16],\n",
-       " [7068, 16],\n",
-       " [1377, 16],\n",
-       " [63, 16]]"
-      ]
-     },
-     "execution_count": 20,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "embeddings = list(get_embedding_sizes(workflow).values())\n",
-    "# We limit the output dimension to 16\n",
-    "embeddings = [[emb[0], min(16, emb[1])] for emb in embeddings]\n",
-    "embeddings"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 21,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "model = TabularModel(emb_szs=embeddings, n_cont=len(CONTINUOUS_COLUMNS), out_sz=2, layers=[512, 256]).cuda()\n",
-    "learn =  Learner(databunch, model, loss_func = torch.nn.CrossEntropyLoss(), metrics=[RocAucBinary(), APScoreBinary()])"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 22,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/html": [
-       "<table border=\"1\" class=\"dataframe\">\n",
-       "  <thead>\n",
-       "    <tr style=\"text-align: left;\">\n",
-       "      <th>epoch</th>\n",
-       "      <th>train_loss</th>\n",
-       "      <th>valid_loss</th>\n",
-       "      <th>roc_auc_score</th>\n",
-       "      <th>average_precision_score</th>\n",
-       "      <th>time</th>\n",
-       "    </tr>\n",
-       "  </thead>\n",
-       "  <tbody>\n",
-       "    <tr>\n",
-       "      <td>0</td>\n",
-       "      <td>0.122053</td>\n",
-       "      <td>0.124867</td>\n",
-       "      <td>0.798310</td>\n",
-       "      <td>0.170563</td>\n",
-       "      <td>2:05:01</td>\n",
-       "    </tr>\n",
-       "  </tbody>\n",
-       "</table>"
-      ],
-      "text/plain": [
-       "<IPython.core.display.HTML object>"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "run_time: 7501.544043064117 - rows: 4373472329 - epochs: 1 - dl_thru: 583009.6182723457\n"
-     ]
-    }
-   ],
-   "source": [
-    "learning_rate = 1.32e-2\n",
-    "epochs = 1\n",
-    "start = time()\n",
-    "learn.fit(epochs, learning_rate)\n",
-    "t_final = time() - start\n",
-    "total_rows = train_data_itrs.num_rows_processed + valid_data_itrs.num_rows_processed\n",
-    "print(f\"run_time: {t_final} - rows: {total_rows} - epochs: {epochs} - dl_thru: {total_rows / t_final}\")"
-   ]
-  }
- ],
- "metadata": {
-  "file_extension": ".py",
-  "kernelspec": {
-   "display_name": "Python 3",
-   "language": "python",
-   "name": "python3"
-  },
-  "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.8"
-  },
-  "mimetype": "text/x-python",
-  "name": "python",
-  "npconvert_exporter": "python",
-  "pygments_lexer": "ipython3",
-  "version": 3
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/examples/scaling-criteo/02-03c-ETL-with-NVTabular-HugeCTR.ipynb b/examples/scaling-criteo/02-ETL-with-NVTabular.ipynb
similarity index 55%
rename from examples/scaling-criteo/02-03c-ETL-with-NVTabular-HugeCTR.ipynb
rename to examples/scaling-criteo/02-ETL-with-NVTabular.ipynb
index 4d051b19fba..96d7002d139 100644
--- a/examples/scaling-criteo/02-03c-ETL-with-NVTabular-HugeCTR.ipynb
+++ b/examples/scaling-criteo/02-ETL-with-NVTabular.ipynb
@@ -2,9 +2,8 @@
  "cells": [
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 1,
    "metadata": {
-    "collapsed": false,
     "jupyter": {
      "outputs_hidden": false
     }
@@ -31,18 +30,69 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "# NVTabular / HugeCTR Criteo Example \n",
-    "Here we'll show how to use NVTabular first as a preprocessing library to prepare the [Criteo Display Advertising Challenge](https://www.kaggle.com/c/criteo-display-ad-challenge) dataset, and then train a model using HugeCTR.\n",
+    "<img src=\"http://developer.download.nvidia.com/compute/machine-learning/frameworks/nvidia_logo.png\" style=\"width: 90px; float: right;\">\n",
+    "\n",
+    "# Scaling Criteo: ETL with NVTabular\n",
+    "\n",
+    "## Overview\n",
+    "\n",
+    "NVTabular is a feature engineering and preprocessing library for tabular data designed to quickly and easily manipulate terabyte scale datasets used to train deep learning based recommender systems. It provides a high level abstraction to simplify code and accelerates computation on the GPU using the RAPIDS cuDF library.<br><br>\n",
+    "\n",
+    "**In this notebook, we will show how to scale NVTabular to multi-GPUs and multiple nodes.** Prerequisite is to be familiar with NVTabular and its API. You can read more NVTabular and its API in our [Getting Started with Movielens notebooks](https://github.com/NVIDIA/NVTabular/tree/main/examples/getting-started-movielens).<br><br>\n",
+    "\n",
+    "The full [Criteo 1TB Click Logs dataset](https://ailab.criteo.com/download-criteo-1tb-click-logs-dataset/) contains ~1.3 TB of uncompressed click logs containing over four billion samples spanning 24 days. In our benchmarks, we are able to preprocess and engineer features in **13.8min with 1x NVIDIA A100 GPU and 1.9min with 8x NVIDIA A100 GPUs**. This is a **speed-up of 100x-10000x** in comparison to different CPU versions, You can read more in our [blog](https://developer.nvidia.com/blog/announcing-the-nvtabular-open-beta-with-multi-gpu-support-and-new-data-loaders/).\n",
+    "\n",
+    "Our pipeline will be representative with most common preprocessing transformation for deep learning recommender models.\n",
+    "* Categorical input features are `Categorified` to be continuous integers (0, ..., |C|) for the embedding layers\n",
+    "* Missing values of continuous input features are filled with 0. Afterwards the continuous features are clipped and normalized.\n",
+    "\n",
+    "### Learning objectives\n",
+    "\n",
+    "In this notebook, we learn how to to scale ETLs with NVTabular\n",
+    "- Learn to use larger than GPU/host memory datasets\n",
+    "- Use multi-GPU or multi node for ETL\n",
+    "- Apply common deep learning ETL workflow\n",
+    "\n",
+    "### Multi-GPU and multi-node scaling\n",
+    "\n",
+    "NVTabular is built on top off [RAPIDS.AI cuDF](https://github.com/rapidsai/cudf/), [dask_cudf](https://docs.rapids.ai/api/cudf/stable/dask-cudf.html) and [dask](https://dask.org/).<br><br>\n",
+    "**Dask** is a task-based library for parallel scheduling and execution. Although it is certainly possible to use the task-scheduling machinery directly to implement customized parallel workflows (we do it in NVTabular), most users only interact with Dask through a Dask Collection API. The most popular \"collection\" API's include:\n",
+    "\n",
+    "* Dask DataFrame: Dask-based version of the Pandas DataFrame/Series API. Note that dask_cudf is just a wrapper around this collection module (dask.dataframe).\n",
+    "* Dask Array: Dask-based version of the NumPy array API\n",
+    "* Dask Bag: Similar to a Dask-based version of PyToolz or a Pythonic version of PySpark RDD\n",
+    "\n",
+    "For example, Dask DataFrame provides a convenient API for decomposing large Pandas (or cuDF) DataFrame/Series objects into a collection of DataFrame partitions.\n",
+    "\n",
+    "<img src=\"./imgs/dask-dataframe.svg\" width=\"20%\">\n",
+    "\n",
+    "We use **dask_cudf** to process large datasets as a collection of cuDF dataframes instead of Pandas. CuDF is a GPU DataFrame library for loading, joining, aggregating, filtering, and otherwise manipulating data.\n",
+    "<br><br>\n",
+    "**Dask enables easily to schedule tasks for multiple workers: multi-GPU or multi-node. We just need to initialize a Dask cluster (`LocalCUDACluster`) and NVTabular will use the cluster to execture the workflow.**"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Getting Started\n",
+    "Here we'll show how to use NVTabular first as a preprocessing library to prepare the [Criteo 1TB Click Logs dataset](https://ailab.criteo.com/download-criteo-1tb-click-logs-dataset/) dataset. The following notebooks can use the output to train a deep learning model.\n",
     "\n",
     "### Data Prep\n",
-    "Before we get started, make sure you've run the [optimize_criteo notebook](../optimize_criteo.ipynb), which will convert the tsv data published by Criteo into the parquet format that our accelerated readers prefer. It's fair to mention at this point that that notebook will take ~30 minutes to run. While we're hoping to release accelerated csv readers in the near future, we also believe that inefficiencies in existing data representations like csv are in no small part a consequence of inefficiencies in the existing hardware/software stack. Accelerating these pipelines on new hardware like GPUs may require us to make new choices about the representations we use to store that data, and parquet represents a strong alternative."
+    "The previous notebook [01-Download-Convert](./01-Download-Convert.ipynb) converted the tsv data published by Criteo into the parquet format that our accelerated readers prefer. Accelerating these pipelines on new hardware like GPUs may require us to make new choices about the representations we use to store that data, and parquet represents a strong alternative."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We load the required libraries."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 2,
    "metadata": {
-    "collapsed": false,
     "jupyter": {
      "outputs_hidden": false
     }
@@ -72,26 +122,20 @@
     "import nvtabular as nvt\n",
     "from nvtabular.ops import Categorify, Clip, FillMissing, HashBucket, LambdaOp, LogOp, Normalize, Rename, get_embedding_sizes\n",
     "from nvtabular.io import Shuffle\n",
-    "from nvtabular.utils import _pynvml_mem_size, device_mem_size\n",
-    "\n",
-    "# HugeCTR\n",
-    "import hugectr\n",
-    "from mpi4py import MPI"
+    "from nvtabular.utils import _pynvml_mem_size, device_mem_size"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### Dataset and Dataset Schema\n",
     "Once our data is ready, we'll define some high level parameters to describe where our data is and what it \"looks like\" at a high level."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 3,
    "metadata": {
-    "collapsed": false,
     "jupyter": {
      "outputs_hidden": false
     }
@@ -99,14 +143,12 @@
    "outputs": [],
    "source": [
     "# define some information about where to get our data\n",
-    "BASE_DIR = \"/raid/criteo/tests/\"\n",
-    "input_path = os.path.join(BASE_DIR, \"crit_int_pq\")\n",
+    "BASE_DIR = os.environ.get('BASE_DIR', '/raid/data/criteo')\n",
+    "input_path = os.path.join(BASE_DIR, \"converted/criteo\")\n",
     "dask_workdir = os.path.join(BASE_DIR, \"test_dask/workdir\")\n",
     "output_path = os.path.join(BASE_DIR, \"test_dask/output\")\n",
     "stats_path = os.path.join(BASE_DIR, \"test_dask/stats\")\n",
     "\n",
-    "NUM_TRAIN_DAYS = 23 # number of days worth of data to use for training, the rest will be used for validation\n",
-    "\n",
     "# Make sure we have a clean worker space for Dask\n",
     "if os.path.isdir(dask_workdir):\n",
     "    shutil.rmtree(dask_workdir)\n",
@@ -125,33 +167,75 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 4,
    "metadata": {
-    "collapsed": false,
     "jupyter": {
      "outputs_hidden": false
     }
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "converted  test_dask\r\n"
+     ]
+    }
+   ],
    "source": [
     "! ls $BASE_DIR"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We use the last day as validation dataset and the remaining days as training dataset."
+   ]
+  },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "['/raid/data/criteo2/converted/criteo/day_1.parquet',\n",
+       " '/raid/data/criteo2/converted/criteo/day_0.parquet']"
+      ]
+     },
+     "execution_count": 5,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "fname = 'day_{}.parquet'\n",
+    "num_days = len([i for i in os.listdir(input_path) if re.match(fname.format('[0-9]{1,2}'), i) is not None])\n",
+    "train_paths = [os.path.join(input_path, fname.format(day)) for day in range(num_days-1)]\n",
+    "valid_paths = [os.path.join(input_path, fname.format(day)) for day in range(num_days-1, num_days)]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
    "metadata": {
-    "collapsed": false,
     "jupyter": {
      "outputs_hidden": false
     }
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "['/raid/data/criteo2/converted/criteo/day_1.parquet']\n",
+      "/raid/data/criteo2/converted/criteo/day_0.parquet\n"
+     ]
+    }
+   ],
    "source": [
-    "fname = 'day_{}.parquet'\n",
-    "num_days = len([i for i in os.listdir(input_path) if re.match(fname.format('[0-9]{1,2}'), i) is not None])\n",
-    "train_paths = [os.path.join(input_path, fname.format(day)) for day in range(NUM_TRAIN_DAYS)]\n",
-    "valid_paths = [os.path.join(input_path, fname.format(day)) for day in range(NUM_TRAIN_DAYS, num_days)]\n",
     "print(train_paths)\n",
     "print(valid_paths)"
    ]
@@ -172,14 +256,45 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 7,
    "metadata": {
-    "collapsed": false,
     "jupyter": {
      "outputs_hidden": false
     }
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<table style=\"border: 2px solid white;\">\n",
+       "<tr>\n",
+       "<td style=\"vertical-align: top; border: 0px solid white\">\n",
+       "<h3 style=\"text-align: left;\">Client</h3>\n",
+       "<ul style=\"text-align: left; list-style: none; margin: 0; padding: 0;\">\n",
+       "  <li><b>Scheduler: </b>tcp://127.0.0.1:37841</li>\n",
+       "  <li><b>Dashboard: </b><a href='http://127.0.0.1:8787/status' target='_blank'>http://127.0.0.1:8787/status</a></li>\n",
+       "</ul>\n",
+       "</td>\n",
+       "<td style=\"vertical-align: top; border: 0px solid white\">\n",
+       "<h3 style=\"text-align: left;\">Cluster</h3>\n",
+       "<ul style=\"text-align: left; list-style:none; margin: 0; padding: 0;\">\n",
+       "  <li><b>Workers: </b>1</li>\n",
+       "  <li><b>Cores: </b>1</li>\n",
+       "  <li><b>Memory: </b>1.08 TB</li>\n",
+       "</ul>\n",
+       "</td>\n",
+       "</tr>\n",
+       "</table>"
+      ],
+      "text/plain": [
+       "<Client: 'tcp://127.0.0.1:37841' processes=1 threads=1, memory=1.08 TB>"
+      ]
+     },
+     "execution_count": 7,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
    "source": [
     "# Dask dashboard\n",
     "dashboard_port = \"8787\"\n",
@@ -225,19 +340,38 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "#### Initilize Memory Pools"
+    "That's it. We initalized our Dask cluster and NVTabular will execute the workflow on multiple GPUs. Similar, we could define a cluster with multiple nodes."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Initilize Memory Pools\n",
+    "\n",
+    "Since allocating memory is often a performance bottleneck, it is usually a good idea to initialize a memory pool on each of our workers. When using a distributed cluster, we must use the `client.run` utility to make sure a function is exectuted on all available workers."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 8,
    "metadata": {
-    "collapsed": false,
     "jupyter": {
      "outputs_hidden": false
     }
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'tcp://127.0.0.1:37259': None}"
+      ]
+     },
+     "execution_count": 8,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
    "source": [
     "# Initialize RMM pool on ALL workers\n",
     "def _rmm_pool():\n",
@@ -254,10 +388,10 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### Preprocessing\n",
+    "### Defining our Preprocessing Pipeline\n",
     "At this point, our data still isn't in a form that's ideal for consumption by neural networks. The most pressing issues are missing values and the fact that our categorical variables are still represented by random, discrete identifiers, and need to be transformed into contiguous indices that can be leveraged by a learned embedding. Less pressing, but still important for learning dynamics, are the distributions of our continuous variables, which are distributed across multiple orders of magnitude and are uncentered (i.e. E[x] != 0).\n",
     "\n",
-    "We can fix these issues in a conscise and GPU-accelerated manner with an NVTabular `Workflow`. We'll instantiate one with our current dataset schema, then symbolically add operations _on_ that schema. By setting all these `Ops` to use `replace=True`, the schema itself will remain unmodified, while the variables represented by each field in the schema will be transformed.\n",
+    "We can fix these issues in a conscise and GPU-accelerated manner with an NVTabular `Workflow`. We explained the NVTabular API in [Getting Started with Movielens notebooks](https://github.com/NVIDIA/NVTabular/tree/main/examples/getting-started-movielens) and hope you are familiar with the syntax.\n",
     "\n",
     "#### Frequency Thresholding\n",
     "One interesting thing worth pointing out is that we're using _frequency thresholding_ in our `Categorify` op. This handy functionality will map all categories which occur in the dataset with some threshold level of infrequency (which we've set here to be 15 occurrences throughout the dataset) to the _same_ index, keeping the model from overfitting to sparse signals."
@@ -265,9 +399,8 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 9,
    "metadata": {
-    "collapsed": false,
     "jupyter": {
      "outputs_hidden": false
     }
@@ -293,14 +426,13 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Now instantiate dataset iterators to loop through our dataset (which we couldn't fit into GPU memory). We need to enforce the required HugeCTR data types, so we set them in a dictionary and give as an argument when creating our dataset"
+    "Now instantiate dataset iterators to loop through our dataset (which we couldn't fit into GPU memory). We need to enforce the required HugeCTR data types, so we set them in a dictionary and give as an argument when creating our dataset."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 10,
    "metadata": {
-    "collapsed": false,
     "jupyter": {
      "outputs_hidden": false
     }
@@ -321,14 +453,22 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 11,
    "metadata": {
-    "collapsed": false,
     "jupyter": {
      "outputs_hidden": false
     }
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "/nvtabular/nvtabular/io/parquet.py:82: UserWarning: Row group memory size (151242048) (bytes) of parquet file is bigger than requested part_size (128000000) for the NVTabular dataset.A row group memory size of 128 MB is generally recommended. You can find info on how to set the row group size of parquet files in https://nvidia.github.io/NVTabular/main/HowItWorks.html#getting-your-data-ready-for-nvtabular\n",
+      "  warnings.warn(\n"
+     ]
+    }
+   ],
    "source": [
     "train_dataset = nvt.Dataset(train_paths, engine='parquet', part_size=part_size)\n",
     "valid_dataset = nvt.Dataset(valid_paths, engine='parquet', part_size=part_size)"
@@ -343,9 +483,8 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 12,
    "metadata": {
-    "collapsed": false,
     "jupyter": {
      "outputs_hidden": false
     }
@@ -367,9 +506,18 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 13,
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 40.6 s, sys: 1.67 s, total: 42.3 s\n",
+      "Wall time: 3min 10s\n"
+     ]
+    }
+   ],
    "source": [
     "%%time\n",
     "workflow.fit(train_dataset)"
@@ -377,14 +525,22 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 14,
    "metadata": {
-    "collapsed": false,
     "jupyter": {
      "outputs_hidden": false
     }
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 3.36 s, sys: 805 ms, total: 4.17 s\n",
+      "Wall time: 1min 58s\n"
+     ]
+    }
+   ],
    "source": [
     "%%time\n",
     "workflow.transform(train_dataset).to_parquet(output_path=output_train_dir,\n",
@@ -397,14 +553,22 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 15,
    "metadata": {
-    "collapsed": false,
     "jupyter": {
      "outputs_hidden": false
     }
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 3.27 s, sys: 682 ms, total: 3.95 s\n",
+      "Wall time: 1min 54s\n"
+     ]
+    }
+   ],
    "source": [
     "%%time\n",
     "workflow.transform(valid_dataset).to_parquet(output_path=output_valid_dir, \n",
@@ -418,163 +582,16 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Get the embeddings table size, to configurate HugeCTR"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false,
-    "jupyter": {
-     "outputs_hidden": false
-    }
-   },
-   "outputs": [],
-   "source": [
-    "embeddings = [c[0] for c in categorify_op.get_embedding_sizes(CATEGORICAL_COLUMNS).values()]\n",
-    "embeddings = np.clip(a=embeddings, a_min=None, a_max=num_buckets).tolist()\n",
-    "print(embeddings)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "And just like that, we have training and validation sets ready to feed to a model!"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## HugeCTR\n",
-    "### Training\n",
-    "We'll run huge_ctr using the DLRM configuration file.\n",
-    "\n",
-    "First, we'll shutdown our Dask client from earlier to free up some memory so that we can share it with HugeCTR."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false,
-    "jupyter": {
-     "outputs_hidden": false
-    }
-   },
-   "outputs": [],
-   "source": [
-    "client.shutdown()\n",
-    "cluster.close()"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "Finally, we run HugeCTR. Please, look at the jupyter-lab console to see the HugeCTR output. For reference, let's time it to see how long it takes..."
+    "In the next notebooks, we will train a deep learning model. Our training pipeline requires information about the data schema to define the neural network architecture. We will save the NVTabular workflow to disk tha we can restore it in the next notebooks."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 17,
    "metadata": {},
    "outputs": [],
    "source": [
-    "%% time\n",
-    "solver = hugectr.solver_parser_helper(num_epochs = 0,\n",
-    "                                    max_iter = 10000,\n",
-    "                                    max_eval_batches = 100,\n",
-    "                                    batchsize_eval = 2720,\n",
-    "                                    batchsize = 2720,\n",
-    "                                    display = 1000,\n",
-    "                                    eval_interval = 3200,\n",
-    "                                    i64_input_key = True,\n",
-    "                                    use_mixed_precision = False,\n",
-    "                                    repeat_dataset = True)\n",
-    "optimizer = hugectr.optimizer.CreateOptimizer(optimizer_type = hugectr.Optimizer_t.SGD,\n",
-    "                                    use_mixed_precision = False)\n",
-    "model = hugectr.Model(solver, optimizer)\n",
-    "model.add(hugectr.Input(data_reader_type = hugectr.DataReaderType_t.Parquet,\n",
-    "                            source = \"/raid/criteo/tests/test_dask/output/train/_file_list.txt\",\n",
-    "                            eval_source = \"/raid/criteo/tests/test_dask/output/valid/_file_list.txt\",\n",
-    "                            check_type = hugectr.Check_t.Non,\n",
-    "                            label_dim = 1, label_name = \"label\",\n",
-    "                            dense_dim = 13, dense_name = \"dense\",\n",
-    "                            slot_size_array = [10000000, 10000000, 3014529, 400781, 11, 2209, 11869, 148, 4, 977, 15, 38713, 10000000, 10000000, 10000000, 584616, 12883, 109, 37, 17177, 7425, 20266, 4, 7085, 1535, 64],\n",
-    "                            data_reader_sparse_param_array = \n",
-    "                            [hugectr.DataReaderSparseParam(hugectr.DataReaderSparse_t.Localized, 30, 1, 26)],\n",
-    "                            sparse_names = [\"data1\"]))\n",
-    "model.add(hugectr.SparseEmbedding(embedding_type = hugectr.Embedding_t.LocalizedSlotSparseEmbeddingHash, \n",
-    "                            max_vocabulary_size_per_gpu = 15500000,\n",
-    "                            embedding_vec_size = 128,\n",
-    "                            combiner = 0,\n",
-    "                            sparse_embedding_name = \"sparse_embedding1\",\n",
-    "                            bottom_name = \"data1\"))\n",
-    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.InnerProduct,\n",
-    "                            bottom_names = [\"dense\"],\n",
-    "                            top_names = [\"fc1\"],\n",
-    "                            num_output=512))\n",
-    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.ReLU,\n",
-    "                            bottom_names = [\"fc1\"],\n",
-    "                            top_names = [\"relu1\"]))\n",
-    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.InnerProduct,\n",
-    "                            bottom_names = [\"relu1\"],\n",
-    "                            top_names = [\"fc2\"],\n",
-    "                            num_output=256))\n",
-    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.ReLU,\n",
-    "                            bottom_names = [\"fc2\"],\n",
-    "                            top_names = [\"relu2\"]))\n",
-    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.InnerProduct,\n",
-    "                            bottom_names = [\"relu2\"],\n",
-    "                            top_names = [\"fc3\"],\n",
-    "                            num_output=128))\n",
-    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.ReLU,\n",
-    "                            bottom_names = [\"fc3\"],\n",
-    "                            top_names = [\"relu3\"]))\n",
-    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.Interaction,\n",
-    "                            bottom_names = [\"relu3\", \"sparse_embedding1\"],\n",
-    "                            top_names = [\"interaction1\"]))\n",
-    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.InnerProduct,\n",
-    "                            bottom_names = [\"interaction1\"],\n",
-    "                            top_names = [\"fc4\"],\n",
-    "                            num_output=1024))\n",
-    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.ReLU,\n",
-    "                            bottom_names = [\"fc4\"],\n",
-    "                            top_names = [\"relu4\"]))\n",
-    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.InnerProduct,\n",
-    "                            bottom_names = [\"relu4\"],\n",
-    "                            top_names = [\"fc5\"],\n",
-    "                            num_output=1024))\n",
-    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.ReLU,\n",
-    "                            bottom_names = [\"fc5\"],\n",
-    "                            top_names = [\"relu5\"]))\n",
-    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.InnerProduct,\n",
-    "                            bottom_names = [\"relu5\"],\n",
-    "                            top_names = [\"fc6\"],\n",
-    "                            num_output=512))\n",
-    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.ReLU,\n",
-    "                            bottom_names = [\"fc6\"],\n",
-    "                            top_names = [\"relu6\"]))\n",
-    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.InnerProduct,\n",
-    "                            bottom_names = [\"relu6\"],\n",
-    "                            top_names = [\"fc7\"],\n",
-    "                            num_output=256))\n",
-    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.ReLU,\n",
-    "                            bottom_names = [\"fc7\"],\n",
-    "                            top_names = [\"relu7\"]))\n",
-    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.InnerProduct,\n",
-    "                            bottom_names = [\"relu7\"],\n",
-    "                            top_names = [\"fc8\"],\n",
-    "                            num_output=1))\n",
-    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.BinaryCrossEntropyLoss,\n",
-    "                            bottom_names = [\"fc8\", \"label\"],\n",
-    "                            top_names = [\"loss\"]))\n",
-    "model.compile()\n",
-    "model.summary()\n",
-    "model.fit()"
+    "workflow.save(os.path.join(output_path, \"workflow\"))"
    ]
   }
  ],
@@ -595,7 +612,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.6.9"
+   "version": "3.8.8"
   },
   "mimetype": "text/x-python",
   "npconvert_exporter": "python",
diff --git a/examples/scaling-criteo/03a-Training-with-TF.ipynb b/examples/scaling-criteo/03a-Training-with-TF.ipynb
new file mode 100644
index 00000000000..09e8d4cf336
--- /dev/null
+++ b/examples/scaling-criteo/03a-Training-with-TF.ipynb
@@ -0,0 +1,553 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Copyright 2021 NVIDIA Corporation. All Rights Reserved.\n",
+    "#\n",
+    "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
+    "# you may not use this file except in compliance with the License.\n",
+    "# You may obtain a copy of the License at\n",
+    "#\n",
+    "#     http://www.apache.org/licenses/LICENSE-2.0\n",
+    "#\n",
+    "# Unless required by applicable law or agreed to in writing, software\n",
+    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
+    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
+    "# See the License for the specific language governing permissions and\n",
+    "# limitations under the License.\n",
+    "# =============================================================================="
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Scaling Criteo: Training with TensorFlow\n",
+    "\n",
+    "## Overview\n",
+    "\n",
+    "We observed that TensorFlow training pipelines can be slow as the dataloader is a bottleneck. The native dataloader in TensorFlow randomly sample each item from the dataset, which is very slow. The window dataloader in TensorFlow is not much faster. In our experiments, we are able to speed-up existing TensorFlow pipelines by 9x using a highly optimized dataloader.<br><br>\n",
+    "\n",
+    "We have already discussed the NVTabular dataloader for TensorFlow in more detail in our [Getting Started with Movielens notebooks](https://github.com/NVIDIA/NVTabular/tree/main/examples/getting-started-movielens).<br><br>\n",
+    "\n",
+    "We will use the same techniques to train a deep learning model for the [Criteo 1TB Click Logs dataset](https://ailab.criteo.com/download-criteo-1tb-click-logs-dataset/).\n",
+    "\n",
+    "### Learning objectives\n",
+    "\n",
+    "In this notebook, we learn how to to scale ETLs with NVTabular\n",
+    "- Use **NVTabular dataloader** with TensorFlow Keras model"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## NVTabular dataloader for TensorFlow\n",
+    "\n",
+    "We’ve identified that the dataloader is one bottleneck in deep learning recommender systems when training pipelines with TensorFlow. The dataloader cannot prepare the next batch fast enough and therefore, the GPU is not fully utilized. \n",
+    "\n",
+    "We developed a highly customized tabular dataloader for accelerating existing pipelines in TensorFlow. In our experiments, we see a speed-up by 9x of the same training workflow with NVTabular dataloader. NVTabular dataloader’s features are:\n",
+    "- removing bottleneck of item-by-item dataloading\n",
+    "- enabling larger than memory dataset by streaming from disk\n",
+    "- reading data directly into GPU memory and remove CPU-GPU communication\n",
+    "- preparing batch asynchronously in GPU to avoid CPU-GPU communication\n",
+    "- supporting commonly used .parquet format\n",
+    "- easy integration into existing TensorFlow pipelines by using similar API - works with tf.keras models\n",
+    "\n",
+    "More information in our [blogpost](https://medium.com/nvidia-merlin/training-deep-learning-based-recommender-systems-9x-faster-with-tensorflow-cc5a2572ea49)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Getting Started"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# External dependencies\n",
+    "import os\n",
+    "import time\n",
+    "import gc\n",
+    "import glob\n",
+    "import os\n",
+    "\n",
+    "import nvtabular as nvt"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We define our base directory, containing the data."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "BASE_DIR = os.environ.get('BASE_DIR', '/raid/data/criteo')\n",
+    "input_path = os.path.join(BASE_DIR, \"test_dask/output\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Defining Hyperparameters"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "First, we define the data schema and differentiate between single-hot and multi-hot categorical features. Note, that we do not have any numerical input features. "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(['/raid/data/criteo2/test_dask/output/train/0.5891ce6774804b929d0bcb05f5a2558b.parquet'],\n",
+       " ['/raid/data/criteo2/test_dask/output/valid/0.606080e99a63402f891540e7c01a2963.parquet'])"
+      ]
+     },
+     "execution_count": 4,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "CONTINUOUS_COLUMNS = ['I' + str(x) for x in range(1,14)]\n",
+    "CATEGORICAL_COLUMNS =  ['C' + str(x) for x in range(1,27)]\n",
+    "LABEL_COLUMNS = ['label']\n",
+    "COLUMNS = CONTINUOUS_COLUMNS + CATEGORICAL_COLUMNS + LABEL_COLUMNS\n",
+    "BATCH_SIZE = int(os.environ.get('BATCH_SIZE', 64*1024))\n",
+    "\n",
+    "# Output from ETL-with-NVTabular\n",
+    "TRAIN_PATHS = sorted(glob.glob(os.path.join(input_path, \"train\", \"*.parquet\")))\n",
+    "VALID_PATHS = sorted(glob.glob(os.path.join(input_path, \"valid\", \"*.parquet\")))\n",
+    "TRAIN_PATHS, VALID_PATHS"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "In the previous notebook, we used NVTabular for ETL and stored the workflow to disk. We can load the NVTabular workflow to extract important metadata for our training pipeline."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "proc = nvt.Workflow.load(os.path.join(input_path, \"workflow\"))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The embedding table shows the cardinality of each categorical variable along with its associated embedding size. Each entry is of the form `(cardinality, embedding_size)`. We limit the output cardinality to 16."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'C1': (10000000, 16),\n",
+       " 'C10': (10000000, 16),\n",
+       " 'C11': (707291, 16),\n",
+       " 'C12': (218510, 16),\n",
+       " 'C13': (11, 16),\n",
+       " 'C14': (2209, 16),\n",
+       " 'C15': (9798, 16),\n",
+       " 'C16': (72, 16),\n",
+       " 'C17': (4, 16),\n",
+       " 'C18': (954, 16),\n",
+       " 'C19': (15, 16),\n",
+       " 'C2': (29612, 16),\n",
+       " 'C20': (10000000, 16),\n",
+       " 'C21': (4553157, 16),\n",
+       " 'C22': (10000000, 16),\n",
+       " 'C23': (291641, 16),\n",
+       " 'C24': (10904, 16),\n",
+       " 'C25': (91, 16),\n",
+       " 'C26': (35, 16),\n",
+       " 'C3': (15050, 16),\n",
+       " 'C4': (7190, 16),\n",
+       " 'C5': (19547, 16),\n",
+       " 'C6': (4, 16),\n",
+       " 'C7': (6492, 16),\n",
+       " 'C8': (1317, 16),\n",
+       " 'C9': (63, 16)}"
+      ]
+     },
+     "execution_count": 6,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "EMBEDDING_TABLE_SHAPES = nvt.ops.get_embedding_sizes(proc)\n",
+    "for key in EMBEDDING_TABLE_SHAPES.keys():\n",
+    "    EMBEDDING_TABLE_SHAPES[key] = (EMBEDDING_TABLE_SHAPES[key][0], min(16, EMBEDDING_TABLE_SHAPES[key][1]))\n",
+    "EMBEDDING_TABLE_SHAPES"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Initializing NVTabular Dataloader for Tensorflow"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We import TensorFlow and some NVTabular TF extensions, such as custom TensorFlow layers supporting multi-hot and the NVTabular TensorFlow data loader."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os, time\n",
+    "os.environ[\"CUDA_VISIBLE_DEVICES\"]=\"0\"\n",
+    "\n",
+    "import tensorflow as tf\n",
+    "\n",
+    "from tensorflow.python.feature_column import feature_column_v2 as fc\n",
+    "\n",
+    "# we can control how much memory to give tensorflow with this environment variable\n",
+    "# IMPORTANT: make sure you do this before you initialize TF's runtime, otherwise\n",
+    "# TF will have claimed all free GPU memory\n",
+    "os.environ['TF_MEMORY_ALLOCATION'] = \"0.5\" # fraction of free memory\n",
+    "from nvtabular.loader.tensorflow import KerasSequenceLoader, KerasSequenceValidater\n",
+    "from nvtabular.framework_utils.tensorflow import layers\n",
+    "from tensorflow.python.feature_column import feature_column_v2 as fc"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "First, we take a look on our data loader and how the data is represented as tensors. The NVTabular data loader are initialized as usually and we specify both single-hot and multi-hot categorical features as cat_names. The data loader will automatically recognize the single/multi-hot columns and represent them accordingly."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "train_dataset_tf = KerasSequenceLoader(\n",
+    "    TRAIN_PATHS,\n",
+    "    batch_size=BATCH_SIZE,\n",
+    "    label_names=LABEL_COLUMNS,\n",
+    "    cat_names=CATEGORICAL_COLUMNS,\n",
+    "    cont_names=CONTINUOUS_COLUMNS,\n",
+    "    engine='parquet',\n",
+    "    shuffle=True,\n",
+    "    buffer_size=0.06,\n",
+    "    parts_per_chunk=1\n",
+    ")\n",
+    "\n",
+    "valid_dataset_tf = KerasSequenceLoader(\n",
+    "    VALID_PATHS,\n",
+    "    batch_size=BATCH_SIZE,\n",
+    "    label_names=LABEL_COLUMNS,\n",
+    "    cat_names = CATEGORICAL_COLUMNS,\n",
+    "    cont_names=CONTINUOUS_COLUMNS,\n",
+    "    engine='parquet',\n",
+    "    shuffle=False,\n",
+    "    buffer_size=0.06,\n",
+    "    parts_per_chunk=1\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Defining Neural Network Architecture"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We will define a common neural network architecture for tabular data.\n",
+    "* Single-hot categorical features are fed into an Embedding Layer\n",
+    "* Each value of a multi-hot categorical features is fed into an Embedding Layer and the multiple Embedding outputs are combined via averaging\n",
+    "* The output of the Embedding Layers are concatenated\n",
+    "* The concatenated layers are fed through multiple feed-forward layers (Dense Layers with ReLU activations)\n",
+    "* The final output is a single number with sigmoid activation function"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "First, we will define some dictonary/lists for our network architecture."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "inputs = {}    # tf.keras.Input placeholders for each feature to be used\n",
+    "emb_layers = []# output of all embedding layers, which will be concatenated\n",
+    "num_layers = []# output of numerical layers"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We create `tf.keras.Input` tensors for all input features."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "for col in CATEGORICAL_COLUMNS:\n",
+    "    inputs[col] =  tf.keras.Input(\n",
+    "        name=col,\n",
+    "        dtype=tf.int32,\n",
+    "        shape=(1,)\n",
+    "    )\n",
+    "    \n",
+    "for col in CONTINUOUS_COLUMNS:\n",
+    "    inputs[col] =  tf.keras.Input(\n",
+    "        name=col,\n",
+    "        dtype=tf.float32,\n",
+    "        shape=(1,)\n",
+    "    )"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Next, we initialize Embedding Layers with `tf.feature_column.embedding_column`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "for col in CATEGORICAL_COLUMNS:\n",
+    "    emb_layers.append(\n",
+    "        tf.feature_column.embedding_column(\n",
+    "            tf.feature_column.categorical_column_with_identity(\n",
+    "                col, \n",
+    "                EMBEDDING_TABLE_SHAPES[col][0]                    # Input dimension (vocab size)\n",
+    "            ), EMBEDDING_TABLE_SHAPES[col][1]                     # Embedding output dimension\n",
+    "        )\n",
+    "    )"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We define `tf.feature_columns` for the continuous input features."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "for col in CONTINUOUS_COLUMNS:\n",
+    "    num_layers.append(\n",
+    "        tf.feature_column.numeric_column(col)\n",
+    "    )"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "NVTabular implemented a custom TensorFlow layer `layers.DenseFeatures`, which takes as an input the different `tf.Keras.Input` and pre-initialized `tf.feature_column` and automatically concatenate them into a flat tensor."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "<KerasTensor: shape=(None, 416) dtype=float32 (created by layer 'dense_features')>"
+      ]
+     },
+     "execution_count": 13,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "emb_layer = layers.DenseFeatures(emb_layers)\n",
+    "x_emb_output = emb_layer(inputs)\n",
+    "x_emb_output"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We add multiple Dense Layers. Finally, we initialize the `tf.keras.Model` and add the optimizer."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "x = tf.keras.layers.Dense(128, activation=\"relu\")(x_emb_output)\n",
+    "x = tf.keras.layers.Dense(128, activation=\"relu\")(x)\n",
+    "x = tf.keras.layers.Dense(128, activation=\"relu\")(x)\n",
+    "x = tf.keras.layers.Dense(1, activation=\"sigmoid\", name=\"output\")(x)\n",
+    "\n",
+    "model = tf.keras.Model(inputs=inputs, outputs=x)\n",
+    "model.compile('sgd', 'binary_crossentropy')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "image/png": "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bs/v379fzz33nObPnx/+QQNxFq+cFBQUqH///qqsrOz2tcrKSqWkpKikpCT8AwfiJF4Z+eijjyRJI0eO7Pa10aNHS5LefvvtsPcHxFNfctIT3vuAF8Sr7llvAq+K57WB14HhVYnuoei5kOwSnRHWmyDZxTsjrDeBFyX6WgIku3hlhPUm8LJY54R5BpJdX2ueeQO8KNbneiDZRVvz9PXwqmhrnnXh8LJo65513vCqaGuedduwGevpYCs+7w/owOf6AR347D6Az+cDesN5HjbivA1bsM4MAJIf68VgK9aFwVas/YKNWN+FWIjJTV3feustSdK0adNisTvX/f73v1dKSorz2Lp1qy677DINHTpUw4cP10UXXaRNmzY52y9cuNDZdty4cVq9erU+97nPKSsrSwMHDtS5556rFStWONs/8MADzvadJwyvvvqq8/yIESO67f/gwYNasWKFs01ffkGrra1NS5cu1fnnn6/s7GxlZmaqpKREP//5z9Xe3i6p48XUzn8PKSkpeuCBB5zv7/z83LlznX03Njbqxhtv1IQJE5Senq4TTjhBX/nKV/Thhx8e8++4qqpKl156qYYPH+48t2vXrqjHlwziVfeB5mXcuHHdvjZ27FhJUnV1dcx/LjkhJ33ltWtDpMgIGYmFROdk+fLlkqT/+q//cmX/5ISc9FWiMvLJJ59oxYoVuvjii5Wdna1f//rXrvwcMkJG+iqeGdm2bZtuueUWPfPMM8rKynL950lkhIzERryvJc8//7xOOukkDRo0SEOGDNGsWbP04osvuvbzyAk56at4ZeT999+XJA0bNky33HKLcnJylJ6ertzcXN14441qampy5eeSETLSV4met//qV7/S+PHj9dnPftaV/ZMRMhIL8crJqFGjtHDhQq1du1Y/+MEP1NjYqKamJv3kJz/Ra6+9pnvuuUf5+fkx/7nkhJz0VbwyEqiTwCKSzhobGyVJW7dujfnPJSNkJBZinRPe+6DuvSBedc96k+D9kxPv8Nq1IVJkhIzEQqJzQs9FTpJdojLCehMy4hXxzAjrTciIV8X7WsJ6E3LiNfHKCOtNgvdPRrwlVjlhnkHde0Vfap55AzXvVX091zMPoO69Jtqap68P3j817x2xnvuyLpya94Jo65513sH7p+69I9qaZ9128P6pefQVtU1tJxvWXwfvnywg3sgCWfAK1lKT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qvuCggJJ6vHksH37dklSfn6+q8cgkROJnETK69eGSJERMhKNeOekra1N8+bN09ixY/Xcc8/F7INrw0VOyEmkkuFaEnix/5VXXnH9Z5ERMhKpeGTk5Zdf1r59+3TOOecE/T1feeWVkqS7777bea6mpsa145DIiERGopEM15LRo0dLik/dkRNyEql4ZWTChAmSpOHDh3f7WiU9YUIAACAASURBVKDmGhsbXT0GiYxIZCRSibqOVFdX629/+5vmz58f159LRshINOKVk1dffVVSx0K9rj73uc9Jkv7yl7+4egwSOZHISaTieS3JysrSQw89pC1btqilpUU7duzQL3/5S+dDI0455RTXj4GMkJFo9DUnvPcRe9S9++JV96w3cQ85cZfXrw2RIiNkJBrxzkmic0VOyEmkkuFawnqTviEj7opHRlhv4i4y4r5kuJaw3qRvyIm74pUR1pu4h4y4z433CZln9A11765oap55g7uoefe5ca5nHtA31L27oq15+nr3UPPuitV5nnXhsUPNuy/aumedt3uoe3f15VzPum13UPOQqG2J2k401l+7hywgGmSBLCQL1lLHHlmwA+sY+oYcJA/O87FHfScnztt9Q10nDuvM3EVtJy/WkvUNtZ04rBdzD3WdXFgXFjvUdmKx9ss91HZisb7LHX6q65jc1PW6665TWlqaXnrppZDb3X777UpNTVVlZWUsfmxC7N69W8aYbs8HijdQzJKUmpqqlpaWbtvu3bu3x32npKTE6Cg7XiS9//77dc0116i6ulrt7e0yxuiRRx6RpG5juPzyyzVq1Cg99thjam5u1k9/+lN94xvf0LBhw5xtMjIyNHToUKWlpam1tVXGmB4f5557bszGkcziVfeBv881a9Z0+1rguUAzkyzICTmR/HVtiBQZISMB8c7Jddddp+bmZi1btkxpaWnO83l5eVq1alWf9h1r5IScSMlxLcnIyJAkNTU1xXzffUFGyIgUn4zccMMNPf79Pv/885Kk+++/33kuLy8vqnG4gYyQkYBkuJY0NDRICq67ZEBOyIkUv4zMnDlTkrRjx45uXwvU3KhRo6Lat1vICBmREncdefTRR/XZz35WRUVFMdmfG8gIGQmIV04Cb36HcuDAgaj27RZyQk6k5JiTvP3225Kkr3zlKzHfd1+QETIS0Nec8N5HB+reW+JV96w3CUZOvMNP14ZIkREyEhDvnHgpV+SEnEjJcS1hvUnfkBF3xSMjrDfpjox4SzJcS1hv0jfkxF3xygjrTYKREW9x431C5hl9Q927K5qaZ97QHTXvLW6c65kH9A11765oa56+Phg17x2xOs+zLpya95Jo65513sGoe+9wo6dn3XbfUPOIFLVNbbuB9dfByAK8hCyQBTexlroDWUBXrGPoG3KQPDjPd6C+7cd5u2+o68RhnVl31LY/sJasb6jtxGG9WDDq2l6sC6O2bcHar2DUtv1Y39U3fqrrmNzUNT8/X//v//0//etf/9IzzzzT4zZVVVV68skndemll6qgoCAWPzYhDh8+rNWrVwc9t379ejU0NGj69OkaPXq08/zo0aO1ffv2oG137typurq6Hvc9cODAoKKfOnWqnnrqqYiOLy0tTWVlZVqxYoWys7N144036oQTTnAC8umnn/b4fRkZGZo/f74+/vhj/fSnP9VvfvMb3XTTTd22+8pXvqK2tjatWLGi29cefPBBjR8/Xm1tbREds1fFq+7PPvtsFRUV6aWXXtLhw4ed548cOaLf/va3ysnJ0YUXXhjVvt1CTsiJ5K9rQ6TICBkJiGdO7r33XpWVlekPf/iD82ZqMiMn5ESKX0ZuvfVWXXHFFT1+7S9/+Ysk6bTTTotq324hI2REot8KhYyQkYB45WTx4sUqLS3t9rwxRsuWLZPU8YJjMiEn5ESKX0b+7d/+TWPHjtWrr74a9PqWJL388suSpC996UtR7dstZISMSInptz755BP9+te/1g033NDnfbmJjJCRgHjl5IwzzpAkvf76692+9o9//EOSdOaZZ0a1b7eQE3IixS8ju3btUmpqqrMgO+CTTz7R4sWL9bWvfU35+flR7dstZISMBPQlJ7z3cRR17y3xqnvWmwQjJ97hp2tDpMgIGQmIZ068lityQk6k+GWE9SbByIh30G8dGxkhIwHxygnrTYKRE++IV0ZYbxKMjHhLtDlhnhGMuvcOP61rp+ap+YBo6555QDDq3juirXn6+mDUvHfEor9hXTg17zXR1j3rvINR994Rbc2zbjsYNY9EorapbTew/joYWYCXkAWy4BavrZkjC2Qh1ljHEIwc2Ifz/FHUtx04bwejrpFI1Da1HQ7WkgWjtu3AerFg1LWdWBdGbduEtV/BqG07sL4rGHUdnZjc1FWS7rrrLt155526/vrrdeedd6q6ulotLS3avn27lixZonPPPVfTpk3TkiVL+vRzzjvvPA0fPlyrVq2K0ZFHZsiQIfrBD36glStX6uDBg/rXv/6lK664Qunp6fr5z38etO0FF1yghoYGPfbYYzpw4IA2bdqkm266KejuxZ2dcsopqq6uVn19vVauXKnNmzdr1qxZER9jv379dM4552jnzp166KGHtGvXLn366ad644039MQTTxzz++bPn6/MzEzdddddmj17tvLy8rpts2DBAk2ePFlXXXWV/vKXv2jfvn1qamrSk08+qfvuu08LFy5UWlpaxMfsVfGo+9TUVC1ZskRNTU361re+pZ07d2r37t264YYbtHHjRj399NMaMGBA0PeQk96Rk/iI17UhUmSkd2QkfuKRk2effVY//OEP9e677yorK0spKSlBj02bNnX7HnLSO3ISH/G6lrz44ou67777tHXrVjU3N2vr1q2644479MILL6i0tFRXX3110PZkpHdkJD7ot3pGRshIZ/HKyfvvv68bbrhBNTU1Onz4sKqqqnTllVdqzZo1+u53v+u8SRFATnpHTuIjHhnJyMjQ4sWLtXv3bn3ta1/Txo0btXfvXj3//PNasGCBzjjjDN14441B30NGekdG4iPe/dYzzzyjwYMH68tf/nLI7chI78hI/MQjJ/Pnz9eUKVO0aNEiPfroo/r444+1e/duLVmyRD/+8Y81duxY3XrrrUHfQ056R07iI17XEmOMvvWtb6mmpkbNzc1677339PnPf16jRo3SL3/5y27bk5HekZH4iSYnvPcRjLr3nnjUPetNgpETb4nXtSFSZKR3ZCR+4pETeq5g5MRb4nUtYb3JUWTEW+i3ekZGyEhn8coJ602OIifeEo+MsN4kGBnxnmjfJ2SecRR17y38jjk177eal6Kve+YBR1H33hJNzdPXB6PmvaWv/Q3rwql5L4qm7lnnHYy695Zoz/Ws2z6Kmvc3art31Lb3sP46GFlAJMhC78iC97CWOhhZ8C/WMRxFDuzCeT4Y9W0PzttHUdf+Rm33jtpODqwlO4ratgPrxYJR13ZiXRi1bRPWfgWjtu3B+q6jqOsomS6WLl1qeng6bO+995658sorTU5Ojunfv7/JysoyZ555pvn5z39umpubu23/8ssvG0k9Pp5++ulu28+aNcsMGzbMvPPOO70ey6BBg7rt86GH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rrnmmnz9619v8xneraXHOvnkk/Pggw/m+uuvz/Tp07P//vunUCjkxz/+cb72ta+lWCw2Od8PUlFRsdPIfWsi7C09l+Y8F22loaEh8+fPT5Kcd955rZq/pUp977nX9uy9Nnfu3EydOjUrV67MRRddlPvuu69Z4fVhw4bluOOOa/HxAAAAAAAAAAAAAAAAANg7VJR6AAAAAAAAAAAAAAAAAAAAPthHP/rRD1x/4IEHZv78+XnzzTezevXqzJgxI926dWvVsU488cQkydKlSxstX79+fWpqappsP2XKlNTV1eXhhx9usu7KK6/M0KFDU1dX16pZ+vbtm1WrViVJKisrM378+FRXV6dQKGTJkiXtMsN7NfdY9fX1efjhhzN48OBccMEFGTBgwI6Q5ttvv73Tx+7Vq1ejkObw4cNzyy237Pj8Qx/6UF599dVG+6xduzYvvfRSm55L0vznoi3MnTs3jzzySCZPnpxTTjmlVfO3VEe499xre/5eO/LII/PHP/4xW7duzV133ZUjjjjiAwO3Q4cO3a3jAQAAAAAAAAAAAAAAANCxiboCAAAAAAAAAAAAAAAAAHRwo0aNarJs0KBBueKKK7J+/fq8/PLLOffcc9O7d+/dPtZll12Wfv36Zc6cObnnnntSW1ubp556KjNmzEhVVVWT7S+//PIcdNBBmTlzZu6+++5s2rQpGzZsyM0335xLL700V199dbp27drqeWbNmpUnnngiW7Zsybp16zJv3rwUi8WMHTu23WZ4t+Yeq0uXLjn22GOzdu3aXHXVVVm/fn3efvvtPPDAA/nZz36208c+4ogjsnr16rz88stZvnx5nn/++RxzzDE71n/uc5/LmjVrcsMNN6S2tjbPPfdcvvrVr2bgwIFtei7bNee5SJKxY8emf//+WbFiRavmamhoyLp167Jo0aKMGzcu8+bNy8yZM3Pbbbc1CnB29nvPvbbre621KioqctJJJ2XlypV55513cuutt+YjH/lIk+0OO+ywPXI8AAAAAAAAAAAAAAAAADqmQrFYLJZ6CAAAAAAAAAAAAAAAAAAA3l99fX0qKyvTu3fvzJo1K+edd16GDh26y/0WLlyYadOmpaVvJ129enUuvPDC3H///dm2bVs+/vGP53vf+16uu+663HfffUmSM888M7feemuSZMOGDfnRj36U6urqvPzyy+nbt28OP/zw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AAAAAAAAKCuirgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAWRF1BQAAAAAAAAAAAAAAAACAErj99ttTKBRSKBTSo0ePUo/Trqqqqnac+/aPioqK7LfffjnssMNy7rnnZuXKlaUeEwAAAAAAAAAAAAAAAADoxERdAQAAAAAAAAAAAAAAAACgBE499dQUi8WMGzeu1KO0u9ra2vz9739PkkycODHFYjHbtm3LqlWrcumll2bVqlX55Cc/mTPOOCNvvfVWiacFAAAAAAAAAAAAAAAAADojUVcAAAAAAAAAAAAAAAAAAKDkunTpkkGDBmXixIm5//77861vfSu/+tWvMn369BSLxVKPBwAAAAAAAAAAAAAAAAB0MqKuAAAAAAAAAAAAAAAAAABAh3PFFVfk05/+dBYvXpzbb7+91OMAAAAAAAAAAAAAAAAAAJ2MqCsAAAAAAAAAAAAAAAAAANDhFAqFzJ49O0ly4403lngaAAAAAAAAAAAAAAAAAKCzEXUFAAAAAAAAAAAAAAAAAIB2sGrVqkyaNCl9+vTJPvvsk2OOOSYPPfTQ+27/xhtv5IILLsiHP/zhdOvWLQMGDMiUKVPyj3/8Y8c21dXVKRQKOz5eeOGFTJs2LX379k3//v0zYcKEPPfcc40ed8uWLbnkkksyYsSI9OrVK/369cvJJ5+cxYsXp76+vsUztKWjjz46SbJixYps27atRXN19msDAAAAAAAAAAAAAAAAAOweUVcAAAAAAAAAAAAAAAAAAGhjzz77bMaMGZNHH300v/vd7/L666/nxhtvzA9+8IMmYdEkee211zJ69OgsXLgwN954YzZs2JBly5Zlw4YNGTNmTJYvX54kmTRpUorFYiZOnJgkmTNnTubMmZNXX301CxYsyP3335/p06c3euzZs2fnJz/5SX7605/m3//+d55++umMGDEiEydOzIMPPtjiGbYbO3Zs+vfvnxUrVuyx6zZ48OAkSV1dXdavX7/XXhsAAAAAAAAAAAAAAAAAoOMRdQUAAAAAAAAAAAAAAAAAgDZ20UUXZePGjbn++uszfvz4VFVV5dBDD80vf/nLvPbaa022nzt3bl588cVce+21+fznP5+qqqqMHDkyt99+e4rFYs4///ydHuess87KmDFjss8+++S4447LSSedlL/97W87gqhJct9992XkyJEZP358evbs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AAAAYAR27tyZJPnZz3425Ph3v/vdYXHPJNm2bVsaGhqyZMmSIcdPP/30zJ8/P7t27cqePXuG/d2CBQuGfP/e976XJPn3v/89eOyiiy7KSy+9lOuuuy5dXV3p7+9Pkrz77rv58Y9//I03jKZnn3023d3daW9vH7V7/i+i29zcnNNOOy2J9w0AAAAAAAAAAAAAAAAAHJ+oKwAAAAAAAAAAAAAAAAAAfE09PT05fPhwTjrppLS2tg47P2vWrGHXf/rppxkYGMi0adNSFMWQz+uvv54kee+994bda9q0aUO+t7S0JEkGBgYGj9133335y1/+kg8//DCLFi3KKaeckosuuih/+9vfRmXDie6FF15IkrS3t6e5udn7BgAAAAAAAAAAAAAAAAC+lKgrAAAAAAAAAAAAAAAAAAB8TZMmTcrUqVPz+eef58iRI8POd3d3D7t++vTpaWpqSm9vb8qyPObnJz/5yYj2FEWRq666Kn//+9/zySefZNu2bSnLMsuXL8+99947JhuqMjAwkPvuuy9Jsm7duiTeNwAAAAAAAAAAAAAAAADw5URdAQAAAAAAAAAAAAAAAABgBC6++OIkyc6dO4ccP3ToUN59991h1y9fvjx9fX158cUXh52788478/3vfz99fX0j2jJ9+vTs3r07SdLc3JzFixdn27ZtKYoiTzzxxJhsqMqmTZvyyiuv5Oc//3muuOKKwePeNwAAAAAAAAAAAAAAAABwPKKuAAAAAAAAAAAAAAAAAAAwArfffntmzJiRjRs35umnn86RI0fy9ttvZ+3atWltbR12/R133JEzzzwz11xzTZ566ql8+umn6e7uzp///Ofcdtttueeee9LU1DTiPddff33++c9/pqenJwcOHMhdd92VsiyzcOHCMdvwVSxcuDAzZ85MV1fXiP5+YGAgBw4cyPbt27No0aLcddddueaaa/LII4+kKIrB67xvAAAAAAAAAAAAAAAAAOB4RF0BAAAAAAAAAAAAAAAAAGAEzjzzzLz88stZsGBBVqxYkVmzZuUXv/hFNmzYkHPOOSc9PT0piiLXXnttkmTWrFl55ZVXsmzZsqxfvz5tbW2ZN29e/vrXv2b79u3p6OhIknR1daUoimzfvj1JMnny5Nx8881JkqIocueddyZJzjvvvCxZsiRJ8txzz2XevHm58sorM2PGjJx99tnZuXNn7r//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\n",
+      "text/plain": [
+       "<IPython.core.display.Image object>"
+      ]
+     },
+     "execution_count": 15,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "# You need to install the dependencies\n",
+    "tf.keras.utils.plot_model(model)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Training the deep learning model"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We can train our model with `model.fit`. We need to use a Callback to add the validation dataloader."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 16,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "3046/3046 [==============================] - 103s 32ms/step - loss: 0.1932\n"
+     ]
+    }
+   ],
+   "source": [
+    "validation_callback = KerasSequenceValidater(valid_dataset_tf)\n",
+    "\n",
+    "history = model.fit(train_dataset_tf, callbacks=[validation_callback], epochs=1)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We save the trained model."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 17,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "INFO:tensorflow:Assets written to: /raid/data/criteo2/test_dask/output/model.savedmodel/assets\n"
+     ]
+    }
+   ],
+   "source": [
+    "model.save(os.path.join(input_path, \"model.savedmodel\"))"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "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.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/scaling-criteo/03c-Training-with-HugeCTR.ipynb b/examples/scaling-criteo/03c-Training-with-HugeCTR.ipynb
new file mode 100644
index 00000000000..1c40cea0207
--- /dev/null
+++ b/examples/scaling-criteo/03c-Training-with-HugeCTR.ipynb
@@ -0,0 +1,318 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Copyright 2021 NVIDIA Corporation. All Rights Reserved.\n",
+    "#\n",
+    "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
+    "# you may not use this file except in compliance with the License.\n",
+    "# You may obtain a copy of the License at\n",
+    "#\n",
+    "#     http://www.apache.org/licenses/LICENSE-2.0\n",
+    "#\n",
+    "# Unless required by applicable law or agreed to in writing, software\n",
+    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
+    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
+    "# See the License for the specific language governing permissions and\n",
+    "# limitations under the License.\n",
+    "# =============================================================================="
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Scaling Criteo: Training with HugeCTR\n",
+    "\n",
+    "## Overview\n",
+    "\n",
+    "HugeCTR is an open-source framework to accelerate the training of CTR estimation models on NVIDIA GPUs. It is written in CUDA C++ and highly exploits GPU-accelerated libraries such as cuBLAS, cuDNN, and NCCL.<br><br>\n",
+    "HugeCTR offers multiple advantages to train deep learning recommender systems:\n",
+    "1. **Speed**: HugeCTR is a highly efficient framework written C++. We experienced upto 10x speed up. HugeCTR on a NVIDIA DGX A100 system proved to be the fastest commercially available solution for training the architecture Deep Learning Recommender Model (DLRM) developed by Facebook.\n",
+    "2. **Scale**: HugeCTR supports model parallel scaling. It distributes the large embedding tables over multiple GPUs or multiple nodes. \n",
+    "3. **Easy-to-use**: Easy-to-use Python API similar to Keras. Examples for popular deep learning recommender systems architectures (Wide&Deep, DLRM, DCN, DeepFM) are available.\n",
+    "\n",
+    "HugeCTR is able to train recommender system models with larger-than-memory embedding tables by leveraging a parameter server. \n",
+    "\n",
+    "You can find more information about HugeCTR [here](https://github.com/NVIDIA/HugeCTR).\n",
+    "\n",
+    "### Learning objectives\n",
+    "\n",
+    "In this notebook, we learn how to to use HugeCTR for training recommender system models\n",
+    "- Use **HugeCTR** to define a recommender system model\n",
+    "- Train Facebook's [Deep Learning Recommendation Model](https://arxiv.org/pdf/1906.00091.pdf) with HugeCTR"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Getting Started"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "As HugeCTR optimizes the training in CUDA++, we need to define the training pipeline and model architecture and execute it via the commandline. We will use the Python API, which is similar to Keras models."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "If you are not familiar with HugeCTR's Python API and parameters, you can read more in its GitHub repository:\n",
+    "- [HugeCTR User Guide](https://github.com/NVIDIA/HugeCTR/blob/master/docs/hugectr_user_guide.md)\n",
+    "- [HugeCTR Python API](https://github.com/NVIDIA/HugeCTR/blob/master/docs/python_interface.md)\n",
+    "- [HugeCTR Configuration File](https://github.com/NVIDIA/HugeCTR/blob/master/docs/configuration_file_setup.md)\n",
+    "- [HugeCTR example architectures](https://github.com/NVIDIA/HugeCTR/tree/master/samples)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We will write the code to a `./model.py` file and execute it. It will create snapshot, which we will use for inference in the next notebook."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "import hugectr"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Overwriting ./model.py\n"
+     ]
+    }
+   ],
+   "source": [
+    "%%writefile './model.py'\n",
+    "\n",
+    "# HugeCTR\n",
+    "import hugectr\n",
+    "from mpi4py import MPI\n",
+    "\n",
+    "solver = hugectr.solver_parser_helper(\n",
+    "                                    vvgpu = [[0]],\n",
+    "                                    max_iter = 10000,\n",
+    "                                    max_eval_batches = 100,\n",
+    "                                    batchsize_eval = 2720,\n",
+    "                                    batchsize = 2720,\n",
+    "                                    display = 1000,\n",
+    "                                    eval_interval = 3200,\n",
+    "                                    snapshot = 3200,\n",
+    "                                    i64_input_key = True,\n",
+    "                                    use_mixed_precision = False,\n",
+    "                                    repeat_dataset = True)\n",
+    "optimizer = hugectr.optimizer.CreateOptimizer(optimizer_type = hugectr.Optimizer_t.SGD,\n",
+    "                                    use_mixed_precision = False)\n",
+    "model = hugectr.Model(solver, optimizer)\n",
+    "model.add(hugectr.Input(data_reader_type = hugectr.DataReaderType_t.Parquet,\n",
+    "                            source = \"/raid/data/criteo/test_dask/output/train/_file_list.txt\",\n",
+    "                            eval_source = \"/raid/data/criteo/test_dask/output/valid/_file_list.txt\",\n",
+    "                            check_type = hugectr.Check_t.Non,\n",
+    "                            label_dim = 1, label_name = \"label\",\n",
+    "                            dense_dim = 13, dense_name = \"dense\",\n",
+    "                            slot_size_array = [10000000, 10000000, 3014529, 400781, 11, 2209, 11869, 148, 4, 977, 15, 38713, 10000000, 10000000, 10000000, 584616, 12883, 109, 37, 17177, 7425, 20266, 4, 7085, 1535, 64],\n",
+    "                            data_reader_sparse_param_array = \n",
+    "                            [hugectr.DataReaderSparseParam(hugectr.DataReaderSparse_t.Localized, 26, 1, 26)],\n",
+    "                            sparse_names = [\"data1\"]))\n",
+    "model.add(hugectr.SparseEmbedding(embedding_type = hugectr.Embedding_t.LocalizedSlotSparseEmbeddingHash, \n",
+    "                            max_vocabulary_size_per_gpu = 15500000,\n",
+    "                            embedding_vec_size = 128,\n",
+    "                            combiner = 0,\n",
+    "                            sparse_embedding_name = \"sparse_embedding1\",\n",
+    "                            bottom_name = \"data1\"))\n",
+    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.InnerProduct,\n",
+    "                            bottom_names = [\"dense\"],\n",
+    "                            top_names = [\"fc1\"],\n",
+    "                            num_output=512))\n",
+    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.ReLU,\n",
+    "                            bottom_names = [\"fc1\"],\n",
+    "                            top_names = [\"relu1\"]))\n",
+    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.InnerProduct,\n",
+    "                            bottom_names = [\"relu1\"],\n",
+    "                            top_names = [\"fc2\"],\n",
+    "                            num_output=256))\n",
+    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.ReLU,\n",
+    "                            bottom_names = [\"fc2\"],\n",
+    "                            top_names = [\"relu2\"]))\n",
+    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.InnerProduct,\n",
+    "                            bottom_names = [\"relu2\"],\n",
+    "                            top_names = [\"fc3\"],\n",
+    "                            num_output=128))\n",
+    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.ReLU,\n",
+    "                            bottom_names = [\"fc3\"],\n",
+    "                            top_names = [\"relu3\"]))\n",
+    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.Interaction,\n",
+    "                            bottom_names = [\"relu3\", \"sparse_embedding1\"],\n",
+    "                            top_names = [\"interaction1\"]))\n",
+    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.InnerProduct,\n",
+    "                            bottom_names = [\"interaction1\"],\n",
+    "                            top_names = [\"fc4\"],\n",
+    "                            num_output=1024))\n",
+    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.ReLU,\n",
+    "                            bottom_names = [\"fc4\"],\n",
+    "                            top_names = [\"relu4\"]))\n",
+    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.InnerProduct,\n",
+    "                            bottom_names = [\"relu4\"],\n",
+    "                            top_names = [\"fc5\"],\n",
+    "                            num_output=1024))\n",
+    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.ReLU,\n",
+    "                            bottom_names = [\"fc5\"],\n",
+    "                            top_names = [\"relu5\"]))\n",
+    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.InnerProduct,\n",
+    "                            bottom_names = [\"relu5\"],\n",
+    "                            top_names = [\"fc6\"],\n",
+    "                            num_output=512))\n",
+    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.ReLU,\n",
+    "                            bottom_names = [\"fc6\"],\n",
+    "                            top_names = [\"relu6\"]))\n",
+    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.InnerProduct,\n",
+    "                            bottom_names = [\"relu6\"],\n",
+    "                            top_names = [\"fc7\"],\n",
+    "                            num_output=256))\n",
+    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.ReLU,\n",
+    "                            bottom_names = [\"fc7\"],\n",
+    "                            top_names = [\"relu7\"]))\n",
+    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.InnerProduct,\n",
+    "                            bottom_names = [\"relu7\"],\n",
+    "                            top_names = [\"fc8\"],\n",
+    "                            num_output=1))\n",
+    "model.add(hugectr.DenseLayer(layer_type = hugectr.Layer_t.BinaryCrossEntropyLoss,\n",
+    "                            bottom_names = [\"fc8\", \"label\"],\n",
+    "                            top_names = [\"loss\"]))\n",
+    "model.compile()\n",
+    "model.summary()\n",
+    "model.fit()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "===================================Model Init====================================\n",
+      "[29d14h45m11s][HUGECTR][INFO]: Global seed is 2284029516\n",
+      "[29d14h45m13s][HUGECTR][INFO]: Peer-to-peer access cannot be fully enabled.\n",
+      "Device 0: Tesla V100-SXM2-32GB\n",
+      "[29d14h45m13s][HUGECTR][INFO]: num of DataReader workers: 1\n",
+      "[29d14h45m13s][HUGECTR][INFO]: num_internal_buffers 1\n",
+      "[29d14h45m13s][HUGECTR][INFO]: num_internal_buffers 1\n",
+      "[29d14h45m13s][HUGECTR][INFO]: Vocabulary size: 54120457\n",
+      "[29d14h45m13s][HUGECTR][INFO]: max_vocabulary_size_per_gpu_=15500000\n",
+      "[29d14h45m13s][HUGECTR][INFO]: All2All Warmup Start\n",
+      "[29d14h45m13s][HUGECTR][INFO]: All2All Warmup End\n",
+      "[29d14h45m27s][HUGECTR][INFO]: gpu0 start to init embedding\n",
+      "[29d14h45m27s][HUGECTR][INFO]: gpu0 init embedding done\n",
+      "==================================Model Summary==================================\n",
+      "Label Name                    Dense Name                    Sparse Name                   \n",
+      "label                         dense                         data1                         \n",
+      "--------------------------------------------------------------------------------\n",
+      "Layer Type                    Input Name                    Output Name                   \n",
+      "--------------------------------------------------------------------------------\n",
+      "LocalizedHash                 data1                         sparse_embedding1             \n",
+      "InnerProduct                  dense                         fc1                           \n",
+      "ReLU                          fc1                           relu1                         \n",
+      "InnerProduct                  relu1                         fc2                           \n",
+      "ReLU                          fc2                           relu2                         \n",
+      "InnerProduct                  relu2                         fc3                           \n",
+      "ReLU                          fc3                           relu3                         \n",
+      "Interaction                   relu3, sparse_embedding1      interaction1                  \n",
+      "InnerProduct                  interaction1                  fc4                           \n",
+      "ReLU                          fc4                           relu4                         \n",
+      "InnerProduct                  relu4                         fc5                           \n",
+      "ReLU                          fc5                           relu5                         \n",
+      "InnerProduct                  relu5                         fc6                           \n",
+      "ReLU                          fc6                           relu6                         \n",
+      "InnerProduct                  relu6                         fc7                           \n",
+      "ReLU                          fc7                           relu7                         \n",
+      "InnerProduct                  relu7                         fc8                           \n",
+      "BinaryCrossEntropyLoss        fc8, label                    loss                          \n",
+      "--------------------------------------------------------------------------------\n",
+      "=====================================Model Fit====================================\n",
+      "[29d14h45m27s][HUGECTR][INFO]: Use non-epoch mode with number of iterations: 10000\n",
+      "[29d14h45m27s][HUGECTR][INFO]: Training batchsize: 2720, evaluation batchsize: 2720\n",
+      "[29d14h45m27s][HUGECTR][INFO]: Evaluation interval: 3200, snapshot interval: 3200\n",
+      "[29d14h45m33s][HUGECTR][INFO]: Iter: 1000 Time(1000 iters): 5.641792s Loss: 0.164940 lr:0.001000\n",
+      "[29d14h45m39s][HUGECTR][INFO]: Iter: 2000 Time(1000 iters): 5.656664s Loss: 0.135722 lr:0.001000\n",
+      "[29d14h45m44s][HUGECTR][INFO]: Iter: 3000 Time(1000 iters): 5.651847s Loss: 0.160054 lr:0.001000\n",
+      "[29d14h45m46s][HUGECTR][INFO]: Evaluation, AUC: 0.552071\n",
+      "[29d14h45m46s][HUGECTR][INFO]: Eval Time for 100 iters: 0.285196s\n",
+      "[29d14h45m47s][HUGECTR][INFO]: Rank0: Dump hash table from GPU0\n",
+      "[29d14h45m48s][HUGECTR][INFO]: Rank0: Write hash table <key,slot_id,value> pairs to file\n",
+      "[29d14h45m49s][HUGECTR][INFO]: Done\n",
+      "[29d14h45m56s][HUGECTR][INFO]: Iter: 4000 Time(1000 iters): 11.744939s Loss: 0.143607 lr:0.001000\n",
+      "[29d14h46m20s][HUGECTR][INFO]: Iter: 5000 Time(1000 iters): 5.678187s Loss: 0.129964 lr:0.001000\n",
+      "[29d14h46m70s][HUGECTR][INFO]: Iter: 6000 Time(1000 iters): 5.678660s Loss: 0.134518 lr:0.001000\n",
+      "[29d14h46m10s][HUGECTR][INFO]: Evaluation, AUC: 0.612846\n",
+      "[29d14h46m10s][HUGECTR][INFO]: Eval Time for 100 iters: 0.218399s\n",
+      "[29d14h46m12s][HUGECTR][INFO]: Rank0: Dump hash table from GPU0\n",
+      "[29d14h46m14s][HUGECTR][INFO]: Rank0: Write hash table <key,slot_id,value> pairs to file\n",
+      "[29d14h46m15s][HUGECTR][INFO]: Done\n",
+      "[29d14h46m23s][HUGECTR][INFO]: Iter: 7000 Time(1000 iters): 15.466943s Loss: 0.131755 lr:0.001000\n",
+      "[29d14h46m29s][HUGECTR][INFO]: Iter: 8000 Time(1000 iters): 5.657035s Loss: 0.136876 lr:0.001000\n",
+      "[29d14h46m34s][HUGECTR][INFO]: Iter: 9000 Time(1000 iters): 5.659428s Loss: 0.141471 lr:0.001000\n",
+      "[29d14h46m38s][HUGECTR][INFO]: Evaluation, AUC: 0.635054\n",
+      "[29d14h46m38s][HUGECTR][INFO]: Eval Time for 100 iters: 0.192339s\n",
+      "[29d14h46m40s][HUGECTR][INFO]: Rank0: Dump hash table from GPU0\n",
+      "[29d14h46m43s][HUGECTR][INFO]: Rank0: Write hash table <key,slot_id,value> pairs to file\n",
+      "[29d14h46m45s][HUGECTR][INFO]: Done\n"
+     ]
+    }
+   ],
+   "source": [
+    "!python model.py"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We trained the model and created snapshots."
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "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.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/scaling-criteo/03d-Training-with-FastAI.ipynb b/examples/scaling-criteo/03d-Training-with-FastAI.ipynb
new file mode 100644
index 00000000000..fe68d307656
--- /dev/null
+++ b/examples/scaling-criteo/03d-Training-with-FastAI.ipynb
@@ -0,0 +1,366 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Copyright 2021 NVIDIA Corporation. All Rights Reserved.\n",
+    "#\n",
+    "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
+    "# you may not use this file except in compliance with the License.\n",
+    "# You may obtain a copy of the License at\n",
+    "#\n",
+    "#     http://www.apache.org/licenses/LICENSE-2.0\n",
+    "#\n",
+    "# Unless required by applicable law or agreed to in writing, software\n",
+    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
+    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
+    "# See the License for the specific language governing permissions and\n",
+    "# limitations under the License.\n",
+    "# =============================================================================="
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "<img src=\"http://developer.download.nvidia.com/compute/machine-learning/frameworks/nvidia_logo.png\" style=\"width: 90px; float: right;\">\n",
+    "\n",
+    "# Scaling Criteo: Training with FastAI\n",
+    "\n",
+    "## Overview\n",
+    "\n",
+    "We observed that training pipelines can be slow as the dataloader is a bottleneck. NVTabular provides a highly optimized dataloader to accelerate training pipelines. We can use the PyTorch dataloader for FastAI models.\n",
+    "\n",
+    "We have already discussed the NVTabular dataloaders in more detail in our [Getting Started with Movielens notebooks](https://github.com/NVIDIA/NVTabular/tree/main/examples/getting-started-movielens).<br><br>\n",
+    "\n",
+    "We will use the same techniques to train a deep learning model for the [Criteo 1TB Click Logs dataset](https://ailab.criteo.com/download-criteo-1tb-click-logs-dataset/).\n",
+    "\n",
+    "### Learning objectives\n",
+    "\n",
+    "In this notebook, we learn how to to scale ETLs with NVTabular\n",
+    "- Use **NVTabular dataloader** with FastAI Tabular model"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## NVTabular dataloader for PyTorch / FastAI\n",
+    "\n",
+    "When training pipelines with PyTorch, the dataloader cannot prepare sequential batches fast enough, so the GPU is not fully utilized. To combat this issue, we’ve developed a highly customized tabular dataloader, `TorchAsyncItr`, to accelerate existing pipelines in PyTorch. The NVTabular dataloader is capable of:\n",
+    "- removing bottlenecks from dataloading by processing large chunks of data at a time instead of item by item\n",
+    "- processing datasets that don’t fit within the GPU or CPU memory by streaming from the disk\n",
+    "- reading data directly into the GPU memory and removing CPU-GPU communication\n",
+    "- preparing batch asynchronously into the GPU to avoid CPU-GPU communication\n",
+    "- supporting commonly used formats such as parquet\n",
+    "- integrating easily into existing PyTorch training pipelines by using a similar API as the native PyTorch dataloader"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Getting Started"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "from time import time\n",
+    "import re\n",
+    "import glob\n",
+    "import warnings\n",
+    "\n",
+    "# tools for data preproc/loading\n",
+    "import torch\n",
+    "import rmm\n",
+    "import nvtabular as nvt\n",
+    "from nvtabular.ops import get_embedding_sizes\n",
+    "from nvtabular.loader.torch import TorchAsyncItr, DLDataLoader\n",
+    "from nvtabular.utils import device_mem_size, get_rmm_size\n",
+    "\n",
+    "# tools for training\n",
+    "from fastai.basics import Learner\n",
+    "from fastai.tabular.model import TabularModel\n",
+    "from fastai.tabular.data import TabularDataLoaders\n",
+    "from fastai.metrics import RocAucBinary, APScoreBinary\n",
+    "from fastai.callback.progress import ProgressCallback"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Dataset and Dataset Schema\n",
+    "Once our data is ready, we'll define some high level parameters to describe where our data is and what it \"looks like\" at a high level."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Data Loading\n",
+    "We'll start by using the parquet files we just created to feed an NVTabular `TorchAsyncItr`, which will loop through the files in chunks. First, we'll reinitialize our memory pool from earlier to free up some memory so that we can share it with PyTorch."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "CONTINUOUS_COLUMNS = ['I' + str(x) for x in range(1,14)]\n",
+    "CATEGORICAL_COLUMNS =  ['C' + str(x) for x in range(1,27)]\n",
+    "LABEL_COLUMNS = ['label']\n",
+    "BASE_DIR = os.environ.get('BASE_DIR', '/raid/data/criteo')\n",
+    "BATCH_SIZE = int(os.environ.get('BATCH_SIZE', 400000))\n",
+    "PARTS_PER_CHUNK = int(os.environ.get('PARTS_PER_CHUNK', 2))\n",
+    "input_path = os.path.join(BASE_DIR, \"test_dask/output\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "train_paths = glob.glob(os.path.join(input_path, 'train', \"*.parquet\"))\n",
+    "valid_paths = glob.glob(os.path.join(input_path, 'valid', \"*.parquet\"))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "train_data = nvt.Dataset(train_paths, engine=\"parquet\", part_mem_fraction=0.04/PARTS_PER_CHUNK)\n",
+    "valid_data = nvt.Dataset(valid_paths, engine=\"parquet\", part_mem_fraction=0.04/PARTS_PER_CHUNK)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "train_data_itrs = TorchAsyncItr(\n",
+    "    train_data,\n",
+    "    batch_size=BATCH_SIZE,\n",
+    "    cats=CATEGORICAL_COLUMNS,\n",
+    "    conts=CONTINUOUS_COLUMNS,\n",
+    "    labels=LABEL_COLUMNS,\n",
+    "    parts_per_chunk=PARTS_PER_CHUNK\n",
+    ")\n",
+    "valid_data_itrs = TorchAsyncItr(\n",
+    "    valid_data,\n",
+    "    batch_size=BATCH_SIZE,\n",
+    "    cats=CATEGORICAL_COLUMNS,\n",
+    "    conts=CONTINUOUS_COLUMNS,\n",
+    "    labels=LABEL_COLUMNS,\n",
+    "    parts_per_chunk=PARTS_PER_CHUNK\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def gen_col(batch):\n",
+    "    return (batch[0], batch[1], batch[2].long())"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "train_dataloader = DLDataLoader(train_data_itrs, collate_fn=gen_col, batch_size=None, pin_memory=False, num_workers=0)\n",
+    "valid_dataloader = DLDataLoader(valid_data_itrs, collate_fn=gen_col, batch_size=None, pin_memory=False, num_workers=0)\n",
+    "databunch = TabularDataLoaders(train_dataloader, valid_dataloader)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Now we have data ready to be fed to our model online!"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Training\n",
+    "One extra handy functionality of NVTabular is the ability to use the stats collected by the `Categorify` op to define embedding dictionary sizes (i.e. the number of rows of your embedding table). It even includes a heuristic for computing a good embedding size (i.e. the number of columns of your embedding table) based off of the number of categories."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "In the previous notebook, we used NVTabular for ETL and stored the workflow to disk. We can load the NVTabular workflow to extract important metadata for our training pipeline."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "workflow = nvt.Workflow.load(os.path.join(input_path, \"workflow\"))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "[[10000000, 16],\n",
+       " [10000000, 16],\n",
+       " [707291, 16],\n",
+       " [218510, 16],\n",
+       " [11, 16],\n",
+       " [2209, 16],\n",
+       " [9798, 16],\n",
+       " [72, 16],\n",
+       " [4, 16],\n",
+       " [954, 16],\n",
+       " [15, 16],\n",
+       " [29612, 16],\n",
+       " [10000000, 16],\n",
+       " [4553157, 16],\n",
+       " [10000000, 16],\n",
+       " [291641, 16],\n",
+       " [10904, 16],\n",
+       " [91, 16],\n",
+       " [35, 16],\n",
+       " [15050, 16],\n",
+       " [7190, 16],\n",
+       " [19547, 16],\n",
+       " [4, 16],\n",
+       " [6492, 16],\n",
+       " [1317, 16],\n",
+       " [63, 16]]"
+      ]
+     },
+     "execution_count": 10,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "embeddings = list(get_embedding_sizes(workflow).values())\n",
+    "# We limit the output dimension to 16\n",
+    "embeddings = [[emb[0], min(16, emb[1])] for emb in embeddings]\n",
+    "embeddings"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "model = TabularModel(emb_szs=embeddings, n_cont=len(CONTINUOUS_COLUMNS), out_sz=2, layers=[512, 256]).cuda()\n",
+    "learn =  Learner(databunch, model, loss_func = torch.nn.CrossEntropyLoss(), metrics=[RocAucBinary(), APScoreBinary()])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<table border=\"1\" class=\"dataframe\">\n",
+       "  <thead>\n",
+       "    <tr style=\"text-align: left;\">\n",
+       "      <th>epoch</th>\n",
+       "      <th>train_loss</th>\n",
+       "      <th>valid_loss</th>\n",
+       "      <th>roc_auc_score</th>\n",
+       "      <th>average_precision_score</th>\n",
+       "      <th>time</th>\n",
+       "    </tr>\n",
+       "  </thead>\n",
+       "  <tbody>\n",
+       "    <tr>\n",
+       "      <td>0</td>\n",
+       "      <td>0.126882</td>\n",
+       "      <td>0.124364</td>\n",
+       "      <td>0.776688</td>\n",
+       "      <td>0.136667</td>\n",
+       "      <td>10:00</td>\n",
+       "    </tr>\n",
+       "  </tbody>\n",
+       "</table>"
+      ],
+      "text/plain": [
+       "<IPython.core.display.HTML object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "run_time: 600.2940483093262 - rows: 395405518 - epochs: 1 - dl_thru: 658686.3873023959\n"
+     ]
+    }
+   ],
+   "source": [
+    "learning_rate = 1.32e-2\n",
+    "epochs = 1\n",
+    "start = time()\n",
+    "learn.fit(epochs, learning_rate)\n",
+    "t_final = time() - start\n",
+    "total_rows = train_data_itrs.num_rows_processed + valid_data_itrs.num_rows_processed\n",
+    "print(f\"run_time: {t_final} - rows: {total_rows} - epochs: {epochs} - dl_thru: {total_rows / t_final}\")"
+   ]
+  }
+ ],
+ "metadata": {
+  "file_extension": ".py",
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "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.8"
+  },
+  "mimetype": "text/x-python",
+  "name": "python",
+  "npconvert_exporter": "python",
+  "pygments_lexer": "ipython3",
+  "version": 3
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/scaling-criteo/04a-Triton-Inference-with-TF.ipynb b/examples/scaling-criteo/04a-Triton-Inference-with-TF.ipynb
new file mode 100644
index 00000000000..966922c59cf
--- /dev/null
+++ b/examples/scaling-criteo/04a-Triton-Inference-with-TF.ipynb
@@ -0,0 +1,664 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Copyright 2021 NVIDIA Corporation. All Rights Reserved.\n",
+    "#\n",
+    "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
+    "# you may not use this file except in compliance with the License.\n",
+    "# You may obtain a copy of the License at\n",
+    "#\n",
+    "#     http://www.apache.org/licenses/LICENSE-2.0\n",
+    "#\n",
+    "# Unless required by applicable law or agreed to in writing, software\n",
+    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
+    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
+    "# See the License for the specific language governing permissions and\n",
+    "# limitations under the License.\n",
+    "# =============================================================================="
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Scaling Criteo: Triton Inference with TensorFlow\n",
+    "\n",
+    "## Overview\n",
+    "\n",
+    "The last step is to deploy the ETL workflow and saved model to production. In the production setting, we want to transform the input data as during training (ETL). We need to apply the same mean/std for continuous features and use the same categorical mapping to convert the categories to continuous integer before we use the deep learning model for a prediction. Therefore, we deploy the NVTabular workflow with the TensorFlow model as an ensemble model to Triton Inference. The ensemble model garantuees that the same transformation are applied to the raw inputs.\n",
+    "\n",
+    "<img src='./imgs/triton-tf.png' width=\"25%\">\n",
+    "\n",
+    "### Learning objectives\n",
+    "\n",
+    "In this notebook, we learn how to deploy our models to production\n",
+    "- Use **NVTabular** to generate config and model files for Triton Inference Server\n",
+    "- Deploy an ensemble of NVTabular workflow and TensorFlow model\n",
+    "- Send example request to Triton Inference Server"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Getting Started\n",
+    "\n",
+    "First, we need to generate the Triton Inference Server configurations and save the models in the correct format. In the previous notebooks [02-ETL-with-NVTabular](./02-ETL-with-NVTabular.ipynb) and [03a-Training-with-TF](./03a-Training-with-TF.ipynb) we saved the NVTabular workflow and TensorFlow model to disk. We will load them."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Saving Ensemble Model for Triton Inference Server"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "\n",
+    "import tensorflow as tf\n",
+    "import nvtabular as nvt"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "BASE_DIR = os.environ.get('BASE_DIR', '/raid/data/criteo')\n",
+    "input_path = os.path.join(BASE_DIR, \"test_dask/output\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "workflow = nvt.Workflow.load(os.path.join(input_path, \"workflow\"))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "model = tf.keras.models.load_model(os.path.join(input_path, \"model.savedmodel\"))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "TensorFlow expect the Integer as `int32` datatype. Therefore, we need to define the NVTabular output datatypes to `int32` for categorical features."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "for key in workflow.output_dtypes.keys():\n",
+    "    if key.startswith('C'):\n",
+    "        workflow.output_dtypes[key] = \"int32\""
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "NVTabular provides an easy function to deploy the ensemble model for Triton Inference Server."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from nvtabular.inference.triton import export_tensorflow_ensemble"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "INFO:tensorflow:Assets written to: /models/criteo_tf/1/model.savedmodel/assets\n"
+     ]
+    }
+   ],
+   "source": [
+    "export_tensorflow_ensemble(model, workflow, \"criteo\", \"/models\", [\"label\"])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We can take a look on the generated files."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "\u001b[01;34m/models\u001b[00m\r\n",
+      "├── \u001b[01;34mcriteo\u001b[00m\r\n",
+      "│   ├── \u001b[01;34m1\u001b[00m\r\n",
+      "│   └── config.pbtxt\r\n",
+      "├── \u001b[01;34mcriteo_nvt\u001b[00m\r\n",
+      "│   ├── \u001b[01;34m1\u001b[00m\r\n",
+      "│   │   ├── model.py\r\n",
+      "│   │   └── \u001b[01;34mworkflow\u001b[00m\r\n",
+      "│   │       ├── \u001b[01;34mcategories\u001b[00m\r\n",
+      "│   │       │   ├── unique.C1.parquet\r\n",
+      "│   │       │   ├── unique.C10.parquet\r\n",
+      "│   │       │   ├── unique.C11.parquet\r\n",
+      "│   │       │   ├── unique.C12.parquet\r\n",
+      "│   │       │   ├── unique.C13.parquet\r\n",
+      "│   │       │   ├── unique.C14.parquet\r\n",
+      "│   │       │   ├── unique.C15.parquet\r\n",
+      "│   │       │   ├── unique.C16.parquet\r\n",
+      "│   │       │   ├── unique.C17.parquet\r\n",
+      "│   │       │   ├── unique.C18.parquet\r\n",
+      "│   │       │   ├── unique.C19.parquet\r\n",
+      "│   │       │   ├── unique.C2.parquet\r\n",
+      "│   │       │   ├── unique.C20.parquet\r\n",
+      "│   │       │   ├── unique.C21.parquet\r\n",
+      "│   │       │   ├── unique.C22.parquet\r\n",
+      "│   │       │   ├── unique.C23.parquet\r\n",
+      "│   │       │   ├── unique.C24.parquet\r\n",
+      "│   │       │   ├── unique.C25.parquet\r\n",
+      "│   │       │   ├── unique.C26.parquet\r\n",
+      "│   │       │   ├── unique.C3.parquet\r\n",
+      "│   │       │   ├── unique.C4.parquet\r\n",
+      "│   │       │   ├── unique.C5.parquet\r\n",
+      "│   │       │   ├── unique.C6.parquet\r\n",
+      "│   │       │   ├── unique.C7.parquet\r\n",
+      "│   │       │   ├── unique.C8.parquet\r\n",
+      "│   │       │   └── unique.C9.parquet\r\n",
+      "│   │       ├── metadata.json\r\n",
+      "│   │       └── workflow.pkl\r\n",
+      "│   └── config.pbtxt\r\n",
+      "└── \u001b[01;34mcriteo_tf\u001b[00m\r\n",
+      "    ├── \u001b[01;34m1\u001b[00m\r\n",
+      "    │   └── \u001b[01;34mmodel.savedmodel\u001b[00m\r\n",
+      "    │       ├── \u001b[01;34massets\u001b[00m\r\n",
+      "    │       ├── saved_model.pb\r\n",
+      "    │       └── \u001b[01;34mvariables\u001b[00m\r\n",
+      "    │           ├── variables.data-00000-of-00001\r\n",
+      "    │           └── variables.index\r\n",
+      "    └── config.pbtxt\r\n",
+      "\r\n",
+      "11 directories, 35 files\r\n"
+     ]
+    }
+   ],
+   "source": [
+    "!tree /models"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Loading Ensemble Model with Triton Inference Server\n",
+    "\n",
+    "We have only saved the models for Triton Inference Server. We started Triton Inference Server in explicit mode, meaning that we need to send a request that Triton will load the ensemble model."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "First, we restart this notebook to free the GPU memory."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'status': 'ok', 'restart': True}"
+      ]
+     },
+     "execution_count": 12,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "import IPython\n",
+    "app = IPython.Application.instance()\n",
+    "app.kernel.do_shutdown(True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We define the BASE_DIR again."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "\n",
+    "BASE_DIR = os.environ.get('BASE_DIR', '/raid/data/criteo')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We connect to the Triton Inference Server."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "client created.\n"
+     ]
+    }
+   ],
+   "source": [
+    "import tritonhttpclient\n",
+    "try:\n",
+    "    triton_client = tritonhttpclient.InferenceServerClient(url=\"triton:8000\", verbose=True)\n",
+    "    print(\"client created.\")\n",
+    "except Exception as e:\n",
+    "    print(\"channel creation failed: \" + str(e))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We deactivate warnings."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import warnings\n",
+    "warnings.filterwarnings('ignore')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We check if the server is alive."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "GET /v2/health/live, headers None\n",
+      "<HTTPSocketPoolResponse status=200 headers={'content-length': '0', 'content-type': 'text/plain'}>\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "True"
+      ]
+     },
+     "execution_count": 8,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "triton_client.is_server_live()  "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We check the available models in the repositories:\n",
+    "- criteo: Ensemble \n",
+    "- criteo_nvt: NVTabular \n",
+    "- criteo_tf: TensorFlow model"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "POST /v2/repository/index, headers None\n",
+      "\n",
+      "<HTTPSocketPoolResponse status=200 headers={'content-type': 'application/json', 'content-length': '62'}>\n",
+      "bytearray(b'[{\"name\":\"criteo\"},{\"name\":\"criteo_nvt\"},{\"name\":\"criteo_tf\"}]')\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "[{'name': 'criteo'}, {'name': 'criteo_nvt'}, {'name': 'criteo_tf'}]"
+      ]
+     },
+     "execution_count": 9,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "triton_client.get_model_repository_index()  "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We load the ensembled model."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "POST /v2/repository/models/criteo/load, headers None\n",
+      "\n",
+      "<HTTPSocketPoolResponse status=200 headers={'content-type': 'application/json', 'content-length': '0'}>\n",
+      "Loaded model 'criteo'\n",
+      "CPU times: user 0 ns, sys: 3.33 ms, total: 3.33 ms\n",
+      "Wall time: 56.5 s\n"
+     ]
+    }
+   ],
+   "source": [
+    "%%time\n",
+    "\n",
+    "triton_client.load_model(model_name='criteo')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Example Request to Triton Inference Server\n",
+    "\n",
+    "Now, the models are loaded and we can create a sample request. We read an example **raw batch** for inference."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "     I1   I2    I3    I4    I5  I6  I7  I8  I9  I10  ...        C17  \\\n",
+      "0     5  110  <NA>    16  <NA>   1   0  14   7    1  ... -771205462   \n",
+      "1    32    3     5  <NA>     1   0   0  61   5    0  ... -771205462   \n",
+      "2  <NA>  233     1   146     1   0   0  99   7    0  ... -771205462   \n",
+      "\n",
+      "          C18         C19         C20         C21        C22        C23  \\\n",
+      "0 -1206449222 -1793932789 -1014091992   351689309  632402057 -675152885   \n",
+      "1 -1578429167 -1793932789   -20981661 -1556988767 -924717482  391309800   \n",
+      "2  1653545869 -1793932789 -1014091992   351689309  632402057 -675152885   \n",
+      "\n",
+      "          C24         C25         C26  \n",
+      "0  2091868316   809724924  -317696227  \n",
+      "1  1966410890 -1726799382 -1218975401  \n",
+      "2   883538181   -10139646  -317696227  \n",
+      "\n",
+      "[3 rows x 39 columns]\n"
+     ]
+    }
+   ],
+   "source": [
+    "import cudf\n",
+    "\n",
+    "# read in the workflow (to get input/output schema to call triton with)\n",
+    "batch_path = os.path.join(BASE_DIR, \"converted/criteo\")\n",
+    "batch = cudf.read_parquet(os.path.join(batch_path, \"*.parquet\"), num_rows=3)\n",
+    "batch = batch[[x for x in batch.columns if x != 'label']]\n",
+    "print(batch)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We prepare the batch for inference by using correct column names and data types. We use the same datatypes as defined in our dataframe."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "I1     int32\n",
+       "I2     int32\n",
+       "I3     int32\n",
+       "I4     int32\n",
+       "I5     int32\n",
+       "I6     int32\n",
+       "I7     int32\n",
+       "I8     int32\n",
+       "I9     int32\n",
+       "I10    int32\n",
+       "I11    int32\n",
+       "I12    int32\n",
+       "I13    int32\n",
+       "C1     int32\n",
+       "C2     int32\n",
+       "C3     int32\n",
+       "C4     int32\n",
+       "C5     int32\n",
+       "C6     int32\n",
+       "C7     int32\n",
+       "C8     int32\n",
+       "C9     int32\n",
+       "C10    int32\n",
+       "C11    int32\n",
+       "C12    int32\n",
+       "C13    int32\n",
+       "C14    int32\n",
+       "C15    int32\n",
+       "C16    int32\n",
+       "C17    int32\n",
+       "C18    int32\n",
+       "C19    int32\n",
+       "C20    int32\n",
+       "C21    int32\n",
+       "C22    int32\n",
+       "C23    int32\n",
+       "C24    int32\n",
+       "C25    int32\n",
+       "C26    int32\n",
+       "dtype: object"
+      ]
+     },
+     "execution_count": 12,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "batch.dtypes "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import tritonclient.http as httpclient\n",
+    "from tritonclient.utils import *\n",
+    "\n",
+    "inputs = []\n",
+    "\n",
+    "col_names = list(batch.columns)\n",
+    "col_dtypes = [np.int32]*len(col_names)\n",
+    "\n",
+    "for i, col in enumerate(batch.columns):\n",
+    "    d = batch[col].values_host.astype(col_dtypes[i])\n",
+    "    d = d.reshape(len(d),1)\n",
+    "    inputs.append(httpclient.InferInput(col_names[i], d.shape, np_to_triton_dtype(col_dtypes[i])))\n",
+    "    inputs[i].set_data_from_numpy(d)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We send the request to the triton server and collect the last output."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "POST /v2/models/criteo/infer, headers {'Inference-Header-Content-Length': 3382}\n",
+      "b'{\"id\":\"1\",\"inputs\":[{\"name\":\"I1\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I2\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I3\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I4\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I5\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I6\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I7\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I8\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I9\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I10\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I11\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I12\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I13\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C1\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C2\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C3\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C4\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C5\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C6\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C7\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C8\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C9\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C10\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C11\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C12\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C13\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C14\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C15\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C16\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C17\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C18\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C19\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C20\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C21\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C22\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C23\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C24\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C25\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C26\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}}],\"outputs\":[{\"name\":\"output\",\"parameters\":{\"binary_data\":true}}]}\\x05\\x00\\x00\\x00 \\x00\\x00\\x00\\x00\\x00\\x00\\x00n\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xe9\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x92\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0e\\x00\\x00\\x00=\\x00\\x00\\x00c\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x002\\x01\\x00\\x00U\\x0c\\x00\\x00\\x1d\\x0c\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x01\\x00\\x00\\x00y\\rwb\\x8d\\xfd\\xf3\\xe5y\\rwbX]\\x1f\\xe2\\xa6\\xff\\xaa\\xa0\\x03B\\x98\\xad/D\\xea\\xaf\\xd5\\x15\\xaao\\r\\xc6\\xbeb\\xcf\\x7f\\\\\\x94!4\\x8a\\xda\\xeeIl8H\\'\\xb08#\\x9f\\xd6<M\\x06U\\xe7\\xcbm\\xcdo\\xcbm\\xcdo\\xcbm\\xcdo!\\xaa\\x805\\x81\\xed\\x16\\xabb\\xeb\\xf5\\xb5\\x03\\x89\\x80()lBC\\x8b\\xcc\\xf2\\xd1\\xa6\\xdf\\xdeFT\\xe1\\xf5\\x1d\\x1f\\x82N.\\xc1}\\x02.\\xa9\\xc0\\xe9}\\xc1}\\x02.1B|\\x0cd\\xdcRf1B|\\x0c\\x1f\\x1d\\x98\\x95\\'N\\xeb\\x99\\x84aq\\x12\\xb7\\xff\\xc5\\x00\\xb7\\xff\\xc5\\x00\\xb7\\xff\\xc5\\x007\\xe5N\\xbe7\\xe5N\\xbe7\\xe5N\\xbe\\xcct\\x0b\\x8a\\x99\\xfe\\xbb\\xf3\\x0b\\r\\x0f\\xf7\\xfa>\\xdcL\\xfa>\\xdcL\\xfa>\\xdcL\\xaaV\\x08\\xd2\\xaaV\\x08\\xd2\\xaaV\\x08\\xd2\\xba\\x0b\\x17\\xb8\\x11\\x15\\xeb\\xa1\\x8d\\x1b\\x8fb\\x0b\\xc2\\x12\\x95\\x0b\\xc2\\x12\\x95\\x0b\\xc2\\x12\\x95(/\\x8e\\xc3c\\xd8\\xbf\\xfe(/\\x8e\\xc3]Z\\xf6\\x14\\xa1<2\\xa3]Z\\xf6\\x14\\x89\\xb0\\xb1%V\\xee\\xe1\\xc8\\x89\\xb0\\xb1%\\x0b\\xfc\\xc1\\xd7\\xe8\\xe9R\\x17\\x0b\\xfc\\xc1\\xd7\\x9c`\\xaf|\\x8a\\x0c5u\\x05\\xb9\\xa94\\xfckC0\\xea!\\x13\\x99\\x02He\\xff\\x1dW\\x10\\xedW\\xe9W\\xb7\\x1dW\\x10\\xed'\n",
+      "<HTTPSocketPoolResponse status=200 headers={'content-type': 'application/json', 'inference-header-content-length': '226', 'content-length': '238'}>\n",
+      "bytearray(b'{\"id\":\"1\",\"model_name\":\"criteo\",\"model_version\":\"1\",\"parameters\":{\"sequence_id\":0,\"sequence_start\":false,\"sequence_end\":false},\"outputs\":[{\"name\":\"output\",\"datatype\":\"FP32\",\"shape\":[3,1],\"parameters\":{\"binary_data_size\":12}}]}')\n",
+      "predicted softmax result:\n",
+      " [[0.02414342]\n",
+      " [0.0328052 ]\n",
+      " [0.02708623]]\n"
+     ]
+    }
+   ],
+   "source": [
+    "# placeholder variables for the output\n",
+    "outputs = [httpclient.InferRequestedOutput(\"output\")]\n",
+    "\n",
+    "# build a client to connect to our server. \n",
+    "# This InferenceServerClient object is what we'll be using to talk to Triton.\n",
+    "# make the request with tritonclient.http.InferInput object\n",
+    "response = triton_client.infer(\"criteo\", inputs, request_id=\"1\",outputs=outputs)\n",
+    "\n",
+    "print(\"predicted softmax result:\\n\", response.as_numpy('output'))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Let's unload the model. We need to unload each model."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 16,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "POST /v2/repository/models/criteo/unload, headers None\n",
+      "\n",
+      "<HTTPSocketPoolResponse status=200 headers={'content-type': 'application/json', 'content-length': '0'}>\n",
+      "Loaded model 'criteo'\n",
+      "POST /v2/repository/models/criteo_nvt/unload, headers None\n",
+      "\n",
+      "<HTTPSocketPoolResponse status=200 headers={'content-type': 'application/json', 'content-length': '0'}>\n",
+      "Loaded model 'criteo_nvt'\n",
+      "POST /v2/repository/models/criteo_tf/unload, headers None\n",
+      "\n",
+      "<HTTPSocketPoolResponse status=200 headers={'content-type': 'application/json', 'content-length': '0'}>\n",
+      "Loaded model 'criteo_tf'\n"
+     ]
+    }
+   ],
+   "source": [
+    "triton_client.unload_model(model_name='criteo')\n",
+    "triton_client.unload_model(model_name='criteo_nvt')\n",
+    "triton_client.unload_model(model_name='criteo_tf')"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "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.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/scaling-criteo/04c-Triton-Inference-with-HugeCTR.ipynb b/examples/scaling-criteo/04c-Triton-Inference-with-HugeCTR.ipynb
new file mode 100644
index 00000000000..bc8551110fe
--- /dev/null
+++ b/examples/scaling-criteo/04c-Triton-Inference-with-HugeCTR.ipynb
@@ -0,0 +1,968 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Copyright 2021 NVIDIA Corporation. All Rights Reserved.\n",
+    "#\n",
+    "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
+    "# you may not use this file except in compliance with the License.\n",
+    "# You may obtain a copy of the License at\n",
+    "#\n",
+    "#     http://www.apache.org/licenses/LICENSE-2.0\n",
+    "#\n",
+    "# Unless required by applicable law or agreed to in writing, software\n",
+    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
+    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
+    "# See the License for the specific language governing permissions and\n",
+    "# limitations under the License.\n",
+    "# =============================================================================="
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Scaling Criteo: Triton Inference with HugeCTR\n",
+    "\n",
+    "## Overview\n",
+    "\n",
+    "The last step is to deploy the ETL workflow and saved model to production. In the production setting, we want to transform the input data as during training (ETL). We need to apply the same mean/std for continuous features and use the same categorical mapping to convert the categories to continuous integer before we use the deep learning model for a prediction. Therefore, we deploy the NVTabular workflow with the HugeCTR model as an ensemble model to Triton Inference. The ensemble model garantuees that the same transformation are applied to the raw inputs.\n",
+    "\n",
+    "<img src='./imgs/triton-hugectr.png' width=\"25%\">\n",
+    "\n",
+    "### Learning objectives\n",
+    "\n",
+    "In this notebook, we learn how to deploy our models to production\n",
+    "- Use **NVTabular** to generate config and model files for Triton Inference Server\n",
+    "- Deploy an ensemble of NVTabular workflow and HugeCTR model\n",
+    "- Send example request to Triton Inference Server"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Getting Started\n",
+    "\n",
+    "First, we need to generate the Triton Inference Server configurations and save the models in the correct format. In the previous notebooks [02-ETL-with-NVTabular](./02-ETL-with-NVTabular.ipynb) and [03c-Training-with-HugeCTR](./03a-Training-with-HugeCTR.ipynb) we saved the NVTabular workflow and HugeCTR model to disk. We will load them."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Saving Ensemble Model for Triton Inference Server"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "After training terminates, we can see that two `.model` files are generated. We need to move them inside a temporary folder, like `criteo_hugectr/1`. Let's create these folders."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "0"
+      ]
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "os.system('mkdir -rf ./criteo_hugectr/')\n",
+    "os.system('mkdir -p ./criteo_hugectr/1')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Now we move our saved `.model` files inside 1 folder. We use only the last snapshot after `9600` iterations."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "0"
+      ]
+     },
+     "execution_count": 4,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "os.system('mv *9600.model ./criteo_hugectr/1/') "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Now we have to create a JSON file for inference which has a similar configuration as our training file. We should remove the solver and optimizer clauses and add the inference clause in the JSON file. The paths of the stored dense model and sparse model(s) should be specified at dense_model_file and sparse_model_file within the inference clause. We need to make some modifications to data in the layers clause. Besides, we need to change the last layer from BinaryCrossEntropyLoss to Sigmoid. The rest of \"layers\" should be exactly the same as that in the training model.py file."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Overwriting ./criteo_hugectr/1/criteo.json\n"
+     ]
+    }
+   ],
+   "source": [
+    "%%writefile './criteo_hugectr/1/criteo.json'\n",
+    "\n",
+    "{\n",
+    "   \"inference\": {\n",
+    "    \"max_batchsize\": 64,\n",
+    "    \"hit_rate_threshold\": 0.6,\n",
+    "    \"dense_model_file\": \"/models/criteo/1/_dense_9600.model\",\n",
+    "    \"sparse_model_file\": \"/models/criteo/1/0_sparse_9600.model\",\n",
+    "    \"label\": 1,\n",
+    "    \"input_key_type\": \"I64\"\n",
+    "  },\n",
+    "  \"layers\": [\n",
+    "    {\n",
+    "      \"name\": \"data\",\n",
+    "      \"type\": \"Data\",\n",
+    "      \"format\": \"Parquet\",\n",
+    "      \"slot_size_array\": [10000000, 10000000, 3014529, 400781, 11, 2209, 11869, 148, 4, 977, 15, 38713, 10000000, 10000000, 10000000, 584616, 12883, 109, 37, 17177, 7425, 20266, 4, 7085, 1535, 64],\n",
+    "      \"source\": \"/raid/data/criteo/test_dask/output/train/_file_list.txt\",\n",
+    "      \"eval_source\": \"/raid/data/criteo/test_dask/output/valid/_file_list.txt\",\n",
+    "      \"check\": \"None\",\n",
+    "      \"label\": {\n",
+    "        \"top\": \"label\",\n",
+    "        \"label_dim\": 1\n",
+    "      },\n",
+    "      \"dense\": {\n",
+    "        \"top\": \"dense\",\n",
+    "        \"dense_dim\": 13\n",
+    "      },\n",
+    "      \"sparse\": [\n",
+    "        {\n",
+    "            \"top\": \"data1\",\n",
+    "            \"type\": \"LocalizedSlot\",\n",
+    "            \"max_feature_num_per_sample\": 26,\n",
+    "            \"max_nnz\": 1,\n",
+    "            \"slot_num\": 26\n",
+    "        }\n",
+    "      ]\n",
+    "    },\n",
+    "    {\n",
+    "          \"name\": \"sparse_embedding1\",\n",
+    "          \"type\": \"LocalizedSlotSparseEmbeddingHash\",\n",
+    "          \"bottom\": \"data1\",\n",
+    "          \"top\": \"sparse_embedding1\",\n",
+    "          \"sparse_embedding_hparam\": {\n",
+    "            \"max_vocabulary_size_per_gpu\": 15500000,\n",
+    "            \"embedding_vec_size\": 128,\n",
+    "            \"combiner\": 0\n",
+    "          }\n",
+    "      },\n",
+    "      {\n",
+    "        \"name\": \"fc1\",\n",
+    "        \"type\": \"InnerProduct\",\n",
+    "        \"bottom\": \"dense\",\n",
+    "        \"top\": \"fc1\",\n",
+    "         \"fc_param\": {\n",
+    "          \"num_output\": 512\n",
+    "        }\n",
+    "      },\n",
+    "  \n",
+    "      {\n",
+    "        \"name\": \"relu1\",\n",
+    "        \"type\": \"ReLU\",\n",
+    "        \"bottom\": \"fc1\",\n",
+    "        \"top\": \"relu1\" \n",
+    "      },\n",
+    "  \n",
+    "      {\n",
+    "        \"name\": \"fc2\",\n",
+    "        \"type\": \"InnerProduct\",\n",
+    "        \"bottom\": \"relu1\",\n",
+    "        \"top\": \"fc2\",\n",
+    "         \"fc_param\": {\n",
+    "          \"num_output\": 256\n",
+    "        }\n",
+    "      },\n",
+    "  \n",
+    "      {\n",
+    "        \"name\": \"relu2\",\n",
+    "        \"type\": \"ReLU\",\n",
+    "        \"bottom\": \"fc2\",\n",
+    "        \"top\": \"relu2\"     \n",
+    "      },\n",
+    "      \n",
+    "      {\n",
+    "        \"name\": \"fc3\",\n",
+    "        \"type\": \"InnerProduct\",\n",
+    "        \"bottom\": \"relu2\",\n",
+    "        \"top\": \"fc3\",\n",
+    "         \"fc_param\": {\n",
+    "          \"num_output\": 128\n",
+    "        }\n",
+    "      },\n",
+    "  \n",
+    "      {\n",
+    "        \"name\": \"relu3\",\n",
+    "        \"type\": \"ReLU\",\n",
+    "        \"bottom\": \"fc3\",\n",
+    "        \"top\": \"relu3\"     \n",
+    "      },\n",
+    "    {\n",
+    "        \"name\": \"interaction1\",\n",
+    "        \"type\": \"Interaction\",\n",
+    "        \"bottom\": [\"relu3\", \"sparse_embedding1\"],\n",
+    "        \"top\": \"interaction1\"\n",
+    "    },\n",
+    "    {\n",
+    "        \"name\": \"fc4\",\n",
+    "        \"type\": \"InnerProduct\",\n",
+    "        \"bottom\": \"interaction1\",\n",
+    "        \"top\": \"fc4\",\n",
+    "         \"fc_param\": {\n",
+    "          \"num_output\": 1024\n",
+    "        }\n",
+    "      },\n",
+    "      {\n",
+    "        \"name\": \"relu4\",\n",
+    "        \"type\": \"ReLU\",\n",
+    "        \"bottom\": \"fc4\",\n",
+    "        \"top\": \"relu4\" \n",
+    "      },\n",
+    "      {\n",
+    "        \"name\": \"fc5\",\n",
+    "        \"type\": \"InnerProduct\",\n",
+    "        \"bottom\": \"relu4\",\n",
+    "        \"top\": \"fc5\",\n",
+    "         \"fc_param\": {\n",
+    "          \"num_output\": 1024\n",
+    "        }\n",
+    "      },\n",
+    "      {\n",
+    "        \"name\": \"relu5\",\n",
+    "        \"type\": \"ReLU\",\n",
+    "        \"bottom\": \"fc5\",\n",
+    "        \"top\": \"relu5\"     \n",
+    "      },\n",
+    "      {\n",
+    "        \"name\": \"fc6\",\n",
+    "        \"type\": \"InnerProduct\",\n",
+    "        \"bottom\": \"relu5\",\n",
+    "        \"top\": \"fc6\",\n",
+    "         \"fc_param\": {\n",
+    "          \"num_output\": 512\n",
+    "        }\n",
+    "      },\n",
+    "      {\n",
+    "        \"name\": \"relu6\",\n",
+    "        \"type\": \"ReLU\",\n",
+    "        \"bottom\": \"fc6\",\n",
+    "        \"top\": \"relu6\"     \n",
+    "      },\n",
+    "      {\n",
+    "        \"name\": \"fc7\",\n",
+    "        \"type\": \"InnerProduct\",\n",
+    "        \"bottom\": \"relu6\",\n",
+    "        \"top\": \"fc7\",\n",
+    "         \"fc_param\": {\n",
+    "          \"num_output\": 256\n",
+    "        }\n",
+    "      },\n",
+    "      {\n",
+    "        \"name\": \"relu7\",\n",
+    "        \"type\": \"ReLU\",\n",
+    "        \"bottom\": \"fc7\",\n",
+    "        \"top\": \"relu7\"     \n",
+    "      },\n",
+    "      {\n",
+    "        \"name\": \"fc8\",\n",
+    "        \"type\": \"InnerProduct\",\n",
+    "        \"bottom\": \"relu7\",\n",
+    "        \"top\": \"fc8\",\n",
+    "         \"fc_param\": {\n",
+    "          \"num_output\": 1\n",
+    "        }\n",
+    "      },\n",
+    "    {\n",
+    "      \"name\": \"sigmoid\",\n",
+    "      \"type\": \"Sigmoid\",\n",
+    "      \"bottom\": \"fc8\",\n",
+    "      \"top\": \"sigmoid\"\n",
+    "    } \n",
+    "  ]\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Now we can save our models to be deployed at the inference stage. To do so we will use export_hugectr_ensemble method below. With this method, we can generate the config.pbtxt files automatically for each model. In doing so, we should also create a hugectr_params dictionary, and define the parameters like where the amazonreview.json file will be read, slots which corresponds to number of categorical features, `embedding_vector_size`, `max_nnz`, and `n_outputs` which is number of outputs.<br><br>\n",
+    "The script below creates an ensemble triton server model where\n",
+    "- workflow is the the nvtabular workflow used in preprocessing,\n",
+    "- hugectr_model_path is the HugeCTR model that should be served. \n",
+    "- This path includes the .model files.name is the base name of the various triton models\n",
+    "- output_path is the path where is model will be saved to.\n",
+    "- cats are the categorical column names\n",
+    "- conts are the continuous column names"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We need to load the NVTabular workflow first"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import nvtabular as nvt\n",
+    "\n",
+    "BASE_DIR = os.environ.get('BASE_DIR', '/raid/data/criteo')\n",
+    "input_path = os.path.join(BASE_DIR, \"test_dask/output\")\n",
+    "workflow = nvt.Workflow.load(os.path.join(input_path, \"workflow\"))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Let's clear the directory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "0"
+      ]
+     },
+     "execution_count": 7,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "os.system('rm -rf /models/*')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from nvtabular.inference.triton import export_hugectr_ensemble\n",
+    "hugectr_params = dict()\n",
+    "hugectr_params[\"config\"] = \"/models/criteo/1/criteo.json\"\n",
+    "hugectr_params[\"slots\"] = 26\n",
+    "hugectr_params[\"max_nnz\"] = 1\n",
+    "hugectr_params[\"embedding_vector_size\"] = 128\n",
+    "hugectr_params[\"n_outputs\"] = 1\n",
+    "export_hugectr_ensemble(workflow=workflow, \n",
+    "                        hugectr_model_path=\"./criteo_hugectr/1/\",\n",
+    "                        hugectr_params=hugectr_params,\n",
+    "                        name=\"criteo\", \n",
+    "                        output_path=\"/models/\", \n",
+    "                        label_columns=[\"label\"], \n",
+    "                        cats=['C' + str(x) for x in range(1,27)],\n",
+    "                        conts=['I' + str(x) for x in range(1,14)],\n",
+    "                        max_batch_size=64)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We can take a look at the generated files."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "\u001b[01;34m/models\u001b[00m\r\n",
+      "├── \u001b[01;34mcriteo\u001b[00m\r\n",
+      "│   ├── \u001b[01;34m1\u001b[00m\r\n",
+      "│   │   ├── 0_sparse_9600.model\r\n",
+      "│   │   ├── _dense_9600.model\r\n",
+      "│   │   └── criteo.json\r\n",
+      "│   └── config.pbtxt\r\n",
+      "├── \u001b[01;34mcriteo_ens\u001b[00m\r\n",
+      "│   ├── \u001b[01;34m1\u001b[00m\r\n",
+      "│   └── config.pbtxt\r\n",
+      "└── \u001b[01;34mcriteo_nvt\u001b[00m\r\n",
+      "    ├── \u001b[01;34m1\u001b[00m\r\n",
+      "    │   ├── model.py\r\n",
+      "    │   └── \u001b[01;34mworkflow\u001b[00m\r\n",
+      "    │       ├── \u001b[01;34mcategories\u001b[00m\r\n",
+      "    │       │   ├── unique.C1.parquet\r\n",
+      "    │       │   ├── unique.C10.parquet\r\n",
+      "    │       │   ├── unique.C11.parquet\r\n",
+      "    │       │   ├── unique.C12.parquet\r\n",
+      "    │       │   ├── unique.C13.parquet\r\n",
+      "    │       │   ├── unique.C14.parquet\r\n",
+      "    │       │   ├── unique.C15.parquet\r\n",
+      "    │       │   ├── unique.C16.parquet\r\n",
+      "    │       │   ├── unique.C17.parquet\r\n",
+      "    │       │   ├── unique.C18.parquet\r\n",
+      "    │       │   ├── unique.C19.parquet\r\n",
+      "    │       │   ├── unique.C2.parquet\r\n",
+      "    │       │   ├── unique.C20.parquet\r\n",
+      "    │       │   ├── unique.C21.parquet\r\n",
+      "    │       │   ├── unique.C22.parquet\r\n",
+      "    │       │   ├── unique.C23.parquet\r\n",
+      "    │       │   ├── unique.C24.parquet\r\n",
+      "    │       │   ├── unique.C25.parquet\r\n",
+      "    │       │   ├── unique.C26.parquet\r\n",
+      "    │       │   ├── unique.C3.parquet\r\n",
+      "    │       │   ├── unique.C4.parquet\r\n",
+      "    │       │   ├── unique.C5.parquet\r\n",
+      "    │       │   ├── unique.C6.parquet\r\n",
+      "    │       │   ├── unique.C7.parquet\r\n",
+      "    │       │   ├── unique.C8.parquet\r\n",
+      "    │       │   └── unique.C9.parquet\r\n",
+      "    │       ├── column_types.json\r\n",
+      "    │       ├── metadata.json\r\n",
+      "    │       └── workflow.pkl\r\n",
+      "    └── config.pbtxt\r\n",
+      "\r\n",
+      "8 directories, 36 files\r\n"
+     ]
+    }
+   ],
+   "source": [
+    "!tree /models"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Loading Ensemble Model with Triton Inference Server\n",
+    "\n",
+    "We have only saved the models for Triton Inference Server. We started Triton Inference Server in explicit mode, meaning that we need to send a request that Triton will load the ensemble model."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We connect to the Triton Inference Server."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "client created.\n"
+     ]
+    },
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "/opt/conda/envs/rapids/lib/python3.8/site-packages/tritonhttpclient/__init__.py:30: DeprecationWarning: The package `tritonhttpclient` is deprecated and will be removed in a future version. Please use instead `tritonclient.http`\n",
+      "  warnings.warn(\n"
+     ]
+    }
+   ],
+   "source": [
+    "import tritonhttpclient\n",
+    "try:\n",
+    "    triton_client = tritonhttpclient.InferenceServerClient(url=\"triton:8000\", verbose=True)\n",
+    "    print(\"client created.\")\n",
+    "except Exception as e:\n",
+    "    print(\"channel creation failed: \" + str(e))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We deactivate warnings."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "/opt/conda/envs/rapids/lib/python3.8/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n",
+      "  and should_run_async(code)\n"
+     ]
+    }
+   ],
+   "source": [
+    "import warnings\n",
+    "warnings.filterwarnings('ignore')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We check if the server is alive."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "GET /v2/health/live, headers None\n",
+      "<HTTPSocketPoolResponse status=200 headers={'content-length': '0', 'content-type': 'text/plain'}>\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "True"
+      ]
+     },
+     "execution_count": 13,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "triton_client.is_server_live()  "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We check the available models in the repositories:\n",
+    "- criteo_ens: Ensemble \n",
+    "- criteo_nvt: NVTabular \n",
+    "- criteo: HugeCTR model"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "POST /v2/repository/index, headers None\n",
+      "\n",
+      "<HTTPSocketPoolResponse status=200 headers={'content-type': 'application/json', 'content-length': '63'}>\n",
+      "bytearray(b'[{\"name\":\"criteo\"},{\"name\":\"criteo_ens\"},{\"name\":\"criteo_nvt\"}]')\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "[{'name': 'criteo'}, {'name': 'criteo_ens'}, {'name': 'criteo_nvt'}]"
+      ]
+     },
+     "execution_count": 14,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "triton_client.get_model_repository_index()  "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We load the models individually."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "POST /v2/repository/models/criteo_nvt/load, headers None\n",
+      "\n"
+     ]
+    },
+    {
+     "ename": "timeout",
+     "evalue": "timed out",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+      "\u001b[0;31mtimeout\u001b[0m                                   Traceback (most recent call last)",
+      "\u001b[0;32m<timed eval>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/tritonclient/http/__init__.py\u001b[0m in \u001b[0;36mload_model\u001b[0;34m(self, model_name, headers, query_params)\u001b[0m\n\u001b[1;32m    608\u001b[0m         \"\"\"\n\u001b[1;32m    609\u001b[0m         \u001b[0mrequest_uri\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"v2/repository/models/{}/load\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mquote\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 610\u001b[0;31m         response = self._post(request_uri=request_uri,\n\u001b[0m\u001b[1;32m    611\u001b[0m                               \u001b[0mrequest_body\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    612\u001b[0m                               \u001b[0mheaders\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mheaders\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/tritonclient/http/__init__.py\u001b[0m in \u001b[0;36m_post\u001b[0;34m(self, request_uri, request_body, headers, query_params)\u001b[0m\n\u001b[1;32m    298\u001b[0m                                               headers=headers)\n\u001b[1;32m    299\u001b[0m         \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 300\u001b[0;31m             response = self._client_stub.post(request_uri=request_uri,\n\u001b[0m\u001b[1;32m    301\u001b[0m                                               body=request_body)\n\u001b[1;32m    302\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/geventhttpclient/client.py\u001b[0m in \u001b[0;36mpost\u001b[0;34m(self, request_uri, body, headers)\u001b[0m\n\u001b[1;32m    270\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    271\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mpost\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrequest_uri\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbody\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34mu''\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mheaders\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 272\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrequest\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mMETHOD_POST\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrequest_uri\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbody\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mbody\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mheaders\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mheaders\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    273\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    274\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mput\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrequest_uri\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbody\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34mu''\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mheaders\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/geventhttpclient/client.py\u001b[0m in \u001b[0;36mrequest\u001b[0;34m(self, method, request_uri, body, headers)\u001b[0m\n\u001b[1;32m    251\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    252\u001b[0m             \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 253\u001b[0;31m                 response = HTTPSocketPoolResponse(sock, self._connection_pool,\n\u001b[0m\u001b[1;32m    254\u001b[0m                                                   block_size=self.block_size, method=method.upper(), headers_type=self.headers_type)\n\u001b[1;32m    255\u001b[0m             \u001b[0;32mexcept\u001b[0m \u001b[0mHTTPConnectionClosed\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/geventhttpclient/response.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, sock, pool, **kw)\u001b[0m\n\u001b[1;32m    296\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msock\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpool\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    297\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_pool\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpool\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 298\u001b[0;31m         \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mHTTPSocketPoolResponse\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msock\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    299\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    300\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mrelease\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/geventhttpclient/response.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, sock, block_size, method, **kw)\u001b[0m\n\u001b[1;32m    168\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_sock\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msock\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    169\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mblock_size\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mblock_size\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 170\u001b[0;31m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_read_headers\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    171\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    172\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mrelease\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/geventhttpclient/response.py\u001b[0m in \u001b[0;36m_read_headers\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m    188\u001b[0m             \u001b[0;32mwhile\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mheaders_complete\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    189\u001b[0m                 \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 190\u001b[0;31m                     \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_sock\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrecv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mblock_size\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    191\u001b[0m                     \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfeed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    192\u001b[0m                     \u001b[0;31m# depending on gevent version we get a conn reset or no data\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/gevent/_socketcommon.py\u001b[0m in \u001b[0;36mrecv\u001b[0;34m(self, *args)\u001b[0m\n\u001b[1;32m    661\u001b[0m                 \u001b[0;31m# QQQ without clearing exc_info test__refcount.test_clean_exit fails\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    662\u001b[0m                 \u001b[0mexc_clear\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# Python 2\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 663\u001b[0;31m             \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_wait\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_read_event\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    664\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    665\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mrecvfrom\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/gevent/_gevent_c_hub_primitives.cpython-38-x86_64-linux-gnu.so\u001b[0m in \u001b[0;36mgevent._gevent_c_hub_primitives.wait_on_socket\u001b[0;34m()\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/gevent/_gevent_c_hub_primitives.cpython-38-x86_64-linux-gnu.so\u001b[0m in \u001b[0;36mgevent._gevent_c_hub_primitives.wait_on_socket\u001b[0;34m()\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/gevent/_gevent_c_hub_primitives.cpython-38-x86_64-linux-gnu.so\u001b[0m in \u001b[0;36mgevent._gevent_c_hub_primitives._primitive_wait\u001b[0;34m()\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/gevent/_gevent_c_hub_primitives.cpython-38-x86_64-linux-gnu.so\u001b[0m in \u001b[0;36mgevent._gevent_c_hub_primitives._primitive_wait\u001b[0;34m()\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/gevent/_gevent_c_hub_primitives.cpython-38-x86_64-linux-gnu.so\u001b[0m in \u001b[0;36mgevent._gevent_c_hub_primitives.WaitOperationsGreenlet.wait\u001b[0;34m()\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/gevent/_gevent_c_hub_primitives.cpython-38-x86_64-linux-gnu.so\u001b[0m in \u001b[0;36mgevent._gevent_c_hub_primitives.WaitOperationsGreenlet.wait\u001b[0;34m()\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/gevent/_gevent_c_hub_primitives.cpython-38-x86_64-linux-gnu.so\u001b[0m in \u001b[0;36mgevent._gevent_c_hub_primitives.WaitOperationsGreenlet.wait\u001b[0;34m()\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/gevent/_gevent_c_waiter.cpython-38-x86_64-linux-gnu.so\u001b[0m in \u001b[0;36mgevent._gevent_c_waiter.Waiter.get\u001b[0;34m()\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/gevent/_gevent_c_greenlet_primitives.cpython-38-x86_64-linux-gnu.so\u001b[0m in \u001b[0;36mgevent._gevent_c_greenlet_primitives.SwitchOutGreenletWithLoop.switch\u001b[0;34m()\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/gevent/_gevent_c_greenlet_primitives.cpython-38-x86_64-linux-gnu.so\u001b[0m in \u001b[0;36mgevent._gevent_c_greenlet_primitives.SwitchOutGreenletWithLoop.switch\u001b[0;34m()\u001b[0m\n",
+      "\u001b[0;32m/opt/conda/envs/rapids/lib/python3.8/site-packages/gevent/_gevent_c_greenlet_primitives.cpython-38-x86_64-linux-gnu.so\u001b[0m in \u001b[0;36mgevent._gevent_c_greenlet_primitives.SwitchOutGreenletWithLoop.switch\u001b[0;34m()\u001b[0m\n",
+      "\u001b[0;32msrc/gevent/_gevent_c_greenlet_primitives.pxd\u001b[0m in \u001b[0;36mgevent._gevent_c_greenlet_primitives._greenlet_switch\u001b[0;34m()\u001b[0m\n",
+      "\u001b[0;31mtimeout\u001b[0m: timed out"
+     ]
+    }
+   ],
+   "source": [
+    "%%time\n",
+    "\n",
+    "triton_client.load_model(model_name='criteo_nvt')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 19,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "POST /v2/repository/models/criteo/load, headers None\n",
+      "\n",
+      "<HTTPSocketPoolResponse status=200 headers={'content-type': 'application/json', 'content-length': '0'}>\n",
+      "Loaded model 'criteo'\n",
+      "CPU times: user 410 µs, sys: 810 µs, total: 1.22 ms\n",
+      "Wall time: 1.02 ms\n"
+     ]
+    }
+   ],
+   "source": [
+    "%%time\n",
+    "\n",
+    "triton_client.load_model(model_name='criteo')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 17,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "POST /v2/repository/models/criteo_ens/load, headers None\n",
+      "\n",
+      "<HTTPSocketPoolResponse status=200 headers={'content-type': 'application/json', 'content-length': '0'}>\n",
+      "Loaded model 'criteo_ens'\n",
+      "CPU times: user 3.08 ms, sys: 0 ns, total: 3.08 ms\n",
+      "Wall time: 36.7 s\n"
+     ]
+    }
+   ],
+   "source": [
+    "%%time\n",
+    "\n",
+    "triton_client.load_model(model_name='criteo_ens')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Example Request to Triton Inference Server\n",
+    "\n",
+    "Now, the models are loaded and we can create a sample request. We read an example **raw batch** for inference."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 20,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "     I1   I2    I3    I4    I5  I6  I7  I8  I9  I10  ...        C17  \\\n",
+      "0     5  110  <NA>    16  <NA>   1   0  14   7    1  ... -771205462   \n",
+      "1    32    3     5  <NA>     1   0   0  61   5    0  ... -771205462   \n",
+      "2  <NA>  233     1   146     1   0   0  99   7    0  ... -771205462   \n",
+      "\n",
+      "          C18         C19         C20         C21        C22        C23  \\\n",
+      "0 -1206449222 -1793932789 -1014091992   351689309  632402057 -675152885   \n",
+      "1 -1578429167 -1793932789   -20981661 -1556988767 -924717482  391309800   \n",
+      "2  1653545869 -1793932789 -1014091992   351689309  632402057 -675152885   \n",
+      "\n",
+      "          C24         C25         C26  \n",
+      "0  2091868316   809724924  -317696227  \n",
+      "1  1966410890 -1726799382 -1218975401  \n",
+      "2   883538181   -10139646  -317696227  \n",
+      "\n",
+      "[3 rows x 39 columns]\n"
+     ]
+    }
+   ],
+   "source": [
+    "import cudf\n",
+    "\n",
+    "# read in the workflow (to get input/output schema to call triton with)\n",
+    "batch_path = os.path.join(BASE_DIR, \"converted/criteo\")\n",
+    "batch = cudf.read_parquet(os.path.join(batch_path, \"*.parquet\"), num_rows=3)\n",
+    "batch = batch[[x for x in batch.columns if x != 'label']]\n",
+    "print(batch)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We prepare the batch for inference by using correct column names and data types. We use the same datatypes as defined in our dataframe."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 21,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "I1     int32\n",
+       "I2     int32\n",
+       "I3     int32\n",
+       "I4     int32\n",
+       "I5     int32\n",
+       "I6     int32\n",
+       "I7     int32\n",
+       "I8     int32\n",
+       "I9     int32\n",
+       "I10    int32\n",
+       "I11    int32\n",
+       "I12    int32\n",
+       "I13    int32\n",
+       "C1     int32\n",
+       "C2     int32\n",
+       "C3     int32\n",
+       "C4     int32\n",
+       "C5     int32\n",
+       "C6     int32\n",
+       "C7     int32\n",
+       "C8     int32\n",
+       "C9     int32\n",
+       "C10    int32\n",
+       "C11    int32\n",
+       "C12    int32\n",
+       "C13    int32\n",
+       "C14    int32\n",
+       "C15    int32\n",
+       "C16    int32\n",
+       "C17    int32\n",
+       "C18    int32\n",
+       "C19    int32\n",
+       "C20    int32\n",
+       "C21    int32\n",
+       "C22    int32\n",
+       "C23    int32\n",
+       "C24    int32\n",
+       "C25    int32\n",
+       "C26    int32\n",
+       "dtype: object"
+      ]
+     },
+     "execution_count": 21,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "batch.dtypes "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import tritonclient.http as httpclient\n",
+    "from tritonclient.utils import *\n",
+    "\n",
+    "inputs = []\n",
+    "\n",
+    "col_names = list(batch.columns)\n",
+    "col_dtypes = [np.int32]*len(col_names)\n",
+    "\n",
+    "for i, col in enumerate(batch.columns):\n",
+    "    d = batch[col].values_host.astype(col_dtypes[i])\n",
+    "    d = d.reshape(len(d),1)\n",
+    "    inputs.append(httpclient.InferInput(col_names[i], d.shape, np_to_triton_dtype(col_dtypes[i])))\n",
+    "    inputs[i].set_data_from_numpy(d)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We send the request to the triton server and collect the last output."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 23,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "POST /v2/models/criteo_ens/infer, headers {'Inference-Header-Content-Length': 3383}\n",
+      "b'{\"id\":\"1\",\"inputs\":[{\"name\":\"I1\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I2\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I3\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I4\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I5\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I6\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I7\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I8\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I9\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I10\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I11\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I12\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"I13\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C1\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C2\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C3\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C4\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C5\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C6\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C7\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C8\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C9\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C10\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C11\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C12\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C13\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C14\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C15\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C16\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C17\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C18\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C19\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C20\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C21\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C22\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C23\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C24\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C25\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}},{\"name\":\"C26\",\"shape\":[3,1],\"datatype\":\"INT32\",\"parameters\":{\"binary_data_size\":12}}],\"outputs\":[{\"name\":\"OUTPUT0\",\"parameters\":{\"binary_data\":true}}]}\\x05\\x00\\x00\\x00 \\x00\\x00\\x00\\x00\\x00\\x00\\x00n\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xe9\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x92\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0e\\x00\\x00\\x00=\\x00\\x00\\x00c\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x002\\x01\\x00\\x00U\\x0c\\x00\\x00\\x1d\\x0c\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x01\\x00\\x00\\x00y\\rwb\\x8d\\xfd\\xf3\\xe5y\\rwbX]\\x1f\\xe2\\xa6\\xff\\xaa\\xa0\\x03B\\x98\\xad/D\\xea\\xaf\\xd5\\x15\\xaao\\r\\xc6\\xbeb\\xcf\\x7f\\\\\\x94!4\\x8a\\xda\\xeeIl8H\\'\\xb08#\\x9f\\xd6<M\\x06U\\xe7\\xcbm\\xcdo\\xcbm\\xcdo\\xcbm\\xcdo!\\xaa\\x805\\x81\\xed\\x16\\xabb\\xeb\\xf5\\xb5\\x03\\x89\\x80()lBC\\x8b\\xcc\\xf2\\xd1\\xa6\\xdf\\xdeFT\\xe1\\xf5\\x1d\\x1f\\x82N.\\xc1}\\x02.\\xa9\\xc0\\xe9}\\xc1}\\x02.1B|\\x0cd\\xdcRf1B|\\x0c\\x1f\\x1d\\x98\\x95\\'N\\xeb\\x99\\x84aq\\x12\\xb7\\xff\\xc5\\x00\\xb7\\xff\\xc5\\x00\\xb7\\xff\\xc5\\x007\\xe5N\\xbe7\\xe5N\\xbe7\\xe5N\\xbe\\xcct\\x0b\\x8a\\x99\\xfe\\xbb\\xf3\\x0b\\r\\x0f\\xf7\\xfa>\\xdcL\\xfa>\\xdcL\\xfa>\\xdcL\\xaaV\\x08\\xd2\\xaaV\\x08\\xd2\\xaaV\\x08\\xd2\\xba\\x0b\\x17\\xb8\\x11\\x15\\xeb\\xa1\\x8d\\x1b\\x8fb\\x0b\\xc2\\x12\\x95\\x0b\\xc2\\x12\\x95\\x0b\\xc2\\x12\\x95(/\\x8e\\xc3c\\xd8\\xbf\\xfe(/\\x8e\\xc3]Z\\xf6\\x14\\xa1<2\\xa3]Z\\xf6\\x14\\x89\\xb0\\xb1%V\\xee\\xe1\\xc8\\x89\\xb0\\xb1%\\x0b\\xfc\\xc1\\xd7\\xe8\\xe9R\\x17\\x0b\\xfc\\xc1\\xd7\\x9c`\\xaf|\\x8a\\x0c5u\\x05\\xb9\\xa94\\xfckC0\\xea!\\x13\\x99\\x02He\\xff\\x1dW\\x10\\xedW\\xe9W\\xb7\\x1dW\\x10\\xed'\n",
+      "<HTTPSocketPoolResponse status=200 headers={'content-type': 'application/json', 'inference-header-content-length': '230', 'content-length': '486'}>\n",
+      "bytearray(b'{\"id\":\"1\",\"model_name\":\"criteo_ens\",\"model_version\":\"1\",\"parameters\":{\"sequence_id\":0,\"sequence_start\":false,\"sequence_end\":false},\"outputs\":[{\"name\":\"OUTPUT0\",\"datatype\":\"FP32\",\"shape\":[3],\"parameters\":{\"binary_data_size\":256}}]}')\n",
+      "predicted sigmoid result:\n",
+      " [0.04084935 0.02853993 0.02994252]\n"
+     ]
+    }
+   ],
+   "source": [
+    "# placeholder variables for the output\n",
+    "outputs = [httpclient.InferRequestedOutput(\"OUTPUT0\")]\n",
+    "\n",
+    "# build a client to connect to our server. \n",
+    "# This InferenceServerClient object is what we'll be using to talk to Triton.\n",
+    "# make the request with tritonclient.http.InferInput object\n",
+    "response = triton_client.infer(\"criteo_ens\", inputs, request_id=\"1\",outputs=outputs)\n",
+    "\n",
+    "print(\"predicted sigmoid result:\\n\", response.as_numpy('OUTPUT0'))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Let's unload the model. We need to unload each model."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 24,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "POST /v2/repository/models/criteo_ens/unload, headers None\n",
+      "\n",
+      "<HTTPSocketPoolResponse status=200 headers={'content-type': 'application/json', 'content-length': '0'}>\n",
+      "Loaded model 'criteo_ens'\n",
+      "POST /v2/repository/models/criteo_nvt/unload, headers None\n",
+      "\n",
+      "<HTTPSocketPoolResponse status=200 headers={'content-type': 'application/json', 'content-length': '0'}>\n",
+      "Loaded model 'criteo_nvt'\n",
+      "POST /v2/repository/models/criteo/unload, headers None\n",
+      "\n",
+      "<HTTPSocketPoolResponse status=200 headers={'content-type': 'application/json', 'content-length': '0'}>\n",
+      "Loaded model 'criteo'\n"
+     ]
+    }
+   ],
+   "source": [
+    "triton_client.unload_model(model_name='criteo_ens')\n",
+    "triton_client.unload_model(model_name='criteo_nvt')\n",
+    "triton_client.unload_model(model_name='criteo')"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "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.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/scaling-criteo/docker-compose-fastai.yml b/examples/scaling-criteo/docker-compose-fastai.yml
new file mode 100644
index 00000000000..405f956ca36
--- /dev/null
+++ b/examples/scaling-criteo/docker-compose-fastai.yml
@@ -0,0 +1,20 @@
+# (1) Use this file for docker runtime configurations that are common to both
+# development and deployment.
+
+# `version : '2.3'` lets us use the `runtime=nvidia` configuration so that our
+# containers can interact with the GPU(s).
+version: '2.3'
+
+volumes:
+  models:
+
+services:
+  lab:
+    runtime: nvidia
+    image: nvcr.io/nvidia/merlin/merlin-pytorch-training:0.4
+    command: "/bin/bash -c 'source activate rapids && pip install jupyterlab jupytext pydot && apt-get update && apt-get install -y tree && python -m ipykernel install --user --name=rapids && jupyter notebook --no-browser --allow-root --port=8888 --ip=0.0.0.0 --NotebookApp.token='demotoken' --NotebookApp.allow_origin='*' --notebook-dir=/'"
+    volumes:
+      - models:/models
+      - /raid/:/raid/
+    ports:
+      - 8888:8888
diff --git a/examples/scaling-criteo/docker-compose-hugectr.yml b/examples/scaling-criteo/docker-compose-hugectr.yml
new file mode 100644
index 00000000000..5399da8f78d
--- /dev/null
+++ b/examples/scaling-criteo/docker-compose-hugectr.yml
@@ -0,0 +1,37 @@
+# (1) Use this file for docker runtime configurations that are common to both
+# development and deployment.
+
+# `version : '2.3'` lets us use the `runtime=nvidia` configuration so that our
+# containers can interact with the GPU(s).
+version: '2.3'
+
+volumes:
+  models:
+
+services:
+  triton:
+    command: "/bin/bash -c 'source activate rapids && pip install grpcio-channelz && tritonserver --model-repository=/models/ --model-control-mode=explicit --backend-config=hugectr,supportlonglong=true --backend-config=hugectr,criteo=/models/criteo/1/criteo.json'"
+    image: nvcr.io/nvidia/merlin/merlin-inference:0.4
+    runtime: nvidia
+    shm_size: "1g"
+    ulimits:
+      memlock: -1
+      stack: 67108864
+    ports:
+      - 8000:8000
+      - 8001:8001
+      - 8002:8002
+    volumes:
+      - models:/models
+
+  lab:
+    runtime: nvidia
+    image: nvcr.io/nvidia/merlin/merlin-training:0.4
+    command: "/bin/bash -c 'source activate rapids && pip install jupyterlab jupytext pydot nvidia-pyindex tritonclient geventhttpclient && apt-get update && apt-get install -y tree && python -m ipykernel install --user --name=rapids && jupyter notebook --no-browser --allow-root --port=8888 --ip=0.0.0.0 --NotebookApp.token='demotoken' --NotebookApp.allow_origin='*' --notebook-dir=/'"
+    volumes:
+      - models:/models
+      - /raid/:/raid/
+    links:
+      - triton
+    ports:
+      - 8888:8888
diff --git a/examples/scaling-criteo/docker-compose-tf.yml b/examples/scaling-criteo/docker-compose-tf.yml
new file mode 100644
index 00000000000..2e8a26f47ca
--- /dev/null
+++ b/examples/scaling-criteo/docker-compose-tf.yml
@@ -0,0 +1,37 @@
+# (1) Use this file for docker runtime configurations that are common to both
+# development and deployment.
+
+# `version : '2.3'` lets us use the `runtime=nvidia` configuration so that our
+# containers can interact with the GPU(s).
+version: '2.3'
+
+volumes:
+  models:
+
+services:
+  triton:
+    command: "/bin/bash -c 'source activate rapids && pip install grpcio-channelz && tritonserver --model-repository=/models/ --model-control-mode=explicit'"
+    image: nvcr.io/nvidia/merlin/merlin-inference:0.4
+    runtime: nvidia
+    shm_size: "1g"
+    ulimits:
+      memlock: -1
+      stack: 67108864
+    ports:
+      - 8000:8000
+      - 8001:8001
+      - 8002:8002
+    volumes:
+      - models:/models
+
+  lab:
+    runtime: nvidia
+    image: nvcr.io/nvidia/merlin/merlin-tensorflow-training:0.4
+    command: "/bin/bash -c 'source activate rapids && pip install jupyterlab jupytext pydot nvidia-pyindex tritonclient geventhttpclient && apt-get update && apt-get install -y tree && python -m ipykernel install --user --name=rapids && jupyter notebook --no-browser --allow-root --port=8888 --ip=0.0.0.0 --NotebookApp.token='demotoken' --NotebookApp.allow_origin='*' --notebook-dir=/'"
+    volumes:
+      - models:/models
+      - /raid/:/raid/
+    links:
+      - triton
+    ports:
+      - 8888:8888
diff --git a/examples/scaling-criteo/imgs/dask-dataframe.svg b/examples/scaling-criteo/imgs/dask-dataframe.svg
new file mode 100644
index 00000000000..7d371234328
--- /dev/null
+++ b/examples/scaling-criteo/imgs/dask-dataframe.svg
@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="812.37378"
+   height="1011.8721"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="New document 1">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.5"
+     inkscape:cx="68.623957"
+     inkscape:cy="300.16925"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1600"
+     inkscape:window-height="876"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2985"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true"
+       spacingx="2px"
+       spacingy="2px"
+       originx="45.029487px"
+       originy="-244.02136px" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(45.029487,203.53131)">
+    <rect
+       style="opacity:0.6;fill:#0000b0;fill-opacity:0.50196078;stroke:#000000;stroke-width:5.58885813;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:0.44292238;stroke-dasharray:none;stroke-dashoffset:0"
+       id="rect2987"
+       width="251.49863"
+       height="195.61005"
+       x="246.14598"
+       y="-200.72348" />
+    <rect
+       style="opacity:0.6;fill:#0000b0;fill-opacity:0.50196078;stroke:#000000;stroke-width:5.58885813;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:0.44292238;stroke-dasharray:none;stroke-dashoffset:0"
+       id="rect3796"
+       width="251.49863"
+       height="139.72145"
+       x="246.14598"
+       y="22.830854" />
+    <rect
+       style="opacity:0.6;fill:#0000b0;fill-opacity:0.50196078;stroke:#000000;stroke-width:5.58885813;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:0.44292238;stroke-dasharray:none;stroke-dashoffset:0"
+       id="rect3798"
+       width="251.49863"
+       height="307.38721"
+       x="246.14598"
+       y="190.4966" />
+    <rect
+       style="opacity:0.6;fill:#0000b0;fill-opacity:0.50196078;stroke:#000000;stroke-width:5.58885813;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:0.44292238;stroke-dasharray:none;stroke-dashoffset:0"
+       id="rect3800"
+       width="251.49863"
+       height="167.66576"
+       x="246.14598"
+       y="525.82812" />
+    <rect
+       style="opacity:0.6;fill:#0000b0;fill-opacity:0.50196078;stroke:#000000;stroke-width:5.58885813;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:0.44292238;stroke-dasharray:none;stroke-dashoffset:0"
+       id="rect3802"
+       width="251.49863"
+       height="83.832878"
+       x="246.14598"
+       y="721.43817" />
+    <text
+       xml:space="preserve"
+       style="font-size:33.53314972px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
+       x="78.480225"
+       y="-116.8906"
+       id="text3804"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3806"
+         x="78.480225"
+         y="-116.8906"
+         style="font-size:39.12200928px">January, 2016</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:33.53314972px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
+       x="78.480225"
+       y="106.66373"
+       id="text3804-1"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3806-5"
+         x="78.480225"
+         y="106.66373"
+         style="font-size:39.12200928px">Febrary, 2016</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:33.53314972px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
+       x="78.480225"
+       y="358.16235"
+       id="text3804-3"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3806-9"
+         x="78.480225"
+         y="358.16235"
+         style="font-size:39.12200928px">March, 2016</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:33.53314972px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
+       x="78.480225"
+       y="609.66095"
+       id="text3804-5"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3806-52"
+         x="78.480225"
+         y="609.66095"
+         style="font-size:39.12200928px">April, 2016</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:33.53314972px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
+       x="78.480225"
+       y="777.32672"
+       id="text3804-51"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3806-7"
+         x="78.480225"
+         y="777.32672"
+         style="font-size:39.12200928px">May, 2016</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:33.53314972px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
+       x="671.9798"
+       y="78.719437"
+       id="text3886"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3888"
+         x="676.48798"
+         y="78.719437"
+         style="font-size:39.12200928px">Pandas </tspan><tspan
+         sodipodi:role="line"
+         x="671.9798"
+         y="127.62195"
+         style="font-size:39.12200928px"
+         id="tspan3894">DataFrame</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:122.2452774px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
+       x="645.08667"
+       y="114.33765"
+       id="text3890"
+       sodipodi:linespacing="125%"
+       transform="scale(0.849412,1.177285)"><tspan
+         sodipodi:role="line"
+         id="tspan3892"
+         x="645.08667"
+         y="114.33765">}</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:33.53314972px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
+       x="661.44617"
+       y="296.68491"
+       id="text3896"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3898"
+         x="665.95435"
+         y="296.68491"
+         style="font-size:39.12200928px">Dask </tspan><tspan
+         sodipodi:role="line"
+         x="661.44617"
+         y="345.5874"
+         id="tspan3904"
+         style="font-size:39.12200928px">DataFrame</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:355.3336792px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
+       x="1527.4156"
+       y="209.45833"
+       id="text3900"
+       sodipodi:linespacing="125%"
+       transform="scale(0.34044434,2.9373377)"><tspan
+         sodipodi:role="line"
+         id="tspan3902"
+         x="1527.4156"
+         y="209.45833">}</tspan></text>
+  </g>
+</svg>
diff --git a/examples/scaling-criteo/imgs/triton-hugectr.png b/examples/scaling-criteo/imgs/triton-hugectr.png
new file mode 100644
index 00000000000..8c372d5d62d
Binary files /dev/null and b/examples/scaling-criteo/imgs/triton-hugectr.png differ
diff --git a/examples/scaling-criteo/imgs/triton-tf.png b/examples/scaling-criteo/imgs/triton-tf.png
new file mode 100644
index 00000000000..e68bcd4fefc
Binary files /dev/null and b/examples/scaling-criteo/imgs/triton-tf.png differ


Test patch:
diff --git a/tests/unit/test_notebooks.py b/tests/unit/test_notebooks.py
index 6af62a0eb72..541f4dc556b 100644
--- a/tests/unit/test_notebooks.py
+++ b/tests/unit/test_notebooks.py
@@ -29,39 +29,124 @@
 TEST_PATH = dirname(dirname(realpath(__file__)))
 
 
-def test_criteo_notebook(tmpdir):
+def test_criteo_tf_notebook(tmpdir):
+    tor = pytest.importorskip("tensorflow")  # noqa
+    # create a toy dataset in tmpdir, and point environment variables so the notebook
+    # will read from it
+    os.system("mkdir -p " + os.path.join(tmpdir, "converted/criteo"))
+    for i in range(24):
+        df = _get_random_criteo_data(1000)
+        df.to_parquet(os.path.join(tmpdir, "converted/criteo", f"day_{i}.parquet"))
+    os.environ["BASE_DIR"] = str(tmpdir)
+
+    def _nb_modify(line):
+        # Disable LocalCUDACluster
+        line = line.replace("client.run(_rmm_pool)", "# client.run(_rmm_pool)")
+        line = line.replace("if cluster is None:", "if False:")
+        line = line.replace("client = Client(cluster)", "# client = Client(cluster)")
+        line = line.replace(
+            "workflow = nvt.Workflow(features, client=client)", "workflow = nvt.Workflow(features)"
+        )
+        line = line.replace("client", "# client")
+        line = line.replace("NUM_GPUS = [0,1,2,3,4,5,6,7]", "NUM_GPUS = [0]")
+        line = line.replace("part_size = int(part_mem_frac * device_size)", "part_size = '128MB'")
+
+        return line
+
+    _run_notebook(
+        tmpdir,
+        os.path.join(
+            dirname(TEST_PATH),
+            "examples/scaling-criteo/",
+            "02-ETL-with-NVTabular.ipynb",
+        ),
+        # disable rmm.reinitialize, seems to be causing issues
+        transform=_nb_modify,
+    )
+
+    def _modify_tf_nb(line):
+        return line.replace(
+            # don't require grqphviz/pydot
+            "tf.keras.utils.plot_model(model)",
+            "# tf.keras.utils.plot_model(model)",
+        )
+
+    _run_notebook(
+        tmpdir,
+        os.path.join(
+            dirname(TEST_PATH),
+            "examples/scaling-criteo/",
+            "03a-Training-with-TF.ipynb",
+        ),
+        transform=_modify_tf_nb,
+    )
+
+
+def test_criteo_pyt_notebook(tmpdir):
     tor = pytest.importorskip("fastai")  # noqa
     # create a toy dataset in tmpdir, and point environment variables so the notebook
     # will read from it
+    os.system("mkdir -p " + os.path.join(tmpdir, "converted/criteo"))
     for i in range(24):
         df = _get_random_criteo_data(1000)
-        df.to_parquet(os.path.join(tmpdir, f"day_{i}.parquet"))
-    os.environ["INPUT_DATA_DIR"] = str(tmpdir)
-    os.environ["OUTPUT_DATA_DIR"] = str(tmpdir)
+        df.to_parquet(os.path.join(tmpdir, "converted/criteo", f"day_{i}.parquet"))
+    os.environ["BASE_DIR"] = str(tmpdir)
+
+    def _nb_modify(line):
+        # Disable LocalCUDACluster
+        line = line.replace("client.run(_rmm_pool)", "# client.run(_rmm_pool)")
+        line = line.replace("if cluster is None:", "if False:")
+        line = line.replace("client = Client(cluster)", "# client = Client(cluster)")
+        line = line.replace(
+            "workflow = nvt.Workflow(features, client=client)", "workflow = nvt.Workflow(features)"
+        )
+        line = line.replace("client", "# client")
+        line = line.replace("NUM_GPUS = [0,1,2,3,4,5,6,7]", "NUM_GPUS = [0]")
+        line = line.replace("part_size = int(part_mem_frac * device_size)", "part_size = '128MB'")
+        return line
 
     _run_notebook(
         tmpdir,
         os.path.join(
             dirname(TEST_PATH),
             "examples/scaling-criteo/",
-            "02-03b-ETL-with-NVTabular-Training-with-PyTorch.ipynb",
+            "02-ETL-with-NVTabular.ipynb",
         ),
         # disable rmm.reinitialize, seems to be causing issues
-        transform=lambda line: line.replace("rmm.reinitialize(", "# rmm.reinitialize("),
+        transform=_nb_modify,
+    )
+
+    _run_notebook(
+        tmpdir,
+        os.path.join(
+            dirname(TEST_PATH),
+            "examples/scaling-criteo/",
+            "03d-Training-with-FastAI.ipynb",
+        ),
     )
 
 
 def test_optimize_criteo(tmpdir):
-    _get_random_criteo_data(1000).to_csv(os.path.join(tmpdir, "day_0"), sep="\t", header=False)
-    os.environ["INPUT_DATA_DIR"] = str(tmpdir)
-    os.environ["OUTPUT_DATA_DIR"] = str(tmpdir)
+    input_path = str(tmpdir.mkdir("input"))
+    _get_random_criteo_data(1000).to_csv(os.path.join(input_path, "day_0"), sep="\t", header=False)
+    os.environ["INPUT_DATA_DIR"] = input_path
+    os.environ["OUTPUT_DATA_DIR"] = str(tmpdir.mkdir("output"))
+    with get_cuda_cluster() as cuda_cluster:
+        scheduler_port = cuda_cluster.scheduler_address
 
-    notebook_path = os.path.join(
-        dirname(TEST_PATH),
-        "examples/scaling-criteo/",
-        "01-Download-Convert.ipynb",
-    )
-    _run_notebook(tmpdir, notebook_path)
+        def _nb_modify(line):
+            # Use cuda_cluster "fixture" port rather than allowing notebook
+            # to deploy a LocalCUDACluster within the subprocess
+            line = line.replace("download_criteo = True", "download_criteo = False")
+            line = line.replace("cluster = None", f"cluster = '{scheduler_port}'")
+            return line
+
+        notebook_path = os.path.join(
+            dirname(TEST_PATH),
+            "examples/scaling-criteo/",
+            "01-Download-Convert.ipynb",
+        )
+        _run_notebook(tmpdir, notebook_path, _nb_modify)
 
 
 @pytest.mark.skip(reason="Need to install pydot / use mock data on this")