def element(block: Block, inputs, op_type, is_exit=False): """ Add an element-wise operator at the end of given block. :param block: ios.Block The block to add the operator :param inputs: Sequence[Sequence[Value]] The inputs of the convolution. 'inputs' contains a list of terms. A term contains a list of values. The values in a term are added up. The terms are concatenated along with the channel dimension. :param op_type: str, must be one of 'mul' and 'add' The element-wise operation type string. :param is_exit: boolean, default False Whether this operator is the exit operator of the block. :return: Value A value represents the output of the operator. """ name = new_name() rel = Element(name, name, inputs, op_type=op_type, output_shape=None) rel.infer_shape() for ti, term in enumerate(inputs): for vi, value in enumerate(term): value.node.uses.append((rel, ti, vi)) if is_exit: block.exit_node = rel else: block.inner_nodes.append(rel) return Value(rel, 0, rel.output_shape[0])
def relu(block: Block, inputs, is_exit=False): """ Add a Relu activation at the end of given block. This function is equivalent to activation(act_type='relu'). :param block: ios.Block The block to add the operator :param inputs: Sequence[Sequence[Value]] The inputs of the convolution. 'inputs' contains a list of terms. A term contains a list of values. The values in a term are added up. The terms are concatenated along with the channel dimension. :param is_exit: boolean, default False Whether this operator is the exit operator of the block. :return: Value A value represents the output of the operator. """ name = new_name() rel = Relu(name, name, inputs, None) rel.infer_shape() for ti, term in enumerate(inputs): for vi, value in enumerate(term): value.node.uses.append((rel, ti, vi)) if is_exit: block.exit_node = rel else: block.inner_nodes.append(rel) return Value(rel, 0, rel.output_shape[0])
def identity(block: Block, inputs, is_exit=False): """ Add Identity operator at the end of given block. Because the inputs can can do the addition and concatenation operation, this operator works as addition and concatenation. :param block: ios.Block The block to add the operator :param inputs: Sequence[Sequence[Value]] The inputs of the convolution. 'inputs' contains a list of terms. A term contains a list of values. The values in a term are added up. The terms are concatenated along with the channel dimension. :param is_exit: boolean, default False Whether this operator is the exit operator of the block. :return: Value A value represents the output of the operator. """ name = new_name() ident = Identity(name, name, inputs, None) ident.infer_shape() for ti, term in enumerate(inputs): for vi, value in enumerate(term): value.node.uses.append((ident, ti, vi)) if is_exit: block.exit_node = ident else: block.inner_nodes.append(ident) return Value(ident, 0, ident.output_shape[0])
def conv2d(block: Block, inputs, out_channels, kernel=(1, 1), stride=(1, 1), padding=(0, 0), groups=1, act="relu", is_exit=False): """ Add a convolution operator to the end of given block. :param block: ios.Block The block to add the operator :param inputs: Sequence[Sequence[Value]] The inputs of the convolution. 'inputs' contains a list of terms. A term contains a list of values. The values in a term are added up. The terms are concatenated along with the channel dimension. :param out_channels: The number of output channels. :param kernel: Tuple[int, int], default (1, 1) The kernel size. :param stride: Tuple[int, int], default (1, 1) The stride size. :param padding: Tuple[int, int], default (0, 0) :param groups: int, default 1 The number of groups. It must be a common factor of the input channels and output channels. :param act: str, default 'relu' The activation applied to the output of convolution. :param is_exit: boolean, default False Whether this operator is the exit operator of the block. :return: Value A value represents the output of the operator. """ name = new_name() conv = Conv(name, name, inputs, out_channels, kernel, stride, padding, groups, act, None) conv.infer_shape() for ti, term in enumerate(inputs): for vi, value in enumerate(term): value.node.uses.append((conv, ti, vi)) if is_exit: block.exit_node = conv else: block.inner_nodes.append(conv) return Value(conv, 0, out_channels)
def placeholder(output_shape): """ Placeholder. :param output_shape: Tuple[int, int, int] The outpu shape of the placeholder, which should be consistent with the input of the network. :return: Value The value represents the placeholder output. """ name = new_name() holder = Placeholder(name, name, output_shape) return Value(holder, 0, holder.output_shape[0])
def get_input(ssnodes, block, nid, idn): """ Merge the inputs of ssnodes and drop duplicate inputs """ terms = list(itertools.chain(*[nd.inputs for nd in ssnodes])) iterms = [ tuple((-1 if value.node is block.enter_node else nid[value.node], value.begin, value.end) for value in term) for term in terms] iterms = dict.fromkeys(sorted(iterms, key=lambda iterm: (len(iterm), iterm))) # git rid of duplicates terms terms = [[Value(block.enter_node if ivalue[0] == -1 else idn[ivalue[0]], ivalue[1], ivalue[2]) for ivalue in iterm] for iterm in iterms] return terms
def get_new_terms(terms, new_node, do_sort=True): nterms = [] for ti, term in enumerate(terms): nterm = [] for vi, value in enumerate(term): nv = out_dict[value.node] nterm.append(Value(nv[0], nv[1] + value.begin, nv[1] + value.end)) nterms.append(nterm) if do_sort: nterms = sorted(nterms, key=lambda nterm: (len(nterm), nterm[0].node.name)) # git rid of duplicates terms for ti, term in enumerate(nterms): for vi, value in enumerate(term): value.node.uses.append((new_node, ti, vi)) return nterms
def sequential(block: Block, hint_name, nodes: List[Node], is_exit=False): """ Add a sequential compound operator at the end of given block. The compound operator contains a sequence of operators that would be executed sequentially. :param block: ios.Block The block to add the operator :param hint_name: str Any string used to tell what the operator represents, which can be the same with other hint name. :param nodes: List[ios.Node] The sequence of the operators in the compound operator. :param is_exit: boolean, default False Whether this operator is the exit operator of the block. :return: Value A value represents the output of the last operator in the operator sequence. """ name = new_name() if hint_name is None: hint_name = name for i in range(len(nodes) - 1): nodes[i].infer_shape() nodes[i + 1].inputs = [[Value(nodes[i], 0, nodes[i].output_shape[0])]] seq = Sequential(name, hint_name, nodes, None) seq.infer_shape() for ti, term in enumerate(nodes[0].inputs): for vi, value in enumerate(term): value.node.uses.append((seq, ti, vi)) if is_exit: block.exit_node = seq else: block.inner_nodes.append(seq) return Value(seq, 0, seq.output_shape[0])
def merge_inputs(inputs: List[List[Value]]): merge_inputs_flag = True if merge_inputs_flag: while True: # merge input merged = False for i in range(1, len(inputs)): if len(inputs[i - 1]) > 1 or len(inputs[i]) > 1: continue va, vb = inputs[i - 1][0], inputs[i][0] if va.node == vb.node and va.end == vb.begin: vc = Value(va.node, va.begin, vb.end) inputs = inputs[:i - 1] + [[vc]] + inputs[i + 1:] merged = True break if not merged: break return inputs
def pool2d(block: Block, inputs, pool_type, kernel=(1, 1), stride=(1, 1), padding=(0, 0), is_exit=False): """ Add a pooling operator at the end of given block. :param block: ios.Block The block to add the operator :param inputs: Sequence[Sequence[Value]] The inputs of the convolution. 'inputs' contains a list of terms. A term contains a list of values. The values in a term are added up. The terms are concatenated along with the channel dimension. :param pool_type: str, must be one of 'max', 'avg', 'global_max' and 'global_avg' The pooling type string. :param kernel: Tuple[int, int], default (1, 1) The kernel size of the pooling operator. :param stride: Tuple[int, int], default (1, 1) The stride of the pooling operator. :param padding: Tuple[int, int], default (1, 1) THe padding size of the pooling operator. :param is_exit: boolean, default False Whether this operator is the exit operator of the block. :return: Value A value represents the output of the operator. """ name = new_name() pool = Pool(name, name, inputs, pool_type, kernel, stride, padding, None) pool.infer_shape() for ti, term in enumerate(inputs): for vi, value in enumerate(term): value.node.uses.append((pool, ti, vi)) if is_exit: block.exit_node = pool else: block.inner_nodes.append(pool) return Value(pool, 0, pool.output_shape[0])