Skip to content

Commit 0049ce0

Browse files
authored
03 image classification (#5192)
* add batch_norm_layer * add img_conv_group layer and test * add check to Tensor.type() * forward can run * with backward * change label data time from int32 to int64 * refine code * follow comment
1 parent 833d0ad commit 0049ce0

File tree

10 files changed

+418
-16
lines changed

10 files changed

+418
-16
lines changed

paddle/framework/operator.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ class OperatorWithKernel : public OperatorBase {
408408
// indicate kernel DataType by input data. Defaultly all input data must be
409409
// same.
410410
virtual DataType IndicateDataType(const ExecutionContext& ctx) const {
411+
VLOG(3) << "Default IndicateDataType " << this->Type();
411412
auto& scope = ctx.scope();
412413
int data_type = -1;
413414
for (auto& input : this->inputs_) {

paddle/framework/tensor.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,16 @@ class Tensor {
126126
inline Tensor Slice(const int& begin_idx, const int& end_idx) const;
127127

128128
platform::Place place() const {
129-
PADDLE_ENFORCE_NOT_NULL(holder_, "Tensor get place() must contains holder");
129+
PADDLE_ENFORCE_NOT_NULL(
130+
holder_, "Tensor not initialized yet when Tensor::place() is called.");
130131
return holder_->place();
131132
}
132133

133-
std::type_index type() const { return holder_->type(); }
134+
std::type_index type() const {
135+
PADDLE_ENFORCE_NOT_NULL(
136+
holder_, "Tensor not initialized yet when Tensor::type() is called.");
137+
return holder_->type();
138+
}
134139

135140
size_t memory_size() const;
136141

paddle/operators/batch_norm_op.cc

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ namespace paddle {
1818
namespace operators {
1919

2020
using Tensor = framework::Tensor;
21+
using LoDTensor = framework::LoDTensor;
2122
template <typename T, int MajorType = Eigen::RowMajor,
2223
typename IndexType = Eigen::DenseIndex>
2324
using EigenMatrix = framework::EigenMatrix<T, MajorType, IndexType>;
@@ -64,6 +65,9 @@ class BatchNormOp : public framework::OperatorWithKernel {
6465
(tensor_format == TensorFormat::NCHW ? x_dims[1]
6566
: x_dims[x_dims.size() - 1]);
6667

68+
PADDLE_ENFORCE(x_dims.size() >= 3 && x_dims.size() <= 5,
69+
"Input x must have 3 to 5 dimensions.");
70+
6771
PADDLE_ENFORCE_EQ(ctx->GetInputDim("Scale").size(), 1UL);
6872
PADDLE_ENFORCE_EQ(ctx->GetInputDim("Scale")[0], C);
6973
PADDLE_ENFORCE_EQ(ctx->GetInputDim("Bias").size(), 1UL);
@@ -108,10 +112,12 @@ class BatchNormOpMaker : public framework::OpProtoAndCheckerMaker {
108112
"Store the global Variance when training");
109113
AddOutput("SavedMean",
110114
"Mean of the current mini batch, "
111-
"will apply to output when training");
115+
"will apply to output when training")
116+
.AsIntermediate();
112117
AddOutput("SavedVariance",
113118
"Variance of the current mini batch, "
114-
"will apply to output when training");
119+
"will apply to output when training")
120+
.AsIntermediate();
115121
AddComment(R"DOC(
116122
https://arxiv.org/pdf/1502.03167.pdf
117123
@@ -135,7 +141,6 @@ class BatchNormKernel<platform::CPUPlace, T> : public framework::OpKernel<T> {
135141

136142
const auto *x = ctx.Input<Tensor>("X");
137143
const auto &x_dims = x->dims();
138-
139144
PADDLE_ENFORCE(x_dims.size() >= 3 && x_dims.size() <= 5,
140145
"The Input dim size should be between 3 and 5");
141146
const int N = x_dims[0];
@@ -289,6 +294,25 @@ class BatchNormGradOp : public framework::OperatorWithKernel {
289294
ctx->SetOutputDim(framework::GradVarName("Scale"), {C});
290295
ctx->SetOutputDim(framework::GradVarName("Bias"), {C});
291296
}
297+
298+
framework::DataType IndicateDataType(
299+
const framework::ExecutionContext &ctx) const override {
300+
VLOG(3) << "IndicateDataType " << this->Type();
301+
const auto *var = ctx.InputVar(framework::GradVarName("Y"));
302+
if (var == nullptr) {
303+
PADDLE_THROW("can't find Y@GRAD");
304+
}
305+
const Tensor *t = nullptr;
306+
if (var->IsType<Tensor>()) {
307+
t = &var->Get<Tensor>();
308+
} else if (var->IsType<LoDTensor>()) {
309+
t = &var->Get<LoDTensor>();
310+
}
311+
if (t == nullptr) {
312+
PADDLE_THROW("can't find Y@GRAD");
313+
}
314+
return framework::ToDataType(t->type());
315+
}
292316
};
293317

294318
template <typename T>

paddle/operators/reshape_op.cc

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,19 @@ class ReshapeOp : public framework::OperatorWithKernel {
3434

3535
auto shape = ctx->Attrs().Get<std::vector<int>>("shape");
3636
PADDLE_ENFORCE(shape.size() > 0, "Attr(shape) shouldn't be empty.");
37-
for (auto dim : shape) {
38-
PADDLE_ENFORCE(dim > 0, "Each dimension of shape must be positive.");
37+
auto x_dims = ctx->GetInputDim("X");
38+
// TODO(qiao) change batch_size
39+
for (int i = 1; i < shape.size(); ++i) {
40+
PADDLE_ENFORCE(shape[i] > 0,
41+
"Each dimension of shape "
42+
"must be positiv except the first.");
43+
}
44+
if (shape[0] < 0) {
45+
shape[0] = x_dims[0];
3946
}
4047
// capacity check
4148
int64_t capacity =
4249
std::accumulate(shape.begin(), shape.end(), 1, std::multiplies<int>());
43-
auto x_dims = ctx->GetInputDim("X");
4450
int64_t in_size = framework::product(x_dims);
4551
PADDLE_ENFORCE_EQ(capacity, in_size,
4652
"The size of Input(X) mismatches with Attr(shape).");

paddle/operators/reshape_op.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,8 @@ class ReshapeKernel : public framework::OpKernel<T> {
2626
void Compute(const framework::ExecutionContext& ctx) const {
2727
auto* out = ctx.Output<framework::Tensor>("Out");
2828
auto* in = ctx.Input<framework::Tensor>("X");
29+
auto out_dims = out->dims();
2930
out->mutable_data<T>(ctx.GetPlace());
30-
31-
auto shape = ctx.Attr<std::vector<int>>("shape");
32-
std::vector<int64_t> shape_int64(shape.size(), 0);
33-
std::transform(shape.begin(), shape.end(), shape_int64.begin(),
34-
[](int a) { return static_cast<int64_t>(a); });
35-
auto out_dims = framework::make_ddim(shape_int64);
3631
out->CopyFrom(*in, ctx.GetPlace(), ctx.device_context());
3732
out->Resize(out_dims);
3833
}

python/paddle/v2/framework/framework.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,10 @@ def all_parameters(self):
352352
return {v for k, v in self.vars.iteritems() if isinstance(v, Parameter)}
353353

354354
def create_var(self, *args, **kwargs):
355-
return Variable(self, *args, **kwargs)
355+
var = Variable(self, *args, **kwargs)
356+
if 'init_attr' in kwargs:
357+
self._prepend_initialize_ops_(var, kwargs['init_attr'])
358+
return var
356359

357360
def has_var(self, name):
358361
return name in self.vars

python/paddle/v2/framework/layers.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ def func(**kwargs):
161161
_create_op_func_('mean')
162162
_create_op_func_('mul')
163163
_create_op_func_('dropout')
164+
_create_op_func_('reshape')
164165

165166

166167
def cast(x, data_type, program=None):
@@ -308,6 +309,96 @@ def pool2d(input,
308309
return pool_out
309310

310311

312+
def batch_norm(input,
313+
act=None,
314+
is_test=False,
315+
momentum=0.9,
316+
epsilon=1e05,
317+
param_attr=None,
318+
bias_attr=None,
319+
data_layout='NCHW',
320+
program=None,
321+
init_program=None):
322+
helper = LayerHelper('batch_norm', **locals())
323+
dtype = helper.input_dtype()
324+
325+
input_shape = input.shape
326+
if data_layout == 'NCHW':
327+
channel_num = input_shape[1]
328+
else:
329+
if data_layout == 'NHWC':
330+
channel_num = input_shape[-1]
331+
else:
332+
raise ValueError("unsupported data layout:" + data_layout)
333+
334+
def get_init_attr(value):
335+
if not isinstance(value, float):
336+
raise ValueError("attr value should be a float")
337+
return {'type': 'fill_constant', 'value': value}
338+
339+
def prepend_init_op(var, init_attr):
340+
assert isinstance(var, Variable)
341+
op_type = init_attr['type']
342+
init_attr['shape'] = var.shape
343+
init_attr['data_type'] = int(var.data_type)
344+
op = var.block.prepend_op(
345+
type=op_type, inputs=None, outputs={'Out': [var]}, attrs=init_attr)
346+
return op
347+
348+
def create_persistable_var(dtype, shape, init_attr=None):
349+
name = unique_name(".".join([helper.name, "xxxx"]))
350+
var = init_program.global_block().create_var(
351+
dtype=dtype, shape=shape, name=name, persistable=True)
352+
if 'init_attr' is not None:
353+
prepend_init_op(var, init_attr)
354+
return program.global_block().create_var(
355+
name=name, dtype=dtype, shape=shape, persistable=True)
356+
357+
param_shape = [channel_num]
358+
359+
# create parameter
360+
scale = helper.create_parameter(
361+
attr=helper.param_attr, shape=param_shape, dtype=dtype)
362+
bias = helper.create_parameter(
363+
attr=helper.param_attr, shape=param_shape, dtype=dtype)
364+
365+
# create input
366+
mean = create_persistable_var(dtype, param_shape, get_init_attr(0.0))
367+
variance = create_persistable_var(dtype, param_shape, get_init_attr(1.0))
368+
369+
# create output
370+
# mean and mean_out share the same memory
371+
mean_out = mean
372+
# variance and variance out share the same memory
373+
variance_out = variance
374+
saved_mean = helper.create_tmp_variable(dtype)
375+
saved_variance = helper.create_tmp_variable(dtype)
376+
377+
batch_norm_out = helper.create_tmp_variable(dtype)
378+
379+
helper.append_op(
380+
type="batch_norm",
381+
inputs={
382+
"X": input,
383+
"Scale": scale,
384+
"Bias": bias,
385+
"Mean": mean,
386+
"Variance": variance
387+
},
388+
outputs={
389+
"Y": batch_norm_out,
390+
"MeanOut": mean_out,
391+
"VarianceOut": variance_out,
392+
"SavedMean": saved_mean,
393+
"SavedVariance": saved_variance
394+
},
395+
attrs={"momentum": momentum,
396+
"epsilon": epsilon,
397+
"is_test": is_test})
398+
399+
return helper.append_activation(batch_norm_out)
400+
401+
311402
class BlockGuard(object):
312403
"""
313404
BlockGuard used to create sub-block in program by using Python `with`

python/paddle/v2/framework/nets.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ def simple_img_conv_pool(input,
77
pool_size,
88
pool_stride,
99
act,
10+
pool_type='max',
1011
program=None,
1112
init_program=None):
1213
conv_out = layers.conv2d(
@@ -20,7 +21,75 @@ def simple_img_conv_pool(input,
2021
pool_out = layers.pool2d(
2122
input=conv_out,
2223
pool_size=pool_size,
23-
pool_type='max',
24+
pool_type=pool_type,
25+
pool_stride=pool_stride,
26+
program=program,
27+
init_program=init_program)
28+
return pool_out
29+
30+
31+
def img_conv_group(input,
32+
conv_num_filter,
33+
pool_size,
34+
conv_padding=1,
35+
conv_filter_size=3,
36+
conv_act=None,
37+
conv_with_batchnorm=False,
38+
conv_batchnorm_drop_rate=None,
39+
pool_stride=1,
40+
pool_type=None,
41+
program=None,
42+
init_program=None):
43+
"""
44+
Image Convolution Group, Used for vgg net.
45+
"""
46+
tmp = input
47+
assert isinstance(conv_num_filter, list) or \
48+
isinstance(conv_num_filter, tuple)
49+
50+
def __extend_list__(obj):
51+
if not hasattr(obj, '__len__'):
52+
return [obj] * len(conv_num_filter)
53+
else:
54+
return obj
55+
56+
conv_padding = __extend_list__(conv_padding)
57+
conv_filter_size = __extend_list__(conv_filter_size)
58+
conv_with_batchnorm = __extend_list__(conv_with_batchnorm)
59+
conv_batchnorm_drop_rate = __extend_list__(conv_batchnorm_drop_rate)
60+
61+
for i in xrange(len(conv_num_filter)):
62+
local_conv_act = conv_act
63+
if conv_with_batchnorm[i]:
64+
local_conv_act = None
65+
66+
tmp = layers.conv2d(
67+
input=tmp,
68+
num_filters=conv_num_filter[i],
69+
filter_size=conv_filter_size[i],
70+
padding=conv_padding[i],
71+
act=local_conv_act,
72+
program=program,
73+
init_program=init_program)
74+
75+
if conv_with_batchnorm[i]:
76+
tmp = layers.batch_norm(
77+
input=tmp,
78+
act=conv_act,
79+
program=program,
80+
init_program=init_program)
81+
drop_rate = conv_batchnorm_drop_rate[i]
82+
if abs(drop_rate) > 1e-5:
83+
tmp = layers.dropout(
84+
x=tmp,
85+
dropout_prob=drop_rate,
86+
program=program,
87+
init_program=init_program)
88+
89+
pool_out = layers.pool2d(
90+
input=tmp,
91+
pool_size=pool_size,
92+
pool_type=pool_type,
2493
pool_stride=pool_stride,
2594
program=program,
2695
init_program=init_program)

0 commit comments

Comments
 (0)