Repository: NVIDIA-Merlin/NVTabular

Issue:
[BUG] KerasSequenceLoader can produce wrong categorical output
**Describe the bug**
The KerasSequenceLoader class can produce incorrect values for categoricals . It expects that the  cat_names passed in are already sorted, and if they aren't sorted the output tensors will be assigned incorerctly

**Steps/Code to reproduce bug**
Passing reverse ordering categorical names causes this test to fail - 
```python
def test_tf_catname_ordering(tmpdir):
    df = cudf.DataFrame({"cat1" : [1] * 100,
                         "cat2" : [2] * 100,
                         "cat3" : [3] * 100,
                          "label": [0] * 100 })
    path = os.path.join(tmpdir, "dataset.parquet")
    df.to_parquet(path)
    cat_names = ["cat3", "cat2", "cat1"]
    cont_names = []
    label_name = ["label"]

    data_itr = tf_dataloader.KerasSequenceLoader(
        [path],
        cat_names=cat_names,
        cont_names=cont_names,
        batch_size=10,
        label_names=label_name,
        shuffle=False,
    )

    for X, y in data_itr:
        assert list(X["cat1"].numpy()) == [1] * 10
        assert list(X["cat2"].numpy()) == [2] * 10
        assert list(X["cat3"].numpy()) == [3] * 10
```
All of the cat1 values should be 1 here - but they are all 3's instead


Discussion:

Resolving patch:
diff --git a/nvtabular/loader/tensorflow.py b/nvtabular/loader/tensorflow.py
--- a/nvtabular/loader/tensorflow.py
+++ b/nvtabular/loader/tensorflow.py
@@ -21,6 +21,7 @@
 from nvtabular.io.dataset import Dataset
 from nvtabular.loader.backend import DataLoader
 from nvtabular.loader.tf_utils import configure_tensorflow, get_dataset_schema_from_feature_columns
+from nvtabular.ops import _get_embedding_order
 
 from_dlpack = configure_tensorflow()
 
@@ -211,6 +212,11 @@ def __init__(
         )
         cat_names, cont_names = _validate_schema(feature_columns, cat_names, cont_names)
 
+        # sort the ccolumns to avoid getting incorrect output
+        # (https://github.com/NVIDIA/NVTabular/issues/412)
+        cat_names = _get_embedding_order(cat_names)
+        cont_names = _get_embedding_order(cont_names)
+
         assert devices is None or len(devices) == 1  # TODO: figure out multi-gpu support
         devices = devices or [0]
         DataLoader.__init__(


Test patch:
diff --git a/tests/unit/test_tf_dataloader.py b/tests/unit/test_tf_dataloader.py
--- a/tests/unit/test_tf_dataloader.py
+++ b/tests/unit/test_tf_dataloader.py
@@ -14,6 +14,8 @@
 # limitations under the License.
 #
 
+import os
+
 import cudf
 import numpy as np
 import pytest
@@ -28,6 +30,31 @@
 tf_dataloader = pytest.importorskip("nvtabular.loader.tensorflow")
 
 
+def test_tf_catname_ordering(tmpdir):
+    df = cudf.DataFrame(
+        {"cat1": [1] * 100, "cat2": [2] * 100, "cat3": [3] * 100, "label": [0] * 100}
+    )
+    path = os.path.join(tmpdir, "dataset.parquet")
+    df.to_parquet(path)
+    cat_names = ["cat3", "cat2", "cat1"]
+    cont_names = []
+    label_name = ["label"]
+
+    data_itr = tf_dataloader.KerasSequenceLoader(
+        [path],
+        cat_names=cat_names,
+        cont_names=cont_names,
+        batch_size=10,
+        label_names=label_name,
+        shuffle=False,
+    )
+
+    for X, y in data_itr:
+        assert list(X["cat1"].numpy()) == [1] * 10
+        assert list(X["cat2"].numpy()) == [2] * 10
+        assert list(X["cat3"].numpy()) == [3] * 10
+
+
 # TODO: include use_columns option
 # TODO: include parts_per_chunk test
 @pytest.mark.parametrize("gpu_memory_frac", [0.01, 0.06])