|
28 | 28 | import json |
29 | 29 | import logging |
30 | 30 |
|
31 | | -from merlin.core.dispatch import concat_columns |
32 | | -from merlin.dag import ColumnSelector, Supports |
| 31 | +from merlin.dag import ColumnSelector, DataFormats, Supports |
| 32 | +from merlin.dag.executors import LocalExecutor, _convert_format, _data_format |
33 | 33 | from merlin.schema import Tags |
34 | | -from merlin.systems.triton.conversions import convert_format, match_representations |
| 34 | +from merlin.systems.triton.conversions import match_representations |
35 | 35 | from merlin.table import TensorTable |
36 | 36 |
|
37 | 37 | LOG = logging.getLogger("merlin-systems") |
@@ -65,8 +65,10 @@ def __init__(self, workflow, output_dtypes, model_config, model_device): |
65 | 65 | f"The following columns were not found in the workflow's output: {missing_cols}" |
66 | 66 | ) |
67 | 67 |
|
68 | | - # recurse over all column groups, initializing operators for inference pipeline |
69 | | - self._initialize_ops(self.workflow.output_node) |
| 68 | + # recurse over all column groups, initializing operators for inference pipeline. |
| 69 | + # (disabled for now while we sort out whether and how we want to use C++ implementations |
| 70 | + # of NVTabular operators for performance optimization) |
| 71 | + # self._initialize_ops(self.workflow.output_node) |
70 | 72 |
|
71 | 73 | def _initialize_ops(self, workflow_node, visited=None): |
72 | 74 | if visited is None: |
@@ -97,98 +99,13 @@ def _initialize_ops(self, workflow_node, visited=None): |
97 | 99 | self._initialize_ops(parent, visited) |
98 | 100 |
|
99 | 101 | def run_workflow(self, input_tensors): |
100 | | - # use our NVTabular workflow to transform the dataset |
101 | | - transformed, kind = self._transform_tensors(input_tensors, self.workflow.output_node) |
102 | | - |
103 | | - # if we don't have tensors in numpy format, convert back so that the we can return |
104 | | - # to triton |
105 | | - if kind != Supports.CPU_DICT_ARRAY: |
106 | | - transformed, kind = convert_format(transformed, kind, Supports.CPU_DICT_ARRAY) |
107 | | - |
108 | | - transformed = TensorTable(transformed).to_dict() |
109 | | - output_dict = match_representations(self.workflow.output_schema, transformed) |
110 | | - |
111 | | - for key, value in output_dict.items(): |
112 | | - output_dict[key] = value.astype(self.output_dtypes[key]) |
113 | | - |
114 | | - return output_dict |
115 | | - |
116 | | - def _transform_tensors(self, input_tensors, workflow_node): |
117 | | - upstream_inputs = [] |
118 | | - |
119 | | - # Gather inputs from the parents and dependency nodes |
120 | | - if workflow_node.parents_with_dependencies: |
121 | | - for parent in workflow_node.parents_with_dependencies: |
122 | | - upstream_tensors, upstream_kind = self._transform_tensors(input_tensors, parent) |
123 | | - if upstream_tensors is not None and upstream_kind: |
124 | | - upstream_inputs.append((upstream_tensors, upstream_kind)) |
125 | | - |
126 | | - # Gather additional input columns from the original input tensors |
127 | | - if workflow_node.selector: |
128 | | - selector_columns = workflow_node.selector.names |
129 | | - to_remove = [] |
130 | | - for upstream_tensors, upstream_kind in upstream_inputs: |
131 | | - for col in selector_columns: |
132 | | - if col in upstream_tensors: |
133 | | - to_remove.append(col) |
134 | | - for col in set(to_remove): |
135 | | - selector_columns.remove(col) |
136 | | - |
137 | | - if selector_columns: |
138 | | - selected_tensors = {c: input_tensors[c] for c in selector_columns} |
139 | | - selected_kinds = Supports.CPU_DICT_ARRAY |
140 | | - upstream_inputs.append((selected_tensors, selected_kinds)) |
141 | | - |
142 | | - # Standardize the formats |
143 | | - tensors, kind = None, None |
144 | | - for upstream_tensors, upstream_kind in upstream_inputs: |
145 | | - if tensors is None: |
146 | | - tensors, kind = upstream_tensors, upstream_kind |
147 | | - else: |
148 | | - if kind != upstream_kind: |
149 | | - # we have multiple different kinds of data here (dataframe/array on cpu/gpu) |
150 | | - # we need to convert to a common format here first before concatenating. |
151 | | - op = workflow_node.op |
152 | | - if op and hasattr(op, "inference_supports"): |
153 | | - target_kind = op.inference_supports |
154 | | - else: |
155 | | - target_kind = Supports.CPU_DICT_ARRAY |
156 | | - # note : the 2nd convert_format call needs to be stricter in what the kind is |
157 | | - # (exact match rather than a bitmask of values) |
158 | | - tensors, kind = convert_format(tensors, kind, target_kind) |
159 | | - upstream_tensors, _ = convert_format(upstream_tensors, upstream_kind, kind) |
160 | | - |
161 | | - tensors = self.concat_tensors([tensors, upstream_tensors], kind) |
162 | | - |
163 | | - # Run the transform |
164 | | - if tensors is not None and kind and workflow_node.op: |
165 | | - try: |
166 | | - # if the op doesn't support the current kind - we need to convert |
167 | | - if ( |
168 | | - hasattr(workflow_node, "inference_supports") |
169 | | - and not workflow_node.inference_supports & kind |
170 | | - ): |
171 | | - tensors, kind = convert_format(tensors, kind, workflow_node.inference_supports) |
172 | | - |
173 | | - tensors = workflow_node.op.transform( |
174 | | - workflow_node.input_columns, |
175 | | - tensors, |
176 | | - ) |
177 | | - |
178 | | - except Exception: |
179 | | - LOG.exception("Failed to transform operator %s", workflow_node.op) |
180 | | - raise |
| 102 | + transformable = TensorTable(input_tensors).to_df() |
| 103 | + transformed = LocalExecutor().transform(transformable, self.workflow.graph) |
181 | 104 |
|
182 | | - return tensors, kind |
| 105 | + if _data_format(transformed) != DataFormats.NUMPY_DICT_ARRAY: |
| 106 | + transformed = _convert_format(transformed, DataFormats.NUMPY_DICT_ARRAY) |
183 | 107 |
|
184 | | - def concat_tensors(self, tensors, kind): |
185 | | - if kind & (Supports.GPU_DATAFRAME | Supports.CPU_DATAFRAME): |
186 | | - return concat_columns(tensors) |
187 | | - else: |
188 | | - output = tensors[0] |
189 | | - for tensor in tensors[1:]: |
190 | | - output.update(tensor) |
191 | | - return output |
| 108 | + return match_representations(self.workflow.output_schema, transformed) |
192 | 109 |
|
193 | 110 | def _get_param(self, config, *args, default=None): |
194 | 111 | config_element = config["parameters"] |
|
0 commit comments