Skip to content

Commit d182c41

Browse files
alankellytensorflower-gardener
authored andcommitted
Check that zero point and scale of inputs and outputs match for Concatenate operator.
The initial checks within VisitConcatenationNode all pass as xnn_define_concatenate is not called when checking if a node may be delegated or not. It is during the second pass that the check fails, causing delegation to fail. INFO: Created TensorFlow Lite XNNPACK delegate for CPU. ERROR: failed to delegate CONCATENATION node #31 ERROR: failed to delegate CONCATENATION node #36 ERROR: Node number 55 (TfLiteXNNPackDelegate) failed to prepare. PiperOrigin-RevId: 518874585
1 parent 7f09826 commit d182c41

File tree

4 files changed

+128
-12
lines changed

4 files changed

+128
-12
lines changed

tensorflow/lite/delegates/xnnpack/concatenation_tester.cc

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -170,16 +170,16 @@ std::vector<char> ConcatenationTester::CreateTfLiteModel(
170170

171171
std::vector<flatbuffers::Offset<Tensor>> tensors;
172172
for (size_t i = 0; i < NumInputs(); i++) {
173-
tensors.push_back(
174-
CreateTensor(builder,
175-
builder.CreateVector<int32_t>(InputShape(i).data(),
176-
InputShape(i).size()),
177-
tensor_type,
178-
/*buffer=*/0, /*name=*/0,
179-
CreateQuantizationParameters(
180-
builder, /*min=*/0, /*max=*/0,
181-
builder.CreateVector<float>({/*scale=*/1.0f}),
182-
builder.CreateVector<int64_t>({/*zero_point=*/0}))));
173+
tensors.push_back(CreateTensor(
174+
builder,
175+
builder.CreateVector<int32_t>(InputShape(i).data(),
176+
InputShape(i).size()),
177+
tensor_type,
178+
/*buffer=*/0, /*name=*/0,
179+
CreateQuantizationParameters(
180+
builder, /*min=*/0, /*max=*/0,
181+
builder.CreateVector<float>({input_scales_[i]}),
182+
builder.CreateVector<int64_t>({input_zero_points_[i]}))));
183183
}
184184

185185
tensors.push_back(CreateTensor(
@@ -189,8 +189,8 @@ std::vector<char> ConcatenationTester::CreateTfLiteModel(
189189
/*buffer=*/0, /*name=*/0,
190190
CreateQuantizationParameters(
191191
builder, /*min=*/0, /*max=*/0,
192-
builder.CreateVector<float>({/*scale=*/1.0f}),
193-
builder.CreateVector<int64_t>({/*zero_point=*/0}))));
192+
builder.CreateVector<float>({output_scale_}),
193+
builder.CreateVector<int64_t>({output_zero_point_}))));
194194

195195
std::vector<int32_t> op_inputs;
196196
for (size_t i = 0; i < NumInputs(); i++) {

tensorflow/lite/delegates/xnnpack/concatenation_tester.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,46 @@ class ConcatenationTester {
5353
}
5454
}
5555
input_shapes_ = shapes;
56+
input_scales_.resize(shapes.size(), 1);
57+
output_scale_ = 1.f;
58+
input_zero_points_.resize(shapes.size(), 0);
59+
output_zero_point_ = 0;
5660
return *this;
5761
}
5862

5963
inline std::vector<int32_t> InputShape(size_t i) const {
6064
return input_shapes_[i];
6165
}
6266

67+
inline ConcatenationTester& InputScales(const std::vector<float> scales) {
68+
input_scales_ = scales;
69+
return *this;
70+
}
71+
72+
inline float InputScale(size_t i) const { return input_scales_[i]; }
73+
74+
inline ConcatenationTester& InputZeroPoint(
75+
const std::vector<int32_t> zero_points) {
76+
input_zero_points_ = zero_points;
77+
return *this;
78+
}
79+
80+
inline float InputZeroPoint(size_t i) const { return input_zero_points_[i]; }
81+
82+
inline ConcatenationTester& OutputScale(float scale) {
83+
output_scale_ = scale;
84+
return *this;
85+
}
86+
87+
inline float OutputScale() const { return output_scale_; }
88+
89+
inline ConcatenationTester& OutputZeroPoint(int32_t zero_point) {
90+
output_zero_point_ = zero_point;
91+
return *this;
92+
}
93+
94+
inline int32_t OutputZeroPoint() const { return output_scale_; }
95+
6396
inline size_t NumInputs() const { return input_shapes_.size(); }
6497

6598
std::vector<int32_t> OutputShape() const {
@@ -86,6 +119,10 @@ class ConcatenationTester {
86119
int axis_;
87120
std::vector<int32_t> output_shape_;
88121
std::vector<std::vector<int32_t>> input_shapes_;
122+
std::vector<float> input_scales_;
123+
float output_scale_;
124+
std::vector<int32_t> input_zero_points_;
125+
int32_t output_zero_point_;
89126
};
90127

91128
} // namespace xnnpack

tensorflow/lite/delegates/xnnpack/unsigned_quantized_concatenation_test.cc

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,5 +324,57 @@ TEST(UnsignedQuantizedConcatenation, 4D_of_4) {
324324
}
325325
}
326326

327+
TEST(UnsignedQuantizedConcatenation, 2D_2_inputs_differnet_zero_points) {
328+
std::unique_ptr<TfLiteDelegate, decltype(&TfLiteXNNPackDelegateDelete)>
329+
xnnpack_delegate(TfLiteXNNPackDelegateCreate(nullptr),
330+
TfLiteXNNPackDelegateDelete);
331+
332+
std::random_device random_device;
333+
auto rng = std::mt19937(random_device());
334+
auto shape_rng =
335+
std::bind(std::uniform_int_distribution<int32_t>(2, 10), std::ref(rng));
336+
337+
for (int i = -1; i < 2; i++) {
338+
// All dimensions must be the same, except for axis.
339+
const std::vector<int32_t> shape1({shape_rng(), shape_rng()});
340+
auto shape2 = SameShapeDifferentAxis(shape1, i, shape_rng());
341+
342+
// clang-format off
343+
ConcatenationTester()
344+
.InputShapes({shape1, shape2})
345+
.Axis(i)
346+
.InputZeroPoint({2, 3})
347+
.OutputZeroPoint(1)
348+
.Test(TensorType_UINT8, xnnpack_delegate.get());
349+
// clang-format on
350+
}
351+
}
352+
353+
TEST(UnsignedQuantizedConcatenation, 2D_2_inputs_differnet_scales) {
354+
std::unique_ptr<TfLiteDelegate, decltype(&TfLiteXNNPackDelegateDelete)>
355+
xnnpack_delegate(TfLiteXNNPackDelegateCreate(nullptr),
356+
TfLiteXNNPackDelegateDelete);
357+
358+
std::random_device random_device;
359+
auto rng = std::mt19937(random_device());
360+
auto shape_rng =
361+
std::bind(std::uniform_int_distribution<int32_t>(2, 10), std::ref(rng));
362+
363+
for (int i = -1; i < 2; i++) {
364+
// All dimensions must be the same, except for axis.
365+
const std::vector<int32_t> shape1({shape_rng(), shape_rng()});
366+
auto shape2 = SameShapeDifferentAxis(shape1, i, shape_rng());
367+
368+
// clang-format off
369+
ConcatenationTester()
370+
.InputShapes({shape1, shape2})
371+
.Axis(i)
372+
.InputScales({2, 3})
373+
.OutputScale(1)
374+
.Test(TensorType_UINT8, xnnpack_delegate.get());
375+
// clang-format on
376+
}
377+
}
378+
327379
} // namespace xnnpack
328380
} // namespace tflite

tensorflow/lite/delegates/xnnpack/xnnpack_delegate.cc

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2821,6 +2821,33 @@ class Subgraph {
28212821
if (axis < 0) axis += NumDimensions(&output_tensor);
28222822
int sum_axis = 0;
28232823

2824+
if (output_tensor.type == kTfLiteUInt8) {
2825+
const int32_t zero_point =
2826+
tensors[node->outputs->data[0]].params.zero_point;
2827+
const float scale = tensors[node->outputs->data[0]].params.scale;
2828+
for (int i = 0; i < num_inputs; i++) {
2829+
if (tensors[node->inputs->data[i]].params.zero_point != zero_point) {
2830+
TF_LITE_MAYBE_KERNEL_LOG(
2831+
logging_context,
2832+
"Mismatching quantization zero point across the %dth input "
2833+
"(%" PRId32 ") and the output (%" PRId32
2834+
") for CONCATENATE operator #%d",
2835+
i, tensors[node->inputs->data[i]].params.zero_point, zero_point,
2836+
node_index);
2837+
return kTfLiteError;
2838+
}
2839+
if (tensors[node->inputs->data[i]].params.scale != scale) {
2840+
TF_LITE_MAYBE_KERNEL_LOG(
2841+
logging_context,
2842+
"Mismatching quantization scale across the %dth input (%f) "
2843+
"and the output (%f) for CONCATENATE operator #%d",
2844+
i, tensors[node->inputs->data[i]].params.scale, scale,
2845+
node_index);
2846+
return kTfLiteError;
2847+
}
2848+
}
2849+
}
2850+
28242851
for (int i = 0; i < num_inputs; i++) {
28252852
const TfLiteTensor& input_tensor = tensors[node->inputs->data[i]];
28262853
TF_LITE_ENSURE_STATUS(CheckTensorFloat32OrQUInt8Type(

0 commit comments

Comments
 (0)