Skip to content

Commit 750914e

Browse files
committed
fix:protobuf NetParameter message create
fix:add PReLU support, ref:ethereon#187
1 parent b48432d commit 750914e

File tree

8 files changed

+237
-106
lines changed

8 files changed

+237
-106
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# self usage for openpose caffe model convert
2+
3+
## lib version
4+
tensorflow 2.11.0
5+
protobuf 3.19.6
6+
If libs update, convert should update to support
7+
18
# Caffe to TensorFlow
29

310
Convert [Caffe](https://github.com/BVLC/caffe/) models to [TensorFlow](https://github.com/tensorflow/tensorflow).

convert.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def validate_arguments(args):
2828

2929
def convert(def_path, caffemodel_path, data_output_path, code_output_path, standalone_output_path, phase):
3030
try:
31-
sess = tf.InteractiveSession()
31+
sess = tf.compat.v1.InteractiveSession()
3232
transformer = TensorFlowTransformer(def_path, caffemodel_path, phase=phase)
3333
print_stderr('Converting data...')
3434
if data_output_path is not None:

kaffe/caffe/caffe_pb2.py

Lines changed: 56 additions & 56 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

kaffe/caffe/resolver.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import sys
2-
2+
from google.protobuf import message_factory
3+
from . import caffe_pb2
34
SHARED_CAFFE_RESOLVER = None
45

56
class CaffeResolver(object):
67
def __init__(self):
8+
self.message_classes = message_factory.MessageFactory()
79
self.import_caffe()
810

911
def import_caffe(self):
@@ -21,7 +23,9 @@ def import_caffe(self):
2123
# Use the protobuf code from the imported distribution.
2224
# This way, Caffe variants with custom layers will work.
2325
self.caffepb = self.caffe.proto.caffe_pb2
24-
self.NetParameter = self.caffepb.NetParameter
26+
self.NetParameter = self.caffepb.NetParameter
27+
else:
28+
self.NetParameter = self.message_classes.GetPrototype(descriptor=caffe_pb2.NETPARAMETER)
2529

2630
def has_pycaffe(self):
2731
return self.caffe is not None

kaffe/layers.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
'Pooling': shape_pool,
3939
'Power': shape_identity,
4040
'ReLU': shape_identity,
41+
'PReLU': shape_identity,
4142
'Scale': shape_identity,
4243
'Sigmoid': shape_identity,
4344
'SigmoidCrossEntropyLoss': shape_scalar,
@@ -81,7 +82,7 @@ class NodeDispatch(object):
8182

8283
@staticmethod
8384
def get_handler_name(node_kind):
84-
if len(node_kind) <= 4:
85+
if len(node_kind) <= 4 or node_kind == 'PReLU':
8586
# A catch-all for things like ReLU and tanh
8687
return node_kind.lower()
8788
# Convert from CamelCase to under_scored

kaffe/tensorflow/network.py

Lines changed: 118 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import numpy as np
2+
import pickle
23
import tensorflow as tf
34

5+
from tensorflow.python.framework import ops
6+
from tensorflow.python.ops import math_ops
7+
from tensorflow.python.ops import nn_ops
8+
49
DEFAULT_PADDING = 'SAME'
510

611

@@ -41,9 +46,9 @@ def __init__(self, inputs, trainable=True):
4146
# If true, the resulting variables are set as trainable
4247
self.trainable = trainable
4348
# Switch variable for dropout
44-
self.use_dropout = tf.placeholder_with_default(tf.constant(1.0),
45-
shape=[],
46-
name='use_dropout')
49+
self.use_dropout = tf.compat.v1.placeholder_with_default(tf.constant(1.0),
50+
shape=[],
51+
name='use_dropout')
4752
self.setup()
4853

4954
def setup(self):
@@ -56,16 +61,26 @@ def load(self, data_path, session, ignore_missing=False):
5661
session: The current TensorFlow session
5762
ignore_missing: If true, serialized weights for missing layers are ignored.
5863
'''
59-
data_dict = np.load(data_path).item()
64+
with open(data_path, 'rb') as handle:
65+
data_dict = pickle.load(handle)
6066
for op_name in data_dict:
61-
with tf.variable_scope(op_name, reuse=True):
62-
for param_name, data in data_dict[op_name].items():
67+
with tf.compat.v1.variable_scope(op_name, reuse=True):
68+
# TODO not sure why name mapping does not work
69+
if 'relu' in op_name:
6370
try:
64-
var = tf.get_variable(param_name)
65-
session.run(var.assign(data))
71+
var = tf.compat.v1.get_variable(op_name)
72+
session.run(var.assign(data_dict[op_name][0]))
6673
except ValueError:
6774
if not ignore_missing:
6875
raise
76+
else:
77+
for param_name, data in data_dict[op_name].iteritems():
78+
try:
79+
var = tf.compat.v1.get_variable(param_name)
80+
session.run(var.assign(data))
81+
except ValueError:
82+
if not ignore_missing:
83+
raise
6984

7085
def feed(self, *args):
7186
'''Set the input(s) for the next operation by replacing the terminal nodes.
@@ -74,7 +89,7 @@ def feed(self, *args):
7489
assert len(args) != 0
7590
self.terminals = []
7691
for fed_layer in args:
77-
if isinstance(fed_layer, str):
92+
if isinstance(fed_layer, basestring):
7893
try:
7994
fed_layer = self.layers[fed_layer]
8095
except KeyError:
@@ -95,42 +110,62 @@ def get_unique_name(self, prefix):
95110

96111
def make_var(self, name, shape):
97112
'''Creates a new TensorFlow variable.'''
98-
return tf.get_variable(name, shape, trainable=self.trainable)
113+
return tf.compat.v1.get_variable(name, shape, trainable=self.trainable)
99114

100115
def validate_padding(self, padding):
101116
'''Verifies that the padding is one of the supported ones.'''
102117
assert padding in ('SAME', 'VALID')
103118

119+
def prelu_layer(self, x, weights, biases, name=None):
120+
"""Computes PRelu(x * weight + biases).
121+
Args:
122+
x: a 2D tensor. Dimensions typically: batch, in_units
123+
weights: a 2D tensor. Dimensions typically: in_units, out_units
124+
biases: a 1D tensor. Dimensions: out_units
125+
name: A name for the operation (optional). If not specified
126+
"nn_prelu_layer" is used.
127+
Returns:
128+
A 2-D Tensor computing prelu(matmul(x, weights) + biases).
129+
Dimensions typically: batch, out_units.
130+
"""
131+
with ops.name_scope(name, "prelu_layer", [x, weights, biases]) as name:
132+
x = ops.convert_to_tensor(x, name="x")
133+
weights = ops.convert_to_tensor(weights, name="weights")
134+
biases = ops.convert_to_tensor(biases, name="biases")
135+
xw_plus_b = nn_ops.bias_add(math_ops.matmul(x, weights), biases)
136+
return self.parametric_relu(xw_plus_b, name=name)
137+
104138
@layer
105139
def conv(self,
106-
input,
140+
inputs,
107141
k_h,
108142
k_w,
109143
c_o,
110144
s_h,
111145
s_w,
112146
name,
113147
relu=True,
148+
prelu=False,
114149
padding=DEFAULT_PADDING,
115150
group=1,
116151
biased=True):
117152
# Verify that the padding is acceptable
118153
self.validate_padding(padding)
119154
# Get the number of channels in the input
120-
c_i = input.get_shape()[-1]
155+
c_i = inputs.get_shape()[-1]
121156
# Verify that the grouping parameter is valid
122157
assert c_i % group == 0
123158
assert c_o % group == 0
124159
# Convolution for a given input and kernel
125160
convolve = lambda i, k: tf.nn.conv2d(i, k, [1, s_h, s_w, 1], padding=padding)
126-
with tf.variable_scope(name) as scope:
127-
kernel = self.make_var('weights', shape=[k_h, k_w, int(c_i) / group, c_o])
161+
with tf.compat.v1.variable_scope(name) as scope:
162+
kernel = self.make_var('weights', shape=[k_h, k_w, c_i / group, c_o])
128163
if group == 1:
129164
# This is the common-case. Convolve the input without any further complications.
130-
output = convolve(input, kernel)
165+
output = convolve(inputs, kernel)
131166
else:
132167
# Split the input into groups and then convolve each of them independently
133-
input_groups = tf.split(3, group, input)
168+
input_groups = tf.split(3, group, inputs)
134169
kernel_groups = tf.split(3, group, kernel)
135170
output_groups = [convolve(i, k) for i, k in zip(input_groups, kernel_groups)]
136171
# Concatenate the groups
@@ -142,33 +177,65 @@ def conv(self,
142177
if relu:
143178
# ReLU non-linearity
144179
output = tf.nn.relu(output, name=scope.name)
180+
elif prelu:
181+
output = self.parametric_relu(output, scope=scope)
145182
return output
146183

147184
@layer
148-
def relu(self, input, name):
149-
return tf.nn.relu(input, name=name)
185+
def relu(self, x, name):
186+
return tf.nn.relu(x, name=name)
187+
188+
@layer
189+
def prelu(self, x, name):
190+
return self.parametric_relu(x, name=name)
191+
192+
def parametric_relu(self, x, scope=None, name="PReLU"):
193+
""" PReLU.
194+
195+
Parametric Rectified Linear Unit. Base on:
196+
https://github.com/tflearn/tflearn/blob/5c23566de6e614a36252a5828d107d001a0d0482/tflearn/activations.py#L188
197+
198+
Arguments:
199+
x: A `Tensor` with type `float`, `double`, `int32`, `int64`, `uint8`,
200+
`int16`, or `int8`.
201+
name: A name for this activation op (optional).
202+
Returns:
203+
A `Tensor` with the same type as `x`.
204+
"""
205+
# tf.zeros(x.shape, dtype=dtype)
206+
with tf.compat.v1.variable_scope(scope, default_name=name, values=[x]) as scope:
207+
# W_init=tf.constant_initializer(0.0)
208+
# alphas = tf.compat.v1.get_variable(name="alphas", shape=x.get_shape()[-1],
209+
# initializer=W_init,
210+
# dtype=tf.float32)
211+
alphas = self.make_var(name, x.get_shape()[-1])
212+
x = tf.nn.relu(x) + tf.multiply(alphas, (x - tf.abs(x))) * 0.5
213+
214+
x.scope = scope
215+
x.alphas = alphas
216+
return x
150217

151218
@layer
152-
def max_pool(self, input, k_h, k_w, s_h, s_w, name, padding=DEFAULT_PADDING):
219+
def max_pool(self, x, k_h, k_w, s_h, s_w, name, padding=DEFAULT_PADDING):
153220
self.validate_padding(padding)
154-
return tf.nn.max_pool(input,
155-
ksize=[1, k_h, k_w, 1],
156-
strides=[1, s_h, s_w, 1],
157-
padding=padding,
158-
name=name)
221+
return tf.nn.max_pool2d(x,
222+
ksize=[1, k_h, k_w, 1],
223+
strides=[1, s_h, s_w, 1],
224+
padding=padding,
225+
name=name)
159226

160227
@layer
161-
def avg_pool(self, input, k_h, k_w, s_h, s_w, name, padding=DEFAULT_PADDING):
228+
def avg_pool(self, x, k_h, k_w, s_h, s_w, name, padding=DEFAULT_PADDING):
162229
self.validate_padding(padding)
163-
return tf.nn.avg_pool(input,
230+
return tf.nn.avg_pool(x,
164231
ksize=[1, k_h, k_w, 1],
165232
strides=[1, s_h, s_w, 1],
166233
padding=padding,
167234
name=name)
168235

169236
@layer
170-
def lrn(self, input, radius, alpha, beta, name, bias=1.0):
171-
return tf.nn.local_response_normalization(input,
237+
def lrn(self, x, radius, alpha, beta, name, bias=1.0):
238+
return tf.nn.local_response_normalization(x,
172239
depth_radius=radius,
173240
alpha=alpha,
174241
beta=beta,
@@ -184,48 +251,53 @@ def add(self, inputs, name):
184251
return tf.add_n(inputs, name=name)
185252

186253
@layer
187-
def fc(self, input, num_out, name, relu=True):
188-
with tf.variable_scope(name) as scope:
189-
input_shape = input.get_shape()
254+
def fc(self, x, num_out, name, relu=True, prelu=False):
255+
with tf.compat.v1.variable_scope(name) as scope:
256+
input_shape = x.get_shape()
190257
if input_shape.ndims == 4:
191258
# The input is spatial. Vectorize it first.
192259
dim = 1
193260
for d in input_shape[1:].as_list():
194261
dim *= d
195-
feed_in = tf.reshape(input, [-1, dim])
262+
feed_in = tf.reshape(x, [-1, dim])
196263
else:
197-
feed_in, dim = (input, input_shape[-1].value)
264+
feed_in, dim = (x, input_shape[-1].value)
198265
weights = self.make_var('weights', shape=[dim, num_out])
199266
biases = self.make_var('biases', [num_out])
200-
op = tf.nn.relu_layer if relu else tf.nn.xw_plus_b
267+
if relu:
268+
op = tf.nn.relu_layer
269+
elif prelu:
270+
op = self.prelu_layer
271+
else:
272+
op = tf.compat.v1.nn.xw_plus_b
201273
fc = op(feed_in, weights, biases, name=scope.name)
202274
return fc
203275

204276
@layer
205-
def softmax(self, input, name):
206-
input_shape = [v.value for v in input.get_shape()]
277+
def softmax(self, x, name):
278+
input_shape = map(lambda v: v.value, x.get_shape())
207279
if len(input_shape) > 2:
208280
# For certain models (like NiN), the singleton spatial dimensions
209281
# need to be explicitly squeezed, since they're not broadcast-able
210282
# in TensorFlow's NHWC ordering (unlike Caffe's NCHW).
211283
if input_shape[1] == 1 and input_shape[2] == 1:
212-
input = tf.squeeze(input, squeeze_dims=[1, 2])
284+
x = tf.squeeze(x, squeeze_dims=[1, 2])
213285
else:
214286
raise ValueError('Rank 2 tensor input expected for softmax!')
215-
return tf.nn.softmax(input, name=name)
287+
return tf.nn.softmax(x, name=name)
216288

217289
@layer
218-
def batch_normalization(self, input, name, scale_offset=True, relu=False):
290+
def batch_normalization(self, x, name, scale_offset=True, relu=False, prelu=False):
219291
# NOTE: Currently, only inference is supported
220-
with tf.variable_scope(name) as scope:
221-
shape = [input.get_shape()[-1]]
292+
with tf.compat.v1.variable_scope(name) as scope:
293+
shape = [x.get_shape()[-1]]
222294
if scale_offset:
223295
scale = self.make_var('scale', shape=shape)
224296
offset = self.make_var('offset', shape=shape)
225297
else:
226298
scale, offset = (None, None)
227299
output = tf.nn.batch_normalization(
228-
input,
300+
x,
229301
mean=self.make_var('mean', shape=shape),
230302
variance=self.make_var('variance', shape=shape),
231303
offset=offset,
@@ -236,9 +308,11 @@ def batch_normalization(self, input, name, scale_offset=True, relu=False):
236308
name=name)
237309
if relu:
238310
output = tf.nn.relu(output)
311+
elif prelu:
312+
output = self.parametric_relu(output, name=scope.name)
239313
return output
240314

241315
@layer
242-
def dropout(self, input, keep_prob, name):
316+
def dropout(self, x, keep_prob, name):
243317
keep = 1 - self.use_dropout + (self.use_dropout * keep_prob)
244-
return tf.nn.dropout(input, keep, name=name)
318+
return tf.nn.dropout(x, keep, name=name)

kaffe/tensorflow/transformer.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from ..errors import KaffeError, print_stderr
44
from ..graph import GraphBuilder, NodeMapper
55
from ..layers import NodeKind
6-
from ..transformers import (DataInjector, DataReshaper, NodeRenamer, ReLUFuser,
6+
from ..transformers import (DataInjector, DataReshaper, NodeRenamer, ReLUFuser, PReLUFuser,
77
BatchNormScaleBiasFuser, BatchNormPreprocessor, ParameterNamer)
88

99
from . import network
@@ -69,6 +69,8 @@ def __init__(self, node, default=True):
6969
self.inject_kwargs = {}
7070
if node.metadata.get('relu', False) != default:
7171
self.inject_kwargs['relu'] = not default
72+
if node.metadata.get('prelu'):
73+
self.inject_kwargs['prelu'] = node.metadata.get('prelu')
7274

7375
def __call__(self, *args, **kwargs):
7476
kwargs.update(self.inject_kwargs)
@@ -104,6 +106,9 @@ def map_convolution(self, node):
104106
def map_relu(self, node):
105107
return TensorFlowNode('relu')
106108

109+
def map_prelu(self, node):
110+
return TensorFlowNode('prelu')
111+
107112
def map_pooling(self, node):
108113
pool_type = node.parameters.pool
109114
if pool_type == 0:
@@ -263,7 +268,10 @@ def transform_data(self):
263268
NodeKind.Convolution: (2, 3, 1, 0),
264269

265270
# (c_o, c_i) -> (c_i, c_o)
266-
NodeKind.InnerProduct: (1, 0)
271+
NodeKind.InnerProduct: (1, 0),
272+
273+
# one dimensional
274+
NodeKind.PReLU: (0)
267275
}),
268276

269277
# Pre-process batch normalization data

0 commit comments

Comments
 (0)