| 455 | } // namespace |
| 456 | |
| 457 | ::tensorflow::Status Quantize::Run(Model* model, std::size_t op_index, |
| 458 | bool* modified) { |
| 459 | *modified = false; |
| 460 | // Our general "quantization" graph transformation consists in replacing |
| 461 | // QuantizedInputArrays[] -> |
| 462 | // DequantizeOperators[] -> |
| 463 | // FloatInputArrays[] -> |
| 464 | // Operator -> |
| 465 | // FloatOutputArray |
| 466 | // by |
| 467 | // QuantizedInputArrays[] -> |
| 468 | // Operator -> |
| 469 | // QuantizedOutputArray -> |
| 470 | // DequantizeOperator -> |
| 471 | // FloatOutputArray |
| 472 | // |
| 473 | // In other words, this is pushing Dequantize operators to the right of |
| 474 | // other operators. |
| 475 | // |
| 476 | |
| 477 | auto& op = *model->operators[op_index]; |
| 478 | if (op.type == OperatorType::kDequantize || |
| 479 | op.type == OperatorType::kFakeQuant) { |
| 480 | return ::tensorflow::Status::OK(); |
| 481 | } |
| 482 | |
| 483 | // Our assumption here is that the input arrays are already quantized - |
| 484 | // that is typically the case in models operating on an input bitmap |
| 485 | // image, and MakeInitialDequantizeOp should have already resolved |
| 486 | // the handling of the input image as an initial Dequantize op. |
| 487 | // |
| 488 | // Thus we are building around the assumption that the graph always starts |
| 489 | // with a quantized input array, and only after some Dequantize op do we have |
| 490 | // float arrays. The problem of quantizing the graph thus becomes a problem of |
| 491 | // pushing Dequantize ops to the right of other ops. |
| 492 | // |
| 493 | // Let us just guard this assumption by the following assertion: |
| 494 | for (const auto& input : op.inputs) { |
| 495 | const auto& input_array = model->GetArray(input); |
| 496 | if (IsInputArray(*model, input) && |
| 497 | input_array.data_type == ArrayDataType::kFloat) { |
| 498 | CHECK(input_array.quantization_params) |
| 499 | << "Input array " << input << " is missing quantization_params"; |
| 500 | } |
| 501 | } |
| 502 | if (!SupportsQuantization(model, op)) { |
| 503 | return tensorflow::errors::InvalidArgument( |
| 504 | "Unimplemented: this graph contains an operator of type ", |
| 505 | HelpfulOperatorTypeName(op), |
| 506 | " for which the quantized form is not yet implemented. Sorry, and " |
| 507 | "patches welcome (that's a relatively fun patch to write, mostly " |
| 508 | "providing the actual quantized arithmetic code for this op)."); |
| 509 | } |
| 510 | |
| 511 | for (const auto& input : op.inputs) { |
| 512 | const auto& array = model->GetArray(input); |
| 513 | if (array.data_type == ArrayDataType::kFloat) { |
| 514 | if (!array.minmax && !array.buffer) { |