Skip to content

Commit a1a7d96

Browse files
mvartani-metafacebook-github-bot
authored andcommitted
Support int16 in vision quantized_layer_norm (#24094)
Summary: The vision quantized_layer_norm kernel only handled uint8 and int8; int16 input aborted at its ET_CHECK_MSG. It also had the same precision problems as the HiFi and generic kernels: - int32 accumulators, which overflow for int16 because one squared value can reach 2^30. - An E[x^2] - E[x]^2 variance, which cancels catastrophically when the mean is large compared to the spread. This kernel is registered for Cadence Vision builds through functions_vision.yaml. This change closes the last int16 gap among the Cadence layer norm kernels. Changes: - Add an int16 (Short) branch to both overloads. - Accumulate in int64 for 16-bit types; 8-bit keeps int32 in the inner loop. - Widen to the accumulator type before squaring, so the product cannot overflow int32. - Apply the zero-point correction in int64 and compute the variance numerator N*sum(v^2) - sum(v)^2 exactly in integers. The per-row mean and variance stay in double as before. - Declare both kernels in vision/operators/operators.h. Reviewed By: DrJessop Differential Revision: D126953450
1 parent 9c86690 commit a1a7d96

2 files changed

Lines changed: 71 additions & 13 deletions

File tree

‎backends/cadence/vision/operators/op_quantized_layer_norm.cpp‎

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include <executorch/runtime/kernel/kernel_includes.h>
1111

1212
#include <cmath>
13+
#include <cstdint>
14+
#include <type_traits>
1315

1416
using ::executorch::aten::Tensor;
1517
using ::executorch::runtime::getLeadingDims;
@@ -49,29 +51,37 @@ void quantized_layer_norm_per_tensor_(
4951
const T* x = in_data + i * last_dim;
5052
T* y = out_data + i * last_dim;
5153

52-
// compute sum and squared sum. The fp32 sum can be approximated as:
53-
// (X_1 - in_zero_point) * in_scale + (X_2 - in_zero_point) * in_scale + ...
54-
// (X_N - in_zero_point) * in_scale.
55-
int32_t sum = 0;
56-
int32_t sq_sum = last_dim * input_zero_point * input_zero_point;
54+
// 16-bit squares overflow an int32 sum after two elements.
55+
using acc_t = std::conditional_t<sizeof(T) == 1, int32_t, int64_t>;
56+
acc_t sum = 0;
57+
acc_t sq_sum = 0;
5758
for (size_t j = 0; j < last_dim; ++j) {
58-
int32_t val = x[j];
59+
const int32_t val = x[j];
5960
sum += val;
60-
sq_sum += val * val;
61+
sq_sum += static_cast<acc_t>(val) * val;
6162
}
62-
sq_sum -= (2 * sum * input_zero_point);
63-
sum -= (last_dim * input_zero_point);
6463

65-
float mean = (input_scale * sum) / last_dim;
66-
float variance =
67-
(sq_sum * input_scale * input_scale) / last_dim - mean * mean;
64+
// Shift raw sums to zero-point-centered sums, then form the variance
65+
// numerator N * sum(v^2) - sum(v)^2 exactly in integers: doing
66+
// E[x^2] - E[x]^2 in fp32 cancels catastrophically for 16-bit inputs.
67+
const int64_t n = static_cast<int64_t>(last_dim);
68+
const int64_t zp = input_zero_point;
69+
const int64_t centered_sum = static_cast<int64_t>(sum) - n * zp;
70+
const int64_t centered_sq_sum =
71+
static_cast<int64_t>(sq_sum) - 2 * zp * sum + n * zp * zp;
72+
const int64_t var_numerator =
73+
n * centered_sq_sum - centered_sum * centered_sum;
74+
75+
const double n_d = static_cast<double>(n);
76+
float mean = input_scale * centered_sum / n_d;
77+
float variance = var_numerator / (n_d * n_d) * (input_scale * input_scale);
6878
float inv_std = 1.0f / std::sqrt(variance + eps);
6979

7080
// y = (x - mean) / std * kGamma + kBeta
7181
for (int j = 0; j < last_dim; ++j) {
7282
// y[j] = (x[j] - mean) / std * kGamma + kBeta;
7383
// Since X is quantized, we dequantize it, compute fp32 result, and
74-
// quantize the result to an int8/uint8 value.
84+
// quantize the result back to T.
7585
float val = kernels::dequantize<T>(x[j], input_scale, input_zero_point);
7686

7787
val = (val - mean) * inv_std * weight_data[j] + bias_data[j];
@@ -144,6 +154,17 @@ void quantized_layer_norm_out(
144154
output_scale,
145155
output_zero_point,
146156
out);
157+
} else if (input.scalar_type() == executorch::aten::ScalarType::Short) {
158+
quantized_layer_norm_<int16_t>(
159+
input,
160+
in_scale,
161+
in_zero_point,
162+
weight,
163+
bias,
164+
eps,
165+
output_scale,
166+
output_zero_point,
167+
out);
147168
} else {
148169
ET_CHECK_MSG(
149170
false,
@@ -186,6 +207,17 @@ void quantized_layer_norm_per_tensor_out(
186207
output_scale,
187208
output_zero_point,
188209
out);
210+
} else if (input.scalar_type() == executorch::aten::ScalarType::Short) {
211+
quantized_layer_norm_per_tensor_<int16_t>(
212+
input,
213+
in_scale,
214+
in_zero_point,
215+
weight,
216+
bias,
217+
eps,
218+
output_scale,
219+
output_zero_point,
220+
out);
189221
} else {
190222
ET_CHECK_MSG(
191223
false,

‎backends/cadence/vision/operators/operators.h‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,32 @@ void quantized_conv2d_nhwc_out(
9696
const ::executorch::aten::Tensor& out_shift,
9797
::executorch::aten::Tensor& out);
9898

99+
void quantized_layer_norm_out(
100+
::executorch::runtime::KernelRuntimeContext& ctx,
101+
const ::executorch::aten::Tensor& input,
102+
const ::executorch::aten::Tensor& in_scale,
103+
const ::executorch::aten::Tensor& in_zero_point,
104+
const ::executorch::aten::IntArrayRef normalized_shape,
105+
const ::executorch::aten::Tensor& weight,
106+
const ::executorch::aten::Tensor& bias,
107+
double eps,
108+
double output_scale,
109+
int64_t output_zero_point,
110+
::executorch::aten::Tensor& out);
111+
112+
void quantized_layer_norm_per_tensor_out(
113+
::executorch::runtime::KernelRuntimeContext& ctx,
114+
const ::executorch::aten::Tensor& input,
115+
double in_scale,
116+
int64_t in_zero_point,
117+
const ::executorch::aten::IntArrayRef normalized_shape,
118+
const ::executorch::aten::Tensor& weight,
119+
const ::executorch::aten::Tensor& bias,
120+
double eps,
121+
double output_scale,
122+
int64_t output_zero_point,
123+
::executorch::aten::Tensor& out);
124+
99125
} // namespace native
100126
} // namespace vision
101127
} // namespace impl

0 commit comments

Comments
 (0)