This repository has been archived by the owner on Feb 7, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reviewed By: xianjiec Differential Revision: D5569206 fbshipit-source-id: ed836315f3ee4d7983da94f2633a3085fe99194d
- Loading branch information
1 parent
3383b68
commit 32f023f
Showing
2 changed files
with
208 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
## @package conv | ||
# Module caffe2.python.layers.conv | ||
from __future__ import absolute_import | ||
from __future__ import division | ||
from __future__ import print_function | ||
from __future__ import unicode_literals | ||
|
||
from caffe2.python import schema | ||
from caffe2.python.layers.layers import ( | ||
ModelLayer, | ||
) | ||
import numpy as np | ||
|
||
|
||
class Conv(ModelLayer): | ||
""" | ||
Convolutional layer | ||
Input: | ||
- input_record: at least has the shape info of C (num_channels) | ||
- output_dim: number of convolutional filters | ||
- kernel_h, kernel_w: kernel size for h and w | ||
- stride_h, stride_w: stride for h and w | ||
- pad_b, pad_l, pad_r, pad_t: padding sizes, if stride == 1, | ||
'None' value will do auto padding | ||
- order: either 'NHWC' or 'NCHW' | ||
""" | ||
|
||
def __init__(self, model, input_record, output_dim, kernel_h, kernel_w, | ||
stride_h, stride_w, pad_b=None, pad_l=None, pad_r=None, | ||
pad_t=None, order='NHWC', kernel_init=None, bias_init=None, | ||
kernel_optim=None, bias_optim=None, | ||
name='conv', **kwargs): | ||
|
||
super(Conv, self).__init__(model, name, input_record, **kwargs) | ||
assert isinstance(input_record, schema.Scalar), "Incorrect input type" | ||
# input num_channels (C) is needed | ||
input_dims = input_record.field_type().shape | ||
|
||
assert (kernel_h > 0 and isinstance(kernel_h, int)), ( | ||
"kernel_h should be positive integer") | ||
assert (kernel_w > 0 and isinstance(kernel_w, int)), ( | ||
"kernel_w should be positive integer") | ||
self.kernel_h = kernel_h | ||
self.kernel_w = kernel_w | ||
|
||
assert (stride_h > 0 and isinstance(stride_h, int)), ( | ||
"stride_h should be positive integer") | ||
assert (stride_w > 0 and isinstance(stride_w, int)), ( | ||
"stride_w should be positive integer") | ||
self.stride_h = stride_h | ||
self.stride_w = stride_w | ||
|
||
# output_dim calculation (http://cs231n.github.io/convolutional-networks/) | ||
# output_dim_w = (input_dim_w - kernel_w + pad_r + pad_l) / stride_w + 1 | ||
# so, do auto_padding requires | ||
# pad_r, pad_l = [(input_dim_w - 1) * stride_w - input_dim_w + kernel_w] / 2 | ||
# similair for pad_t and pad_b to auto pad kernel_h | ||
# here we only do auto padding for stride = 1 case | ||
if stride_h == 1: | ||
pad_t = int((kernel_h - 1) / 2) if pad_t is None else pad_t | ||
pad_b = int((kernel_h - 1) / 2) if pad_b is None else pad_b | ||
else: | ||
pad_t = 0 if pad_t is None else pad_t | ||
pad_b = 0 if pad_b is None else pad_b | ||
|
||
if stride_w == 1: | ||
pad_r = int((kernel_w - 1) / 2) if pad_r is None else pad_r | ||
pad_l = int((kernel_w - 1) / 2) if pad_l is None else pad_l | ||
else: | ||
pad_r = 0 if pad_r is None else pad_r | ||
pad_l = 0 if pad_l is None else pad_l | ||
|
||
assert (pad_t >= 0 and isinstance(pad_t, int)), "pad_t should be int >= 0" | ||
assert (pad_b >= 0 and isinstance(pad_b, int)), "pad_b should be int >= 0" | ||
assert (pad_r >= 0 and isinstance(pad_r, int)), "pad_r should be int >= 0" | ||
assert (pad_l >= 0 and isinstance(pad_l, int)), "pad_l should be int >= 0" | ||
self.pad_t = pad_t | ||
self.pad_b = pad_b | ||
self.pad_r = pad_r | ||
self.pad_l = pad_l | ||
|
||
assert order in ['NHWC', 'NCHW'], "order should either 'NHWC' or 'NCHW'" | ||
self.order = order | ||
|
||
if order == 'NHWC': | ||
input_c = input_dims[-1] | ||
kernel_shape = [output_dim, kernel_h, kernel_w, input_c] | ||
elif order == 'NCHW': | ||
input_c = input_dims[0] | ||
kernel_shape = [output_dim, input_c, kernel_h, kernel_w] | ||
assert input_c > 0, ( | ||
"Number of input channels in conv parameters should be positive") | ||
|
||
kernel_init = kernel_init if kernel_init else ( | ||
'XavierFill', {} | ||
) | ||
bias_init = bias_init if bias_init else ( | ||
'ConstantFill', {'value': 0.0} | ||
) | ||
|
||
self.kernel = self.create_param( | ||
param_name='conv_kernel', | ||
shape=kernel_shape, | ||
initializer=kernel_init, | ||
optimizer=kernel_optim, | ||
) | ||
|
||
self.bias = self.create_param( | ||
param_name='conv_bias', | ||
shape=[output_dim], | ||
initializer=bias_init, | ||
optimizer=bias_optim, | ||
) | ||
|
||
# the output_schema only has the num of output channels | ||
# output_h and output_w would be inferred internally | ||
self.output_schema = schema.Scalar( | ||
(np.float32, (output_dim,)), | ||
self.get_next_blob_reference('output') | ||
) | ||
|
||
def add_ops(self, net): | ||
net.Conv( | ||
self.input_record.field_blobs() + [self.kernel, self.bias], | ||
self.output_schema.field_blobs(), | ||
kernel_h=self.kernel_h, | ||
kernel_w=self.kernel_w, | ||
stride_h=self.stride_h, | ||
stride_w=self.stride_w, | ||
pad_t=self.pad_t, | ||
pad_l=self.pad_l, | ||
pad_b=self.pad_b, | ||
pad_r=self.pad_r, | ||
order=self.order | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters