This chapter is experimental. A trained model is baked into the code at compile time instead of being loaded at run time (no loading code, and no file to ship alongside it).
The result can be read back as numbers. Suited to small models
[OnnxGpu("…")]
GPU
Much larger models fit, but the result lands in a buffer, so using it as numbers takes one readback step
Pick the former when you read the values and branch on them, as in gesture recognition, and the latter when the result goes straight to the screen, as in reworking an image.
The input buffer (the same type as in Running a GPU kernel). The range of values to assign to a cell is written in the generated file, next to the input field.
FilterOutput
The output buffer. Read its cells as values from 0 to 1
LoadFilter()
Creates the buffers and loads the weights. Call it once from Start
RunFilter()
Runs one pass
FilterLayer0
The kernel for each layer. Gpu.Run launches it, so you never call it directly
FilterStyles
Generated only when 2 or more paths are listed. The number of paths listed
UseFilter(int style)
Generated only when 2 or more paths are listed. Chooses which weights to use by their position in the list (counting from 0). Right after LoadFilter() it is 0
Value range
For most models, assigning a value between 0 and 1 to a cell passes it through correctly, because the 8-bit steps line up exactly with the range the model accepts. A model that takes its input as 16-bit floats also passes values outside 0 to 1 through unchanged. Which one applies is decided by the model, and is written in the input field of the generated file.
What the output means
The range the output values actually stand for is written in the generated file.
Reading it back
The result lands in a buffer. To use it as numbers, take it out with Gpu.Texture and hand it to the runtime’s asynchronous readback
Swapping weights
Models that share one graph and differ only in their weights become one when their paths are listed, as in [OnnxGpu("a.onnx", "b.onnx")]. The buffers and kernels stay as one set, and only the weights are kept once per listed model
kernel_shape has equal height and width. strides and dilations are handled. group must be 1, or equal to both the input and output channel counts (depthwise). auto_pad is not accepted
DepthToSpace
Only the DCR mode. The block size is a power of 2, and the channel count must be divisible by its square
Concat
Only along the channel axis. Up to 4 inputs, each with a channel count that is a multiple of 4
Add
Both inputs come from earlier operators and have the same shape. Adding a constant is not accepted
QuantizeLinear
Read as the quantization scale, not as a computation. Scale and zero point are constants
DequantizeLinear
Read as the quantization scale, not as a computation. Putting a Cast (float16) and Cast (float32) pair in place of quantization carries that edge as half-precision floats
Pad
Only reflect, placed directly before a Conv with no padding of its own (it is folded into the Conv)
InstanceNormalization
scale and B are constants
Resize
Only linear interpolation. The coordinate_transformation_mode values half_pixel, pytorch_half_pixel, align_corners and asymmetric are handled. Only height and width can change
PRelu
The slope is a constant with either 1 value or one per channel
MaxPool
kernel_shape has equal height and width, and dilations are not accepted. The positions of the maxima (Indices) cannot be output
Elu
8-bit input only
Relu
Half-precision float input is accepted as well
LeakyRelu
Half-precision float input is accepted as well
Tanh
8-bit input only
Sigmoid
8-bit input only
Clip
The bounds are constants. Half-precision float input is accepted as well
Quantize the weights to 8 bits, and quantize the output to 8 bits as well, before exporting
Weights are baked in as 8-bit integers. The input and the values between layers can be carried as 8 bits or as half-precision floats (a Cast pair), but the output is received as 8 bits
The scale is decided at run time
Export it in a form that is fixed at export time
To bake it in, the value steps have to be fixed at compile time
Not an 8-bit integer
Quantize to 8-bit integers
Even at 8 bits, a format that represents fractions is not accepted
The shape is dynamic
Fix the input shape before exporting
When an axis length is decided at run time, the texture size cannot be determined
A listed model differs from the first in more than its weights
Export it with the same graph, changing only the weights
Names the layer that differs. A model whose scales built into the computation differ is also an error here
An unsupported operator is present
Rewrite it into a form that does not use that operator
It names what was present. It never leaves one out and passes the model anyway
The error speaks in terms of the model, so fix the exporting side.
For a model whose channel count exceeds 4, the texture extends sideways
Where the weights are kept
Small layers are kept as constants and large ones as textures. Which one applies is decided per layer
Stride
strides other than 1 are handled for Conv and MaxPool
Width
For a layer with more than 4 channels it must be a power of 2. Concat and DepthToSpace need it regardless of the channel count. Height has no restriction
Class declaration
partial is required. The generated file becomes the rest of that class
The result is not guaranteed to match the original model. Values are rounded when quantized to 8 bits, and values carried in half precision follow the GPU’s own rounding when stored, so they differ from what the exporting runtime computes by that much.