载入中...
搜索中...
未找到
AffineQuant.cpp
浏览该文件的文档.
4
5#include <algorithm>
6#include <cmath>
7#include <limits>
8
10namespace {
11int value(ByteView v, size_t i) {
12 const int x = v.bytes[i];
13 return v.signedValues && x >= 128 ? x - 256 : x;
14}
15bool validZero(int z, bool sign) { return sign ? z >= -128 && z <= 127 : z >= 0 && z <= 255; }
16double evenRound(double x) {
17 const double lo = std::floor(x), fraction = x - lo;
18 return lo + (fraction > 0.5 || (fraction == 0.5 && std::fmod(lo, 2.0) != 0));
19}
20bool fits(int64_t n) { return n >= INT32_MIN && n <= INT32_MAX; }
21bool product(size_t a, size_t b, size_t limit) { return b == 0 || a <= limit / b; }
22} // namespace
23
24Result<std::vector<uint8_t>> quantize(std::span<const float> input, float scale, int zero, bool sign) {
25 if (!(scale > 0) || !std::isfinite(scale) || !validZero(zero, sign))
26 return Result<std::vector<uint8_t>>::failure(
27 Diagnostic::error(DiagnosticCode::InvalidArgument, "Invalid affine scale or zero point"));
28 std::vector<uint8_t> out;
29 out.reserve(input.size());
30 for (float x : input) {
31 if (!std::isfinite(x))
32 return Result<std::vector<uint8_t>>::failure(
33 Diagnostic::error(DiagnosticCode::InvalidArgument, "Nonfinite quantization input"));
34 const double rounded = evenRound(static_cast<double>(x) / scale) + zero;
35 const int q = static_cast<int>(std::clamp(rounded, sign ? -128.0 : 0.0, sign ? 127.0 : 255.0));
36 out.push_back(static_cast<uint8_t>(q));
37 }
38 return Result<std::vector<uint8_t>>::success(std::move(out));
39}
40
42 float lo = 0, hi = 0;
43 for (float x : input) {
44 if (!std::isfinite(x))
46 Diagnostic::error(DiagnosticCode::InvalidArgument, "Nonfinite dynamic quantization input"));
47 lo = std::min(lo, x);
48 hi = std::max(hi, x);
49 }
51 result.scale = hi == lo ? 1.f : static_cast<float>((static_cast<double>(hi) - lo) / 255.0);
52 if (!(result.scale > 0) || !std::isfinite(result.scale))
54 Diagnostic::error(DiagnosticCode::InvalidArgument, "Dynamic quantization scale is not representable"));
55 result.zeroPoint = static_cast<uint8_t>(std::clamp(evenRound(-static_cast<double>(lo) / result.scale), 0.0, 255.0));
56 auto bytes = quantize(input, result.scale, result.zeroPoint, false);
57 if (!bytes.ok()) return Result<QuantizedActivation>::failure(bytes.status());
58 result.values = std::move(bytes.value());
59 return Result<QuantizedActivation>::success(std::move(result));
60}
61
62Result<std::vector<float>> dequantize(ByteView input, std::span<const float> scales, std::span<const int32_t> zeros,
63 size_t inner) {
64 if (scales.empty() || scales.size() != zeros.size() || inner == 0 || !product(scales.size(), inner, SIZE_MAX) ||
65 input.bytes.size() % (scales.size() * inner))
66 return Result<std::vector<float>>::failure(
67 Diagnostic::error(DiagnosticCode::InvalidArgument, "Invalid affine channel layout"));
68 for (size_t i = 0; i < scales.size(); ++i)
69 if (!(scales[i] > 0) || !std::isfinite(scales[i]) || !validZero(zeros[i], input.signedValues))
70 return Result<std::vector<float>>::failure(
71 Diagnostic::error(DiagnosticCode::InvalidArgument, "Invalid affine scale or zero point"));
72 std::vector<float> out(input.bytes.size());
73 for (size_t i = 0; i < out.size(); ++i) {
74 const size_t channel = (i / inner) % scales.size();
75 out[i] = static_cast<float>(value(input, i) - zeros[channel]) * scales[channel];
76 }
77 return Result<std::vector<float>>::success(std::move(out));
78}
79
80Result<std::vector<int32_t>> matmul(ByteView a, ByteView b, size_t m, size_t k, size_t n, int aZero, int bZero,
82 if (!validZero(aZero, a.signedValues) || !validZero(bZero, b.signedValues) || !product(m, k, SIZE_MAX) ||
83 !product(k, n, SIZE_MAX) || !product(m, n, INT32_MAX) || a.bytes.size() != m * k || b.bytes.size() != k * n)
84 return Result<std::vector<int32_t>>::failure(
85 Diagnostic::error(DiagnosticCode::InvalidArgument, "Invalid integer matmul dimensions or zero points"));
86 if (compute && m && n && k) {
87 try {
88 const int32_t z = bZero;
89 return Result<std::vector<int32_t>>::success(
90 onnx_detail::gpuMatmul(*compute, a, b, m, k, n, aZero, {&z, 1}));
91 } catch (const std::exception& e) {
93 }
94 }
95 std::vector<int32_t> out(m * n);
96 for (size_t r = 0; r < m; ++r)
97 for (size_t c = 0; c < n; ++c) {
98 int64_t sum = 0;
99 for (size_t j = 0; j < k; ++j)
100 sum += static_cast<int64_t>(value(a, r * k + j) - aZero) * (value(b, j * n + c) - bZero);
101 if (!fits(sum))
102 return Result<std::vector<int32_t>>::failure(
103 Diagnostic::error(DiagnosticCode::InvalidArgument, "Integer matmul accumulator overflow"));
104 out[r * n + c] = static_cast<int32_t>(sum);
105 }
106 return Result<std::vector<int32_t>>::success(std::move(out));
107}
108
109Result<detail::ConvExtent> detail::validateConv(size_t xSize, size_t wSize, bool xSigned, bool wSigned,
110 const ConvShape& s, int xZero, std::span<const int32_t> wZeros) {
111 for (int d : {s.batch, s.channels, s.height, s.width, s.outputs, s.kernelH, s.kernelW, s.strideH, s.strideW,
112 s.dilationH, s.dilationW, s.groups})
113 if (d <= 0 || d > 65536)
115 Diagnostic::error(DiagnosticCode::InvalidArgument, "Invalid integer convolution dimensions"));
116 if (s.channels % s.groups || s.outputs % s.groups || s.padTop < 0 || s.padBottom < 0 || s.padLeft < 0 ||
117 s.padRight < 0 || !validZero(xZero, xSigned) ||
118 (wZeros.size() != 1 && wZeros.size() != static_cast<size_t>(s.outputs)))
120 Diagnostic::error(DiagnosticCode::InvalidArgument, "Invalid convolution groups, padding or zero points"));
121 for (int z : wZeros)
122 if (!validZero(z, wSigned))
124 Diagnostic::error(DiagnosticCode::InvalidArgument, "Invalid convolution weight zero point"));
125 const int64_t h = static_cast<int64_t>(s.height) + s.padTop + s.padBottom -
126 static_cast<int64_t>(s.dilationH) * (s.kernelH - 1) - 1;
127 const int64_t width = static_cast<int64_t>(s.width) + s.padLeft + s.padRight -
128 static_cast<int64_t>(s.dilationW) * (s.kernelW - 1) - 1;
129 if (h < 0 || width < 0)
131 Diagnostic::error(DiagnosticCode::InvalidArgument, "Convolution kernel exceeds padded input"));
132 const int64_t oh = h / s.strideH + 1, ow = width / s.strideW + 1;
133 int64_t outCount = 1;
134 for (int64_t d : {static_cast<int64_t>(s.batch), static_cast<int64_t>(s.outputs), oh, ow}) {
135 if (d > INT32_MAX / outCount)
137 Diagnostic::error(DiagnosticCode::InvalidArgument, "Convolution output too large"));
138 outCount *= d;
139 }
140 const int cg = s.channels / s.groups;
141 size_t inputCount = 1, weightCount = 1;
142 for (int d : {s.batch, s.channels, s.height, s.width}) {
143 if (!product(inputCount, d, INT32_MAX))
145 Diagnostic::error(DiagnosticCode::InvalidArgument, "Convolution input too large"));
146 inputCount *= d;
147 }
148 for (int d : {s.outputs, cg, s.kernelH, s.kernelW}) {
149 if (!product(weightCount, d, INT32_MAX))
151 Diagnostic::error(DiagnosticCode::InvalidArgument, "Convolution weights too large"));
152 weightCount *= d;
153 }
154 if (outCount > INT32_MAX || xSize != inputCount || wSize != weightCount)
156 Diagnostic::error(DiagnosticCode::InvalidArgument, "Convolution buffer size mismatch or output too large"));
157 return Result<detail::ConvExtent>::success({oh, ow, outCount});
158}
160 std::span<const int32_t> wZeros, OnnxCompute* compute) {
161 auto extent =
162 detail::validateConv(x.bytes.size(), w.bytes.size(), x.signedValues, w.signedValues, s, xZero, wZeros);
163 if (!extent.ok()) return Result<std::vector<int32_t>>::failure(extent.status());
164 const auto [oh, ow, outCount] = extent.value();
165 const int cg = s.channels / s.groups;
166 if (compute) {
167 try {
168 return Result<std::vector<int32_t>>::success(onnx_detail::gpuConv(*compute, x, w, s, xZero, wZeros));
169 } catch (const std::exception& e) {
171 }
172 }
173 std::vector<int32_t> out(static_cast<size_t>(outCount));
174 for (int b = 0; b < s.batch; ++b)
175 for (int o = 0; o < s.outputs; ++o)
176 for (int64_t y = 0; y < oh; ++y)
177 for (int64_t z = 0; z < ow; ++z) {
178 int64_t sum = 0;
179 for (int c = 0; c < cg; ++c)
180 for (int ky = 0; ky < s.kernelH; ++ky)
181 for (int kx = 0; kx < s.kernelW; ++kx) {
182 const int64_t iy = y * s.strideH - s.padTop + static_cast<int64_t>(ky) * s.dilationH;
183 const int64_t ix = z * s.strideW - s.padLeft + static_cast<int64_t>(kx) * s.dilationW;
184 if (iy < 0 || ix < 0 || iy >= s.height || ix >= s.width) continue;
185 const size_t xi =
186 ((static_cast<size_t>(b) * s.channels + (o / (s.outputs / s.groups)) * cg + c) *
187 s.height +
188 iy) *
189 s.width +
190 ix;
191 const size_t wi = ((static_cast<size_t>(o) * cg + c) * s.kernelH + ky) * s.kernelW + kx;
192 sum += static_cast<int64_t>(value(x, xi) - xZero) *
193 (value(w, wi) - wZeros[wZeros.size() == 1 ? 0 : o]);
194 }
195 if (!fits(sum))
196 return Result<std::vector<int32_t>>::failure(Diagnostic::error(
197 DiagnosticCode::InvalidArgument, "Integer convolution accumulator overflow"));
198 out[((static_cast<size_t>(b) * s.outputs + o) * oh + y) * ow + z] = static_cast<int32_t>(sum);
199 }
200 return Result<std::vector<int32_t>>::success(std::move(out));
201}
202} // namespace eve::tensor::affine
double value
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
const std::string & s
EvpackChunkInput input
Definition Evpack.cpp:170
glm::vec3 n
Definition Grass.cpp:63
std::array< double, 10 > q
double r
float v
std::int32_t c
int h
std::uint32_t width
std::array< float, 3 > scale
std::uint64_t bytes
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
std::vector< float > scales
Definition OnnxLstm.cpp:27
std::vector< int64_t > zeros
Definition OnnxLstm.cpp:28
OnnxCompute * compute
float d
int limit
Definition TreeMesh.cpp:164
float m[16]
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
GPU execution boundary for native ONNX; retains no model and retains compiled resources for the lifet...
Definition OnnxCompute.h:25
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.
std::vector< int32_t > gpuConv(OnnxCompute &d, affine::ByteView x, affine::ByteView w, const affine::ConvShape &c, int xz, std::span< const int32_t > wz)
Gpu conv.
std::vector< int32_t > gpuMatmul(OnnxCompute &d, affine::ByteView a, affine::ByteView b, size_t m, size_t k, size_t n, int az, std::span< const int32_t > bz)
Gpu matmul.
Borrowed packed 8-bit values, valid for the duration of a synchronous call.
Definition AffineQuant.h:24
Explicit 1D/2D NCHW convolution geometry; missing 1D height is one.
Definition AffineQuant.h:65
ONNX unsigned activation quantization; owns bytes and scalar parameters.
Definition AffineQuant.h:17