Apply constraints to ops if they have any. We have made the restriction that for int8 quantized concat, the inputs and outpus must have the same scale and zero point. The other ones with constraints(averagepool, maxpool, gather, softmax, tanh etc) are handled in QuantizeWeightsAndInput.
| 261 | // constraints(averagepool, maxpool, gather, softmax, tanh etc) are handled in |
| 262 | // QuantizeWeightsAndInput. |
| 263 | TfLiteStatus ApplyConstraints(ModelT* model, ErrorReporter* error_reporter) { |
| 264 | for (int subgraph_idx = 0; subgraph_idx < model->subgraphs.size(); |
| 265 | subgraph_idx++) { |
| 266 | SubGraphT* subgraph = model->subgraphs.at(subgraph_idx).get(); |
| 267 | // Iterate backward to avoid messing with index. |
| 268 | for (int op_idx = subgraph->operators.size() - 1; op_idx >= 0; op_idx--) { |
| 269 | OperatorT* op = subgraph->operators[op_idx].get(); |
| 270 | const BuiltinOperator op_code = |
| 271 | model->operator_codes[op->opcode_index]->builtin_code; |
| 272 | operator_property::OperatorProperty property = |
| 273 | operator_property::GetOperatorProperty(op_code); |
| 274 | if (!property.quantizable) { |
| 275 | continue; |
| 276 | } |
| 277 | // Basically only Concat passes this check. |
| 278 | if (!property.restrict_same_input_output_scale || |
| 279 | (property.inputs.size() == 1 && property.outputs.size() == 1 && |
| 280 | property.biases.empty())) { |
| 281 | continue; |
| 282 | } |
| 283 | // If ApplyConstraints and requant is needed, use the min of min and max |
| 284 | // of max, which means using the scale and zero point of output. |
| 285 | TensorT* output_tensor = subgraph->tensors[op->outputs[0]].get(); |
| 286 | if (!utils::QuantizationParametersExist(output_tensor)) { |
| 287 | error_reporter->Report( |
| 288 | "Unable to get scale or zero point from the tensor at %d, which " |
| 289 | "is the output tensor for concat.", |
| 290 | op->outputs[0]); |
| 291 | return kTfLiteError; |
| 292 | } |
| 293 | const float output_scale = output_tensor->quantization->scale[0]; |
| 294 | const float output_zp = output_tensor->quantization->zero_point[0]; |
| 295 | for (size_t input_idx = 0; input_idx < op->inputs.size(); ++input_idx) { |
| 296 | TensorT* input_tensor = subgraph->tensors[op->inputs[input_idx]].get(); |
| 297 | if (!utils::QuantizationParametersExist(input_tensor)) { |
| 298 | error_reporter->Report( |
| 299 | "Unable to get scale or zero point from tensor at %d, which is " |
| 300 | "an input tensor of concat.", |
| 301 | op->inputs[input_idx]); |
| 302 | return kTfLiteError; |
| 303 | } |
| 304 | if (input_tensor->quantization->scale[0] == output_scale && |
| 305 | input_tensor->quantization->zero_point[0] == output_zp) { |
| 306 | // This input does not need to be requantized. |
| 307 | continue; |
| 308 | } |
| 309 | |
| 310 | std::unique_ptr<TensorT> additional_tensor; |
| 311 | const string requant_tensor_name = input_tensor->name + "_requantized"; |
| 312 | utils::MakeTensorWithQuantParam( |
| 313 | requant_tensor_name, input_tensor->shape, TensorType_INT8, |
| 314 | output_scale, output_zp, &additional_tensor); |
| 315 | const int32_t additional_tensor_idx = subgraph->tensors.size(); |
| 316 | subgraph->tensors.push_back(std::move(additional_tensor)); |
| 317 | |
| 318 | // Add requant op before this input. |
| 319 | // There are better ways to handle this, which is to try to push the |
| 320 | // rescale upwards recurrsively and hope all upstream ops can absort |
no test coverage detected