To be standardarized.
- Bug-introducing model: such as
model.onnx
for ONNX models and a saved-model folder in TensorFlow. - Oracle:
oracle.pkl
contains a dictionary"input"
: A dictionary of input."output"
: A dictionary of output if the results are expected to be compared orNone
if the output containsNaN
orInf
as undefined behaviours.
- Meta information:
meta.json
meta information."system"
: like"tvm-gpu"
and"ort-cpu"
"version"
: a version string hooked from${SystemPackage}.__version__
"symptom"
:"crash"
or"inconsistency"
or"timeout"
"version_id"
(optional): an identifier of the system's version (e.g., git hash or version strings)
An abstract operator contains information to construct a materialized operator. We will start with a simplified pooling 2D example.
The initializer of Abstract Operators (AO) takes a list of symbolic integers. In this way, during model generation, we can construct operators by feeding a certain number of symbolic integers.
class Pool2d(UnaryOpBase):
def __init__(self, kw, kh, stride, pad):
# Step 1: Invoke daddy's constructor
super().__init__()
# Step 2: Take arguments as attributes
self.kw, self.kh = kw, kh
self.stride = stride
self.pad = pad
# Step 3: Define desired operator input and output ranks
# Typing: List[Tuple] where each tuple has some ranks
# [ input.0(ok_rank.0, ok_rank.1, ...), input.1(...), ... ]
# Why [(4,)]?
# 1. Pooling2D only takes one input/output => one tuple;
# 2. Pooling2D only accepts NCHW tensors => the only viable dim is 4;
self.inp_ranks = [(4,)]
self.out_ranks = [(4,)]
type_transfer
is a type inference function to infer the output type (shape and data type) given inputs’ type information.
def type_infer(self, itensors: List[AbsTensor]):
n, c, h, w = itensors[0].shape
return [ # List
AbsTensor(shape=[n, c,
((h - self.kh) + 2 * self.pad) // self.stride,
((w - self.kw) + 2 * self.pad) // self.stride,
], dtype=itensors[0].dtype)]
requires
returns constraints (predicates) that must be satisfied when inserting this operator into a computational graph.
def requires(self, itensors: List[AbsTensor]):
n, c, h, w = itensors[0].shape
return [ # List
self.kh >= 1, self.kw >= 1, self.stride >= 1, self.pad >= 0,
self.kh <= h + 2 * self.pad,
self.kw <= w + 2 * self.pad,
]
- Viable data types:
inp_dtypes
: Similar toself.inp_ranks
, it contains a list of independent and viable input data types.out_dtypes
Sometimes, we want to define an AO that can take variadic numbers of arguments, e.g., Padding
can take a padding list which can be of 4 pad sizes (for (H and W) x (left-most and right-most)) for 4D tensors (e.g., NCHW) and 6 pad sizes for 5D tensors.
Therefore, we need to let the model generator know how many arguments / symbolic integers a (padding) operator accepts. To specify this information, we overload the class data member num_var_param: List[int]
which takes a list of integers, each of which is an acceptable number of arguments. For example, to create a padding operator that accepts 4 ~ 6D tensors.
Operators like Concat
require input tensors to have the same shape (and thus ranks).
To do so, we overload the same_inp_dims
class variable to True
:
class Concat(AbsOpBase)
...
same_inp_dims = True
...
def __init__(...):
...