From fa930678ea9c83d3d7a3b63c4ea9d6a0faf26ca1 Mon Sep 17 00:00:00 2001 From: Antkillerfarm Date: Tue, 24 Aug 2021 12:42:46 +0800 Subject: [PATCH] add Programming_Guide.md & Operators.md (#157) --- docs/Operators.md | 770 +++++++++++++++++++++++++++++++ docs/Operators.md.template | 20 - docs/Programming_Guide.md | 83 ++++ docs/image/architecture.png | Bin 0 -> 13246 bytes docs/image/graph_lifecycle.png | Bin 0 -> 31004 bytes include/tim/vx/ops/activations.h | 1 - 6 files changed, 853 insertions(+), 21 deletions(-) create mode 100644 docs/Operators.md create mode 100644 docs/Programming_Guide.md create mode 100644 docs/image/architecture.png create mode 100644 docs/image/graph_lifecycle.png diff --git a/docs/Operators.md b/docs/Operators.md new file mode 100644 index 0000000..4f272b7 --- /dev/null +++ b/docs/Operators.md @@ -0,0 +1,770 @@ + + +# Table of Contents +- [Operators](#operators) + - [Activation](#activation) + - [AddN](#addn) + - [ArgMin/ArgMax](#argminargmax) + - [Batch2Space](#batch2space) + - [BatchNorm](#batchnorm) + - [Clip](#clip) + - [Concat](#concat) + - [Conv2d](#conv2d) + - [DeConv2d](#deconv2d) + - [DeConv1d](#deconv1d) + - [DepthToSpace](#depthtospace) + - [Dropout](#dropout) + - [Add](#add) + - [Sub](#sub) + - [Multiply](#multiply) + - [Div](#div) + - [Pow](#pow) + - [Minimum](#minimum) + - [Maximum](#maximum) + - [FloorDiv](#floordiv) + - [FullyConnected](#fullyconnected) + - [Gather](#gather) + - [GatherNd](#gathernd) + - [GroupedConv2d](#groupedconv2d) + - [L2Normalization](#l2normalization) + - [LocalResponseNormalization](#localresponsenormalization) + - [And](#and) + - [Or](#or) + - [LogSoftmax](#logsoftmax) + - [Matmul](#matmul) + - [MaxpoolWithArgmax](#maxpoolwithargmax) + - [MaxUnpool2d](#maxunpool2d) + - [Moments](#moments) + - [NBG](#nbg) + - [Pad](#pad) + - [Pool2d](#pool2d) + - [ReduceMin](#reducemin) + - [ReduceMax](#reducemax) + - [ReduceAny](#reduceany) + - [ReduceAll](#reduceall) + - [ReduceProd](#reduceprod) + - [ReduceMean](#reducemean) + - [ReduceSum](#reducesum) + - [Greater](#greater) + - [GreaterOrEqual](#greaterorequal) + - [Less](#less) + - [LessOrEqual](#lessorequal) + - [NotEqual](#notequal) + - [Equal](#equal) + - [Reorg](#reorg) + - [Reshape](#reshape) + - [Resize](#resize) + - [Resize1d](#resize1d) + - [Reverse](#reverse) + - [ScatterND](#scatternd) + - [Select](#select) + - [DataConvert](#dataconvert) + - [Neg](#neg) + - [Abs](#abs) + - [Sin](#sin) + - [Exp](#exp) + - [Log](#log) + - [Sqrt](#sqrt) + - [Rsqrt](#rsqrt) + - [Square](#square) + - [LogicalNot](#logicalnot) + - [Floor](#floor) + - [Cast](#cast) + - [Slice](#slice) + - [Softmax](#softmax) + - [Space2Batch](#space2batch) + - [SpaceToDepth](#spacetodepth) + - [Split](#split) + - [Squeeze](#squeeze) + - [Stack](#stack) + - [StridedSlice](#stridedslice) + - [Tile](#tile) + - [Transpose](#transpose) + - [Unidirectional sequence lstm](#unidirectional-sequence-lstm) + - [Unstack](#unstack) + + +# Operators + + +## Activation + +Activation functions: + +``` +Relu(x) : max(0, x) + +Relu1(x) : -1 if x <= -1; x if -1 < x < 1; 1 if x >= 1 + +Relu6(x) : 0 if x <= 0; x if 0 < x < 6; 6 if x >= 6 + +Elu(x) : x if x >= 0 else alpha*(e^x - 1) + +Tanh(x) : (1 - e^{-2x})/(1 + e^{-2x}) + +Sigmoid(x) : 1/(1 + e^{-x}) + +Swish(x) : x * sigmoid(x) + +HardSwish(x) : 0 if x <= -3; x(x + 3)/6 if -3 < x < 3; x if x >= 3 + +Mish(x) : x if x >= 0 else alpha * x + +HardSigmoid(x) : min(max(alpha*x + beta, 0), 1) + +SoftRelu(x) : log(1 + e^x). Also known as SoftPlus. + +LeakyRelu(x) : alpha * x if x <= 0; x if x > 0. alpha is a scalar. + +Prelu(x) : alpha * x if x <= 0; x if x > 0. alpha is a tensor. +- axis : describes the axis of the inputs when coerced to 2D. + +Linear(x, a, b) : a*x + b. + +Gelu(x) : x * P(X <= x), where P(x) ~ N(0, 1). https://tensorflow.google.cn/api_docs/python/tf/nn/gelu +``` + + +## AddN + +``` +AddN(x) : Input0 + Input1 + ... + InputN +``` + + +## ArgMin/ArgMax + +Computes the indices of the **min/max** elements of the input tensor's element +along the provided **axis**. The type of the output tensor is integer. + + +## Batch2Space + +This operation reshapes the batch dimension (dimension 0) into M + 1 dimensions +of shape **block_size** + [batch], interleaves these blocks back into the grid +defined by the spatial dimensions [1, ..., M], to obtain a result with the same +rank as the input. This is the reverse transformation of Space2Batch. + +- crop : corp the output tensor for ROI usage. + + +## BatchNorm + +Carries out batch normalization as described in the paper +https://arxiv.org/abs/1502.03167. + +$$\hat x_i\leftarrow \frac{x_i-\mu_\mathcal{B}}{\sqrt{\sigma_\mathcal{B}^2+\epsilon}}$$ + +$$y_i=\gamma\hat x_i+\beta\equiv BN_{\gamma,\beta}(x_i)$$ + + +## Clip + +Clip(x) : min if x <= min; x if min < x < max; max if x >= max + + +## Concat + +Concatenate a list of tensors into a single tensor. + +- axis : Which axis to concat on. + + +## Conv2d + +Performs a 2-D convolution operation, include classic Conv2D / +Depthwise Conv2D / Group Conv2D / Dilation Conv2D. + +Input: +- input [WHCN or CWHN]. +- kernel [ WHIcOc ] (Ic: Input Channels. Oc: Output Channels). +- bias [ O ]. Optional. + +Attribute: +- weights : the output channel number for weight tensor. +- ksize : the height and width for weight tensor. +- padding : AUTO, VALID or SAME. +- pad : pad value for each spatial axis. +- stride : stride along each spatial axis. +- dilation : dilation value along each spatial axis of the filter. +- multiplier: function similar to group attribute on other framework, +but the value is different. multiplier = weights / group. +- layout : WHCN or CWHN. + + +## DeConv2d + +Performs the transpose of 2-D convolution operation. + +This operation is sometimes called "deconvolution" after Deconvolutional Networks, +but is actually the transpose (gradient) of Conv2D rather than an actual deconvolution. + +- oc_count_ : the out channel count for weight tensor. +- pad_type : SAME, VALID or AUTO. +- ksize : the height and width for weight tensor. +- padding : AUTO, VALID or SAME. +- pad : pad value for each spatial axis. +- stride : stride along each spatial axis. +- output_padding : specifying the amount of padding along the height and width of +the output tensor. +- group : the feature count of each group. +- input_layout : Layout for input, WHCN by default. +- kernel_layout: Layout for kernel, WHIO by default. + + +## DeConv1d + +Performs the transpose of 1-D convolution operation. + +This operation is sometimes called "deconvolution1d" after Deconvolutional Networks, +but is actually the transpose (gradient) of Conv2D rather than an actual deconvolution. + +- weights : the channel number for weight tensor. +- ksize : the length for weight tensor. +- padding : AUTO, VALID or SAME. +- pad : pad value for each spatial axis. +- stride : stride along each spatial axis. +- output_padding : specifying the amount of padding along the height and width of +the output tensor. + + +## DepthToSpace + +DepthToSpace rearranges (permutes) data from depth into blocks of spatial data. +This is the reverse transformation of SpaceToDepth. + +Chunks of data of size block_size * block_size from depth are rearranged into +non-overlapping blocks of size block_size x block_size. + +The width of the output tensor is input_depth * block_size, whereas the height +is input_height * block_size. The depth of the input tensor must be divisible +by block_size * block_size + +- crop : corp the output tensor for ROI usage. + + +## Dropout + +The Dropout layer randomly sets input units to 0 with a frequency of rate at +each step during training time, which helps prevent overfitting. + +TIM-VX only focus on inference time, and just scaling input tensor by **ratio** +for Dropout operator. + + +## Add + +Add(x, y) : x + y. This operation supports broadcasting. + + +## Sub + +Sub(x, y) : x - y. This operation supports broadcasting. + + +## Multiply + +Multiply(x, y) : Multiplies two tensors, element-wise, also known as Hadamard +product. This operation supports broadcasting. + +- scale: scaling the product. + + +## Div + +Div(x, y) : x / y. This operation supports broadcasting. + + +## Pow + +Pow(x, y) : x ^ y. This operation supports broadcasting. + + +## Minimum + +Minimum(x, y) : min(x, y). This operation supports broadcasting. + + +## Maximum + +Maximum(x, y) : max(x, y). This operation supports broadcasting. + + +## FloorDiv + +FloorDiv(x, y): floor( x / y ). This operation supports broadcasting. + + +## FullyConnected + +Denotes a fully (densely) connected layer, which connects all elements in the +input tensor with each element in the output tensor. + +- axis: Describes the axis of the inputs when coerced to 2D. +- weights: the output channel number for weight tensor. + + +## Gather + +Gather slices from input, **axis** according to **indices**. + + +## GatherNd + +An operation similar to Gather but gathers across multiple axis at once. + + +## GroupedConv2d + +Performs a grouped 2-D convolution operation. + +Input: +- input [WHCN or CWHN]. +- kernel [ WHIcOc ] (Ic: Input Channels. Oc: Output Channels). +- bias [ O ]. Optional. + +Attribute: +- weights : the output channel number for weight tensor. +- ksize : the height and width for weight tensor. +- padding : AUTO, VALID or SAME. +- pad : pad value for each spatial axis. +- stride : stride along each spatial axis. +- dilation : dilation value along each spatial axis of the filter. +- group_number: Split conv to n group. +- layout : WHCN or CWHN. + + +## L2Normalization + +Applies L2 normalization along the axis dimension: + +``` +output[batch, row, col, channel] = +input[batch, row, col, channel] / +sqrt(sum_{c} pow(input[batch, row, col, c], 2)) +``` + + +## LocalResponseNormalization + +Applies Local Response Normalization along the depth dimension: + +``` +sqr_sum[a, b, c, d] = sum( +pow(input[a, b, c, d - depth_radius : d + depth_radius + 1], 2)) +output = input / pow((bias + alpha * sqr_sum), beta) +``` + + +## And + +Returns the truth value of x AND y element-wise. This operation supports broadcasting. + + +## Or + +Returns the truth value of x OR y element-wise. This operation supports broadcasting. + + +## LogSoftmax + +Computes the log softmax activation on the input tensor element-wise, per batch. + +``` +logsoftmax = logits - log(reduce_sum(exp(logits), axis)) +``` + + +## Matmul + +Multiplies matrix a by matrix b, producing a * b. + +- transpose_a: If True, a is transposed before multiplication. +- transpose_b: If True, b is transposed before multiplication. +- adjoint_a: If True, a is conjugated and transposed before multiplication. +- adjoint_b: If True, b is conjugated and transposed before multiplication. + + +## MaxpoolWithArgmax + +Performs an 2-D Max pooling operation and return indices + +- padding : AUTO, VALID or SAME. +- ksize : filter size. +- stride : stride along each spatial axis. +- round_type : CEILING or FLOOR. + + +## MaxUnpool2d + +Performs an 2-D Max pooling operation upsample + +- stride : stride along each spatial axis. +- ksize : filter size. + + +## Moments + +The mean and variance are calculated by aggregating the contents of x across axes. +If x is 1-D and axes = [0] this is just the mean and variance of a vector. + +- axes : Axes along which to compute mean and variance. +- keep_dims : Produce moments with the same dimensionality as input. + + +## NBG + +Network Binary Graph is a precompile technology, which can compile a fuse graph into +a bianry file. + + +## Pad + +Pads a tensor. + +- const_val : the value to pad. + + +## Pool2d + +Performs an 2-D pooling operation. + +- type : MAX, AVG, L2 or AVG_ANDROID. +- padding : AUTO, VALID or SAME. +- ksize : filter size. +- stride : stride along each spatial axis. +- round_type : CEILING or FLOOR. + + +## ReduceMin + +Reduces a tensor by computing the minimum of elements along given dimensions. + +- axis : the dimensions to reduce. +- keep_dims : If keep_dims is true, the reduced dimensions are retained with +length 1. Otherwise, the rank of the tensor is reduced by 1 for each entry +in dimensions + + +## ReduceMax + +Reduces a tensor by computing the maximum of elements along given dimensions. + +- axis : the dimensions to reduce. +- keep_dims : If keep_dims is true, the reduced dimensions are retained with +length 1. Otherwise, the rank of the tensor is reduced by 1 for each entry +in dimensions + + +## ReduceAny + +Reduces a tensor by computing the "logical or" of elements along given dimensions. + +- axis : the dimensions to reduce. +- keep_dims : If keep_dims is true, the reduced dimensions are retained with +length 1. Otherwise, the rank of the tensor is reduced by 1 for each entry +in dimensions + + +## ReduceAll + +Reduces a tensor by computing the "logical and" of elements along given dimensions. + +- axis : the dimensions to reduce. +- keep_dims : If keep_dims is true, the reduced dimensions are retained with +length 1. Otherwise, the rank of the tensor is reduced by 1 for each entry +in dimensions + + +## ReduceProd + +Reduces a tensor by computing the multiplying of elements along given dimensions. + +- axis : the dimensions to reduce. +- keep_dims : If keep_dims is true, the reduced dimensions are retained with +length 1. Otherwise, the rank of the tensor is reduced by 1 for each entry +in dimensions + + +## ReduceMean + +Reduces a tensor by computing the mean of elements along given dimensions. + +- axis : the dimensions to reduce. +- keep_dims : If keep_dims is true, the reduced dimensions are retained with +length 1. Otherwise, the rank of the tensor is reduced by 1 for each entry +in dimensions + + +## ReduceSum + +Reduces a tensor by computing the summing of elements along given dimensions. + +- axis : the dimensions to reduce. +- keep_dims : If keep_dims is true, the reduced dimensions are retained with +length 1. Otherwise, the rank of the tensor is reduced by 1 for each entry +in dimensions + + +## Greater + +For input tensors x and y, computes x > y elementwise. + + +## GreaterOrEqual + +For input tensors x and y, computes x >= y elementwise. + + +## Less + +For input tensors x and y, computes x < y elementwise. + + +## LessOrEqual + +For input tensors x and y, computes x <= y elementwise. + + +## NotEqual + +For input tensors x and y, computes x != y elementwise. + + +## Equal + +For input tensors x and y, computes x == y elementwise. + + +## Reorg + +The layer used in YOLOv2. See also https://github.com/pjreddie/darknet/blob/master/src/reorg_layer.c + + +## Reshape + +Given tensor, this operation returns a tensor that has the same values as tensor, but with a newly specified shape. + +- size : defining the shape of the output tensor. + + +## Resize + +Resizes images to given size. + +- type : NEAREST_NEIGHBOR, BILINEAR or AREA. +- factor : scale the input size. DO NOT use it with target_height / target_width together. +- align_corners : If True, the centers of the 4 corner pixels of the input and output +tensors are aligned, preserving the values at the corner pixels. +- half_pixel_centers : If True, the pixel centers are assumed to be at (0.5, 0.5). +This is the default behavior of image.resize in TF 2.0. If this parameter is True, +then align_corners parameter must be False. +- target_height / target_width : output height / width. DO NOT use it with factor together. + + +## Resize1d + +Resize1ds 1D tensors to given size. + +- type : NEAREST_NEIGHBOR, BILINEAR or AREA. +- factor : scale the input size. DO NOT use it with target_height / target_width together. +- align_corners : If True, the centers of the 4 corner pixels of the input and output +tensors are aligned, preserving the values at the corner pixels. +- half_pixel_centers : If True, the pixel centers are assumed to be at (0.5, 0.5). +This is the default behavior of image.resize in TF 2.0. If this parameter is True, +then align_corners parameter must be False. +- target_height / target_width : output height / width. DO NOT use it with factor together. + + +## Reverse + +Reverses specific dimensions of a tensor. + +- axis : The indices of the dimensions to reverse. + + +## ScatterND + +Scatter updates into a new tensor according to indices. + +- shape : The shape of the resulting tensor. + + +## Select + +Using a tensor of booleans c and input tensors x and y select values elementwise +from both input tensors: O[i] = C[i] ? x[i] : y[i]. + + +## DataConvert + +Change the format from input tensor to output tensor. + + +## Neg + +Neg(x) : -x + + +## Abs + +Abs(x) : x if x >= 0; -x if x < 0. + + +## Sin + +Sin(x) : sin(x) + + +## Exp + +Exp(x) : e^x + + +## Log + +Log(x) : ln(x) + + +## Sqrt + +Sqrt(x) : $$\sqrt{x}$$ + + +## Rsqrt + +Rsqrt(x) : $$\frac{1}{\sqrt{x}}$$ + + +## Square + +Square : x^2 + + +## LogicalNot + +LogicalNot(x) : NOT x + + +## Floor + +returns the largest integer less than or equal to a given number. + + +## Cast + +Change the format from input tensor to output tensor. This operation ignores +the scale and zeroPoint of quanized tensors. + + +## Slice + +Extracts a slice of specified size from the input tensor starting at a specified location. + +- start : the beginning indices of the slice in each dimension. +- length : the size of the slice in each dimension. + + +## Softmax + +Computes the softmax activation on the input tensor element-wise, per batch, +by normalizing the input vector so the maximum coefficient is zero: + +``` +output[batch, i] = +exp((input[batch, i] - max(input[batch, :])) * beta) / +sum_{k}{exp((input[batch, k] - max(input[batch, :])) * beta)} +``` + + +## Space2Batch + +This operation divides "spatial" dimensions [1, ..., M] of the input into a grid +of blocks of shape **block_size**, and interleaves these blocks with the "batch" +dimension (0) such that in the output, the spatial dimensions [1, ..., M] correspond +to the position within the grid, and the batch dimension combines both the position +within a spatial block and the original batch position. Prior to division into blocks, +the spatial dimensions of the input are optionally zero padded according to paddings. +This is the reverse transformation of Batch2Space. + +- pad : the paddings for each spatial dimension of the input tensor. + + +## SpaceToDepth + +SpaceToDepth rearranges blocks of spatial data into depth. More specifically, +this op outputs a copy of the input tensor where values from the height and +width dimensions are moved to the depth dimension. This is the reverse +transformation of DepthToSpace. + + +## Split + +Splits a tensor along a given axis into num_splits subtensors. + +- axis : the axis along which to split. +- slices : indicating the number of splits along given axis. + + +## Squeeze + +Removes dimensions of size 1 from the shape of a tensor. + +- axis : the dimensions to squeeze. + + +## Stack + +Packs the list of tensors in inputs into a tensor with rank one higher than +each tensor in values, by packing them along the **axis** dimension. + + +## StridedSlice + +Extracts a strided slice of a tensor. + +Roughly speaking, this op extracts a slice of size (end - begin) / stride from +the given input tensor. Starting at the location specified by begin the slice +continues by adding stride to the index until all dimensions are not less than end. +Note that a stride can be negative, which causes a reverse slice. + +- begin_dims : the starts of the dimensions of the input tensor to be sliced. +- end_dims : the ends of the dimensions of the input tensor to be sliced. +- stride_dims : the strides of the dimensions of the input tensor to be sliced. +- begin_mask : if the ith bit of begin_mask is set, begin[i] is ignored and +the fullest possible range in that dimension is used instead. +- end_mask : if the ith bit of end_mask is set, end[i] is ignored and the fullest +possible range in that dimension is used instead. +- shrink_axis_mask : if the ith bit of shrink_axis_mask is set, the ith dimension +specification shrinks the dimensionality by 1, taking on the value at index begin[i]. +In this case, the ith specification must define a slice of size 1, +e.g. begin[i] = x, end[i] = x + 1. + + +## Tile + +Constructs a tensor by tiling a given tensor. +- multiples : Must be one of the following types: int32, int64. +Length must be the same as the number of dimensions in input. + + +## Transpose + +Transposes the input tensor, permuting the dimensions according to the +**perm** tensor. + +The returned tensor's dimension i corresponds to the input dimension perm[i]. +If perm is not given, it is set to (n-1...0), where n is the rank of the input +tensor. Hence by default, this operation performs a regular matrix transpose on +2-D input Tensors. + + +## Unidirectional sequence lstm +how to bind input/output: take unidirectional_sequence_lstm_test.cc + + +## Unstack + +Unpacks the given dimension of a rank-R tensor into rank-(R-1) tensors. +- axis : An int. The axis to unstack along. Defaults to the first dimension. +Negative values wrap around, so the valid range is [-R, R). \ No newline at end of file diff --git a/docs/Operators.md.template b/docs/Operators.md.template index e99ba27..8b12ab2 100644 --- a/docs/Operators.md.template +++ b/docs/Operators.md.template @@ -1,23 +1,3 @@ -# Memory Layout - -There are two different memory layout mode: - -- Row-Major. Also called C contiguous, which is firstly introduced in C by AT&T Bell Lab(1960s). Caffe/TensorFlow/Pytorch follow the Row-Major layout mode. - -- Column-Major. Also called Fortran contiguous, which is firstly introduced in Fortran by IBM(1958). Matlab/OpenGL/OpenCL/OpenVX follow the Column-Major layout mode. - -See also : - -http://eigen.tuxfamily.org/dox/group__TopicStorageOrders.html - -On the other hand, a memory layout can be described by both Row-Major and Column-Major mode. - -It easily tranlate the layout mode from one to another: just reverse the dimesions of the tensor. - -For example, TensorFlow usually describe the tensor as NHWC format. This is equal the CWHN format in OpenVX. - -Likewise, the WHCN format in OpenVX is equal the NCHW format in Caffe. - # Operators {DOCS} diff --git a/docs/Programming_Guide.md b/docs/Programming_Guide.md new file mode 100644 index 0000000..c209ae7 --- /dev/null +++ b/docs/Programming_Guide.md @@ -0,0 +1,83 @@ +# Basic Concepts + +## Context + +The OpenVX context is the object domain for all OpenVX objects. All data objects live in the context as well as all framework objects. The OpenVX context keeps reference counts on all objects and must do garbage collection during its deconstruction to free lost references. + +## Node + +A node is an instance of a kernel that will be paired with a specific set of references (the parameters). Nodes are created from and associated with a single graph only. + +**Node is also known as Operation.** + +## Graph + +A set of nodes connected in a directed (only goes one-way) acyclic (does not loop back) fashion. A Graph may have sets of Nodes that are unconnected to other sets of Nodes within the same Graph. + +**One context may have several graphs.** + +## Tensor + +An multidimensional data object. + +TIM-VX support the following tensor type: + +- Normal Tensor. The tensor which is allocated in host memory, which is managed by OpenVX driver. Normal Tensor is usually used for input/output tensors of the graph. +- Viratul Tensor. The tensor which is allocated in target memory. Viratul Tensor is usually used for intermediate tensors of the graph. Viratul Tensor is faster than the Normal Tensor due to reduce the memory copy between host and target. +- Handle Tensor. The tensor which is allocated in host memory, which is managed by user application. App usually use mmap() to allocate continuous memory for tensor usage. Handle Tensor is also usually used for input/output tensors of the graph. Sometimes the memory of Normal Tensor will be released/relocated by OpenVX driver, and this may be inefficient. You can use Handle Tensor to avoid release/relocate memory. + +## Memory Layout + +There are two different memory layout mode: + +- Row-Major. Also called C contiguous, which is firstly introduced in C by AT&T Bell Lab(1960s). Caffe/TensorFlow/Pytorch follow the Row-Major layout mode. + +- Column-Major. Also called Fortran contiguous, which is firstly introduced in Fortran by IBM(1958). Matlab/OpenGL/OpenCL/OpenVX follow the Column-Major layout mode. + +See also : + +http://eigen.tuxfamily.org/dox/group__TopicStorageOrders.html + +On the other hand, a memory layout can be described by both Row-Major and Column-Major mode. + +It easily tranlate the layout mode from one to another: just reverse the dimesions of the tensor. + +For example, TensorFlow usually describe the tensor as NHWC format. This is equal the CWHN format in OpenVX. + +Likewise, the WHCN format in OpenVX is equal the NCHW format in Caffe. + +## Data Types + +TIM-VX support the following data type: + +- INT8 +- INT16 +- INT32 +- INT64 +- UINT8 +- UINT16 +- UINT32 +- UINT64 +- FLOAT16 +- FLOAT32 +- FLOAT64 + +# Graph lifecycle + +![Graph lifecycle](/docs/image/graph_lifecycle.png) + +- Graph Construction: Create Node/Tensor/Graph. + +- Graph Verification: The graphs are checked for consistency, correctness, and other conditions.Memory allocation may occur. + +- Graph Execution: Between executions data may be updated by the client or some other external mechanism. + +- Graph Deconstruction: Release Node/Tensor/Graph. + +# Software architecture + +![Software architecture](/docs/image/architecture.png) + +- Ovxlib: the C wrapper for OpenVX driver. + +- TIM-VX: the C++ wrapper for OpenVX driver. diff --git a/docs/image/architecture.png b/docs/image/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..dcd1d621f88ddf26dbab9ea58d2d0ba09baf82e0 GIT binary patch literal 13246 zcmcJ02Q-#{ANM7jY$7{^k{MA}MkHlL_DUjKi0qNQqJ+qnmA&_-vLc(zvS;>wzjy!t z|9zkLeb4)x^PclO_cSE3JYcXdCcP4F?Op zL+n;t4PP+qB;{0b;N^<*Bmn+@(f*Ex{X^@g_Kt?njS&+oYfEEJJEP~u##VNw*7ob@ z^gMei-_-U@9I<0a5Z{)TmM%&e=qF=jVq)6iZ3qkqcoKl7AT7-zb>0TPb@8z$841Z3 zV)XccfV+NFQ}FA2EeIPEQ{*KqArg{^^Z&^|d0oUC^z`XdrdzkD7Z(>lfBo8Ya&AjWn*KbkmDLHhJGG5q7#NH^Jj7L1RRro9riiVrt#@#6Fya~d=auXC)ZCnd_U7}nOOMrwF(j05Bu@yV z>Q8pH@5c*~w6?Zh9SaTRG_al|$kmFK%IeSRAx2$TSyMBxsfji%Lto~_u0xdD=;)|M z@l%4it~m8|+a-!A@#772KO%-OxG!7naQb|M{>=1+@k&Z^JUwPvY3cVImw%B;N)0jH zI+Vw0($_rr!j@>THeR`t&3UtdjFj|SwWIZTtqUcRls|cXYi?CKQH*&>;1cYBNmmp{ zp+@0j3_N^01i6<^Dt>l+9gC2bz4GHvWN&v@=KlTj$Zho@gyWHPb7Q0KZ|Q)5fCS^4 zH@|396qHlV(A~0c_|*}eoXWgv{#0(h!sGP7gtrFGVDMA!d26`7R!rR3<1dlxE&0)J zA|neNmLGlDVZq#=PxRp85*Dgx?l7eX`ydsNlJ#K~nj zbSSN7Ov1u~F)<|+LyGZ2m$Je}(Gp@~uW-e^A8lx8xZBqNd;b1zvQk6a$KZ-n)4Xh{ zRix+37m{f0Qod+@cGUY^zfndUU0hszc|b|Hp`)Xt)mY3h6spzj-Y@+gX%Bw&fkxSM zlK(;xvk2MF&~(24^~!Q=PP~*?G2xiL`035$f{pydZBfTH;uk9#uU1x;6)ow{UKIvH zSFwCamWqpb^CqO(zdR-F=i0-I#hIB%R@P?br%%M?msx0KO&7F&{z?~*x+#)`j@Cjg7yTmvUcRa3nWMN?D3+QtJ z9ByS__1jt7}Hlq_3J7s^`z2dz&+r?uQe`UhCgF-z&K`mDM$rBuz5T4s+ zW+Lx6X+o4!-y5wA4-S_+lkqv3mQ2G%ZD>FMj>OTfg6ZT0E;9AApP@(8Boy{*AdD$E zc%6}%nS?_<%+p7TyEAqR^Kg$L__|3`fpcj+tVTu0mFD8bz^jdpzrM;HA5ESZn2i!b zht3xF127J=%*lfJWSy4X3f4YG_9gqHyIHAzO-) z(kL6dt=5*n93I=5t0k7p`@@B^Gwy9;)R7cVI(K||iXqnlb2Q(c+;C7>YUf*`fd1U|*Zf?@POf=QS!Gttxu&1 zn)<7`xr(x~Uss&Z)Z$I*H>_?fR*Aeu%vKZkn?FAIPC|PuOpfPU(ewQ~13K1y3}kq; zis`BuB`K*wL%Yen50e}btg|FS%5<%5?*4s!E~Z1>xX+&9J}xe{L*JNMs9}|n$yFQl zC(=idiszRfsnn1DoHv#4sPJ3#$CWG5uc0kr(W+xZ`IA&>X*!Zt zRzY81$hOzb6xF$3r=rnLD%nm*+YIC<32z8oV&~(-xOK}JPJWA0w|nV?3JK%k4Gj+z zYp#xc&1OcX26H>H7WXn&R?HFP;lnt#-3N;GArCVM8F@EZ;QW3adyw%n_zNE&DH_7c zYMH(2vQ~*b*4M|?JU;$t+{(}}qfb#jxSdRTX{w$o<(8Av`rdfurQ!1YB&|4+mzE#a zaeKNRU~c?;ooik%8yj1SzVY)L5rJ;-!Pbw6s3;8UbwOgPii=iObq@6N>-62c!oroK zb^fvwb91X|W#Z1aJ_Pi)BW&3|nVGsg{1#q%wSEH?^&V$K$*N2JPKx`J`7Gj4B1n?m z{6ys8KwC>1=V-~I&aB#IgAe}ad-A~>zoOR-XIj)^uA5+sh{~oKwNobLm@DPhmzCYP zcQ3(JYiweIpev4M$caQtx`mR5_nFTP%UeSSZp6KB05Bv>^}5tMIP93Od{6Pk)pMGT zqF%=(!qp5336UnhFlR7XXS_e{uQ)x;i&s|06?rK7CFCMaM2r*l@?a=secb8JkdH=5 zCQ;-Y4}X;`bWTpoc+n%WzVFzGo0I@;zs33)kneS|73FnQ$ATeED)wiJg35%#nPqb3}P{$6au zxWJJ5Y0nERWMPTAp~w1aXO|^x?Bm}*1%_*0vd+x7`$;C>p6_=1R%vG)HCBr>{BUF6 zU3p1#{i#rrgSq_uxZOpg_MKgKbZ2M3{gvXILiVQDJ+UY4JT@Aqoj)Y7rM$m_A&{@7 zS??|iumc;hdH%a8h(h4(jzZevPW-}^m)m$Nehcc9rI?u!%FDM-A`G65NUYj+b&lqo zP&26z!9Midj{oo%+ayU_+Gb{QCWo&2Peads zLK^^$wv{OqELb8|D#4GqiEB;bAdH6+mvFq(8d2}^z;xl3?MQ1YW?5NT+m3F`|)Juw8vD(cFm*y{Yr-j-z4 zP9Mo8DJ{Kyt)uHfqc>))l12DR9g%Y%le#|Jyvy7>sUXMX)D^&2)t8C%=Jue*b}x&MG? zZS88kH!M&K_!7i$t3D+q9iPA6QyIy~%?%yMQ8$7m5b!D00_Mi;?(RN^-1zei{QS~HeU5)u+@?CmYy+EQO@ z;NajWblJ1Qz{Z}1+a*wY2iG0E<*@Y1(-XnK#FWt2ub;*fY?&SJP7vj=UmY#|!tk)Y z4fWbpw1nk1B}@5JGrho+Ab#a6{@EA+Ta3s{zI*rXsg}jXlz4skK%-U0KN}uPOcEh; zh5fUH^P%y0g{_)#HJreY@^k|41Ya0%330}BGBstF38T&WZI`mPCN;fYPfjPiJ9WJ` zFyL-#I&c`MNNJfhX*IR<3OBfmozpvfU(nna`z~)Ue#hQh`+<&7QBfH-djGI-WYEgT zg&GhwKi!{I|D?f7dvKtM(9;Go+_J~1tE-DNzei}p&CN|K>P(I#Wtap+afGM9U|>;v z6%eqX`7SOl$QR!wIf;=5F)%Ul=TOhPt*)-FA}Y7%1y{Ix-WrJ!SibHjNJ65_+1WX2 z_$Bg0iJe}0eP?;7@c$$usW7W9D|*77J`}pO>lHnuA6scTcH>KFKjB7XB6ju{1(Hmwg)qv1L<5H=ASdtES9o-$nP$ZC|I&T;Lh;n%m}g^yd!dHBpi z9VbuFs;aOC4llsu9w}-x{V|Fs|IQWL((*LlFEe=)CqC0_acQZ!C5_%K^fhAtPKBY_xPux)xq5Rul%k3KYZZ$sBj{!@?$=6 z-G9;}TpD0QLD`zBQR2o1os9!sK(UEO_QMA*M^**p>wd$-Coy7f%&e!emf7iTheL&1%PFs%<;p5*RE07+WPZ4EU_(oORj#u9vQF{Bs(R|XJ|5y zXTGcu5OO}t)&r!3mUaf;jCrckrNcvoJDQrffR?pq-6e=OI=IJri|2_;ES|Ai5#f!V zgSl=o%8w4%6b!a?kvILRNi3eQlZ=!rOx42{}RhP^ckgRb{Oy@1c= zy6~0F38mA-fQ^PYxzu|dAJYhVi0K$tGpb$!ScG*x2IqG2qX$yW7dAFFWK~pX;jZ$_ z#6?_%??14FLHp1$F+E*$u4mHs?b~1+yrrPdPVoZZ6I2&PGX`mar~?DN$a~!!n~lv{ zTG}LZWwj>EJRAo2% zn53Eyxkt|3mR>USB>NH)-t#10%ngA>1dKH`J2rH6Q37S-%=(yWqz}9)0H!TFcRjb2 zxU)U?*NzSzo2j4eb!C~E#Pi)J7`~ETZ`kVFe^i&Vba@5zHzRD3v0`p|K6!a!VRT1$ zh|RhlnnI!&!jRbRZ=t(?@Syoe^?{JXRn_qe^mXXmH9RF8h?%yn|^ua-0KFXb2$Y$ya~ z-l!ppzw77f$~wC-oqJcY-a96Sd-|6E{k?RnP&Z2M3IE@}RiZ38wPIA4hv4x#@6m^m zvDLNaYR5vW6BrrcZ!c_`g*rS`;d*?udjOBLJ(#-=7XfkLe0Sm*=Fu*vkB^UVl_P^{ zkK;Y*@MdOsdVpitM$1;c(XqwaqBz*hYn{3}Khb^4rBfZ9%_H60io=q~Y}%aFpB*lA z1L~y`s;E-!QBRel9xfAnffWaDG^NP;aGL-XK=+)YhQgxEyx-ZeGb?e6(?4$Bs(gW~1Zi{zJfMp15XU>7)TioJb1 z-khz*ti~0@oZ^Rij9HZWuIN&JHNJwAI=u!06nL1ft{)k>xzQCACJIVclyZ9tjr1>x zxMKSe>EoD~tWz%!k5mHB8K`jHp%pu_Q1Iyr4>(OhiG}l+%R~9?H7~aiL_tAMqmZde znNrUuo?5PV2ZAle^78U>0)S8fqSzkHb=kukgo> zdwJ=^Ec9YTR6K~XvND3`>Y{(rAi>1;*ZT+RcGragfSBD~)j@8zhtWm6e!a1sC+T6@ z>_@=E$9E2S8x=M6!4Yc)6teVp%uGzqPeD;r$F)vZ3G9j!lqa-KqL-aQh%a35Gi>%t zh5z{huhLZJBlduQCr%B-tLZ|_|m%+u9g z-@rm}2&MoDN|sywz(6eidI|8mxVT&r5vlo0fzI3`*_TpNQyVU~X7Kj$vE5zKjQYNW z+Rbe9p*-*r8UU0f=Zx8yq|sb!V2Fx}@?BUkP0Prbg*pQP$8zBkpxDz0dwY8WD=R`8 z8X6}jr-q4%I_0)!Z&8|kN?U73%fH7TZ$p*&Z{EBJkKnGNqO6q_*HsRVzZG~EJ3BiK zEv=^S-^p~VUs%57{R`%jkdk^&O&RFaI29&;MeV`++FH?b=g#rC>{$XrmBgkbv>Cq zJs};hiV7?#!Fck7C3^*cc$3H3kv^zuXL)-G8!{>ST_cg(Ko)l}$}UdG;U4W{hPP-NINktQI}HkfV_ zUFe-1aDD*q3utO*Nz-+Ing>P_f|#0{&$csow8(SUvY?dr^`U%*Fa~i#1lY453i3+% zul-Z89rEqom}{SVdd`7vHOj=xtNfe$ty~q+F@HuF2RGMlgzT|`h&UB_yq?|xqrsw6vyhgEcdt-VtP zMIK;3Wpva%IyoOGfM-RrTg53UDen@(+Y`wE82@*yqAa%C(w=}om}iN`tJM)ZJM9uH z%Y&bkK!{L^Aek`CcGd?)jh(T&1x~j5_hSrv)8%VHeIL)9O%OQ{ecJV0($FxhVs*Sy zYPisdq^LMQcnpO}LBqc!ekPOJ?f+-^n=z@WMGco((!&JDcHFz9QNE_`dSFaA@p2vs z3c}-cv~r9WKa)=l)mK&Z1jwrT=uyBJDQOrpzdh&g-+c^=jx;ERKa<_Mq>?z|&eb_T@Z6g7^m489C?z1TpI=Nj8S zc09Zo9+38DX<(wXB#5pze)%GQxrU+d`}LO2*f?c9y&zD(#Y$hs#+3NNgg$>hBw;j0 znuP#8J`)#6UdspJmsA2~UyP14G=>|Sn;D=Hj%VvU+_y`FNdyS6V9&rsz(ER19kNOj z0T5$IG^ES(BA)AY+P;-`)u}bd?xl6+gEe7OQ&~9pW1gVQt8z)BdqQ9FxRhOYKQJCM zY7Y)QA@uSJyGmIm`u5#BPgsos$mttvGmn0o1stdrJG*2I^ERS^Zd9 z(Gmvo86-^vwF@Bb+sxgMxmQ0vPEIRcN2H)|vbmHa!6Q)GSY+&@tmBFeGGy;NZU#J# z9EFX|*x$pOLBZC{%*=&t%qv}SCQV&kMChFj*iSya85{(hT;8u?CSmf`w}|ihUDZ=&=Cv?l%jsP=RpT39HH02*h@S+4hRXs z&glC7oqM)+uc_rkg^IdNNz-GqRS*q{=4Oc2c@vOkgmlU<>pG??~Fz~gDF#A$Hi%__B(aFdy2YC(4Bdoc2*O(bG!%r4{@4dxFp># zgxZ~kAxAuLfHqF{%<0G2u5T$GA1)IujFyUzmYpouSum##F8pv2Vq%g+klx;EL-S>C zLb|G)8ZkjZ%(1er_Nm6=%~s0bhy?daiOJ^X^UAl$2fr{ zC7iRGpUK|7ySjyje9ypEA6uWjjtSB|3Ti2Q!1K*tSB*MpZFSse#LjOTa}har&YRbM zL8RvFv}5L(8aKK;Ye5sJ-6Y^PuVOp4i|1F1Nos7FFSP#OjG% zZeDwK$`~)acb!#H&u4e_Lr(2rt1$v1gV9jlW#~*FFsE$w)zmJF9h&k3sSCs2~YUZoZ}$99O#bEE8by}QwS zXKIJv8%uUfW?IJ}i~n~50RSk&!on(al$9rv-hlDlhXKv=&k!1KYLe99y4LybFYC9X zr-w8;_tPg9l=YjNo7?&;PVhS%5VC*!#>5z4t6dmM)bgyZs!DmeHHXm0p9=^+(){b^ z>{qT30+K}#cqT+dM1L@^o15GI!9gJCw#$tFqT6cOj)3qB3fUV~**>)eG&HoC-vc=& zAYd>(L&5t8&lgqBE5z^1=p^2-eE(AmPhw_ztHBE$egPNxHZaf*)>~6=VffELu+T+2 zii+|Kds8M*^@$__<_QQ7_i$EE7B4Yu#ws!GJ%=bgdKBb1xzku^bd>fNm~CxmQ2PUB zIkn5~{tad~zAK6wetvx^Gdr8Kyt4A3Y0~3LmHnpYyLV-O>$SkfA#DIC+wJO5u;p+8 zTJh7aZ1q)VOd;fzpWjTE;ABg!%bsdkxy3U)ZEbas{3)RSmjvWsO_k6ZvJw(ER335% zs>g|^sb1ZLv_ijd4H_bezJIbe=}W2SB!M*ikuBRENi7c-rh+s&KwQWHb;$p@_}hmM zA8hA3FRqN06S*C3d%e5BPzb;u1L5N00)rLe4y047t3M1Jmfi4bSt~0m1H!^&WMr`T z`1q7`bjH6)!Csv(LlmU3ua6YaqE|)++i1DOD?Jya}3d3L7K>fduKVEM^RBx2yzHtg$OQ8fl#+U6UOr@pxC1TxVCjIrj%5(AICUxxSg=W(pxxv=2<=N(*f9~p&1h|)zsF;hX{z2 zR7)O|kJ37J<;p}Y4U`MubF~qxr-SssV|jR=p}U} zvhJq@$Ze44BVuB3L`6mAc36I3(_gs2>L-YF#uk702i4xtsM>ZhefA6;QBhGTc)9iT zPo|{Ia;)XoeBvJHwDo^wO7O6;9YI>Ulm|Df`<$~By{4jq3dH7_!`GMWE)${H;xj+O z6Ar=I*;(7wQGq}DEG#a7GH-EVAz52c{O>Rch+{GDM+27G6cgT}q~4y+PV#7&LQdfz zEJwUYska^BbJ@2*-W?y;v8^Q%GF1E=Gg%KyIJ{(nMIX%A*~$lP2RfQ6#03|Q_3dapNaH~*g) zw*Mj7>f{*-fY_#A9c&51j^95nYE(YQcy9L)g-C0T@`#|W%vQ9)ZaLU;nQ12SPcBj! zvrpD=@LLQCu12(r)d@yBUU@GM67sU`!g!4)e9OwV$!r9zbUoIsH1sow3nK>uxoU5 zN5&7*M+Oa^xQM5O&(o<^%1LQZVq02Plox3(TnMSCXoeVtz%BcQiOemJevbUkmeJA- zL+i=atWY^r&?Aim0DYlJj|<9CmFLD(9m|KIjuO9XYRIc!a9Vm_d^Eo-OaUZc$l2aH z^axX;fxF&9wkJXATTyy!g($lzzJ;Z3!L#q@L65>k?kb$pUAlB8iBjDczEvt3pA!8G zHeafc?g7y9>=h(QD(u*ZQe{L>U9>?8cXHZiW}3kVx|vz3r&}jp_wqUcsPp25D2Rd~ zky-Ym!<=?|`#h*(wQWN%7w!P6giX(~7#a|0h`fB1oSWOSG;oS4DHL44zT{M&M!wCGo`4@O#|(LgKhcW*(Vp&Z3U9`#zH}izx&K`tzM%lm26`35*^E&!d3l%4#CrTIS13uy`zb)%1`b}! z!{@vfm^d&A57uCm-{m|*p8rVk2T*}^OT~pyS}d~+RT6mq`W`2jSnn6KP1LTVBae;h zr3{i%_Xk=WCv%&|D}N~Kxz~?s{LNct4&R7%jJ92N9{jWJKMVv@tCTOlG%-qz4Y_S) zm0giru}=sPx$yZf93i3YrudusNgii~LUyd@bJUM=YED;17*OVi)4{LXE2B}qYB^!k zJ>nj%>iTYsyII-U(wdrAP)VXXH`%FXBIa*7Lp-!%H5AIq!fHzg2ckH|^$fn06ckK1 zc7^v36}%*sx5Q@?M6r#&CEZ4e-hiQdOU>)qT<;CgiJuA#<|ks1y}7a9cL3TgpG>$rHez65;dRrlt^r6LkNffSs6tWE6JCP`X~Z)(7nLo# zbZHs`#MJ;dTDqeYOKn+b!p1Z?^Q6>N?5W0Fn_s_-;ZnbiCgqitFLi%7!o~AF$EN5W z-kUn9yo{qmjIlCta>K$R0g9+Tz{qiRlxVhn=-6M$^_bUUlsx<$=K}`qa!^`;t4g4l z(=Oc=tMC9EuVh2~?Nwow)t(}*SyEARCkzGZWQbINSAU5+e^)!Ps3;Oa+S&#fRvtg; z*D`k7-@rsXIn*DciR>rrwuRq;5t5XiPLQsW(Kb|bdU)i3r_7LeB_LQh#M|2fAv)CnM@ZuAG+#=6=r*h)57r@% zI-2PHj8#;0a!*k5E3$FCMX&hjnehM#Vzc~+6$v2n21lIe%NNR)cahIY5)xA344`(t zaN)((Wi2g%GrMhwlYrX^cZ)b}INSzU2u8}_FattJHyVPnJ0M{*J;R4wH?eAj)J&tg zKBvpfkuy6R+nrx-gLg6%M3?(};Mf6sC?N2F6tUTSOb)ZAwU}th{hb3{^nzy7!-X^w zxaibWqaunweyiTwpgw|gFc8`k4XCN?Seb5u#@6)S&yOE(0Lc}?LVU36FDIDghe{@A zejnE^;gMLMXtsqkBlv%0D1He=MlY(DTj+7`>m7?pF$Q}2Q>Q-!_~bCO@nEYSl@x+d zqwLSH6%nU6+}SD?ll7IB%!ONfiTg`$s*iv&7DwGj!TaD z-%-*bjoEeecyZTzT40=^iBwEhK8WiUdj50EES~}+xlOxu>1&TQHAXUCd$>m^*f@q$ z4RQiva;v8&k3Z>&QiH+5$Zi_^wQljdU;~8026JihpGQp0qQsZa3pc@^x^3(NX6wCs zYxmr_i4H&xfN({go~ZxB3`wL3esSkODySeow z_-w9K*P-qVQd5(4sXQj#gi&H(0>Pi_7r^mHE3^SBj%M*YDt=R$(%WkAd!FGvq z!zcaJvu&JGaNc}bzq2Fypbhl4WJn$4xQT`CXp)L@kbA$MPZ z>iv*RUI0V0Fh9vDC;cibIpHTss!M~zf~-%8pajCFrtndRmVyEwXiSf)ub!-?@|bKFaQAWFc*L;*zqKP}a&2sb!5oE^%w1_k}b`T+Jy1S?QVvSBl*-plugco zMc0>}n3$1Ezt;Hz35oFE))bds2;^bQXAVoAePi25e+WGcWCMSZ#RQUQR~G+p zdhoea^C-tEY?&dJx1?6Dhen8C6VbQOSl4(}JFaJ22=X}-0^0;if#>Xuwey|@9)Ql< z=xE5`0MLqlo#-KDoER2Hh^pe)>n4L9jLB#^RrYcVBgGm2>r)bQQ&Wu6va#vk8kYu-$ekZpP1FRYEKSfZkI zckbLF5XB%N=@pLz)9_PnuA!wR0dVgxdoRW1QZb-bNd&k~<598x|Ii0Z4m~2av*_@3 TI)B0ka1gmW3etI!2HyVzmGsi$ literal 0 HcmV?d00001 diff --git a/docs/image/graph_lifecycle.png b/docs/image/graph_lifecycle.png new file mode 100644 index 0000000000000000000000000000000000000000..d12799fd4ede0ab41dc9f2d08f57a02d8c3b2a0e GIT binary patch literal 31004 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ⅇ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 literal 0 HcmV?d00001 diff --git a/include/tim/vx/ops/activations.h b/include/tim/vx/ops/activations.h index 2eadede..8b0366a 100644 --- a/include/tim/vx/ops/activations.h +++ b/include/tim/vx/ops/activations.h @@ -65,7 +65,6 @@ namespace ops { * Linear(x, a, b) : a*x + b. * * Gelu(x) : x * P(X <= x), where P(x) ~ N(0, 1). https://tensorflow.google.cn/api_docs/python/tf/nn/gelu - * ``` */