1 #ifndef CAFFE2_OPERATORS_SPARSE_TO_DENSE_MASK_OP_H_ 2 #define CAFFE2_OPERATORS_SPARSE_TO_DENSE_MASK_OP_H_ 5 #include <unordered_map> 7 #include "caffe2/core/context.h" 8 #include "caffe2/core/operator.h" 9 #include "caffe2/core/tensor.h" 10 #include "caffe2/utils/math.h" 14 template <
class Context>
17 USE_OPERATOR_CONTEXT_FUNCTIONS;
18 template <
class... Args>
21 std::vector<int64_t> mask =
22 this->
template GetRepeatedArgument<int64_t>(
"mask");
23 featuresCount_ = mask.size();
25 CAFFE_ENFORCE(!mask.empty(),
"mask can't be empty");
26 auto biggest = *std::max_element(mask.begin(), mask.end());
27 dense_.assign(std::min(kMaxDenseSize, biggest + 1), -1);
28 for (
int i = 0; i < mask.size(); i++) {
30 CAFFE_ENFORCE_GE(
id, 0,
"Only positive IDs are allowed.");
31 if (
id >= kMaxDenseSize) {
32 CAFFE_ENFORCE(sparse_.count(
id) == 0,
"Duplicated id: ", id);
35 CAFFE_ENFORCE(dense_[
id] == -1,
"Duplicated id: ",
id);
42 const int64_t kMaxDenseSize = 1024 * 128;
44 std::unordered_map<int64_t, int> sparse_;
45 std::vector<int> dense_;
48 inline int getFeatureIdx(int64_t
id)
const {
49 if (
id >= kMaxDenseSize) {
50 const auto& iter = sparse_.find(
id);
51 if (iter == sparse_.end()) {
57 return (
id >= dense_.size()) ? -1 : dense_[
id];
62 template <
class Context>
65 USE_OPERATOR_CONTEXT_FUNCTIONS;
66 template <
class... Args>
69 returnPresenceMask_ = this->
template GetSingleArgument<bool>(
70 "return_presence_mask",
false);
71 maxSkippedSparseIndices_ =
72 this->
template GetSingleArgument<int32_t>(
73 "max_skipped_indices", kMaxSkippedSparseIndices);
76 bool RunOnDevice()
override {
78 this,
Input(INDICES));
81 template <
typename TInd>
82 bool DoRunWithType() {
83 auto& sparse_indices =
Input(INDICES);
84 CAFFE_ENFORCE_EQ(sparse_indices.dim(), 1);
85 auto& sparse_values =
Input(VALUES);
86 CAFFE_ENFORCE_GE(sparse_values.dim(), 1);
87 CAFFE_ENFORCE_EQ(sparse_indices.numel(), sparse_values.size(0));
88 auto& default_value =
Input(DEFAULT);
89 CAFFE_ENFORCE_EQ(default_value.dim() + 1, sparse_values.dim());
90 CAFFE_ENFORCE_EQ(default_value.numel(), sparse_values.size_from_dim(1));
91 CAFFE_ENFORCE(sparse_values.dtype() == default_value.dtype());
93 const TInd* sparse_indices_vec = sparse_indices.template data<TInd>();
94 const char* sparse_values_vec =
95 static_cast<const char*
>(sparse_values.raw_data());
96 const void* default_val = default_value.raw_data();
98 int64_t block_size = default_value.numel();
99 size_t block_nbytes = default_value.nbytes();
101 const int cols = this->featuresCount_;
103 int32_t sparse_indices_length = sparse_indices.dim32(0);
104 const int32_t* lengths_vec =
nullptr;
105 auto* output = Output(OUTPUTVALUE);
106 Tensor* presence_mask =
nullptr;
107 if (returnPresenceMask_) {
108 presence_mask = Output(PRESENCEMASK);
110 vector<int64_t> shape;
111 if (InputSize() == 4) {
112 auto& lengths =
Input(LENGTHS);
113 CAFFE_ENFORCE_EQ(lengths.dim(), 1);
114 lengths_vec = lengths.template data<int32_t>();
115 rows = lengths.dim32(0);
120 lengths_vec = &sparse_indices_length;
122 shape.push_back(rows);
124 shape.push_back(cols);
125 if (returnPresenceMask_) {
126 presence_mask->Resize(shape);
130 default_value.sizes().begin(),
131 default_value.sizes().end());
132 output->Resize(shape);
137 static_cast<char*
>(output->raw_mutable_data(sparse_values.dtype()));
138 for (
int i = 0; i < cols * rows; i++) {
139 context_.CopyItemsSameDevice(
140 default_value.dtype(),
143 output_data + i * block_nbytes);
145 bool* presence_mask_data =
nullptr;
146 if (returnPresenceMask_) {
147 presence_mask_data = presence_mask->template mutable_data<bool>();
148 math::Set<bool, Context>(
149 rows * cols,
false, presence_mask_data, &context_);
153 for (
int r = 0; r < rows; r++) {
154 for (
int c = 0; c < lengths_vec[r]; c++) {
155 const auto sparse_index = sparse_indices_vec[offset + c];
156 if (sparse_index < 0 ||
157 sparse_index >= std::numeric_limits<TInd>::max()) {
159 ++skippedSparseIndices_,
160 maxSkippedSparseIndices_,
161 "Too many sparse indices skipped");
164 int idx = this->getFeatureIdx(sparse_index);
166 context_.CopyItemsSameDevice(
167 sparse_values.dtype(),
169 sparse_values_vec + (offset + c) * block_nbytes,
170 output_data + (r * cols + idx) * block_nbytes);
171 if (returnPresenceMask_) {
172 presence_mask_data[r * cols + idx] =
true;
176 offset += lengths_vec[r];
183 static const uint32_t kMaxSkippedSparseIndices = 5;
185 bool returnPresenceMask_;
186 uint32_t maxSkippedSparseIndices_ = 0;
187 uint32_t skippedSparseIndices_ = 0;
189 INPUT_TAGS(INDICES, VALUES, DEFAULT, LENGTHS);
190 OUTPUT_TAGS(OUTPUTVALUE, PRESENCEMASK);
193 template <
class Context>
196 USE_OPERATOR_CONTEXT_FUNCTIONS;
197 template <
class... Args>
201 bool RunOnDevice()
override {
203 this,
Input(INDICES));
206 template <
typename TInd>
207 bool DoRunWithType() {
208 auto& sparse_indices =
Input(INDICES);
209 CAFFE_ENFORCE_EQ(sparse_indices.dim(), 1);
210 auto& gradient_output =
Input(GOUTPUT);
212 int64_t block_size = gradient_output.size_from_dim(1);
213 size_t block_nbytes = gradient_output.itemsize() * block_size;
215 const int cols = this->featuresCount_;
218 int32_t default_length = sparse_indices.dim32(0);
219 const int32_t* lengths_vec =
nullptr;
220 auto* output = Output(GVALUES);
221 vector<int64_t> shape;
222 if (InputSize() > LENGTHS) {
225 auto& lengths =
Input(LENGTHS);
226 lengths_vec = lengths.template data<int32_t>();
227 rows = lengths.dim32(0);
228 CAFFE_ENFORCE_EQ(lengths.dim(), 1);
229 CAFFE_ENFORCE_GE(gradient_output.dim(), 2);
230 CAFFE_ENFORCE_EQ(gradient_output.size(0), rows);
231 CAFFE_ENFORCE_EQ(gradient_output.size(1), cols);
232 block_nbytes /= gradient_output.size(1);
233 block_size /= gradient_output.size(1);
240 lengths_vec = &default_length;
241 CAFFE_ENFORCE_GE(gradient_output.dim(), 1);
242 CAFFE_ENFORCE_EQ(gradient_output.size(0), cols);
244 shape.push_back(default_length);
248 gradient_output.sizes().begin() + iter_offset,
249 gradient_output.sizes().end());
250 output->Resize(shape);
252 const TInd* sparse_indices_vec = sparse_indices.template data<TInd>();
253 const char* gradient_output_vec =
254 static_cast<const char*
>(gradient_output.raw_data());
257 static_cast<char*
>(output->raw_mutable_data(gradient_output.dtype()));
258 memset(output_data, 0, output->nbytes());
259 math::Set<char, Context>(
260 default_length * gradient_output.itemsize(), 0, output_data, &context_);
265 vector<bool> gradient_used(cols,
false);
266 for (
int r = 0; r < rows; r++) {
267 std::fill(gradient_used.begin(), gradient_used.end(),
false);
268 for (
int c = lengths_vec[r] - 1; c >= 0; c--) {
269 int idx = this->getFeatureIdx(sparse_indices_vec[offset + c]);
270 if (idx != -1 && !gradient_used[idx]) {
271 gradient_used[idx] =
true;
272 context_.CopyItemsSameDevice(
273 gradient_output.dtype(),
275 gradient_output_vec + (r * cols + idx) * block_nbytes,
276 output_data + (offset + c) * block_nbytes);
279 offset += lengths_vec[r];
285 INPUT_TAGS(INDICES, GOUTPUT, LENGTHS);
286 OUTPUT_TAGS(GVALUES);
291 #endif // CAFFE2_OPERATORS_SPARSE_TO_DENSE_MASK_OP_H_
const Tensor & Input(int idx, DeviceType type=Context::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 ...