Skip to content

Add class to yield results form onnx model and computes differences between two runs #71

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Feb 6, 2024
Merged
Prev Previous commit
Next Next commit
text
  • Loading branch information
xadupre committed Feb 5, 2024
commit 09e79d2f6902705b921799d3f5461a5e24e40164
61 changes: 60 additions & 1 deletion _unittests/ut_reference/test_evaluator_yield.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@
make_tensor_value_info,
)
from onnx_array_api.ext_test_case import ExtTestCase
from onnx_array_api.reference import YieldEvaluator, ResultType, DistanceExecution
from onnx_array_api.reference import (
YieldEvaluator,
ResultType,
DistanceExecution,
ResultExecution,
)


class TestArrayTensor(ExtTestCase):
Expand Down Expand Up @@ -355,6 +360,60 @@ def test_distance_sequence_diff2(self):
self.assertEqual(d, 5)
self.assertEqual(align, [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)])

def test_distance_sequence_str(self):
s1 = [
(ResultType.INPUT, np.dtype("float32"), (2, 2), "ABCD", None, "A"),
(ResultType.INPUT, np.dtype("float32"), (2, 2), "ABCD", None, "B"),
(ResultType.INPUT, np.dtype("float32"), (2, 3), "ABCD", None, "X"),
(ResultType.RESULT, np.dtype("float32"), (2, 2), "CEIO", "Exp", "H"),
(
ResultType.RESULT,
np.dtype("float32"),
(2, 2),
"CEIO",
"LinearRegression",
"Y1",
),
(ResultType.RESULT, np.dtype("float32"), (2, 2), "CEIO", "Abs", "Y"),
(ResultType.OUTPUT, np.dtype("float32"), (2, 2), "CEIO", None, "Y"),
]
s2 = [
(ResultType.INPUT, np.dtype("float32"), (2, 2), "ABCD", None, "A"),
(ResultType.INPUT, np.dtype("float32"), (2, 2), "ABCD", None, "B"),
(ResultType.INPUT, np.dtype("float32"), (2, 2), "ABCD", None, "X"),
(
ResultType.RESULT,
np.dtype("float32"),
(2, 2),
"CEIO",
"LinearRegression",
"Y1",
),
(ResultType.RESULT, np.dtype("float32"), (2, 3), "CEIP", "Abs", "Z"),
(ResultType.OUTPUT, np.dtype("float32"), (2, 2), "CEIP", None, "Y"),
]
s1 = [ResultExecution(*s) for s in s1]
s2 = [ResultExecution(*s) for s in s2]

dc = DistanceExecution()
d, align = dc.distance_sequence(s1, s2)
self.assertEqual(d, 1008)
self.assertEqual(align, [(0, 0), (1, 1), (2, 2), (3, 2), (4, 3), (5, 4)])
text = dc.to_str(s1, s2, align)
expected = """
=|INPUTfloat322x2ABCDA|INPUTfloat322x2ABCDA
=|INPUTfloat322x2ABCDB|INPUTfloat322x2ABCDB
~|INPUTfloat322x3ABCDX|INPUTfloat322x2ABCDX
-|RESULTfloat322x2CEIOExpH|
=|RESULTfloat322x2CEIOLinearReY1|RESULTfloat322x2CEIOLinearReY1
~|RESULTfloat322x2CEIOAbsY|RESULTfloat322x3CEIPAbsZ
""".replace(
" ", ""
).strip(
"\n "
)
self.assertEqual(expected, text.replace(" ", "").strip("\n"))


if __name__ == "__main__":
unittest.main(verbosity=2)
7 changes: 6 additions & 1 deletion onnx_array_api/reference/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@
)
from onnx.reference.op_run import to_array_extended
from .evaluator import ExtendedReferenceEvaluator
from .evaluator_yield import DistanceExecution, ResultType, YieldEvaluator
from .evaluator_yield import (
DistanceExecution,
ResultExecution,
ResultType,
YieldEvaluator,
)


def from_array_extended(tensor: np.array, name: Optional[str] = None) -> TensorProto:
Expand Down
142 changes: 118 additions & 24 deletions onnx_array_api/reference/evaluator_yield.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
from dataclasses import dataclass
from typing import Any, Dict, List, Iterator, Optional, Tuple
from enum import IntEnum
import numpy as np
from onnx import ModelProto
from .evaluator import ExtendedReferenceEvaluator


def _align(res: str, limit: int) -> str:
if len(res) == limit:
return res
if len(res) > limit:
return res[:limit]
return res + " " * (limit - len(res))


class ResultType(IntEnum):
RESULT = 1
INITIALIZER = 2
Expand All @@ -16,7 +25,47 @@ def __repr__(self):
return f"{self.__class__.__name__}.{self._name_}"


RESULT_TYPE = Tuple[ResultType, "np.dtype", Tuple[int, ...], str, str, str]
@dataclass
class ResultExecution:
"""
The description of a result.
"""

kind: ResultType
dtype: object
shape: tuple
summary: str
op_type: str
name: str

def __len__(self) -> int:
return 6

def __getitem__(self, i: int) -> Any:
if i == 0:
return self.kind
if i == 1:
return self.dtype
if i == 2:
return self.shape
if i == 3:
return self.summary
if i == 4:
return self.op_type
if i == 5:
return self.name
raise IndexError(f"i={i} out of boundary")

def __str__(self):
els = [
_align(self.kind._name_, 6),
_align(str(self.dtype).replace("dtype(", "").replace(")", ""), 8),
_align("x".join(map(str, self.shape)), 15),
self.summary,
_align(self.op_type or "", 8),
self.name or "",
]
return " ".join(els)


class YieldEvaluator:
Expand Down Expand Up @@ -101,27 +150,6 @@ def enumerate_results(
)
yield ResultType.OUTPUT, name, results[name], None

def enumerate_summarized(
self,
output_names: Optional[List[str]] = None,
feed_inputs: Optional[Dict[str, Any]] = None,
) -> Iterator[RESULT_TYPE]:
"""
Executes the onnx model and enumerate intermediate results without their names.

Args:
output_names: requested outputs by names, None for all
feed_inputs: dictionary `{ input name: input value }`

Returns:
iterator on tuple(result kind, node.type, dtype, shape, value, result name)
"""
for kind, name, value, op_type in self.enumerate_results(
output_names, feed_inputs
):
summary = self.make_summary(value)
yield kind, value.dtype, value.shape, summary, op_type, name

def make_summary(self, value: Any, length: int = 4, modulo: int = 26) -> str:
"""
Create a short string summarizing the value (discretization).
Expand All @@ -147,6 +175,29 @@ def make_summary(self, value: Any, length: int = 4, modulo: int = 26) -> str:
s = "".join([chr(65 + i) for i in value4i])
return s

def enumerate_summarized(
self,
output_names: Optional[List[str]] = None,
feed_inputs: Optional[Dict[str, Any]] = None,
) -> Iterator[ResultExecution]:
"""
Executes the onnx model and enumerate intermediate results without their names.

Args:
output_names: requested outputs by names, None for all
feed_inputs: dictionary `{ input name: input value }`

Returns:
iterator on tuple(result kind, node.type, dtype, shape, value, result name)
"""
for kind, name, value, op_type in self.enumerate_results(
output_names, feed_inputs
):
summary = self.make_summary(value)
yield ResultExecution(
kind, value.dtype, value.shape, summary, op_type, name
)


class DistanceExecution:
"""
Expand All @@ -170,7 +221,7 @@ def __init__(self, max_lag: int = 50):
self.max_lag = max_lag
self.insert_cost = 1000

def distance_pair(self, r1: RESULT_TYPE, r2: RESULT_TYPE) -> float:
def distance_pair(self, r1: ResultExecution, r2: ResultExecution) -> float:
"""
(ResultType.RESULT, np.dtype("float32"), (2, 2), "CEIO", "Abs"),

Expand Down Expand Up @@ -214,7 +265,7 @@ def _cost_summary(self, s1: str, s2: str) -> float:
return d

def distance_sequence(
self, s1: List[RESULT_TYPE], s2: List[RESULT_TYPE]
self, s1: List[ResultExecution], s2: List[ResultExecution]
) -> Tuple[float, List[Tuple[int, int]]]:
"""
Computes the distance between two sequences of results.
Expand Down Expand Up @@ -258,3 +309,46 @@ def distance_sequence(
way.append(last)
last = predecessor[last]
return distance[len(s1) - 1, len(s2) - 1], list(reversed(way))[1:]

def to_str(
self,
s1: List[ResultExecution],
s2: List[ResultExecution],
alignment: List[Tuple[int, int]],
column_size: int = 50,
) -> str:
"""
Prints out the alignment between two sequences into a string.
:param s1: first sequence
:param s2: second sequence
:param alignment: alignment
:param column_size: column size
:return: test
"""
rows = []
last = -1, -1
for i, j in alignment:
assert i < len(s1), f"Unexpected value i={i} >= len(s1)={len(s1)}"
assert j < len(s2), f"Unexpected value i={j} >= len(s2)={len(s2)}"
expected = last[0] + 1, last[1] + 1

if expected == (i, j):
d1 = s1[i]
d2 = s2[j]
d = self.distance_pair(d1, d2)
symbol = "=" if d == 0 else "~"
rows.append(
f"{symbol} | {_align(str(d1), column_size)} | {_align(str(d2), column_size)}"
)
elif i == last[0]:
d2 = s2[j]
rows.append(
f"+ | {_align('', column_size)} | {_align(str(d2), column_size)} "
)
else:
d1 = s1[i]
rows.append(
f"- | {_align(str(d1), column_size)} | {_align('', column_size)}"
)
last = i, j
return "\n".join(rows)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy