1 #include "caffe2/operators/one_hot_ops.h" 3 #include "caffe2/core/operator.h" 4 #include "caffe2/core/tensor.h" 10 bool BatchOneHotOp<CPUContext>::DoRunWithType() {
11 auto& input = Input(X);
12 auto& lens = Input(LENS);
13 auto& vals = Input(VALS);
14 CAFFE_ENFORCE_GE(input.dim(), 1);
15 auto N = input.size(0);
16 auto D = input.size_from_dim(1);
17 CAFFE_ENFORCE_EQ(lens.numel(),
D);
19 const auto* lens_data = lens.template data<int32_t>();
20 int64_t output_dim = 0;
21 valsOffsets_.resize(
D + 1);
22 for (int64_t i = 0; i <
D; i++) {
23 CAFFE_ENFORCE_GE(lens_data[i], 0);
24 valsOffsets_[i] = output_dim;
25 output_dim += lens_data[i];
27 valsOffsets_[D] = output_dim;
29 CAFFE_ENFORCE_EQ(vals.numel(), output_dim);
31 auto* output = Output(ONE_HOT, {N, output_dim}, at::dtype<T>());
33 const auto* input_data = input.template data<T>();
34 const auto* vals_data = vals.template data<T>();
35 auto* output_data = output->template mutable_data<T>();
37 for (int64_t i = 0; i < N; ++i) {
38 for (int64_t j = 0; j < D; j++) {
39 const auto input_val = input_data[i * D + j];
40 for (int64_t k = valsOffsets_[j]; k < valsOffsets_[j + 1]; ++k) {
41 output_data[k] = vals_data[k] == input_val;
44 output_data += output_dim;
50 vector<TensorShape> TensorInferenceForBatchOneHot(
52 const vector<TensorShape>& in) {
53 std::vector<int64_t> output_dims(2);
54 output_dims[0] = in[0].dims(0);
55 output_dims[1] = in[2].dims(0);
56 return vector<TensorShape>{
57 CreateTensorShape(vector<int64_t>{output_dims}, in[0].data_type())};
60 vector<TensorShape> TensorInferenceForBucketBatchOneHot(
62 const vector<TensorShape>& in) {
63 std::vector<int64_t> output_dims(2);
64 output_dims[0] = in[0].dims(0);
65 output_dims[1] = in[1].dims(0) + in[2].dims(0);
66 return vector<TensorShape>{
67 CreateTensorShape(vector<int64_t>{output_dims}, in[0].data_type())};
70 OpSchema::Cost CostInferenceForBatchOneHot(
71 const OperatorDef& def,
72 const vector<TensorShape>& in) {
73 CAFFE_ENFORCE_EQ(in.size(), 3,
"BatchOneHot requires three inputs");
74 struct OpSchema::Cost c;
75 const TensorShape output = TensorInferenceForBatchOneHot(def, in)[0];
77 const auto& data = in[0];
78 const auto& length = in[1];
79 const auto& values = in[2];
81 uint64_t nBytesData = nElemFromDim(data) *
sizeof(data.data_type());
82 uint64_t nBytesLength = nElemFromDim(length) *
sizeof(length.data_type());
83 uint64_t nBytesValues = nElemFromDim(values) *
sizeof(values.data_type());
85 c.bytes_read = nBytesData + nBytesLength + nBytesValues;
86 c.bytes_written = nElemFromDim(output) *
sizeof(output.data_type());
92 void OneHotOp<CPUContext>::DoOneHotOp(
97 const int64_t* indices_ptr = indices.template data<int64_t>();
98 float* one_hots_ptr = one_hots->template mutable_data<float>();
99 memset(one_hots_ptr, 0, one_hots->nbytes());
100 for (
int i = 0; i < batch_size; ++i) {
101 auto label_idx = indices_ptr[i];
102 DCHECK((0 <= label_idx) && (label_idx < index_size));
103 one_hots_ptr[label_idx] = 1.0;
104 one_hots_ptr += index_size;
109 bool BatchBucketOneHotOp<CPUContext>::RunOnDevice() {
110 auto& input = Input(X);
111 auto& lens = Input(LENS);
112 auto& boundaries = Input(BOUNDARIES);
113 CAFFE_ENFORCE_GE(input.dim(), 1);
114 auto N = input.size(0);
115 auto D = input.size_from_dim(1);
116 CAFFE_ENFORCE_EQ(lens.numel(), D);
118 const auto* lens_data = lens.template data<int32_t>();
121 std::accumulate(lens_data, lens_data + lens.numel(), 0),
123 "The sum of length should be equal to the length of boundaries");
125 int64_t output_dim = 0;
126 for (int64_t i = 0; i < D; i++) {
127 CAFFE_ENFORCE_GT(lens_data[i], 0);
129 output_dim += (lens_data[i] + 1);
132 auto* output = Output(ONE_HOT, {N, output_dim}, at::dtype<float>());
134 const auto* input_data = input.template data<float>();
135 const auto* boundaries_data = boundaries.template data<float>();
136 auto* output_data = output->template mutable_data<float>();
138 math::Set<float, CPUContext>(output->numel(), 0.f, output_data, &context_);
141 for (int64_t i = 0; i < N; i++) {
142 auto* boundaries_offset = boundaries_data;
143 int64_t output_offset = 0;
145 for (int64_t j = 0; j < D; j++) {
147 int64_t lower_bucket_idx = std::lower_bound(
149 boundaries_offset + lens_data[j],
153 int64_t upper_bucket_idx = std::upper_bound(
155 boundaries_offset + lens_data[j],
159 int64_t bucket_idx = (lower_bucket_idx + upper_bucket_idx) / 2;
160 output_data[i * output_dim + output_offset + bucket_idx] = 1.0;
161 boundaries_offset += lens_data[j];
162 output_offset += (lens_data[j] + 1);
172 template <
class... Args>
174 :
Operator(std::forward<Args>(args)...) {}
176 bool RunOnDevice()
override {
177 auto& lengths =
Input(0);
178 auto& indices =
Input(1);
179 auto& index_size_tensor =
Input(2);
180 CAFFE_ENFORCE(lengths.dim() == 1);
181 CAFFE_ENFORCE(indices.dim() == 1);
182 CAFFE_ENFORCE(index_size_tensor.numel() == 1);
183 auto batch_size = lengths.numel();
184 auto index_size = *index_size_tensor.data<int64_t>();
185 CAFFE_ENFORCE(index_size > 0);
187 auto* lengths_ptr = lengths.data<int32_t>();
188 auto* indices_ptr = indices.data<int64_t>();
190 auto* one_hots = Output(0, {batch_size, index_size}, at::dtype<float>());
191 auto* one_hots_ptr = one_hots->template mutable_data<float>();
192 if (one_hots->numel() == 0) {
195 memset(one_hots_ptr, 0, one_hots->nbytes());
197 for (
int i = 0; i < batch_size; ++i) {
198 for (
int j = 0; j < lengths_ptr[i]; ++j) {
199 DCHECK(el_idx < indices.numel());
200 auto label_idx = indices_ptr[el_idx++];
201 DCHECK((0 <= label_idx) && (label_idx < index_size));
202 one_hots_ptr[label_idx] = 1.0;
204 one_hots_ptr += index_size;
214 OPERATOR_SCHEMA(BatchBucketOneHot)
217 .DisallowInputFillers()
219 Input is a matrix tensor. Its first dimension is the batch 220 size. For each column, bucketize it based on the boundary values and then do 221 one hot encoding. The `lengths` specifies the number of boundary values for each 222 column. The final number of buckets is this number plus 1. This would also be 223 the expanded feature size. `boundaries` specifies all the boundary values. 224 Note that each bucket is right-inclusive. That is, given boundary values 225 [b1, b2, b3], the buckets are defined as (-int, b1], (b1, b2], (b2, b3], (b3, inf). 228 data = [[2, 3], [4, 1], [2, 5]], lengths = [2, 3], 229 If boundaries = [0.1, 2.5, 1, 3.1, 4.5], then 230 output = [[0, 1, 0, 0, 1, 0, 0], [0, 0, 1, 1, 0, 0, 0], [0, 1, 0, 0, 0, 0, 1]] 232 If boundaries = [0.1, 2.5, 1, 1, 3.1], then 233 output = [[0, 1, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0, 0], [0, 1, 0, 0, 0, 0, 1]] 236 .Input(0, "data",
"input tensor matrix")
237 .Input(1,
"lengths",
"the size is the same as the width of the `data`")
238 .Input(2,
"boundaries",
"bucket boundaries")
242 "output matrix that expands each input column with one hot encoding" 243 "based on the bucketization")
244 .TensorInferenceFunction(TensorInferenceForBucketBatchOneHot);
246 OPERATOR_SCHEMA(BatchOneHot)
249 .ValueKeyLengthInputFillers(
254 Input is a matrix tensor. Its first dimension is the batch 255 size. Expand each column of it using one hot encoding. The `lengths` specifies 256 the size of each column after encoding, and the `values` is the dictionary value 257 of one-hot encoding for each column. For example 259 If data = [[2, 3], [4, 1], [2, 5]], lengths = [2, 3], 260 and values = [2, 4, 1, 3, 5], then 262 output = [[1, 0, 0, 1, 0], [0, 1, 1, 0, 0], [1, 0, 0, 0, 1]] 264 .Input(0, "data",
"input tensor matrix")
265 .Input(1,
"lengths",
"the size is the same as the width of the `data`")
266 .Input(2,
"values",
"one hot encoding dictionary values")
270 "output matrix that expands each input column with one hot encoding")
271 .TensorInferenceFunction(TensorInferenceForBatchOneHot)
272 .CostInferenceFunction(
275 OPERATOR_SCHEMA(OneHot)
278 .DisallowInputFillers()
280 The *OneHot* op accepts two inputs *indices* and *index_size_tensor*, and produces a single output *one_hots*. For each index in *indices* the op creates a one-hot row in *one_hots* of length *index_size_tensor* where all entries are zero except the entry at the index is 1. The size of *one_hots* is *len(indices)* x *index_size_tensor*. 284 - https://github.com/caffe2/caffe2/blob/master/caffe2/operators/one_hot_ops.h 285 - https://github.com/caffe2/caffe2/blob/master/caffe2/operators/one_hot_ops.cc 290 <summary> <b>Example</b> </summary> 296 workspace.ResetWorkspace() 298 op = core.CreateOperator( 300 ["indices", "index_size_tensor"], 304 workspace.FeedBlob("indices", np.array([0,1,2,3,4]).astype(np.long)) 305 print("indices:\n", workspace.FetchBlob("indices")) 307 workspace.FeedBlob("index_size_tensor", np.array([5]).astype(np.long)) 308 print("index_size_tensor:\n", workspace.FetchBlob("index_size_tensor")) 310 workspace.RunOperatorOnce(op) 311 print("one_hots: \n", workspace.FetchBlob("one_hots")) 335 .Input(0, "indices",
"The active index for each example in the batch.")
339 "Scalar with the size of the index. Must be in CPU context")
340 .Output(0,
"one_hots",
"Matrix of size len(indices) x index_size");
342 OPERATOR_SCHEMA(SegmentOneHot)
345 .DisallowInputFillers()
347 Given a sequence of indices, segmented by the lengths tensor, returns a matrix 348 that has the elements in each sequence set to 1.0, and 0.0 everywhere else. 350 .Input(0, "lengths",
"Size of each segment.")
351 .Input(1,
"indices",
"Active indices, of size sum(lengths)")
352 .Input(2,
"index_size_tensor",
"Size of the index")
353 .Output(0,
"one_hots",
"Matrix of size len(lengths) x index_size");
355 NO_GRADIENT(BatchOneHot);
357 NO_GRADIENT(SegmentOneHot);
358 NO_GRADIENT(BucketBatchOneHot);
const Tensor & Input(int idx, DeviceType type=CPUContext::GetDeviceType())
Retrieve a non-owning reference to the input at position 'idx' for this operator. ...
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
std::function< struct Cost(const OperatorDef &, const vector< TensorShape > &)> CostInferenceFunctionType
Registers a function that takes in an OperatorDef and a series of input shapes and returns the total ...