/* Copyright 2015 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ #include "tensorflow/cc/ops/const_op.h" #include "tensorflow/cc/ops/image_ops.h" #include "tensorflow/cc/ops/nn_ops.h" #include "tensorflow/cc/ops/sendrecv_ops.h" #include "tensorflow/cc/ops/standard_ops.h" #include "tensorflow/core/framework/tensor_testutil.h" #include "tensorflow/core/lib/core/status_test_util.h" #include "tensorflow/core/lib/io/path.h" #include "tensorflow/core/platform/test.h" #include "tensorflow/core/platform/test_benchmark.h" #include "tensorflow/core/public/session.h" #include "tensorflow/tools/graph_transforms/transform_utils.h" namespace tensorflow { namespace graph_transforms { // Declare here, so we don't need a public header. Status FreezeRequantizationRanges(const GraphDef& input_graph_def, const TransformFuncContext& context, GraphDef* output_graph_def); struct MinMaxRecord { string name; float min; float max; }; Status ExtractMinMaxRecords(const string& log_file_name, std::vector* records); class FreezeRequantizationRangesTest : public ::testing::Test { protected: void TestFreezeRequantizationRanges() { auto root = tensorflow::Scope::NewRootScope(); using namespace ::tensorflow::ops; // NOLINT(build/namespaces) Tensor quantized_tensor(DT_QUINT8, TensorShape({1, 6})); test::FillValues(&quantized_tensor, {0, 0, 0, 0, 0, 0}); Output quantized_op = Const(root.WithOpName("quantized_op"), Input::Initializer(quantized_tensor)); Tensor quantized_min_tensor(DT_FLOAT, TensorShape({})); test::FillValues(&quantized_min_tensor, {2.0f}); Output quantized_min_op = Const(root.WithOpName("quantized_min_op"), Input::Initializer(quantized_min_tensor)); Tensor quantized_max_tensor(DT_FLOAT, TensorShape({})); test::FillValues(&quantized_max_tensor, {2.0f}); Output quantized_max_op = Const(root.WithOpName("quantized_max_op"), Input::Initializer(quantized_min_tensor)); Tensor offset_tensor(DT_QUINT8, TensorShape({6})); test::FillValues(&offset_tensor, {1, 2, 3, 4, 5, 6}); Output offset_op = Const(root.WithOpName("offset_op"), Input::Initializer(offset_tensor)); Tensor offset_min_tensor(DT_FLOAT, TensorShape({})); test::FillValues(&offset_min_tensor, {0.0f}); Output offset_min_op = Const(root.WithOpName("offset_min_op"), Input::Initializer(offset_min_tensor)); Tensor offset_max_tensor(DT_FLOAT, TensorShape({})); test::FillValues(&offset_max_tensor, {255.0f}); Output offset_max_op = Const(root.WithOpName("offset_max_op"), Input::Initializer(offset_max_tensor)); QuantizedBiasAdd quantized_bias_add_op( root.WithOpName("bias_add_op"), quantized_op, offset_op, quantized_min_op, quantized_max_op, offset_min_op, offset_max_op, DT_QINT32); RequantizationRange requantization_range_op( root.WithOpName("requantization_range_op"), quantized_bias_add_op.output, quantized_bias_add_op.min_out, quantized_bias_add_op.max_out); Requantize requantize_op( root.WithOpName("requantize_op"), quantized_bias_add_op.output, quantized_bias_add_op.min_out, quantized_bias_add_op.max_out, requantization_range_op.output_min, requantization_range_op.output_max, DT_QUINT8); Output dequantize_op = Dequantize(root.WithOpName("dequantize_op"), requantize_op.output, requantize_op.output_min, requantize_op.output_max); GraphDef graph_def; TF_ASSERT_OK(root.ToGraphDef(&graph_def)); const string min_max_log_file_name = io::JoinPath(testing::TmpDir(), "min_max_log_file.txt"); { std::unique_ptr file; TF_ASSERT_OK( Env::Default()->NewWritableFile(min_max_log_file_name, &file)); TF_ASSERT_OK(file->Append("Something irrelevant\n")); TF_ASSERT_OK( file->Append("[SomePrefix] " ";requantization_range_op__print__;__requant_min_max:" "[-2.4313571][10.584145]\n")); TF_ASSERT_OK(file->Append("Something else irrelevant\n")); } TransformFuncContext context; context.input_names = {}; context.output_names = {"dequantize_op"}; context.params = {{"min_max_log_file", {min_max_log_file_name}}}; GraphDef frozen_graph_def; TF_EXPECT_OK( FreezeRequantizationRanges(graph_def, context, &frozen_graph_def)); std::map node_map; MapNamesToNodes(frozen_graph_def, &node_map); EXPECT_EQ(0, node_map.count("requantization_range_op")); EXPECT_EQ(1, node_map.count("requantize_op")); const string& min_input = NodeNameFromInput(node_map.at("requantize_op")->input(3)); ASSERT_EQ(1, node_map.count(min_input)); EXPECT_EQ("Const", node_map.at(min_input)->op()); const string& max_input = NodeNameFromInput(node_map.at("requantize_op")->input(4)); ASSERT_EQ(1, node_map.count(max_input)); EXPECT_EQ("Const", node_map.at(max_input)->op()); std::unique_ptr original_session(NewSession(SessionOptions())); TF_ASSERT_OK(original_session->Create(graph_def)); std::vector original_outputs; TF_ASSERT_OK( original_session->Run({}, {"dequantize_op"}, {}, &original_outputs)); std::unique_ptr frozen_session(NewSession(SessionOptions())); TF_ASSERT_OK(frozen_session->Create(frozen_graph_def)); std::vector frozen_outputs; TF_ASSERT_OK( frozen_session->Run({}, {"dequantize_op"}, {}, &frozen_outputs)); ASSERT_EQ(original_outputs.size(), frozen_outputs.size()); ASSERT_EQ(1, frozen_outputs.size()); test::ExpectTensorNear(original_outputs[0], frozen_outputs[0], 0.5); } void TestExtractMinMaxRecords() { const string min_max_log_file_name = io::JoinPath(testing::TmpDir(), "min_max_log_file2.txt"); { std::unique_ptr file; TF_ASSERT_OK( Env::Default()->NewWritableFile(min_max_log_file_name, &file)); TF_ASSERT_OK(file->Append("Something irrelevant\n")); TF_ASSERT_OK( file->Append("[SomePrefix] " ";requantization_range_op__print__;__requant_min_max:" "[-2.4313571][10.584145]\n")); TF_ASSERT_OK(file->Append("Something else irrelevant\n")); TF_ASSERT_OK(file->Append( "[SomeOtherPrefix] " ";other_requantization_range_op__print__;__requant_min_max:" "[-1.0][2.0]\n")); TF_ASSERT_OK(file->Append("Something else irrelevant\n")); TF_ASSERT_OK( file->Append("[SomePrefix] " ";requantization_range_op__print__;__requant_min_max:" "[-1.bad][2.0]\n")); } std::vector records; TF_ASSERT_OK(ExtractMinMaxRecords(min_max_log_file_name, &records)); ASSERT_EQ(2, records.size()); EXPECT_EQ("requantization_range_op", records[0].name); EXPECT_NEAR(-2.4313571f, records[0].min, 1e-5f); EXPECT_NEAR(10.584145f, records[0].max, 1e-5f); EXPECT_EQ("other_requantization_range_op", records[1].name); EXPECT_NEAR(-1.0f, records[1].min, 1e-5f); EXPECT_NEAR(2.0f, records[1].max, 1e-5f); } }; TEST_F(FreezeRequantizationRangesTest, TestFreezeRequantizationRanges) { TestFreezeRequantizationRanges(); } TEST_F(FreezeRequantizationRangesTest, TestExtractMinMaxRecords) { TestExtractMinMaxRecords(); } } // namespace graph_transforms } // namespace tensorflow