Caffe2 - Python API
A deep learning, cross platform ML framework
conv.py
1 # Copyright (c) 2016-present, Facebook, Inc.
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 
16 ## @package conv
17 # Module caffe2.python.helpers.conv
18 from __future__ import absolute_import
19 from __future__ import division
20 from __future__ import print_function
21 from __future__ import unicode_literals
22 
23 from caffe2.python import core
24 from caffe2.python.modeling import initializers
25 from caffe2.python.modeling.parameter_info import ParameterTags
26 
27 def _ConvBase(
28  model,
29  is_nd,
30  blob_in,
31  blob_out,
32  dim_in,
33  dim_out,
34  kernel,
35  weight_init=None,
36  bias_init=None,
37  WeightInitializer=None,
38  BiasInitializer=None,
39  group=1,
40  transform_inputs=None,
41  use_cudnn=False,
42  order="NCHW",
43  cudnn_exhaustive_search=False,
44  ws_nbytes_limit=None,
45  **kwargs
46 ):
47  kernels = []
48  if is_nd:
49  if not isinstance(kernel, list):
50  kernels = [kernel]
51  else:
52  kernels = kernel
53  else:
54  if isinstance(kernel, list):
55  assert len(kernel) == 2, "Conv support only a 2D kernel."
56  kernels = kernel
57  else:
58  kernels = [kernel] * 2
59 
60  requested_engine = kwargs.get('engine')
61  if requested_engine is not None:
62  if use_cudnn and requested_engine != 'CUDNN':
63  raise ValueError(
64  'When use_cudnn=True, the only engine you can specify is '
65  '"CUDNN"')
66  elif not use_cudnn and requested_engine == 'CUDNN':
67  raise ValueError(
68  'When use_cudnn=False, the only engine you can specify is '
69  '""')
70 
71  if use_cudnn:
72  kwargs['engine'] = 'CUDNN'
73  kwargs['exhaustive_search'] = cudnn_exhaustive_search
74  if ws_nbytes_limit:
75  kwargs['ws_nbytes_limit'] = ws_nbytes_limit
76 
77  use_bias =\
78  False if ("no_bias" in kwargs and kwargs["no_bias"]) else True
79  blob_out = blob_out or model.net.NextName()
80  weight_shape = [dim_out]
81  if order == "NCHW":
82  weight_shape.append(int(dim_in / group))
83  weight_shape.extend(kernels)
84  else:
85  weight_shape.extend(kernels)
86  weight_shape.append(int(dim_in / group))
87 
88  WeightInitializer = initializers.update_initializer(
89  WeightInitializer, weight_init, ("XavierFill", {})
90  )
91  BiasInitializer = initializers.update_initializer(
92  BiasInitializer, bias_init, ("ConstantFill", {})
93  )
94  if not model.init_params:
95  WeightInitializer = initializers.ExternalInitializer()
96  BiasInitializer = initializers.ExternalInitializer()
97 
98  weight = model.create_param(
99  param_name=blob_out + '_w',
100  shape=weight_shape,
101  initializer=WeightInitializer,
102  tags=ParameterTags.WEIGHT
103  )
104  if use_bias:
105  bias = model.create_param(
106  param_name=blob_out + '_b',
107  shape=[dim_out, ],
108  initializer=BiasInitializer,
109  tags=ParameterTags.BIAS
110  )
111 
112  if use_bias:
113  inputs = [blob_in, weight, bias]
114  else:
115  inputs = [blob_in, weight]
116 
117  if transform_inputs is not None:
118  transform_inputs(model, blob_out, inputs)
119 
120  # For the operator, we no longer need to provide the no_bias field
121  # because it can automatically figure this out from the number of
122  # inputs.
123  if 'no_bias' in kwargs:
124  del kwargs['no_bias']
125  if group != 1:
126  kwargs['group'] = group
127  if is_nd:
128  return model.net.Conv(
129  inputs,
130  blob_out,
131  kernels=kernels,
132  order=order,
133  **kwargs)
134  else:
135  if isinstance(kernel, list):
136  return model.net.Conv(
137  inputs,
138  blob_out,
139  kernel_h=kernel[0],
140  kernel_w=kernel[1],
141  order=order,
142  **kwargs)
143  else:
144  return model.net.Conv(
145  inputs,
146  blob_out,
147  kernel=kernel,
148  order=order,
149  **kwargs)
150 
151 
152 
153 def conv_nd(
154  model,
155  blob_in,
156  blob_out,
157  dim_in,
158  dim_out,
159  kernel,
160  weight_init=None,
161  bias_init=None,
162  WeightInitializer=None,
163  BiasInitializer=None,
164  group=1,
165  transform_inputs=None,
166  order="NCHW",
167  **kwargs
168 ):
169  """N-dimensional convolution for inputs with NCHW storage order.
170  """
171  assert order == "NCHW", "ConvNd only supported for NCHW storage."
172  return _ConvBase(model, True, blob_in, blob_out, dim_in, dim_out, kernel,
173  weight_init, bias_init, WeightInitializer, BiasInitializer,
174  group, transform_inputs, order=order, **kwargs)
175 
176 
177 def conv(
178  model,
179  blob_in,
180  blob_out,
181  dim_in,
182  dim_out,
183  kernel,
184  weight_init=None,
185  bias_init=None,
186  WeightInitializer=None,
187  BiasInitializer=None,
188  group=1,
189  transform_inputs=None,
190  **kwargs
191 ):
192  """2-dimensional convolution.
193  """
194  return _ConvBase(model, False, blob_in, blob_out, dim_in, dim_out, kernel,
195  weight_init, bias_init, WeightInitializer, BiasInitializer,
196  group, transform_inputs, **kwargs)
197 
198 
199 def conv_transpose(
200  model,
201  blob_in,
202  blob_out,
203  dim_in,
204  dim_out,
205  kernel,
206  weight_init=None,
207  bias_init=None,
208  use_cudnn=False,
209  order="NCHW",
210  cudnn_exhaustive_search=False,
211  ws_nbytes_limit=None,
212  **kwargs
213 ):
214  """ConvTranspose.
215  """
216  weight_init = weight_init if weight_init else ('XavierFill', {})
217  bias_init = bias_init if bias_init else ('ConstantFill', {})
218  blob_out = blob_out or model.net.NextName()
219  weight_shape = (
220  [dim_in, dim_out, kernel, kernel]
221  if order == "NCHW" else [dim_in, kernel, kernel, dim_out]
222  )
223  if model.init_params:
224  weight = model.param_init_net.__getattr__(weight_init[0])(
225  [],
226  blob_out + '_w',
227  shape=weight_shape,
228  **weight_init[1]
229  )
230  bias = model.param_init_net.__getattr__(bias_init[0])(
231  [],
232  blob_out + '_b',
233  shape=[dim_out, ],
234  **bias_init[1]
235  )
236  else:
237  weight = core.ScopedBlobReference(
238  blob_out + '_w', model.param_init_net)
239  bias = core.ScopedBlobReference(
240  blob_out + '_b', model.param_init_net)
241  model.AddParameter(weight, ParameterTags.WEIGHT)
242  model.AddParameter(bias, ParameterTags.BIAS)
243  if use_cudnn:
244  kwargs['engine'] = 'CUDNN'
245  kwargs['exhaustive_search'] = cudnn_exhaustive_search
246  if ws_nbytes_limit:
247  kwargs['ws_nbytes_limit'] = ws_nbytes_limit
248  return model.net.ConvTranspose(
249  [blob_in, weight, bias],
250  blob_out,
251  kernel=kernel,
252  order=order,
253  **kwargs
254  )
255 
256 
257 def group_conv(
258  model,
259  blob_in,
260  blob_out,
261  dim_in,
262  dim_out,
263  kernel,
264  weight_init=None,
265  bias_init=None,
266  group=1,
267  **kwargs
268 ):
269  """Group Convolution.
270 
271  This is essentially the same as Conv with a group argument passed in.
272  We specialize this for backward interface compatibility.
273  """
274  return conv(model, blob_in, blob_out, dim_in, dim_out, kernel,
275  weight_init=weight_init, bias_init=bias_init,
276  group=group, **kwargs)
277 
278 
279 def group_conv_deprecated(
280  model,
281  blob_in,
282  blob_out,
283  dim_in,
284  dim_out,
285  kernel,
286  weight_init=None,
287  bias_init=None,
288  group=1,
289  use_cudnn=False,
290  order="NCHW",
291  cudnn_exhaustive_search=False,
292  ws_nbytes_limit=None,
293  **kwargs
294 ):
295  """GroupConvolution's deprecated interface.
296 
297  This is used to simulate a group convolution via split and concat. You
298  should always use the new group convolution in your new code.
299  """
300  weight_init = weight_init if weight_init else ('XavierFill', {})
301  bias_init = bias_init if bias_init else ('ConstantFill', {})
302  use_bias = False if ("no_bias" in kwargs and kwargs["no_bias"]) else True
303  if use_cudnn:
304  kwargs['engine'] = 'CUDNN'
305  kwargs['exhaustive_search'] = cudnn_exhaustive_search
306  if ws_nbytes_limit:
307  kwargs['ws_nbytes_limit'] = ws_nbytes_limit
308  if dim_in % group:
309  raise ValueError("dim_in should be divisible by group.")
310  if dim_out % group:
311  raise ValueError("dim_out should be divisible by group.")
312  splitted_blobs = model.net.DepthSplit(
313  blob_in,
314  ['_' + blob_out + '_gconv_split_' + str(i) for i in range(group)],
315  dimensions=[int(dim_in / group) for i in range(group)],
316  order=order
317  )
318  weight_shape = (
319  [dim_out / group, dim_in / group, kernel, kernel]
320  if order == "NCHW" else
321  [dim_out / group, kernel, kernel, dim_in / group]
322  )
323  # Make sure that the shapes are of int format. Especially for py3 where
324  # int division gives float output.
325  weight_shape = [int(v) for v in weight_shape]
326  conv_blobs = []
327  for i in range(group):
328  if model.init_params:
329  weight = model.param_init_net.__getattr__(weight_init[0])(
330  [],
331  blob_out + '_gconv_%d_w' % i,
332  shape=weight_shape,
333  **weight_init[1]
334  )
335  if use_bias:
336  bias = model.param_init_net.__getattr__(bias_init[0])(
337  [],
338  blob_out + '_gconv_%d_b' % i,
339  shape=[int(dim_out / group)],
340  **bias_init[1]
341  )
342  else:
343  weight = core.ScopedBlobReference(
344  blob_out + '_gconv_%d_w' % i, model.param_init_net)
345  if use_bias:
346  bias = core.ScopedBlobReference(
347  blob_out + '_gconv_%d_b' % i, model.param_init_net)
348  model.AddParameter(weight, ParameterTags.WEIGHT)
349  if use_bias:
350  model.AddParameter(bias, ParameterTags.BIAS)
351  if use_bias:
352  inputs = [weight, bias]
353  else:
354  inputs = [weight]
355  if 'no_bias' in kwargs:
356  del kwargs['no_bias']
357  conv_blobs.append(
358  splitted_blobs[i].Conv(
359  inputs,
360  blob_out + '_gconv_%d' % i,
361  kernel=kernel,
362  order=order,
363  **kwargs
364  )
365  )
366  concat, concat_dims = model.net.Concat(
367  conv_blobs,
368  [blob_out,
369  "_" + blob_out + "_concat_dims"],
370  order=order
371  )
372  return concat
Module caffe2.python.helpers.conv.