Caffe2 - C++ API
A deep learning, cross platform ML framework
one_hot_ops.cc
1 
17 #include "caffe2/operators/one_hot_ops.h"
18 
19 #include "caffe2/core/operator.h"
20 #include "caffe2/core/tensor.h"
21 
22 namespace caffe2 {
23 
24 template <>
25 template <typename T>
26 bool BatchOneHotOp<CPUContext>::DoRunWithType() {
27  auto& input = Input(X);
28  auto& lens = Input(LENS);
29  auto& vals = Input(VALS);
30  CAFFE_ENFORCE_GE(input.ndim(), 1);
31  auto N = input.dim(0);
32  auto D = input.size_from_dim(1);
33  CAFFE_ENFORCE_EQ(lens.size(), D);
34 
35  const auto* lens_data = lens.template data<int32_t>();
36  TIndex output_dim = 0;
37  for (TIndex i = 0; i < D; i++) {
38  CAFFE_ENFORCE_GE(lens_data[i], 0);
39  output_dim += lens_data[i];
40  }
41  CAFFE_ENFORCE_EQ(vals.size(), output_dim);
42  auto* output = Output(ONE_HOT);
43  output->Resize(N, output_dim);
44 
45  const auto* input_data = input.template data<T>();
46  const auto* vals_data = vals.template data<T>();
47  auto* output_data = output->template mutable_data<T>();
48  // eigen is column-major
49  auto input_m = ConstEigenMatrixMap<T>(input_data, D, N);
50  auto output_m = EigenMatrixMap<T>(output_data, output_dim, N);
51 
52  // `p` is the column position in output_data, that points to the next
53  // column to be filled.
54  TIndex p = 0;
55  // one-hot encoding for each example.
56  for (TIndex j = 0; j < D; j++) {
57  for (TIndex t = 0; t < lens_data[j]; t++) {
58  output_m.row(p) =
59  input_m.row(j).cwiseEqual(vals_data[p]).template cast<T>();
60  p++;
61  }
62  }
63  return true;
64 }
65 
66 template <>
67 void OneHotOp<CPUContext>::DoOneHotOp(
68  TIndex batch_size,
69  TIndex index_size,
70  const Tensor<CPUContext>& indices,
71  Tensor<CPUContext>* one_hots) {
72  const TIndex* indices_ptr = indices.template data<TIndex>();
73  float* one_hots_ptr = one_hots->template mutable_data<float>();
74  memset(one_hots_ptr, 0, one_hots->nbytes());
75  for (int i = 0; i < batch_size; ++i) {
76  auto label_idx = indices_ptr[i];
77  DCHECK((0 <= label_idx) && (label_idx < index_size));
78  one_hots_ptr[label_idx] = 1.0;
79  one_hots_ptr += index_size;
80  }
81 }
82 
83 template <>
84 bool BatchBucketOneHotOp<CPUContext>::RunOnDevice() {
85  auto& input = Input(X);
86  auto& lens = Input(LENS);
87  auto& boundaries = Input(BOUNDARIES);
88  CAFFE_ENFORCE_GE(input.ndim(), 1);
89  auto N = input.dim(0);
90  auto D = input.size_from_dim(1);
91  CAFFE_ENFORCE_EQ(lens.size(), D);
92 
93  const auto* lens_data = lens.template data<int32_t>();
94 
95  CAFFE_ENFORCE_EQ(
96  std::accumulate(lens_data, lens_data + lens.size(), 0),
97  boundaries.size(),
98  "The sum of length should be equal to the length of boundaries");
99 
100  TIndex output_dim = 0;
101  for (TIndex i = 0; i < D; i++) {
102  CAFFE_ENFORCE_GT(lens_data[i], 0);
103  // Number of buckets is number of bucket edges + 1
104  output_dim += (lens_data[i] + 1);
105  }
106  auto* output = Output(ONE_HOT);
107  output->Resize(N, output_dim);
108 
109  const auto* input_data = input.template data<float>();
110  const auto* boundaries_data = boundaries.template data<float>();
111  auto* output_data = output->template mutable_data<float>();
112 
113  math::Set<float, CPUContext>(output->size(), 0.f, output_data, &context_);
114 
115  TIndex pos = 0;
116  for (TIndex i = 0; i < N; i++) {
117  auto* boundaries_offset = boundaries_data;
118  TIndex output_offset = 0;
119 
120  for (TIndex j = 0; j < D; j++) {
121  // here we assume the boundary values for each feature are sorted
122  TIndex bucket_idx = std::lower_bound(
123  boundaries_offset,
124  boundaries_offset + lens_data[j],
125  input_data[pos]) -
126  boundaries_offset;
127  output_data[i * output_dim + output_offset + bucket_idx] = 1.0;
128  boundaries_offset += lens_data[j];
129  output_offset += (lens_data[j] + 1);
130  pos++;
131  }
132  }
133 
134  return true;
135 };
136 
137 class SegmentOneHotOp : public Operator<CPUContext> {
138  public:
139  SegmentOneHotOp(const OperatorDef& operator_def, Workspace* ws)
140  : Operator(operator_def, ws) {}
141 
142  bool RunOnDevice() override {
143  auto& lengths = Input(0);
144  auto& indices = Input(1);
145  auto& index_size_tensor = Input(2);
146  CAFFE_ENFORCE(lengths.ndim() == 1);
147  CAFFE_ENFORCE(indices.ndim() == 1);
148  CAFFE_ENFORCE(index_size_tensor.size() == 1);
149  auto batch_size = lengths.size();
150  auto index_size = *index_size_tensor.data<int64_t>();
151  CAFFE_ENFORCE(index_size > 0);
152 
153  auto* lengths_ptr = lengths.data<int32_t>();
154  auto* indices_ptr = indices.data<int64_t>();
155  auto* one_hots = Output(0);
156  one_hots->Resize(batch_size, index_size);
157  auto* one_hots_ptr = one_hots->mutable_data<float>();
158  if (one_hots->size() == 0) {
159  return true;
160  }
161  memset(one_hots_ptr, 0, one_hots->nbytes());
162  int el_idx = 0;
163  for (int i = 0; i < batch_size; ++i) {
164  for (int j = 0; j < lengths_ptr[i]; ++j) {
165  DCHECK(el_idx < indices.size());
166  auto label_idx = indices_ptr[el_idx++];
167  DCHECK((0 <= label_idx) && (label_idx < index_size));
168  one_hots_ptr[label_idx] = 1.0;
169  }
170  one_hots_ptr += index_size;
171  }
172  return true;
173  }
174 };
175 REGISTER_CPU_OPERATOR(BatchBucketOneHot, BatchBucketOneHotOp<CPUContext>);
176 REGISTER_CPU_OPERATOR(BatchOneHot, BatchOneHotOp<CPUContext>);
177 REGISTER_CPU_OPERATOR(OneHot, OneHotOp<CPUContext>);
178 REGISTER_CPU_OPERATOR(SegmentOneHot, SegmentOneHotOp);
179 
180 OPERATOR_SCHEMA(BatchBucketOneHot)
181  .NumInputs(3)
182  .NumOutputs(1)
183  .SetDoc(R"DOC(
184 Input is a matrix tensor. Its first dimension is the batch
185 size. For each column, bucketize it based on the boundary values and then do
186 one hot encoding. The `lengths` specifies the number of boundary values for each
187 column. The final number of buckets is this number plus 1. This would also be
188 the expanded feature size. `boundaries` specifies all the boundary values.
189 Note that each bucket is right-inclusive. That is, given boundary values
190 [b1, b2, b3], the buckets are defined as (-int, b1], (b1, b2], (b2, b3], (b3, inf).
191 For example
192 
193  If data = [[2, 3], [4, 1], [2, 5]], lengths = [2, 3],
194  and boundaries = [0.1, 2.5, 1, 3.1, 4.5], then
195 
196  output = [[0, 1, 0, 0, 1, 0, 0], [0, 0, 1, 1, 0, 0, 0], [0, 1, 0, 0, 0, 0, 1]]
197 
198 )DOC")
199  .Input(0, "data", "input tensor matrix")
200  .Input(1, "lengths", "the size is the same as the width of the `data`")
201  .Input(2, "boundaries", "bucket boundaries")
202  .Output(
203  0,
204  "output",
205  "output matrix that expands each input column with one hot encoding"
206  "based on the bucketization");
207 
208 OPERATOR_SCHEMA(BatchOneHot)
209  .NumInputs(3)
210  .NumOutputs(1)
211  .SetDoc(R"DOC(
212 Input is a matrix tensor. Its first dimension is the batch
213 size. Expand each column of it using one hot encoding. The `lengths` specifies
214 the size of each column after encoding, and the `values` is the dictionary value
215 of one-hot encoding for each column. For example
216 
217  If data = [[2, 3], [4, 1], [2, 5]], lengths = [2, 3],
218  and values = [2, 4, 1, 3, 5], then
219 
220  output = [[1, 0, 0, 1, 0], [0, 1, 1, 0, 0], [1, 0, 0, 0, 1]]
221 )DOC")
222  .Input(0, "data", "input tensor matrix")
223  .Input(1, "lengths", "the size is the same as the width of the `data`")
224  .Input(2, "values", "one hot encoding dictionary values")
225  .Output(
226  0,
227  "output",
228  "output matrix that expands each input column with one hot encoding");
229 
230 OPERATOR_SCHEMA(OneHot)
231  .NumInputs(2)
232  .NumOutputs(1)
233  .SetDoc(R"DOC(
234 Given a sequence of indices, one for each example in a batch, returns a matrix
235 where each inner dimension has the size of the index and has 1.0 in the index
236 active in the given example, and 0.0 everywhere else.
237 )DOC")
238  .Input(0, "indices", "The active index for each example in the batch.")
239  .Input(
240  1,
241  "index_size_tensor",
242  "Scalar with the size of the index. Must be in CPU context")
243  .Output(0, "one_hots", "Matrix of size len(indices) x index_size");
244 
245 OPERATOR_SCHEMA(SegmentOneHot)
246  .NumInputs(3)
247  .NumOutputs(1)
248  .SetDoc(R"DOC(
249 Given a sequence of indices, segmented by the lengths tensor, returns a matrix
250 that has the elements in each sequence set to 1.0, and 0.0 everywhere else.
251 )DOC")
252  .Input(0, "lengths", "Size of each segment.")
253  .Input(1, "indices", "Active indices, of size sum(lengths)")
254  .Input(2, "index_size_tensor", "Size of the index")
255  .Output(0, "one_hots", "Matrix of size len(lengths) x index_size");
256 
257 NO_GRADIENT(BatchOneHot);
258 NO_GRADIENT(OneHot);
259 NO_GRADIENT(SegmentOneHot);
260 NO_GRADIENT(BucketBatchOneHot);
261 } // namespace caffe2
Workspace is a class that holds all the related objects created during runtime: (1) all blobs...
Definition: workspace.h:63
Copyright (c) 2016-present, Facebook, Inc.
Copyright (c) 2016-present, Facebook, Inc.