Unlike other tf.*.conv2d layers, tf.nn.conv2d does not create many nodes (within a scope) in the graph, it just acts like other layers. tf.nn.conv2d only creates one node in the graph, and no internal nodes such as 'kernel' are created. The format of native model file is also changed, a flag named has_bias is added, so change the version number.
Showing the changes to tools/python/convert_from_tensorflow.py (1 of 6 files). Show all
Unified · Split
| @@ -118,7 +118,7 @@ class TFConverter: · 117 unmodified lines | |||
| 118 | return knode, bnode, dnode, anode | 118 | return knode, bnode, dnode, anode |
| 119 | 119 | ||
| 120 | 120 | ||
| 121 | def dump_conv2d_to_file(self, node, f): | 121 | def dump_complex_conv2d_to_file(self, node, f): |
| 122 | assert(node.op == 'Conv2D') | 122 | assert(node.op == 'Conv2D') |
| 123 | self.layer_number = self.layer_number + 1 | 123 | self.layer_number = self.layer_number + 1 |
| 124 | self.converted_nodes.add(node.name) | 124 | self.converted_nodes.add(node.name) |
| @@ -153,7 +153,8 @@ class TFConverter: · 28 unmodified lines | |||
| 153 | kernel = kernel.reshape(filter_height, filter_width, in_channels, out_channels) | 153 | kernel = kernel.reshape(filter_height, filter_width, in_channels, out_channels) |
| 154 | kernel = np.transpose(kernel, [3, 0, 1, 2]) | 154 | kernel = np.transpose(kernel, [3, 0, 1, 2]) |
| 155 | 155 | ||
| 156 | np.array([self.op2code[node.op], dilation, padding, self.conv_activations[activation], in_channels, out_channels, filter_height], dtype=np.uint32).tofile(f) | 156 | has_bias = 1 |
| 157 | np.array([self.op2code[node.op], dilation, padding, self.conv_activations[activation], in_channels, out_channels, filter_height, has_bias], dtype=np.uint32).tofile(f) | ||
| 157 | kernel.tofile(f) | 158 | kernel.tofile(f) |
| 158 | 159 | ||
| 159 | btensor = bnode.attr['value'].tensor | 160 | btensor = bnode.attr['value'].tensor |
| @@ -173,6 +174,41 @@ class TFConverter: · 13 unmodified lines | |||
| 173 | np.array([input_operand_index, output_operand_index], dtype=np.uint32).tofile(f) | 174 | np.array([input_operand_index, output_operand_index], dtype=np.uint32).tofile(f) |
| 174 | 175 | ||
| 175 | 176 | ||
| 177 | def dump_simple_conv2d_to_file(self, node, f): | ||
| 178 | assert(node.op == 'Conv2D') | ||
| 179 | self.layer_number = self.layer_number + 1 | ||
| 180 | self.converted_nodes.add(node.name) | ||
| 181 | |||
| 182 | node0 = self.name_node_dict[node.input[0]] | ||
| 183 | node1 = self.name_node_dict[node.input[1]] | ||
| 184 | if node0.op == 'Const': | ||
| 185 | knode = node0 | ||
| 186 | input_name = node.input[1] | ||
| 187 | else: | ||
| 188 | knode = node1 | ||
| 189 | input_name = node.input[0] | ||
| 190 | |||
| 191 | ktensor = knode.attr['value'].tensor | ||
| 192 | filter_height = ktensor.tensor_shape.dim[0].size | ||
| 193 | filter_width = ktensor.tensor_shape.dim[1].size | ||
| 194 | in_channels = ktensor.tensor_shape.dim[2].size | ||
| 195 | out_channels = ktensor.tensor_shape.dim[3].size | ||
| 196 | kernel = np.frombuffer(ktensor.tensor_content, dtype=np.float32) | ||
| 197 | kernel = kernel.reshape(filter_height, filter_width, in_channels, out_channels) | ||
| 198 | kernel = np.transpose(kernel, [3, 0, 1, 2]) | ||
| 199 | |||
| 200 | has_bias = 0 | ||
| 201 | dilation = 1 | ||
| 202 | padding = node.attr['padding'].s.decode("utf-8") | ||
| 203 | np.array([self.op2code[node.op], dilation, self.conv_paddings[padding], self.conv_activations['None'], | ||
| 204 | in_channels, out_channels, filter_height, has_bias], dtype=np.uint32).tofile(f) | ||
| 205 | kernel.tofile(f) | ||
| 206 | |||
| 207 | input_operand_index = self.add_operand(input_name, Operand.IOTYPE_INPUT) | ||
| 208 | output_operand_index = self.add_operand(node.name, Operand.IOTYPE_OUTPUT) | ||
| 209 | np.array([input_operand_index, output_operand_index], dtype=np.uint32).tofile(f) | ||
| 210 | |||
| 211 | |||
| 176 | def dump_depth2space_to_file(self, node, f): | 212 | def dump_depth2space_to_file(self, node, f): |
| 177 | assert(node.op == 'DepthToSpace') | 213 | assert(node.op == 'DepthToSpace') |
| 178 | self.layer_number = self.layer_number + 1 | 214 | self.layer_number = self.layer_number + 1 |
| @@ -222,10 +258,12 @@ class TFConverter: · 43 unmodified lines | |||
| 222 | scope_name = TFConverter.get_scope_name(node.name) | 258 | scope_name = TFConverter.get_scope_name(node.name) |
| 223 | if scope_name in self.conv2d_scope_names: | 259 | if scope_name in self.conv2d_scope_names: |
| 224 | if node.op == 'Conv2D': | 260 | if node.op == 'Conv2D': |
| 225 | self.dump_conv2d_to_file(node, f) | 261 | self.dump_complex_conv2d_to_file(node, f) |
| 226 | continue | 262 | continue |
| 227 | 263 | ||
| 228 | if node.op == 'DepthToSpace': | 264 | if node.op == 'Conv2D': |
| 265 | self.dump_simple_conv2d_to_file(node, f) | ||
| 266 | elif node.op == 'DepthToSpace': | ||
| 229 | self.dump_depth2space_to_file(node, f) | 267 | self.dump_depth2space_to_file(node, f) |
| 230 | elif node.op == 'MirrorPad': | 268 | elif node.op == 'MirrorPad': |
| 231 | self.dump_mirrorpad_to_file(node, f) | 269 | self.dump_mirrorpad_to_file(node, f) |
| @@ -312,10 +350,16 @@ class TFConverter: · 80 unmodified lines | |||
| 312 | 350 | ||
| 313 | 351 | ||
| 314 | def generate_conv2d_scope_info(self): | 352 | def generate_conv2d_scope_info(self): |
| 315 | # conv2d is a sub block in graph, get the scope name | 353 | # mostly, conv2d is a sub block in graph, get the scope name |
| 316 | for node in self.nodes: | 354 | for node in self.nodes: |
| 317 | if node.op == 'Conv2D': | 355 | if node.op == 'Conv2D': |
| 318 | scope = TFConverter.get_scope_name(node.name) | 356 | scope = TFConverter.get_scope_name(node.name) |
| 357 | # for the case tf.nn.conv2d is called directly | ||
| 358 | if scope == '': | ||
| 359 | continue | ||
| 360 | # for the case tf.nn.conv2d is called within a scope | ||
| 361 | if scope + '/kernel' not in self.name_node_dict: | ||
| 362 | continue | ||
| 319 | self.conv2d_scope_names.add(scope) | 363 | self.conv2d_scope_names.add(scope) |
| 320 | 364 | ||
| 321 | # get the input name to the conv2d sub block | 365 | # get the input name to the conv2d sub block |