载入中...
搜索中...
未找到
OnnxQuant.cpp
浏览该文件的文档.
5
6#include <algorithm>
7#include <cmath>
8
10namespace {
11affine::ByteView bytes(const RuntimeTensor& t) {
12 if (t.element != OnnxElement::Int8 && t.element != OnnxElement::UInt8)
13 throw Failure("Expected packed int8/uint8 tensor");
14 return {t.bytes, t.element == OnnxElement::Int8};
15}
16template <class T>
17T checked(Result<T> result) {
18 if (!result.ok()) throw Failure(result.error()->message(), result.error()->code());
19 return std::move(result.value());
20}
21int zero(const std::vector<const RuntimeTensor*>& in, size_t i, const RuntimeTensor& owner) {
22 if (i >= in.size() || !in[i]) return 0;
23 if (in[i]->element != owner.element || count(in[i]->shape) != 1)
24 throw Failure("Expected matching scalar zero point");
25 return static_cast<int>(integer(*in[i]));
26}
27} // namespace
28std::vector<RuntimeTensor> executeQuant(const Node& n, const std::vector<const RuntimeTensor*>& in,
30 const auto& x = required(in, 0);
31 if (n.op == "DynamicQuantizeLinear") {
32 if (compute && count(x.shape)) {
33 if (x.element != OnnxElement::Float32) throw Failure("Expected FP32 quantization input");
34 const size_t total = count(x.shape);
35 auto stats = dispatchFloat(
36 *compute, {&x}, {3},
37 "float lo=0.0,hi=0.0,invalid=0.0;for(uint j=0;j<" + std::to_string(total) +
38 "u;++j){float v=x0[j];if(isinf(v))invalid=1.0;if(!isnan(v)){lo=min(lo,v);hi=max(hi,v);}}float "
39 "s=(hi==lo)?1.0:(hi-lo)/255.0;y[0]=s;y[1]=clamp(roundEven(-lo/s),0.0,255.0);y[2]=invalid;",
40 1);
41 auto values = floats(stats);
42 if (values[2] != 0 || !std::isfinite(values[0]) || !(values[0] > 0))
43 throw Failure("Nonfinite quantization input/range");
44 auto scale = make(OnnxElement::Float32, {}, std::vector<float>{values[0]});
45 auto zeroPoint = make(OnnxElement::UInt8, {}, std::vector<uint8_t>{static_cast<uint8_t>(values[1])});
46 auto params = make(OnnxElement::Float32, {2}, std::vector<float>{values[0], values[1]});
47 std::string source =
48 "#version 450\nlayout(local_size_x=64)in;layout(std430,binding=0)readonly buffer X{float "
49 "x[];};layout(std430,binding=1)readonly buffer P{float p[];};layout(std430,binding=2)writeonly buffer "
50 "Y{uint y[];};void main(){uint word=gl_GlobalInvocationID.x;if(word>=" +
51 std::to_string((total + 3) / 4) +
52 "u)return;uint packed=0u;for(uint c=0u;c<4u;++c){uint j=word*4u+c;if(j<" + std::to_string(total) +
53 "u){float v=x[j];uint "
54 "q=isnan(v)?0u:uint(clamp(roundEven(v/p[0])+p[1],0.0,255.0));packed|=q<<(c*8u);}}y[word]=packed;}";
55 const std::vector<OnnxBuffer> inputs{x.bytes.buffer(), params.bytes.buffer()};
56 auto q = checked(compute->enqueue(source, inputs, total, static_cast<uint32_t>((total + 3) / 4)));
57 return {{OnnxElement::UInt8, x.shape, std::move(q)}, std::move(scale), std::move(zeroPoint)};
58 }
59 auto input = floats(x);
60 // ORT CPU DynamicQuantizeLinear ignores NaN for range estimation and stores code 0.
61 // Exported Kokoro phase graphs produce 0/0 at zero-amplitude STFT bins.
62 std::vector<size_t> nanPositions;
63 for (size_t i = 0; i < input.size(); ++i)
64 if (std::isnan(input[i])) {
65 nanPositions.push_back(i);
66 input[i] = 0;
67 }
68 auto q = checked(affine::dynamicQuantize(input));
69 for (auto i : nanPositions) q.values[i] = 0;
70 return {{OnnxElement::UInt8, x.shape, std::move(q.values)},
71 make(OnnxElement::Float32, {}, std::vector<float>{q.scale}),
72 make(OnnxElement::UInt8, {}, std::vector<uint8_t>{q.zeroPoint})};
73 }
74 if (n.op == "QuantizeLinear" || n.op == "DequantizeLinear") {
75 const auto scales = floats(required(in, 1));
76 if (required(in, 1).shape.size() > 1) throw Failure("Quantization scale must be scalar or vector");
77 if (scales.empty()) throw Failure("Empty quantization scales");
78 const RuntimeTensor* zp = in.size() > 2 ? in[2] : nullptr;
79 const auto type = n.op == "DequantizeLinear" ? x.element : (zp ? zp->element : OnnxElement::UInt8);
81 throw Failure("Unsupported quantization dtype", DiagnosticCode::Unsupported);
82 if (zp && (zp->element != type || zp->shape != required(in, 1).shape))
83 throw Failure("Scale/zero point mismatch");
84 std::vector<int32_t> zeros(scales.size(), 0);
85 if (zp)
86 for (size_t i = 0; i < zeros.size(); ++i) zeros[i] = static_cast<int32_t>(integer(*zp, i));
87 size_t inner = 1;
88 if (scales.size() > 1) {
89 const int a = axis(attr(n, "axis", 1), x.shape.size());
90 if (static_cast<size_t>(x.shape[a]) != scales.size()) throw Failure("Scale channel count mismatch");
91 for (size_t i = a + 1; i < x.shape.size(); ++i) inner *= x.shape[i];
92 }
93 if (n.op == "DequantizeLinear")
94 return {make(OnnxElement::Float32, x.shape, checked(affine::dequantize(bytes(x), scales, zeros, inner)))};
95 const auto values = floats(x);
96 std::vector<uint8_t> out(values.size());
97 for (size_t i = 0; i < values.size(); i += inner) {
98 const size_t channel = (i / inner) % scales.size();
99 auto q = checked(affine::quantize(std::span(values).subspan(i, inner), scales[channel], zeros[channel],
101 std::copy(q.begin(), q.end(), out.begin() + i);
102 }
103 return {{type, x.shape, std::move(out)}};
104 }
105 if (n.op == "MatMulInteger") {
106 const auto& b = required(in, 1);
107 if (x.shape.size() < 2 || b.shape.size() != 2)
108 throw Failure("MatMulInteger requires rank >=2 A and rank 2 B", DiagnosticCode::Unsupported);
109 const size_t k = x.shape.back(), columns = b.shape[1];
110 if (k != static_cast<size_t>(b.shape[0]) || k == 0) throw Failure("Invalid MatMulInteger inner dimension");
111 const size_t rows = count(x.shape) / k;
112 auto shape = x.shape;
113 shape.back() = columns;
114 if (compute) {
115 if ((x.element != OnnxElement::Int8 && x.element != OnnxElement::UInt8) ||
116 (b.element != OnnxElement::Int8 && b.element != OnnxElement::UInt8))
117 throw Failure("Expected packed integer matrices");
118 std::vector<int32_t> bz{zero(in, 3, b)};
119 return {{OnnxElement::Int32, shape,
120 gpuMatmulResident(*compute, x.bytes.buffer(), b.bytes.buffer(), x.element == OnnxElement::Int8,
121 b.element == OnnxElement::Int8, rows, k, columns, zero(in, 2, x), bz)}};
122 }
123 return {make(
125 checked(affine::matmul(bytes(x), bytes(b), rows, k, columns, zero(in, 2, x), zero(in, 3, b), compute)))};
126 }
127 if (n.op == "ConvInteger") {
128 const auto& w = required(in, 1);
129 if ((x.shape.size() != 3 && x.shape.size() != 4) || w.shape.size() != x.shape.size())
130 throw Failure("ConvInteger supports 1D/2D NCHW", DiagnosticCode::Unsupported);
131 const size_t spatial = x.shape.size() - 2;
132 if (n.attrs.contains("kernel_shape") &&
133 n.attrs.at("kernel_shape").integers != std::vector<int64_t>(w.shape.begin() + 2, w.shape.end()))
134 throw Failure("Convolution kernel_shape mismatch");
135 auto strides = attrs(n, "strides", std::vector<int64_t>(spatial, 1));
136 auto pads = attrs(n, "pads", std::vector<int64_t>(2 * spatial, 0));
137 auto dilations = attrs(n, "dilations", std::vector<int64_t>(spatial, 1));
138 if (strides.size() != spatial || pads.size() != 2 * spatial || dilations.size() != spatial)
139 throw Failure("Invalid convolution attributes");
140 auto narrow = [](int64_t v) {
141 if (v < 0 || v > 65536) throw Failure("Convolution attribute out of range");
142 return static_cast<int>(v);
143 };
145 s.batch = narrow(x.shape[0]);
146 s.channels = narrow(x.shape[1]);
147 s.outputs = narrow(w.shape[0]);
148 s.width = narrow(x.shape.back());
149 s.kernelW = narrow(w.shape.back());
150 s.strideW = narrow(strides.back());
151 s.dilationW = narrow(dilations.back());
152 s.padLeft = narrow(pads[spatial - 1]);
153 s.padRight = narrow(pads.back());
154 s.groups = narrow(attr(n, "group", 1));
155 if (spatial == 2) {
156 s.height = narrow(x.shape[2]);
157 s.kernelH = narrow(w.shape[2]);
158 s.strideH = narrow(strides[0]);
159 s.dilationH = narrow(dilations[0]);
160 s.padTop = narrow(pads[0]);
161 s.padBottom = narrow(pads[2]);
162 }
163 if (!s.groups || s.channels % s.groups || w.shape[1] != s.channels / s.groups)
164 throw Failure("Convolution channel mismatch");
165 std::vector<int32_t> wz{0};
166 if (in.size() > 3 && in[3]) {
167 if (in[3]->element != w.element) throw Failure("Weight zero-point type mismatch");
168 wz.clear();
169 for (auto z : ints(*in[3])) wz.push_back(static_cast<int32_t>(z));
170 }
171 if (compute) {
172 if ((x.element != OnnxElement::Int8 && x.element != OnnxElement::UInt8) ||
173 (w.element != OnnxElement::Int8 && w.element != OnnxElement::UInt8))
174 throw Failure("Expected packed convolution inputs");
175 const auto extent =
176 checked(affine::detail::validateConv(x.bytes.size(), w.bytes.size(), x.element == OnnxElement::Int8,
177 w.element == OnnxElement::Int8, s, zero(in, 2, x), wz));
178 std::vector<int64_t> outputShape{s.batch, s.outputs};
179 if (spatial == 2) outputShape.push_back(extent.height);
180 outputShape.push_back(extent.width);
181 return {{OnnxElement::Int32, outputShape,
182 gpuConvResident(*compute, x.bytes.buffer(), w.bytes.buffer(), x.element == OnnxElement::Int8,
183 w.element == OnnxElement::Int8, s, zero(in, 2, x), wz)}};
184 }
185 auto values = checked(affine::conv(bytes(x), bytes(w), s, zero(in, 2, x), wz, compute));
186 std::vector<int64_t> shape{s.batch, s.outputs};
187 if (spatial == 2)
188 shape.push_back(
189 (s.height + s.padTop + s.padBottom - int64_t(s.dilationH) * (s.kernelH - 1) - 1) / s.strideH + 1);
190 shape.push_back((s.width + s.padLeft + s.padRight - int64_t(s.dilationW) * (s.kernelW - 1) - 1) / s.strideW +
191 1);
193 }
194 throw Failure("Unsupported quantized operation", DiagnosticCode::Unsupported);
195}
196} // namespace eve::tensor::onnx_detail
float w
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
eve::action::ActionSpatialBinding spatial
const std::string & s
int bz
Definition CaveMesh.cpp:114
std::map< std::string, Var > values
EvpackChunkInput input
Definition Evpack.cpp:170
int rows
ShaderImageInput shape
glm::vec3 n
Definition Grass.cpp:63
int inputs
Definition GridGraph.cpp:23
std::array< double, 10 > q
float v
std::array< float, 3 > scale
bool required
std::uint64_t bytes
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
OnnxTransferStats stats
Definition OnnxGpgpu.cpp:56
std::vector< float > scales
Definition OnnxLstm.cpp:27
std::vector< int64_t > zeros
Definition OnnxLstm.cpp:28
OnnxCompute * compute
float t
std::uint32_t count
std::string element
int columns
const UnitySourceAsset & source
float wz
GPU execution boundary for native ONNX; retains no model and retains compiled resources for the lifet...
Definition OnnxCompute.h:25
const OnnxBuffer & buffer() const
Buffer.
std::vector< ParamSpec > params
Result< ConvExtent > validateConv(size_t xSize, size_t wSize, bool xSigned, bool wSigned, const ConvShape &, int, std::span< const int32_t >)
Validate conv.
Result< std::vector< int32_t > > conv(ByteView x, ByteView w, const ConvShape &s, int xZero, std::span< const int32_t > wZeros, OnnxCompute *compute)
Integer Conv, supporting groups, asymmetric padding and dilation.
Result< std::vector< uint8_t > > quantize(std::span< const float > input, float scale, int zero, bool sign)
Affine quantization to int8/uint8 bytes, saturating and rounding ties to even.
Result< std::vector< float > > dequantize(ByteView input, std::span< const float > scales, std::span< const int32_t > zeros, size_t inner)
Affine dequantization using scalar or per-axis scale/zero point.
Result< std::vector< int32_t > > matmul(ByteView a, ByteView b, size_t m, size_t k, size_t n, int aZero, int bZero, OnnxCompute *compute)
Integer row-major [M,K] x [K,N], subtracting scalar zero points.
Result< QuantizedActivation > dynamicQuantize(std::span< const float > input)
Quantize finite FP32 activations with ONNX DynamicQuantizeLinear semantics.
OnnxBuffer gpuConvResident(OnnxCompute &d, OnnxBuffer x, OnnxBuffer w, bool xs, bool ws, const affine::ConvShape &c, int xz, std::span< const int32_t > wz)
Gpu conv resident.
int64_t integer(const RuntimeTensor &v, size_t i=0)
Integer.
std::vector< int64_t > ints(const RuntimeTensor &v)
Ints.
int64_t attr(const Node &n, const char *key, int64_t fallback)
Attr.
std::vector< int64_t > attrs(const Node &n, const char *key, std::vector< int64_t > fallback)
Attrs.
std::vector< RuntimeTensor > executeQuant(const Node &node, const std::vector< const RuntimeTensor * > &inputs, OnnxCompute *compute=nullptr)
Execute quant.
Definition OnnxQuant.cpp:28
RuntimeTensor make(OnnxElement e, std::vector< int64_t > shape, const std::vector< T > &values)
Make.
int axis(int64_t a, size_t rank)
Axis.
std::vector< float > floats(const RuntimeTensor &v)
Floats.
RuntimeTensor dispatchFloat(OnnxCompute &, const std::vector< const RuntimeTensor * > &, const std::vector< int64_t > &, const std::string &, size_t work=0)
Dispatches float.
OnnxBuffer gpuMatmulResident(OnnxCompute &d, OnnxBuffer a, OnnxBuffer b, bool as, bool bs, size_t m, size_t k, size_t n, int az, std::span< const int32_t > bz, size_t bOffset)
Gpu matmul resident.
Explicit 1D/2D NCHW convolution geometry; missing 1D height is one.
Definition AffineQuant.h:65