Sometimes FakeQuantWithMinMaxVars ops are added at the end of a chain of linear ops like Relu, MaxPool, etc, several steps from the Conv2D or BiasAdd op that we want to apply the trained constant conversions to. This pass tries to move FakeQuant ops up the input chain, so they're as close as possible to the 32-bit conversion, and so can be easily merged into the automatic dynamic Requantizes.
| 531 | // the 32-bit conversion, and so can be easily merged into the automatic dynamic |
| 532 | // Requantizes. |
| 533 | Status HoistFakeQuants(const GraphDef& input_graph_def, |
| 534 | const TransformFuncContext& context, |
| 535 | GraphDef* output_graph_def) { |
| 536 | GraphDef current_graph_def = input_graph_def; |
| 537 | const int max_depth = 3; |
| 538 | for (int depth = max_depth; depth > 0; --depth) { |
| 539 | OpTypePattern pattern = {"*"}; |
| 540 | for (int i = 0; i < depth; ++i) { |
| 541 | pattern = {"*", {pattern}}; |
| 542 | } |
| 543 | pattern = {"FakeQuantWithMinMaxVars", {pattern, {"Const"}, {"Const"}}}; |
| 544 | GraphDef hoisted_graph_def; |
| 545 | TF_RETURN_IF_ERROR(ReplaceMatchingOpTypes( |
| 546 | current_graph_def, pattern, |
| 547 | [depth](const NodeMatch& match, const std::set<string>& input_nodes, |
| 548 | const std::set<string>& output_nodes, |
| 549 | std::vector<NodeDef>* new_nodes) { |
| 550 | const NodeDef& fake_quant_node = match.node; |
| 551 | const NodeDef& fake_quant_min_node = match.inputs[1].node; |
| 552 | const NodeDef& fake_quant_max_node = match.inputs[2].node; |
| 553 | std::vector<NodeDef> linear_nodes; |
| 554 | NodeMatch current_match = match; |
| 555 | for (int i = 0; i <= depth; ++i) { |
| 556 | linear_nodes.push_back(current_match.inputs[0].node); |
| 557 | current_match = current_match.inputs[0]; |
| 558 | } |
| 559 | NodeDef new_fake_quant_node; |
| 560 | new_fake_quant_node = fake_quant_node; |
| 561 | new_fake_quant_node.set_name(fake_quant_node.name() + "_hoisted"); |
| 562 | new_fake_quant_node.set_input( |
| 563 | 0, linear_nodes[linear_nodes.size() - 2].input(0)); |
| 564 | new_nodes->push_back(new_fake_quant_node); |
| 565 | |
| 566 | new_nodes->push_back(fake_quant_min_node); |
| 567 | new_nodes->push_back(fake_quant_max_node); |
| 568 | |
| 569 | linear_nodes[linear_nodes.size() - 2].set_input( |
| 570 | 0, new_fake_quant_node.name()); |
| 571 | linear_nodes.front().set_name(fake_quant_node.name()); |
| 572 | for (const NodeDef& linear_node : linear_nodes) { |
| 573 | new_nodes->push_back(linear_node); |
| 574 | } |
| 575 | |
| 576 | return Status::OK(); |
| 577 | }, |
| 578 | {}, &hoisted_graph_def)); |
| 579 | current_graph_def = hoisted_graph_def; |
| 580 | } |
| 581 | *output_graph_def = current_graph_def; |
| 582 | |
| 583 | return Status::OK(); |
| 584 | } |
| 585 | |
| 586 | // Converts any float ops that have eight-bit equivalents into their quantized |
| 587 | // forms, so that as much calculation as possible is done in the lower-precision |
no test coverage detected