ONE - On-device Neural Engine
Loading...
Searching...
No Matches
onnx_legalizer Namespace Reference

Data Structures

class  _ModelTransformerHelper
 
class  _TensorInfo
 
class  LegalizeOptions
 

Functions

 _reverse_str (s)
 
 _parse_tensor_name (name)
 
 _get_tensor_infos (model)
 
 _dtype_to_np (dtype)
 
 _generate_one_direction_RNN (transformer, X, W, R, B, initial_h, clip, activation_name)
 
 _transform_unidirectional_RNN (transformer, original_node, x, tensor_infos, activation, clip, direction, hidden_size, layout)
 
 _transform_bidirectional_RNN (transformer, original_node, x, tensor_infos, activations, clip, hidden_size, layout)
 
 _legalize_RNN (transformer, tensor_infos, node)
 
 _generate_one_direction_LSTM (transformer, X, W, R, B, initial_h, initial_c, P, clip, act, dtype, hidden_size, batch_size)
 
 _transform_unidirectional_LSTM (transformer, original_node, x, tensor_infos, activations, clip, direction, hidden_size, layout)
 
 _transform_bidirectional_LSTM (transformer, original_node, x, tensor_infos, activations, clip, hidden_size, layout)
 
 _legalize_LSTM (transformer, tensor_infos, node)
 
 legalize (model, options)
 

Variables

 options = LegalizeOptions()
 
 unroll_lstm
 
 unroll_rnn
 
 model = onnx.load(sys.argv[1])
 

Function Documentation

◆ _dtype_to_np()

onnx_legalizer._dtype_to_np (   dtype)
protected
Convert onnx dtype value to numpy dtype class

For more types see:
https://github.com/onnx/onnx/blob/96516aecd4c110b0ac57eba08ac236ebf7205728/onnx/onnx.proto3#L484

Args:
    dtype (int): onnx dtype

Returns:
    numpy data type: numpy dtype, like np.float32

Definition at line 328 of file onnx_legalizer.py.

328def _dtype_to_np(dtype):
329 """Convert onnx dtype value to numpy dtype class
330
331 For more types see:
332 https://github.com/onnx/onnx/blob/96516aecd4c110b0ac57eba08ac236ebf7205728/onnx/onnx.proto3#L484
333
334 Args:
335 dtype (int): onnx dtype
336
337 Returns:
338 numpy data type: numpy dtype, like np.float32
339 """
340
341 if dtype == 1:
342 return np.float32
343 else:
344 raise NotImplementedError('unsupported data type')
345
346

Referenced by _transform_bidirectional_LSTM(), _transform_bidirectional_RNN(), _transform_unidirectional_LSTM(), and _transform_unidirectional_RNN().

◆ _generate_one_direction_LSTM()

onnx_legalizer._generate_one_direction_LSTM (   transformer,
  X,
  W,
  R,
  B,
  initial_h,
  initial_c,
  P,
  clip,
  act,
  dtype,
  hidden_size,
  batch_size 
)
protected
Generate subgraph for one direction of unrolled LSTM layer

Args:
    transformer (_ModelTransformerHelper): helper for model generation
    X (list of str): names of tensors in input sequence. Each tensor shape: [batch_size, input_size]
    W (str): name of concatenated weight tensor: [input, output, forget, cell]
    R (str): name of concatenated recurrence weights tensor: [input, output, forget, cell]
    B (str): name of concatenated bias tensor: [input, output, forget, cell]
    initial_h (str or None): name of tensor containing initial hidden state. Shape [batch_size, hidden_size]
    initial_c (str or None): name of tensor containing initial cell state. Shape [batch_size, hidden_size]
    P (str or None): name of concatenated peephole tensor: [input, output, forget]
    clip (float or None): range which clips input of activations
    act (dict of str):  activation functions {'f': 'Sigmoid', 'g': 'Tanh', 'h': 'Tanh'}
    dtype (numpy dtype): data type used in created LSTM operation
    hidden_size (int): hidden dimension
    batch_size (int): batch dimension

Definition at line 621 of file onnx_legalizer.py.

622 act, dtype, hidden_size, batch_size):
623 """Generate subgraph for one direction of unrolled LSTM layer
624
625 Args:
626 transformer (_ModelTransformerHelper): helper for model generation
627 X (list of str): names of tensors in input sequence. Each tensor shape: [batch_size, input_size]
628 W (str): name of concatenated weight tensor: [input, output, forget, cell]
629 R (str): name of concatenated recurrence weights tensor: [input, output, forget, cell]
630 B (str): name of concatenated bias tensor: [input, output, forget, cell]
631 initial_h (str or None): name of tensor containing initial hidden state. Shape [batch_size, hidden_size]
632 initial_c (str or None): name of tensor containing initial cell state. Shape [batch_size, hidden_size]
633 P (str or None): name of concatenated peephole tensor: [input, output, forget]
634 clip (float or None): range which clips input of activations
635 act (dict of str): activation functions {'f': 'Sigmoid', 'g': 'Tanh', 'h': 'Tanh'}
636 dtype (numpy dtype): data type used in created LSTM operation
637 hidden_size (int): hidden dimension
638 batch_size (int): batch dimension
639 """
640 # one direction LSTM:
641 #
642 # For details see:
643 # https://github.com/onnx/onnx/blob/5cf5feef5ec3fd5527b2fdb6c29780e3b705059f/docs/Changelog.md#LSTM-7
644 #
645 # it = f(Xt*(Wi^T) + Ht-1*(Ri^T) + Pi (.) Ct-1 + Wbi + Rbi)
646 # ft = f(Xt*(Wf^T) + Ht-1*(Rf^T) + Pf (.) Ct-1 + Wbf + Rbf)
647 # ct = g(Xt*(Wc^T) + Ht-1*(Rc^T) + Wbc + Rbc)
648 # Ct = ft (.) Ct-1 + it (.) ct
649 # ot = f(Xt*(Wo^T) + Ht-1*(Ro^T) + Po (.) Ct + Wbo + Rbo)
650 # Ht = ot (.) h(Ct)
651 #
652 # X - input tensor
653 # i - input gate
654 # o - output gate
655 # f - forget gate
656 # c - cell gate
657 # t - time step (t-1 means previous time step)
658 # W[iofc] - W parameter weight matrix for input, output, forget, and cell gates
659 # R[iofc] - R recurrence weight matrix for input, output, forget, and cell gates
660 # Wb[iofc] - W bias vectors for input, output, forget, and cell gates
661 # Rb[iofc] - R bias vectors for input, output, forget, and cell gates
662 # P[iof] - P peephole weight vector for input, output, and forget gates
663 # WB[iofc] - W parameter weight matrix for backward input, output, forget, and cell gates
664 # RB[iofc] - R recurrence weight matrix for backward input, output, forget, and cell gates
665 # WBb[iofc] - W bias vectors for backward input, output, forget, and cell gates
666 # RBb[iofc] - R bias vectors for backward input, output, forget, and cell gates
667 # PB[iof] - P peephole weight vector for backward input, output, and forget gates
668 # H - Hidden state
669
670 seq_length = len(X)
671 state_h_tensors = []
672
673 w_tensors = transformer.make_split(W, split_sizes=[hidden_size] * 4, axis=0)
674 W = {'i': w_tensors[0], 'o': w_tensors[1], 'f': w_tensors[2], 'c': w_tensors[3]}
675
676 r_tensors = transformer.make_split(R, split_sizes=[hidden_size] * 4, axis=0)
677 R = {'i': r_tensors[0], 'o': r_tensors[1], 'f': r_tensors[2], 'c': r_tensors[3]}
678
679 if B is not None:
680 separate_b_tensors = transformer.make_split(B,
681 split_sizes=[hidden_size] * 8,
682 axis=0)
683 b_tensors = []
684 for i in range(4):
685 b_tensors += [
686 transformer.make_add(separate_b_tensors[i], separate_b_tensors[i + 4])
687 ]
688 else:
689 b_tensors = [
690 transformer.make_constant_tensor(np.zeros(
691 (hidden_size), dtype=dtype), 'zero_b')
692 ] * 4
693 B = {'i': b_tensors[0], 'o': b_tensors[1], 'f': b_tensors[2], 'c': b_tensors[3]}
694
695 if initial_h is not None:
696 previous_h_state_tensor = initial_h
697 else:
698 previous_h_state_tensor = transformer.make_constant_tensor(
699 np.zeros((batch_size, hidden_size), dtype=dtype), 'initial_h')
700
701 if initial_c is not None:
702 previous_c_state_tensor = initial_c
703 else:
704 previous_c_state_tensor = transformer.make_constant_tensor(
705 np.zeros((batch_size, hidden_size), dtype=dtype), 'initial_c')
706
707 if P is not None:
708 p_tensors = transformer.make_split(P, split_sizes=[hidden_size] * 3, axis=0)
709 P = {'i': p_tensors[0], 'o': p_tensors[1], 'f': p_tensors[2]}
710 else:
711 zero = transformer.make_constant_tensor(np.zeros((hidden_size), dtype=dtype),
712 'zero_peephole')
713 P = {'i': zero, 'o': zero, 'f': zero}
714
715 for i in range(seq_length):
716 # it = f(Xt*(Wi^T) + Ht-1*(Ri^T) + Pi (.) Ct-1 + Wbi + Rbi)
717 it = transformer.make_gemm(X[i], W['i'], B['i'], trans_b=True)
718 it = transformer.make_gemm(previous_h_state_tensor, R['i'], it, trans_b=True)
719 peephole_it = transformer.make_mul(P['i'], previous_c_state_tensor)
720 it = transformer.make_add(it, peephole_it)
721 if clip is not None:
722 it = transformer.make_clip(it, min=-clip, max=clip)
723 it = transformer.make_act(it, act['f'])
724
725 # ft = f(Xt*(Wf^T) + Ht-1*(Rf^T) + Pf (.) Ct-1 + Wbf + Rbf)
726 ft = transformer.make_gemm(X[i], W['f'], B['f'], trans_b=True)
727 ft = transformer.make_gemm(previous_h_state_tensor, R['f'], ft, trans_b=True)
728 peephole_ft = transformer.make_mul(P['f'], previous_c_state_tensor)
729 ft = transformer.make_add(ft, peephole_ft)
730 if clip is not None:
731 ft = transformer.make_clip(ft, min=-clip, max=clip)
732 ft = transformer.make_act(ft, act['f'])
733
734 # ct = g(Xt*(Wc^T) + Ht-1*(Rc^T) + Wbc + Rbc)
735 ct = transformer.make_gemm(X[i], W['c'], B['c'], trans_b=True)
736 ct = transformer.make_gemm(previous_h_state_tensor, R['c'], ct, trans_b=True)
737 if clip is not None:
738 ct = transformer.make_clip(ct, min=-clip, max=clip)
739 ct = transformer.make_act(ct, act['g'])
740
741 # Ct = ft (.) Ct-1 + it (.) ct
742 ft_Ct = transformer.make_mul(ft, previous_c_state_tensor)
743 it_ct = transformer.make_mul(it, ct)
744 Ct = transformer.make_add(ft_Ct, it_ct)
745 previous_c_state_tensor = Ct
746
747 # ot = f(Xt*(Wo^T) + Ht-1*(Ro^T) + Po (.) Ct + Wbo + Rbo)
748 ot = transformer.make_gemm(X[i], W['o'], B['o'], trans_b=True)
749 ot = transformer.make_gemm(previous_h_state_tensor, R['o'], ot, trans_b=True)
750 peephole_ot = transformer.make_mul(P['o'], Ct)
751 ot = transformer.make_add(ot, peephole_ot)
752 if clip is not None:
753 ot = transformer.make_clip(ot, min=-clip, max=clip)
754 ot = transformer.make_act(ot, act['f'])
755
756 # Ht = ot (.) h(Ct)
757 Ht = transformer.make_act(Ct, act['h'])
758 Ht = transformer.make_mul(ot, Ht)
759 previous_h_state_tensor = Ht
760 state_h_tensors += [Ht]
761
762 return (state_h_tensors, previous_c_state_tensor)
763
764

Referenced by _transform_bidirectional_LSTM(), and _transform_unidirectional_LSTM().

◆ _generate_one_direction_RNN()

onnx_legalizer._generate_one_direction_RNN (   transformer,
  X,
  W,
  R,
  B,
  initial_h,
  clip,
  activation_name 
)
protected
Generate subgraph of one direction of unrolled RNN layer

Args:
    transformer (_ModelTransformerHelper): helper for model generation
    X (list of str): names of input tensors in sequence. Tensor shapes: [batch_size, input_size].
    W (str): name of weight tensor
    R (str): name of recurrence weight tensor
    B (str): name of bias tensor
    initial_h (str or None): name of tensor containing initial hidden state. Shape [batch_size, hidden_size]
    clip (float or None): range which clips input of activations
    act (str): activation function

Definition at line 347 of file onnx_legalizer.py.

348 activation_name):
349 """Generate subgraph of one direction of unrolled RNN layer
350
351 Args:
352 transformer (_ModelTransformerHelper): helper for model generation
353 X (list of str): names of input tensors in sequence. Tensor shapes: [batch_size, input_size].
354 W (str): name of weight tensor
355 R (str): name of recurrence weight tensor
356 B (str): name of bias tensor
357 initial_h (str or None): name of tensor containing initial hidden state. Shape [batch_size, hidden_size]
358 clip (float or None): range which clips input of activations
359 act (str): activation function
360 """
361 # one direction RNN:
362 #
363 # For details see:
364 # https://github.com/onnx/onnx/blob/5cf5feef5ec3fd5527b2fdb6c29780e3b705059f/docs/Changelog.md#RNN-7
365 #
366 # H = f(X*(W^T) + h*(R^T) + B)
367 #
368 # H - new hidden state
369 # h - previous hidden state
370 # X - current input
371 # W - input weights matrix
372 # R - reccurent weights matrix
373 # Wb - input weights matmul bias
374 # Rb - reccurent weights matmul bias
375 # f - activation function
376
377 seq_length = len(X)
378 first_iter = 0
379 state_tensors = []
380 if initial_h is not None:
381 previous_state_tensor = initial_h
382 else:
383 first_iter = 1
384 state_tensor = transformer.make_gemm(X[0], W, B, trans_b=True)
385 if clip != None:
386 state_tensor = transformer.make_clip(state_tensor, min=-clip, max=clip)
387 previous_state_tensor = transformer.make_act(state_tensor, activation_name)
388 state_tensors += [previous_state_tensor]
389
390 for i in range(first_iter, seq_length):
391 state_tensor = transformer.make_gemm(X[i], W, B, trans_b=True)
392 state_tensor = transformer.make_gemm(previous_state_tensor,
393 R,
394 state_tensor,
395 trans_b=True)
396 if clip != None:
397 state_tensor = transformer.make_clip(state_tensor, min=-clip, max=clip)
398 previous_state_tensor = transformer.make_act(state_tensor, activation_name)
399 state_tensors += [previous_state_tensor]
400 return state_tensors
401
402

Referenced by _transform_bidirectional_RNN(), and _transform_unidirectional_RNN().

◆ _get_tensor_infos()

onnx_legalizer._get_tensor_infos (   model)
protected
Infer tensor shapes and dtypes
Args:
    model (onnx.onnx_ml_pb2.ModelProto): model to process

Returns:
    dict from str to _TensorInfo: maps tensor name to shape and dtype information

Definition at line 304 of file onnx_legalizer.py.

304def _get_tensor_infos(model):
305 """Infer tensor shapes and dtypes
306 Args:
307 model (onnx.onnx_ml_pb2.ModelProto): model to process
308
309 Returns:
310 dict from str to _TensorInfo: maps tensor name to shape and dtype information
311 """
312
313 inferred_shape_model = onnx.shape_inference.infer_shapes(model)
314
315 infos = {}
316 for tensor in list(inferred_shape_model.graph.value_info) + list(
317 inferred_shape_model.graph.input):
318 info = _TensorInfo(tensor.type.tensor_type.elem_type, [])
319 for dim in tensor.type.tensor_type.shape.dim:
320 info.shape += [dim.dim_value]
321 infos[tensor.name] = info
322
323 for tensor in list(model.graph.initializer):
324 infos[tensor.name] = _TensorInfo(tensor.data_type, tensor.dims)
325 return infos
326
327

Referenced by legalize().

◆ _legalize_LSTM()

onnx_legalizer._legalize_LSTM (   transformer,
  tensor_infos,
  node 
)
protected
Unroll LSTM operation

Args:
    transformer (_ModelTransformerHelper): transformation helper
    tensor_infos (dict from str to _TensorInfo): dict maps tensor name to it's shape and dtype info
    node (onnx.onnx_ml_pb2.NodeProto): LSTM operation to unroll

Definition at line 947 of file onnx_legalizer.py.

947def _legalize_LSTM(transformer, tensor_infos, node):
948 """Unroll LSTM operation
949
950 Args:
951 transformer (_ModelTransformerHelper): transformation helper
952 tensor_infos (dict from str to _TensorInfo): dict maps tensor name to it's shape and dtype info
953 node (onnx.onnx_ml_pb2.NodeProto): LSTM operation to unroll
954 """
955 inputs = node.input
956 if len(inputs) > 4 and inputs[4] != '':
957 raise NotImplementedError('Variadic length of output is not supported')
958 # attributes
959 activation_alpha = []
960 activation_beta = []
961 activations = ['Sigmoid', 'Tanh', 'Tanh'] * 2
962 clip = None
963 direction = 'forward'
964 hidden_size = 0
965 input_forget = 0
966 layout = 0
967
968 for attr in node.attribute:
969 if attr.name == 'activation_alpha':
970 activation_alpha = attr.floats
971 if attr.name == 'activation_beta':
972 activation_beta = attr.floats
973 if attr.name == 'activations':
974 activations = list(map(lambda item: item.decode('UTF-8'), list(attr.strings)))
975 if attr.name == 'clip':
976 clip = attr.f
977 if attr.name == 'direction':
978 direction = attr.s.decode('UTF-8')
979 if attr.name == 'hidden_size':
980 hidden_size = attr.i
981 if attr.name == 'input_forget':
982 input_forget = attr.i
983 if attr.name == 'layout':
984 layout = attr.i
985
986 if len(activation_alpha) > 0 or len(activation_beta) > 0:
987 raise NotImplementedError('Unsupported parameters for LSTM activations')
988
989 for act in activations:
990 if act not in ['Relu', 'Tanh', 'Sigmoid']:
991 raise NotImplementedError('Unsupported activation function')
992
993 if input_forget != 0:
994 raise NotImplementedError('Unsupported input_forget attribute value')
995
996 seq_length_dim = layout
997 seq_length = tensor_infos[inputs[0]].shape[seq_length_dim]
998 if hidden_size == 0:
999 hidden_size = tensor_infos[inputs[2]].shape[2]
1000
1001 input_split_tensor = transformer.make_split(inputs[0],
1002 split_sizes=[1] * seq_length,
1003 axis=seq_length_dim)
1004 x = []
1005 for i in range(len(input_split_tensor)):
1006 input_frame_tensor = input_split_tensor[i]
1007 squeezed_frame_tensor = transformer.make_squeeze(input_frame_tensor, axes=[0])
1008 x += [squeezed_frame_tensor]
1009
1010 if direction in ['forward', 'reverse']:
1011 _transform_unidirectional_LSTM(transformer, node, x, tensor_infos, activations,
1012 clip, direction, hidden_size, layout)
1013 elif direction == 'bidirectional':
1014 _transform_bidirectional_LSTM(transformer, node, x, tensor_infos, activations,
1015 clip, hidden_size, layout)
1016 else:
1017 raise RuntimeError('Unknown LSTM type')
1018
1019 transformer.mark_for_deletion(node)
1020
1021

References _transform_bidirectional_LSTM(), and _transform_unidirectional_LSTM().

Referenced by legalize().

◆ _legalize_RNN()

onnx_legalizer._legalize_RNN (   transformer,
  tensor_infos,
  node 
)
protected
Unroll RNN operation

Args:
    transformer (_ModelTransformerHelper): transformation helper
    tensor_infos (dict from str to _TensorInfo): dict maps tensor name to it's shape and dtype info
    node (onnx.onnx_ml_pb2.NodeProto): RNN operation to unroll

Definition at line 552 of file onnx_legalizer.py.

552def _legalize_RNN(transformer, tensor_infos, node):
553 """Unroll RNN operation
554
555 Args:
556 transformer (_ModelTransformerHelper): transformation helper
557 tensor_infos (dict from str to _TensorInfo): dict maps tensor name to it's shape and dtype info
558 node (onnx.onnx_ml_pb2.NodeProto): RNN operation to unroll
559 """
560 inputs = node.input
561 if len(inputs) > 4 and inputs[4] != '':
562 raise NotImplementedError('Variadic length of output is not supported')
563 # attributes
564 activation_alpha = []
565 activation_beta = []
566 activations = ['Tanh', 'Tanh']
567 clip = None
568 direction = 'forward'
569 hidden_size = 0
570 layout = 0
571
572 for attr in node.attribute:
573 if attr.name == 'activation_alpha':
574 activation_alpha = attr.floats
575 if attr.name == 'activation_beta':
576 activation_beta = attr.floats
577 if attr.name == 'activations':
578 activations = list(map(lambda item: item.decode('UTF-8'), list(attr.strings)))
579 if attr.name == 'clip':
580 clip = attr.f
581 if attr.name == 'direction':
582 direction = attr.s.decode('UTF-8')
583 if attr.name == 'hidden_size':
584 hidden_size = attr.i
585 if attr.name == 'layout':
586 layout = attr.i
587
588 if len(activation_alpha) > 0 or len(activation_beta) > 0:
589 raise NotImplementedError('Unsupported parameters for LSTM activations')
590
591 for act in activations:
592 if act not in ['Relu', 'Tanh', 'Sigmoid']:
593 raise NotImplementedError('Unsupported activation function')
594
595 seq_length_dim = layout
596 seq_length = tensor_infos[inputs[0]].shape[seq_length_dim]
597 if hidden_size == 0:
598 hidden_size = tensor_infos[inputs[2]].shape[2]
599
600 input_split_tensor = transformer.make_split(inputs[0],
601 split_sizes=[1] * seq_length,
602 axis=seq_length_dim)
603 x = []
604 for i in range(len(input_split_tensor)):
605 input_frame_tensor = input_split_tensor[i]
606 squeezed_frame_tensor = transformer.make_squeeze(input_frame_tensor, axes=[0])
607 x += [squeezed_frame_tensor]
608
609 if direction in ['forward', 'reverse']:
610 _transform_unidirectional_RNN(transformer, node, x, tensor_infos, activations[0],
611 clip, direction, hidden_size, layout)
612 elif direction == 'bidirectional':
613 _transform_bidirectional_RNN(transformer, node, x, tensor_infos, activations,
614 clip, hidden_size, layout)
615 else:
616 raise RuntimeError('Unknown RNN type')
617
618 transformer.mark_for_deletion(node)
619
620

References _transform_bidirectional_RNN(), and _transform_unidirectional_RNN().

Referenced by legalize().

◆ _parse_tensor_name()

onnx_legalizer._parse_tensor_name (   name)
protected
Splits tensor name to base part and serial number

Most of tensor names have following format: "tensor_123".
This  function breaks name into two values: "tensor_" and 123.
Tensor names like this: "321" are broken into "" and 321.

Serial number is used to create unique tensor names using given base name.

Args:
    name (str): tensor name

Returns:
    tuple of str, int: base name and serial number of tensor

Definition at line 52 of file onnx_legalizer.py.

52def _parse_tensor_name(name):
53 """Splits tensor name to base part and serial number
54
55 Most of tensor names have following format: "tensor_123".
56 This function breaks name into two values: "tensor_" and 123.
57 Tensor names like this: "321" are broken into "" and 321.
58
59 Serial number is used to create unique tensor names using given base name.
60
61 Args:
62 name (str): tensor name
63
64 Returns:
65 tuple of str, int: base name and serial number of tensor
66 """
67 rev = _reverse_str(name)
68 m = re.match('(\d*)(.*)', rev)
69 if m.groups()[0] != '':
70 return (_reverse_str(m.groups()[1]), int(_reverse_str(m.groups()[0])))
71 else:
72 return (_reverse_str(m.groups()[1]), 0)
73
74

References _reverse_str().

◆ _reverse_str()

onnx_legalizer._reverse_str (   s)
protected

Definition at line 48 of file onnx_legalizer.py.

48def _reverse_str(s):
49 return ''.join(reversed(s))
50
51

Referenced by _parse_tensor_name().

◆ _transform_bidirectional_LSTM()

onnx_legalizer._transform_bidirectional_LSTM (   transformer,
  original_node,
  x,
  tensor_infos,
  activations,
  clip,
  hidden_size,
  layout 
)
protected
Generate Bidirectional unrolled LSTM

Args:
    transformer (_ModelTransformerHelper): transformation helper
    original_node (onnx.onnx_ml_pb2.NodeProto): bidirectional LSTM operation to unroll
    x (list of str): list of input tensors (input tensor split along "time" dimension)
    tensor_infos (dict from str to _TensorInfo): dict maps tensor name to it's shape and dtype info
    activations (list of str): list of length 6, containing names of forward and reverse activations
    clip (float or None): range which clips input of activations
    hidden_size (int): size of hidden state
    layout (int): See attribute description:
        https://github.com/onnx/onnx/blob/5cf5feef5ec3fd5527b2fdb6c29780e3b705059f/docs/Operators.md#attributes-37

Definition at line 837 of file onnx_legalizer.py.

838 activations, clip, hidden_size, layout):
839 """Generate Bidirectional unrolled LSTM
840
841 Args:
842 transformer (_ModelTransformerHelper): transformation helper
843 original_node (onnx.onnx_ml_pb2.NodeProto): bidirectional LSTM operation to unroll
844 x (list of str): list of input tensors (input tensor split along "time" dimension)
845 tensor_infos (dict from str to _TensorInfo): dict maps tensor name to it's shape and dtype info
846 activations (list of str): list of length 6, containing names of forward and reverse activations
847 clip (float or None): range which clips input of activations
848 hidden_size (int): size of hidden state
849 layout (int): See attribute description:
850 https://github.com/onnx/onnx/blob/5cf5feef5ec3fd5527b2fdb6c29780e3b705059f/docs/Operators.md#attributes-37
851 """
852
853 inputs = original_node.input
854 outputs = original_node.output
855
856 w = transformer.make_split(inputs[1], split_sizes=[1, 1], axis=0)
857 r = transformer.make_split(inputs[2], split_sizes=[1, 1], axis=0)
858 for d in range(2):
859 w[d] = transformer.make_squeeze(w[d], axes=[0])
860 r[d] = transformer.make_squeeze(r[d], axes=[0])
861
862 b = [None, None]
863 if len(inputs) > 3 and inputs[3] != '':
864 b = transformer.make_split(inputs[3], split_sizes=[1, 1], axis=0)
865 for d in range(2):
866 b[d] = transformer.make_squeeze(b[d], axes=[0])
867
868 initial_h = [None, None]
869 if len(inputs) > 5 and inputs[5] != '':
870 direction_dim = layout
871 initial_h = transformer.make_split(inputs[5],
872 split_sizes=[1, 1],
873 axis=direction_dim)
874 for d in range(2):
875 initial_h[d] = transformer.make_squeeze(initial_h[d], axes=[direction_dim])
876
877 initial_c = [None, None]
878 if len(inputs) > 6 and inputs[6] != '':
879 direction_dim = layout
880 initial_c = transformer.make_split(inputs[6],
881 split_sizes=[1, 1],
882 axis=direction_dim)
883 for d in range(2):
884 initial_c[d] = transformer.make_squeeze(initial_c[d], axes=[direction_dim])
885
886 p = [None, None]
887 if len(inputs) > 7 and inputs[7] != '':
888 p = transformer.make_split(inputs[7], split_sizes=[1, 1], axis=0)
889 for d in range(2):
890 p[d] = transformer.make_squeeze(p[d], axes=[0])
891
892 dtype = _dtype_to_np(tensor_infos[inputs[0]].dtype)
893 batch_size = tensor_infos[inputs[0]].shape[1 - layout]
894
895 act = [{
896 'f': activations[0],
897 'g': activations[1],
898 'h': activations[2]
899 }, {
900 'f': activations[3],
901 'g': activations[4],
902 'h': activations[5]
903 }]
904
905 state_f_h_tensors, state_f_c_tensor = _generate_one_direction_LSTM(
906 transformer, x, w[0], r[0], b[0], initial_h[0], initial_c[0], p[0], clip, act[0],
907 dtype, hidden_size, batch_size)
908 x.reverse()
909 state_b_h_tensors, state_b_c_tensor = _generate_one_direction_LSTM(
910 transformer, x, w[1], r[1], b[1], initial_h[1], initial_c[1], p[1], clip, act[1],
911 dtype, hidden_size, batch_size)
912 state_b_h_tensors.reverse()
913
914 y_direction_dim = layout + 1
915 y_c_direction_dim = layout
916 state_layout_tensors = []
917 seq_length_dim = layout
918 for f_h_state, b_h_state in zip(state_f_h_tensors, state_b_h_tensors):
919 state_f_layout_tensors = transformer.make_unsqueeze(
920 f_h_state, axes=[seq_length_dim, y_direction_dim])
921 state_b_layout_tensors = transformer.make_unsqueeze(
922 b_h_state, axes=[seq_length_dim, y_direction_dim])
923 state_layout_tensors += [
924 transformer.make_concat([state_f_layout_tensors, state_b_layout_tensors],
925 axis=y_direction_dim)
926 ]
927
928 last_f_state_layout_tensor = transformer.make_unsqueeze(state_f_h_tensors[-1],
929 axes=[y_c_direction_dim])
930 last_b_state_layout_tensor = transformer.make_unsqueeze(state_b_h_tensors[0],
931 axes=[y_c_direction_dim])
932
933 Y_h = outputs[1]
934 transformer.make_node('Concat',
935 [last_f_state_layout_tensor, last_b_state_layout_tensor], [Y_h],
936 axis=y_c_direction_dim)
937
938 Y_f_c = transformer.make_unsqueeze(state_f_c_tensor, axes=[y_c_direction_dim])
939 Y_b_c = transformer.make_unsqueeze(state_b_c_tensor, axes=[y_c_direction_dim])
940 Y_c = outputs[2]
941 transformer.make_node('Concat', [Y_f_c, Y_b_c], [Y_c], axis=y_c_direction_dim)
942
943 Y = outputs[0]
944 transformer.make_node('Concat', state_layout_tensors, [Y], axis=seq_length_dim)
945
946

References _dtype_to_np(), and _generate_one_direction_LSTM().

Referenced by _legalize_LSTM().

◆ _transform_bidirectional_RNN()

onnx_legalizer._transform_bidirectional_RNN (   transformer,
  original_node,
  x,
  tensor_infos,
  activations,
  clip,
  hidden_size,
  layout 
)
protected
Generate Bidirectional unrolled RNN

Args:
    transformer (_ModelTransformerHelper): transformation helper
    original_node (onnx.onnx_ml_pb2.NodeProto): bidirectional RNN operation to unroll
    x (list of str): list of input tensors (input tensor split along "time" dimension)
    tensor_infos (dict from str to _TensorInfo): dict maps tensor name to it's shape and dtype info
    activations (list of str): list of len (2) containing names of forward and reverse activations
    clip (float or None): range which clips input of activations
    hidden_size (int): size of hidden state
    layout (int): See attribute description:
        https://github.com/onnx/onnx/blob/5cf5feef5ec3fd5527b2fdb6c29780e3b705059f/docs/Operators.md#attributes-56

Definition at line 460 of file onnx_legalizer.py.

461 clip, hidden_size, layout):
462 """Generate Bidirectional unrolled RNN
463
464 Args:
465 transformer (_ModelTransformerHelper): transformation helper
466 original_node (onnx.onnx_ml_pb2.NodeProto): bidirectional RNN operation to unroll
467 x (list of str): list of input tensors (input tensor split along "time" dimension)
468 tensor_infos (dict from str to _TensorInfo): dict maps tensor name to it's shape and dtype info
469 activations (list of str): list of len (2) containing names of forward and reverse activations
470 clip (float or None): range which clips input of activations
471 hidden_size (int): size of hidden state
472 layout (int): See attribute description:
473 https://github.com/onnx/onnx/blob/5cf5feef5ec3fd5527b2fdb6c29780e3b705059f/docs/Operators.md#attributes-56
474 """
475
476 inputs = original_node.input
477 outputs = original_node.output
478 w_bi = transformer.make_split(inputs[1], split_sizes=[1, 1], axis=0)
479 r_bi = transformer.make_split(inputs[2], split_sizes=[1, 1], axis=0)
480 w = []
481 r = []
482 for d in range(2):
483 w += [transformer.make_squeeze(w_bi[d], axes=[0])]
484 r += [transformer.make_squeeze(r_bi[d], axes=[0])]
485
486 b = []
487 if len(inputs) > 3 and inputs[3] != '':
488 raw_bias_tensors = transformer.make_split(inputs[3], split_sizes=[1, 1], axis=0)
489 for d in range(2):
490 raw_bias_tensors_squeezed = transformer.make_squeeze(raw_bias_tensors[d],
491 axes=[0])
492 splitted_bias_tensors = transformer.make_split(raw_bias_tensors_squeezed,
493 split_sizes=[hidden_size] * 2,
494 axis=0)
495 b += [
496 transformer.make_add(splitted_bias_tensors[0], splitted_bias_tensors[1])
497 ]
498 else:
499 data_type = _dtype_to_np(tensor_infos[inputs[2]].dtype)
500 b = [
501 transformer.make_constant_tensor(np.zeros(hidden_size, dtype=data_type),
502 "zero_bias")
503 ] * 2
504 initial_h = [None, None]
505 if len(inputs) > 5 and inputs[5] != '':
506 direction_dim = layout
507 initial_h = transformer.make_split(inputs[5],
508 split_sizes=[1, 1],
509 axis=direction_dim)
510 for d in range(2):
511 initial_h[d] = transformer.make_squeeze(initial_h[d], axes=[direction_dim])
512
513 state_f_tensors = _generate_one_direction_RNN(transformer, x, w[0], r[0], b[0],
514 initial_h[0], clip, activations[0])
515 x.reverse()
516 state_b_tensors = _generate_one_direction_RNN(transformer, x, w[1], r[1], b[1],
517 initial_h[1], clip, activations[1])
518 state_b_tensors.reverse()
519
520 y_direction_dim = layout + 1
521 y_h_direction_dim = layout
522 state_layout_tensors = []
523 seq_length_dim = layout
524 seq_length = len(x)
525 for t in range(seq_length):
526 state_f = state_f_tensors[t]
527 state_b = state_b_tensors[t]
528 state_layout_tensors_f = transformer.make_unsqueeze(
529 state_f, axes=[seq_length_dim, y_direction_dim])
530 state_layout_tensors_b = transformer.make_unsqueeze(
531 state_b, axes=[seq_length_dim, y_direction_dim])
532 state_layout_tensors += [
533 transformer.make_concat([state_layout_tensors_f, state_layout_tensors_b],
534 axis=y_direction_dim)
535 ]
536
537 last_f_state_layout_tensor = transformer.make_unsqueeze(state_f_tensors[-1],
538 axes=[y_h_direction_dim])
539 last_b_state_layout_tensor = transformer.make_unsqueeze(state_b_tensors[0],
540 axes=[y_h_direction_dim])
541
542 # use low-level interface to attach to existing tensors
543 Y_h = outputs[1]
544 transformer.make_node('Concat',
545 [last_f_state_layout_tensor, last_b_state_layout_tensor], [Y_h],
546 axis=y_h_direction_dim)
547
548 Y = outputs[0]
549 transformer.make_node('Concat', state_layout_tensors, [Y], axis=seq_length_dim)
550
551

References _dtype_to_np(), and _generate_one_direction_RNN().

Referenced by _legalize_RNN().

◆ _transform_unidirectional_LSTM()

onnx_legalizer._transform_unidirectional_LSTM (   transformer,
  original_node,
  x,
  tensor_infos,
  activations,
  clip,
  direction,
  hidden_size,
  layout 
)
protected
Generate Simple (forward or reverse) unrolled LSTM

Args:
    transformer (_ModelTransformerHelper): transformation helper
    original_node (onnx.onnx_ml_pb2.NodeProto): unidirectional LSTM operation to unroll
    x (list of str): list of input tensors (input tensor split along "time" dimension)
    tensor_infos (dict from str to _TensorInfo): dict maps tensor name to it's shape and dtype info
    activations (list of str): list of length 3 containing names of activation functions
    clip (float or None): range which clips input of activations
    direction (str): "forward" or "reverse"
    hidden_size (int): size of hidden state
    layout (int): See attribute description:
        https://github.com/onnx/onnx/blob/5cf5feef5ec3fd5527b2fdb6c29780e3b705059f/docs/Operators.md#attributes-37

Definition at line 765 of file onnx_legalizer.py.

766 activations, clip, direction, hidden_size, layout):
767 """Generate Simple (forward or reverse) unrolled LSTM
768
769 Args:
770 transformer (_ModelTransformerHelper): transformation helper
771 original_node (onnx.onnx_ml_pb2.NodeProto): unidirectional LSTM operation to unroll
772 x (list of str): list of input tensors (input tensor split along "time" dimension)
773 tensor_infos (dict from str to _TensorInfo): dict maps tensor name to it's shape and dtype info
774 activations (list of str): list of length 3 containing names of activation functions
775 clip (float or None): range which clips input of activations
776 direction (str): "forward" or "reverse"
777 hidden_size (int): size of hidden state
778 layout (int): See attribute description:
779 https://github.com/onnx/onnx/blob/5cf5feef5ec3fd5527b2fdb6c29780e3b705059f/docs/Operators.md#attributes-37
780 """
781
782 inputs = original_node.input
783 outputs = original_node.output
784 if direction == 'reverse':
785 x.reverse()
786 w = transformer.make_squeeze(inputs[1], axes=[0])
787 r = transformer.make_squeeze(inputs[2], axes=[0])
788
789 b = None
790 if len(inputs) > 3 and inputs[3] != '':
791 b = transformer.make_squeeze(inputs[3], axes=[0])
792
793 initial_h = None
794 if len(inputs) > 5 and inputs[5] != '':
795 direction_dim = layout
796 initial_h = transformer.make_squeeze(inputs[5], axes=[direction_dim])
797
798 initial_c = None
799 if len(inputs) > 6 and inputs[6] != '':
800 direction_dim = layout
801 initial_c = transformer.make_squeeze(inputs[6], axes=[direction_dim])
802
803 p = None
804 if len(inputs) > 7 and inputs[7] != '':
805 p = transformer.make_squeeze(inputs[7], axes=[0])
806
807 dtype = _dtype_to_np(tensor_infos[inputs[0]].dtype)
808 batch_size = tensor_infos[inputs[0]].shape[1 - layout]
809
810 act = {'f': activations[0], 'g': activations[1], 'h': activations[2]}
811
812 state_h_tensors, state_c_tensor = _generate_one_direction_LSTM(
813 transformer, x, w, r, b, initial_h, initial_c, p, clip, act, dtype, hidden_size,
814 batch_size)
815
816 y_direction_dim = layout + 1
817 y_h_direction_dim = layout
818 state_layout_tensors = []
819 seq_length_dim = layout
820 for h_state in state_h_tensors:
821 state_layout_tensors += [
822 transformer.make_unsqueeze(h_state, axes=[seq_length_dim, y_direction_dim])
823 ]
824
825 # use low-level interface to attach to existing tensors
826 Y_h = outputs[1]
827 transformer.make_node('Unsqueeze', [state_h_tensors[-1]], [Y_h],
828 axes=[y_h_direction_dim])
829 Y_c = outputs[2]
830 transformer.make_node('Unsqueeze', [state_c_tensor], [Y_c], axes=[y_h_direction_dim])
831 if direction == 'reverse':
832 state_layout_tensors.reverse()
833 Y = outputs[0]
834 transformer.make_node('Concat', state_layout_tensors, [Y], axis=seq_length_dim)
835
836

References _dtype_to_np(), and _generate_one_direction_LSTM().

Referenced by _legalize_LSTM().

◆ _transform_unidirectional_RNN()

onnx_legalizer._transform_unidirectional_RNN (   transformer,
  original_node,
  x,
  tensor_infos,
  activation,
  clip,
  direction,
  hidden_size,
  layout 
)
protected
Generate Simple (forward or reverse) unrolled RNN

Args:
    transformer (_ModelTransformerHelper): transformation helper
    original_node (onnx.onnx_ml_pb2.NodeProto): unidirectional RNN operation to unroll
    x (list of str): list of input tensors (input tensor split along "time" dimension)
    tensor_infos (dict from str to _TensorInfo): dict maps tensor name to it's shape and dtype info
    activation (str): name of activation function
    clip (float or None): range which clips input of activations
    direction (str): "forward" or "reverse"
    hidden_size (int): size of hidden state
    layout (int): See attribute description:
        https://github.com/onnx/onnx/blob/5cf5feef5ec3fd5527b2fdb6c29780e3b705059f/docs/Operators.md#attributes-56

Definition at line 403 of file onnx_legalizer.py.

404 clip, direction, hidden_size, layout):
405 """Generate Simple (forward or reverse) unrolled RNN
406
407 Args:
408 transformer (_ModelTransformerHelper): transformation helper
409 original_node (onnx.onnx_ml_pb2.NodeProto): unidirectional RNN operation to unroll
410 x (list of str): list of input tensors (input tensor split along "time" dimension)
411 tensor_infos (dict from str to _TensorInfo): dict maps tensor name to it's shape and dtype info
412 activation (str): name of activation function
413 clip (float or None): range which clips input of activations
414 direction (str): "forward" or "reverse"
415 hidden_size (int): size of hidden state
416 layout (int): See attribute description:
417 https://github.com/onnx/onnx/blob/5cf5feef5ec3fd5527b2fdb6c29780e3b705059f/docs/Operators.md#attributes-56
418 """
419
420 inputs = original_node.input
421 outputs = original_node.output
422 if direction == 'reverse':
423 x.reverse()
424 w = transformer.make_squeeze(inputs[1], axes=[0])
425 r = transformer.make_squeeze(inputs[2], axes=[0])
426 if len(inputs) > 3 and inputs[3] != '':
427 raw_bias_tensor = transformer.make_squeeze(inputs[3], axes=[0])
428 splitted_bias_tensors = transformer.make_split(raw_bias_tensor,
429 split_sizes=[hidden_size] * 2,
430 axis=0)
431 b = transformer.make_add(splitted_bias_tensors[0], splitted_bias_tensors[1])
432 else:
433 data_type = _dtype_to_np(tensor_infos[inputs[2]].dtype)
434 b = transformer.make_constant_tensor(np.zeros(hidden_size, dtype=data_type),
435 "zero_bias")
436 if len(inputs) > 5 and inputs[5] != '':
437 direction_dim = layout
438 initial_h = transformer.make_squeeze(inputs[5], axes=[direction_dim])
439 else:
440 initial_h = None
441 state_tensors = _generate_one_direction_RNN(transformer, x, w, r, b, initial_h, clip,
442 activation)
443 y_direction_dim = layout + 1
444 y_h_direction_dim = layout
445 state_layout_tensors = []
446 seq_length_dim = layout
447 for state in state_tensors:
448 state_layout_tensors += [
449 transformer.make_unsqueeze(state, axes=[seq_length_dim, y_direction_dim])
450 ]
451
452 # use low-level interface to attach to existing tensors
453 Y_h = outputs[1]
454 transformer.make_node('Unsqueeze', [state_tensors[-1]], [Y_h],
455 axes=[y_h_direction_dim])
456 Y = outputs[0]
457 transformer.make_node('Concat', state_layout_tensors, [Y], axis=seq_length_dim)
458
459

References _dtype_to_np(), and _generate_one_direction_RNN().

Referenced by _legalize_RNN().

◆ legalize()

onnx_legalizer.legalize (   model,
  options 
)
Replace selected operations in onnx model

Replaces operations, selected by given options with different operation sequences.
For example remove unsupported parts of graph with sequences of supported operations.

Note that graph is changes inplace.

Args:
    model (onnx.onnx_ml_pb2.ModelProto): target model
    options (LegalizeOptions):

Definition at line 1022 of file onnx_legalizer.py.

1022def legalize(model, options):
1023 """Replace selected operations in onnx model
1024
1025 Replaces operations, selected by given options with different operation sequences.
1026 For example remove unsupported parts of graph with sequences of supported operations.
1027
1028 Note that graph is changes inplace.
1029
1030 Args:
1031 model (onnx.onnx_ml_pb2.ModelProto): target model
1032 options (LegalizeOptions):
1033 """
1034 tensor_infos = _get_tensor_infos(model)
1035
1036 transformer = _ModelTransformerHelper(model)
1037
1038 node_id = 0
1039 while node_id < len(model.graph.node):
1040 node = model.graph.node[node_id]
1041 if node.op_type == 'RNN' and options.unroll_rnn:
1042 # opset version is required by split operation
1043 if model.opset_import[0].version >= 13:
1044 raise NotImplementedError(
1045 'Can not generate code with opcode version 13 and greater')
1046 transformer.set_insert_id(node_id)
1047 _legalize_RNN(transformer, tensor_infos, node)
1048 node_id = transformer.get_insert_id()
1049 elif node.op_type == 'LSTM' and options.unroll_lstm:
1050 if model.opset_import[0].version >= 13:
1051 raise NotImplementedError(
1052 'Can not generate code with opcode version 13 and greater')
1053 transformer.set_insert_id(node_id)
1054 _legalize_LSTM(transformer, tensor_infos, node)
1055 node_id = transformer.get_insert_id()
1056 node_id += 1
1057
1058 transformer.delete_marked_nodes()
1059
1060

References _get_tensor_infos(), _legalize_LSTM(), and _legalize_RNN().

Variable Documentation

◆ model

onnx_legalizer.model = onnx.load(sys.argv[1])

Definition at line 1073 of file onnx_legalizer.py.

◆ options

onnx_legalizer.options = LegalizeOptions()

Definition at line 1070 of file onnx_legalizer.py.

◆ unroll_lstm

onnx_legalizer.unroll_lstm

Definition at line 1071 of file onnx_legalizer.py.

◆ unroll_rnn

onnx_legalizer.unroll_rnn

Definition at line 1072 of file onnx_legalizer.py.