Caffe2 - C++ API
A deep learning, cross platform ML framework
pack_segments.cc
1 #include "caffe2/operators/pack_segments.h"
2 
3 namespace caffe2 {
4 
5 template <>
6 template <typename T>
7 bool PackSegmentsOp<CPUContext>::DoRunWithType() {
8  return DispatchHelper<
9  TensorTypes2<char, int32_t, int64_t, float, std::string>,
10  T>::call(this, Input(DATA));
11 }
12 
13 template <>
14 template <typename T, typename Data_T>
15 bool PackSegmentsOp<CPUContext>::DoRunWithType2() {
16  const auto& data = Input(DATA);
17  const auto& lengths = Input(LENGTHS);
18 
19  Tensor* presence_mask = nullptr;
20  if (return_presence_mask_) {
21  presence_mask = Output(1);
22  }
23 
24  CAFFE_ENFORCE_GE(data.dim(), 1, "DATA should be at least 1-D");
25  CAFFE_ENFORCE_EQ(lengths.dim(), 1, "LENGTH should be 1-D");
26 
27  // Find the length of the longest sequence.
28  const T* l = lengths.template data<T>();
29  T max_length = 0;
30  int64_t total_length = 0;
31  for (T i = 0; i < lengths.size(0); ++i) {
32  max_length = std::max(max_length, l[i]);
33  total_length += l[i];
34  }
35  if (max_length_ != -1) {
36  // Final dim must be greater than the max_length
37  CAFFE_ENFORCE_GE(
38  max_length_,
39  max_length,
40  "Pre-defined max_length should be greater than the real max_length");
41  max_length = max_length_;
42  }
43 
44  // Total lengths must be the same as data.dims(0)
45  CAFFE_ENFORCE_EQ(
46  data.size(0),
47  total_length,
48  " PackSegments requires that the sum of the lengths ",
49  total_length,
50  " is equal to the first data dimension ",
51  data.size(0));
52 
53  auto shape =
54  data.sizes().vec(); // Shape of output is batch_size x max_len x ...
55  shape[0] = max_length;
56  shape.insert(shape.begin(), lengths.numel());
57  auto* output = Output(0, shape, at::dtype(data.dtype()));
58 
59  // create output tensor
60  auto* out = static_cast<char*>(output->raw_mutable_data(data.dtype()));
61 
62  bool* presence_mask_data = nullptr;
63  if (return_presence_mask_) {
64  // Shape of presence is batch_size x max_len
65  std::vector<int64_t> presence_shape{lengths.numel(), max_length};
66  presence_mask->Resize(presence_shape);
67  presence_mask_data = presence_mask->template mutable_data<bool>();
68  }
69 
70  if (!data.size(0)) {
71  // Return empty output (with the proper shape)
72  return true;
73  }
74 
75  // Do padding
76  if (output->template IsType<float>()) {
77  math::Set<float, CPUContext>(
78  output->numel(),
79  padding_,
80  output->template mutable_data<float>(),
81  &context_);
82  }
83  if (return_presence_mask_) {
84  memset(presence_mask_data, (int)false, presence_mask->numel());
85  }
86 
87  auto block_size = data.size_from_dim(1);
88  auto block_bytesize = data.itemsize() * block_size;
89  const auto* d = static_cast<const char*>(data.raw_data());
90  int64_t start = 0;
91  for (int64_t i = 0; i < lengths.size(0); ++i) {
92  context_.CopyItemsSameDevice(
93  data.dtype(),
94  l[i] * block_size,
95  d + block_bytesize * start,
96  out + block_bytesize * max_length * i);
97  if (return_presence_mask_) {
98  memset(presence_mask_data + max_length * i, (int)true, l[i]);
99  }
100  start += l[i];
101  }
102 
103  return true;
104 }
105 
106 template <>
107 template <typename T>
108 bool UnpackSegmentsOp<CPUContext>::DoRunWithType() {
109  return DispatchHelper<
110  TensorTypes2<char, int32_t, int64_t, float, std::string>,
111  T>::call(this, Input(DATA));
112 }
113 
114 template <>
115 template <typename T, typename Data_T>
116 bool UnpackSegmentsOp<CPUContext>::DoRunWithType2() {
117  const auto& data = Input(DATA);
118  const auto& lengths = Input(LENGTHS);
119  auto* output = Output(0);
120 
121  CAFFE_ENFORCE_GE(data.dim(), 2, "DATA should be at least 2-D");
122  CAFFE_ENFORCE_EQ(lengths.dim(), 1, "LENGTH should be 1-D");
123  if (max_length_ != -1) {
124  CAFFE_ENFORCE_EQ(
125  max_length_,
126  data.size(1),
127  "max_length should be equal to the second dimension of the packed segments");
128  }
129  const T* l = lengths.template data<T>();
130 
131  int64_t total_l = std::accumulate(l, l + lengths.size(0), (int64_t)0);
132 
133  auto shape = data.sizes().vec();
134  CAFFE_ENFORCE_EQ(
135  shape[0], lengths.size(0), "LENGTH should match DATA in dimension 0");
136  shape.erase(shape.begin());
137  shape[0] = total_l;
138  output->Resize(shape);
139  // create output tensor
140  auto* out = static_cast<char*>(output->raw_mutable_data(data.dtype()));
141  if (!(data.size(0) && data.size(1))) {
142  return true;
143  }
144  auto block_size = data.size_from_dim(2);
145  auto block_bytesize = data.itemsize() * block_size;
146  const auto* d = static_cast<const char*>(data.raw_data());
147  int64_t start = 0;
148  for (int64_t i = 0; i < lengths.size(0); ++i) {
149  context_.CopyItemsSameDevice(
150  data.dtype(),
151  l[i] * block_size,
152  d + block_bytesize * data.size(1) * i,
153  out + block_bytesize * start);
154  start += l[i];
155  }
156  return true;
157 }
158 
159 REGISTER_CPU_OPERATOR(PackSegments, PackSegmentsOp<CPUContext>);
160 REGISTER_CPU_OPERATOR(UnpackSegments, UnpackSegmentsOp<CPUContext>);
161 
162 OPERATOR_SCHEMA(PackSegments)
163  .NumInputs(2)
164  .NumOutputs(1, 2)
165  .SetDoc(
166  "Map N dim tensor to N+1 dim based on length blob. Sequences that \
167  are shorter than the longest sequence are padded with zeros.")
168  .Input(
169  0,
170  "lengths",
171  "1-d int/long tensor contains the length in each of the output.")
172  .Input(1, "tensor", "N dim Tensor.")
173  .Output(
174  0,
175  "packed_tensor",
176  "N + 1 dim Tensor"
177  "where dim(1) is the max length"
178  ", dim(0) is the batch size.")
179  .Output(
180  1,
181  "presence_mask",
182  "2 dim boolean tensor"
183  ", false where packed_tensor is padded, true otherwise.")
184  .Arg("max_length", "The pre-defined max_length for the packed segments")
185  .Arg(
186  "pad_minf",
187  "Padding number in the packed segments. Use true to pad \
188  -infinity, otherwise pad zeros")
189  .Arg(
190  "return_presence_mask",
191  "bool whether to return presence mask, false by default");
192 OPERATOR_SCHEMA(UnpackSegments)
193  .NumInputs(2)
194  .NumOutputs(1)
195  .SetDoc("Map N+1 dim tensor to N dim based on length blob")
196  .Input(
197  0,
198  "lengths",
199  "1-d int/long tensor contains the length in each of the input.")
200  .Input(1, "tensor", "N+1 dim Tensor.")
201  .Output(0, "packed_tensor", "N dim Tensor")
202  .Arg("max_length", "The pre-defined max_length for the packed segments");
203 
205  using GradientMakerBase::GradientMakerBase;
206  vector<OperatorDef> GetGradientDefs() override {
207  return SingleGradientDef(
208  "UnpackSegments",
209  "",
210  vector<string>{I(0), GO(0)},
211  vector<string>{GI(1)});
212  }
213 };
214 REGISTER_GRADIENT(PackSegments, GetPackSegmentsGradient);
215 
217  using GradientMakerBase::GradientMakerBase;
218  vector<OperatorDef> GetGradientDefs() override {
219  return SingleGradientDef(
220  "PackSegments", "", vector<string>{I(0), GO(0)}, vector<string>{GI(1)});
221  }
222 };
223 REGISTER_GRADIENT(UnpackSegments, GetUnpackSegmentsGradient);
224 } // namespace caffe2
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
Definition: blob.h:13
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 ...