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