1 #include "caffe2/operators/sequence_ops.h" 2 #include "caffe2/core/operator.h" 3 #include "caffe2/core/tensor.h" 9 void GatherPaddingOp<CPUContext>::GatherPadding(
11 const int lengths_size,
15 const int* lengths_ptr,
19 (!std::is_same<bool, T>::value),
20 "GatherPadding should not be executed on an input of type bool, as " 21 "addition is not properly defined with booleans.");
22 int64_t total_length = 0;
23 for (
int i = 0; i < lengths_size; ++i) {
25 const auto length = lengths_ptr[i];
26 total_length += length;
27 CAFFE_ENFORCE_LE(total_length, outer_size);
29 for (
int j = 0; j < startPaddingWidth_; ++j) {
30 for (
int k = 0; k < block_size; ++k) {
33 #pragma warning(suppress: 4804) 34 padding_start_ptr[k] += in_ptr[k];
38 in_ptr += block_size * (length - pad_width);
40 for (
int j = 0; j < endPaddingWidth_; ++j) {
41 for (
int k = 0; k < block_size; ++k) {
42 #pragma warning(suppress: 4804) 43 padding_end_ptr[k] += in_ptr[k];
52 bool RemovePaddingOp<CPUContext>::DoRunWithType() {
53 const auto& in = Input(0);
54 CAFFE_ENFORCE_GE(in.ndim(), 1);
55 const int32_t outer_size = in.dims()[0];
56 const auto block_size = std::accumulate(
57 in.dims().begin() + 1, in.dims().end(), 1, std::multiplies<TIndex>());
58 const auto pad_width = startPaddingWidth_ + endPaddingWidth_;
61 const int32_t* lengths_ptr = &outer_size;
62 int64_t lengths_size = 1;
63 if (InputSize() > 1) {
64 const auto& lengths = Input(1);
65 lengths_ptr = lengths.data<int32_t>();
66 lengths_size = lengths.size();
69 auto* out = Output(0);
71 auto out_dims = in.dims();
72 out_dims[0] -= pad_width * lengths_size;
73 out->Resize(std::move(out_dims));
75 const auto* in_ptr = in.template data<T>();
76 auto* out_ptr = out->template mutable_data<T>();
77 int64_t total_length = 0;
78 for (
int i = 0; i < lengths_size; ++i) {
80 const auto length = lengths_ptr[i];
81 total_length += length;
82 CAFFE_ENFORCE_LE(total_length, outer_size);
84 in_ptr + block_size * startPaddingWidth_,
85 in_ptr + block_size * (length - endPaddingWidth_),
87 in_ptr += block_size * length;
88 out_ptr += block_size * (length - pad_width);
90 if (OutputSize() == 1) {
93 auto* lengths_out = Output(1);
94 lengths_out->Resize(lengths_size);
97 lengths_ptr + lengths_size,
98 lengths_out->mutable_data<int32_t>(),
99 [pad_width](int32_t x) { return x - pad_width; });
104 template <
typename T>
105 bool AddPaddingOp<CPUContext>::MakePadding(
108 const int32_t* lengths_ptr,
109 int32_t lengths_size,
111 const T* padding_start_ptr,
112 const T* padding_end_ptr,
113 int64_t block_size) {
115 lengths_ptr = &outer_size;
118 int64_t total_length = 0;
119 for (
int i = 0; i < lengths_size; ++i) {
121 const auto length = lengths_ptr[i];
122 total_length += length;
123 CAFFE_ENFORCE_LE(total_length, outer_size);
125 if (!padding_start_ptr) {
126 memset(out_ptr, 0, block_size * startPaddingWidth_ *
sizeof(T));
127 out_ptr += block_size * startPaddingWidth_;
129 for (
int j = 0; j < startPaddingWidth_; ++j) {
130 std::copy(padding_start_ptr, padding_start_ptr + block_size, out_ptr);
131 out_ptr += block_size;
135 const auto num_elems = block_size * length;
136 std::copy(in_ptr, in_ptr + num_elems, out_ptr);
138 out_ptr += num_elems;
140 if (!padding_end_ptr) {
141 memset(out_ptr, 0, block_size * endPaddingWidth_ *
sizeof(T));
142 out_ptr += block_size * endPaddingWidth_;
144 for (
int j = 0; j < endPaddingWidth_; ++j) {
145 std::copy(padding_end_ptr, padding_end_ptr + block_size, out_ptr);
146 out_ptr += block_size;
150 if (OutputSize() == 1) {
153 auto* lengths_out = Output(1);
154 lengths_out->Resize(lengths_size);
155 const auto pad_width = startPaddingWidth_ + endPaddingWidth_;
158 lengths_ptr + lengths_size,
159 lengths_out->mutable_data<int32_t>(),
160 [pad_width](int32_t x) { return x + pad_width; });
165 bool PadEmptySamplesOp<CPUContext>::RunOnDevice() {
166 auto& lengths = Input(0);
167 auto* lengthsPtr = lengths.template data<int32_t>();
168 CAFFE_ENFORCE(lengths.ndim() == 1,
"LENGTH should be 1-D");
169 CAFFE_ENFORCE(InputSize() >= 1,
"Input size must be no less than 1");
171 auto* out_lengths = Output(0);
174 for (
int i = 0; i < lengths.size(); ++i) {
175 if (lengthsPtr[i] == 0) {
178 sumLen += lengthsPtr[i];
181 out_lengths->Resize(lengths.size());
182 auto* outLengthsPtr = out_lengths->template mutable_data<int32_t>();
183 for (
int i = 0; i < lengths.size(); ++i) {
184 if (lengthsPtr[i] == 0) {
185 outLengthsPtr[i] = 1;
187 outLengthsPtr[i] = lengthsPtr[i];
191 for (
int k = 0; k < InputSize() - 1; k++) {
192 auto& features = Input(1 + k);
193 CAFFE_ENFORCE(features.ndim() >= 1,
"FEATURE should at least 1-D");
195 features.dim(0) == sumLen,
"FEATURE and LENGTH should be consistent");
196 const auto block_size = features.size_from_dim(1);
198 auto* out_features = Output(1 + k);
199 auto outDim = features.dims();
200 outDim.at(0) += needPadding;
201 out_features->Resize(outDim);
203 static_cast<char*
>(out_features->raw_mutable_data(features.meta()));
204 auto src_base =
static_cast<const char*
>(features.raw_data());
207 zero.Resize(block_size);
209 static_cast<const char*
>(zero.raw_mutable_data(features.meta()));
212 for (
int i = 0; i < lengths.size(); ++i) {
213 if (lengthsPtr[i] == 0) {
214 context_.template CopyItems<CPUContext, CPUContext>(
218 dst + start_dest * features.meta().itemsize());
219 start_dest += block_size;
221 auto src = src_base + start_src * features.meta().itemsize();
222 context_.template CopyItems<CPUContext, CPUContext>(
224 lengthsPtr[i] * block_size,
226 dst + start_dest * features.meta().itemsize());
227 start_src += lengthsPtr[i] * block_size;
228 start_dest += lengthsPtr[i] * block_size;
235 REGISTER_CPU_OPERATOR(AddPadding, AddPaddingOp<CPUContext>);
236 REGISTER_CPU_OPERATOR(RemovePadding, RemovePaddingOp<CPUContext>);
237 REGISTER_CPU_OPERATOR(GatherPadding, GatherPaddingOp<CPUContext>);
238 REGISTER_CPU_OPERATOR(PadEmptySamples, PadEmptySamplesOp<CPUContext>);
241 using GradientMakerBase::GradientMakerBase;
242 vector<OperatorDef> GetGradientDefs()
override {
244 vector<std::string> g_inputs{GO(0)};
245 if (Def().input_size() > 1) {
246 CAFFE_ENFORCE(Def().output_size() > 1);
247 g_inputs.push_back(O(1));
250 vector<OperatorDef> ops;
252 ops.push_back(CreateOperatorDef(
253 "RemovePadding",
"", g_inputs, vector<string>{GI(0)}));
255 if (Def().input_size() >= 3) {
256 std::vector<string> padding_grads{GI(2)};
257 if (Def().input_size() == 4) {
258 padding_grads.push_back(GI(3));
260 auto g_inputs2 = g_inputs;
262 CreateOperatorDef(
"GatherPadding",
"", g_inputs2, padding_grads));
270 using GradientMakerBase::GradientMakerBase;
271 vector<OperatorDef> GetGradientDefs()
override {
273 vector<std::string> g_inputs{GO(0)};
274 if (Def().input_size() > 1) {
275 CAFFE_ENFORCE(Def().output_size() > 1);
276 g_inputs.push_back(O(1));
284 OPERATOR_SCHEMA(AddPadding)
288 Given a partitioned tensor T<N, D1..., Dn>, where the partitions are 289 defined as ranges on its outer-most (slowest varying) dimension N, 290 with given range lengths, return a tensor T<N + 2*padding_width, D1 ..., Dn> 291 with paddings added to the start and end of each range. 292 Optionally, different paddings can be provided for beginning and end. Paddings 293 provided must be a tensor T<D1..., Dn>. 295 If no padding is provided, add zero padding. 296 If no lengths vector is provided, add padding only once, 297 at the start and end of data. 301 "Number of copies of padding to add around each range.")
304 "(Optional) Specifies a different end-padding width.")
305 .Input(0,
"data_in",
"(T<N, D1..., Dn>) Input data")
309 "(i64) Num of elements in each range. sum(lengths) = N.")
310 .Input(2,
"start_padding",
"T<D1..., Dn> Padding data for range start.")
314 "T<D1..., Dn> (optional) Padding for range end. " 315 "If not provided, start_padding is used as end_padding as well.")
316 .Output(0,
"data_out",
"(T<N + 2*padding_width, D1..., Dn>) Padded data.")
317 .Output(1,
"lengths_out",
"(i64, optional) Lengths for each padded range.");
319 OPERATOR_SCHEMA(RemovePadding)
323 Remove padding around the edges of each segment of the input data. This is 324 the reverse opration of AddPadding, and uses the same arguments and conventions 325 for input and output data format. 327 .Arg("padding_width",
"Outer-size of padding to remove around each range.")
330 "(Optional) Specifies a different end-padding width.")
331 .Input(0,
"data_in",
"T<N, D1..., Dn> Input data")
335 "(i64) Num of elements in each range. sum(lengths) = N. " 336 "If not provided, considers all data as a single segment.")
337 .Output(0,
"data_out",
"(T<N - 2*padding_width, D1..., Dn>) Unpadded data.")
341 "(i64, optional) Lengths for each unpadded range.");
343 OPERATOR_SCHEMA(GatherPadding)
347 Gather the sum of start and end paddings in a padded input sequence. Used in 348 order to compute the gradients of AddPadding w.r.t the padding tensors. 350 .Arg("padding_width",
"Outer-size of padding present around each range.")
353 "(Optional) Specifies a different end-padding width.")
354 .Input(0,
"data_in",
"T<N, D1..., Dn> Padded input data")
358 "(i64) Num of elements in each range. sum(lengths) = N. " 359 "If not provided, considers all data as a single segment.")
363 "Sum of all start paddings, or of all " 364 "paddings if end_padding_sum is not provided.")
368 "T<D1..., Dn> Sum of all end paddings, if provided.");
370 OPERATOR_SCHEMA(PadEmptySamples)
371 .NumInputs(1, INT_MAX)
372 .NumOutputs(1, INT_MAX)
374 Pad empty field given lengths and index features, 376 Input(0) is a blob pointing to the lengths of samples in one batch, 377 [Input(1),... Input(num_fields)] a list of tensors containing the data for 378 each field of the features. 380 PadEmptySamples is thread safe. 382 .Input(0, "lengths",
"A blob containing a pointer to the lengths.")
386 "Tensor containing lengths with empty sample padded.");
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
static vector< OperatorDef > SingleGradientDef(const Args &...args)
a helper function to allow one to create one single operator def, which is usually the case for many ...