1 /* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 #include "tensorflow/lite/toco/tensorflow_util.h"
16
17 #include <string.h>
18 #include <memory>
19 #include <set>
20
21 #ifdef GOOGLE_PLATFORM
22 #include "file/logging/log_lines.h"
23 #endif
24 #include "google/protobuf/map.h"
25 #include "absl/strings/str_split.h"
26 #include "absl/strings/string_view.h"
27 #include "tensorflow/lite/toco/toco_port.h"
28 #include "tensorflow/lite/toco/tooling_util.h"
29 #include "tensorflow/core/framework/attr_value.pb.h"
30 #include "tensorflow/core/framework/node_def.pb.h"
31 #include "tensorflow/core/framework/tensor.pb.h"
32 #include "tensorflow/core/framework/types.pb.h"
33 #include "tensorflow/core/platform/logging.h"
34
35 namespace toco {
36
37 using tensorflow::AttrValue;
38 using tensorflow::GraphDef;
39
LogDumpGraphDef(int log_level,const string & message,const GraphDef & tf_graph)40 void LogDumpGraphDef(int log_level, const string& message,
41 const GraphDef& tf_graph) {
42 if (!VLOG_IS_ON(log_level)) {
43 return;
44 }
45 std::set<string> ops;
46 for (const auto& node : tf_graph.node()) {
47 ops.insert(node.op());
48 }
49 string dump;
50 toco::port::AppendF(&dump, R"MSG(
51 BEGIN DUMP OF TENSORFLOW GRAPHDEF (%s)
52 There are %d nodes.
53 There are %zu different op types:
54 )MSG",
55 message, tf_graph.node_size(), ops.size());
56 for (const auto& op : ops) {
57 toco::port::AppendF(&dump, " %s\n", op);
58 }
59 dump.append(R"MSG(
60 PROTO DUMP
61 )MSG");
62 for (const auto& node : tf_graph.node()) {
63 toco::port::AppendF(&dump, R"MSG(
64 BEGIN NODE: name = %s
65 op = %s
66 inputs = [
67 )MSG",
68 node.name(), node.op());
69 for (const auto& input : node.input()) {
70 toco::port::AppendF(&dump, " %s\n", input);
71 }
72 dump.append(" ]\n");
73 for (const auto& attr : node.attr()) {
74 toco::port::AppendF(&dump, " ATTR: name = %s\n", attr.first);
75 if (attr.second.value_case() == AttrValue::kFunc) {
76 dump.append(" func\n");
77 } else if (attr.second.value_case() == AttrValue::kPlaceholder) {
78 toco::port::AppendF(&dump, " placeholder: %s\n",
79 attr.second.placeholder());
80 } else if (attr.second.value_case() == AttrValue::kS) {
81 dump.append(" string:\n");
82 dump.append(R"MSG(
83 BEGIN EMBEDDED STRING
84 )MSG");
85 const auto& lines = absl::StrSplit(attr.second.s(), '\n');
86 for (const auto& line : lines) {
87 toco::port::AppendF(&dump, " %s\n", line);
88 }
89 dump.append(R"MSG(
90 END EMBEDDED STRING
91 )MSG");
92 } else if (attr.second.value_case() == AttrValue::kI) {
93 toco::port::AppendF(&dump, " int: %lld\n", attr.second.i());
94 } else if (attr.second.value_case() == AttrValue::kF) {
95 toco::port::AppendF(&dump, " float: %g\n", attr.second.f());
96 } else if (attr.second.value_case() == AttrValue::kB) {
97 toco::port::AppendF(&dump, " bool: %s\n",
98 attr.second.b() ? "true" : "false");
99 } else if (attr.second.value_case() == AttrValue::kType) {
100 toco::port::AppendF(&dump, " type: %s\n",
101 tensorflow::DataType_Name(attr.second.type()));
102 } else if (attr.second.value_case() == AttrValue::kShape) {
103 dump.append(" shape: [ ");
104 const auto& shape = attr.second.shape();
105 for (int i = 0; i < shape.dim_size(); i++) {
106 toco::port::AppendF(&dump, "%lld ", shape.dim(i).size());
107 }
108 dump.append("]\n");
109 } else if (attr.second.value_case() == AttrValue::kTensor) {
110 const auto& tensor = attr.second.tensor();
111 dump.append(" TENSOR:\n");
112 toco::port::AppendF(&dump, " type: %s\n",
113 tensorflow::DataType_Name(tensor.dtype()));
114 const auto& shape = tensor.tensor_shape();
115 dump.append(" shape: [ ");
116 for (int i = 0; i < shape.dim_size(); i++) {
117 toco::port::AppendF(&dump, "%lld ", shape.dim(i).size());
118 }
119 dump.append("]\n");
120 if (!tensor.tensor_content().empty()) {
121 toco::port::AppendF(&dump, " tensor_content: %zu bytes\n",
122 tensor.tensor_content().size());
123 }
124 if (tensor.dtype() == tensorflow::DT_INT32) {
125 CHECK_EQ(0, tensor.tensor_content().size() % sizeof(int32));
126 const int size = tensor.tensor_content().size() / sizeof(int32);
127 std::vector<int32> data(size);
128 toco::port::CopyToBuffer(tensor.tensor_content(),
129 reinterpret_cast<char*>(data.data()));
130 const int kMaxValsToPrint = 4;
131 dump.append(" tensor_content as ints: [ ");
132 for (int i = 0; i < kMaxValsToPrint && i < size; i++) {
133 toco::port::AppendF(&dump, "%d ", data[i]);
134 }
135 if (size > kMaxValsToPrint) {
136 dump.append("... ");
137 }
138 dump.append("]\n");
139 }
140 if (tensor.dtype() == tensorflow::DT_FLOAT) {
141 CHECK_EQ(0, tensor.tensor_content().size() % sizeof(float));
142 const int size = tensor.tensor_content().size() / sizeof(float);
143 std::vector<float> data(size);
144 toco::port::CopyToBuffer(tensor.tensor_content(),
145 reinterpret_cast<char*>(data.data()));
146 const int kMaxValsToPrint = 4;
147 dump.append(" tensor_content as floats: [ ");
148 for (int i = 0; i < kMaxValsToPrint && i < size; i++) {
149 toco::port::AppendF(&dump, "%g ", data[i]);
150 }
151 if (size > kMaxValsToPrint) {
152 dump.append("... ");
153 }
154 dump.append("]\n");
155 }
156 if (tensor.int_val_size()) {
157 toco::port::AppendF(&dump, " int_val: %d ints: [ ",
158 tensor.int_val_size());
159 const int kMaxValsToPrint = 4;
160 for (int i = 0; i < kMaxValsToPrint && i < tensor.int_val_size();
161 i++) {
162 toco::port::AppendF(&dump, "%d ", tensor.int_val(i));
163 }
164 if (tensor.int_val_size() > kMaxValsToPrint) {
165 dump.append("... ");
166 }
167 dump.append("]\n");
168 }
169 if (tensor.float_val_size()) {
170 toco::port::AppendF(&dump, " float_val: %d floats: [ ",
171 tensor.float_val_size());
172 const int kMaxValsToPrint = 4;
173 for (int i = 0; i < kMaxValsToPrint && i < tensor.float_val_size();
174 i++) {
175 toco::port::AppendF(&dump, "%g ", tensor.float_val(i));
176 }
177 if (tensor.float_val_size() > kMaxValsToPrint) {
178 dump.append("... ");
179 }
180 dump.append("]\n");
181 }
182 if (tensor.string_val_size()) {
183 toco::port::AppendF(&dump, " string_val: %d strings\n",
184 tensor.string_val_size());
185 }
186 } else if (attr.second.value_case() == AttrValue::kList) {
187 dump.append(" LIST\n");
188 }
189 }
190 dump.append("END NODE\n");
191 }
192 toco::port::AppendF(&dump, "END DUMP OF TENSORFLOW GRAPHDEF (%s)\n", message);
193 #if defined(GOOGLE_PLATFORM)
194 VLOG_LINES(log_level, dump);
195 #else
196 VLOG(log_level) << dump;
197 #endif
198 }
199 } // namespace toco
200