Caffe2 - C++ API
A deep learning, cross platform ML framework
h_softmax_op.cc
1 
17 #include "caffe2/operators/h_softmax_op.h"
18 
19 #include <queue>
20 #include <stack>
21 
22 namespace caffe2 {
23 
24 template <>
25 float HSoftmaxOp<float, CPUContext>::RunForwardSingle(const float* X,
26  const float* W, const float* b, int target, float* int_output,
27  const float* bias_multiplier, int dim_out, int dim_in,
28  int& int_output_offset) {
29 
30  // W * x
31  float* fc_output_data = int_output + int_output_offset;
32 
33  math::Gemm<float, CPUContext>(CblasNoTrans, CblasTrans, 1, dim_out, dim_in, 1,
34  X, W, 0, fc_output_data, &context_);
35  math::Gemv<float, CPUContext>(CblasNoTrans, dim_out, 1, 1,
36  b, bias_multiplier, 1, fc_output_data, &context_);
37 
38  int_output_offset += dim_out;
39 
40  //Softmax
41  float* softmax_output_data = int_output + int_output_offset;
42 
43  if (scale_.size() != 1) {
44  scale_.Resize(1);
45  }
46  if (sum_multiplier_.size() != dim_out) {
47  sum_multiplier_.Resize(dim_out);
48  math::Set<float, CPUContext>(dim_out, 1.f,
49  sum_multiplier_.mutable_data<float>(), &context_);
50  }
51  math::RowwiseMax<float, CPUContext>(1, dim_out, fc_output_data,
52  scale_.mutable_data<float>(), &context_);
53 
54  // Put the intermediate result X - max(X) into Y
55  context_.template Copy<float, CPUContext, CPUContext>(dim_out, fc_output_data,
56  softmax_output_data);
57  // Subtract the scale
58  math::Gemv<float, CPUContext>(CblasNoTrans, dim_out, 1, -1,
59  sum_multiplier_.data<float>(), scale_.data<float>(), 1, softmax_output_data,
60  &context_);
61 
62  // Exponentiation
63  math::Exp<float, CPUContext>(dim_out, softmax_output_data,
64  softmax_output_data, &context_);
65  math::Gemv<float, CPUContext>(CblasNoTrans, 1, dim_out, 1,
66  softmax_output_data, sum_multiplier_.data<float>(), 0,
67  scale_.mutable_data<float>(), &context_);
68 
69  // Do division
70  const float scale = *scale_.data<float>();
71  for (int j = 0; j < dim_out; ++j) {
72  softmax_output_data[j] /= scale;
73  }
74 
75  int_output_offset += dim_out;
76 
77  if (target < 0) {
78  return -1;
79  }
80  //Return cross entropy loss
81  return -log(std::max(softmax_output_data[target], kLOG_THRESHOLD()));
82 }
83 
84 // Implementation for the CPU context.
85 template <>
86 bool HSoftmaxOp<float, CPUContext>::RunOnDevice() {
87  auto& X = Input(0);
88  const auto& W = Input(1);
89  const auto& b = Input(2);
90  auto& label = Input(3);
91  auto* Y = Output(0);
92  auto* intermediate_output = Output(1);
93 
94  // Batch size
95  int M = X.ndim() > 1 ? X.dim32(0) : 1;
96  // Input feature dimension
97  int K = X.size() / M;
98  CAFFE_ENFORCE_GE(W.ndim(), 2); // N*K
99  CAFFE_ENFORCE_EQ(b.ndim(), 1); // N
100  CAFFE_ENFORCE_EQ(K, W.size() / (W.dim32(0)));
101  // Sum of output dimensions of all hierarchy nodes
102  int N = W.dim32(0);
103  CAFFE_ENFORCE_EQ(N, b.dim32(0));
104  Y->Resize(M);
105  auto* Ydata = Y->mutable_data<float>();
106  math::Set<float, CPUContext>(M, 0.f, Ydata, &context_);
107  const auto* labeldata = label.data<int>();
108 
109  auto hierarchy = getHierarchyForLabels(M, labeldata, hierarchy_all_map_);
110  int int_output_size = getIntermediateOutputSize(labeldata, M, hierarchy);
111  intermediate_output->Resize(int_output_size);
112  float * int_output_data = intermediate_output->mutable_data<float>();
113  int int_output_offset = 0;
114 
115  if (bias_multiplier_.size() != M) {
116  bias_multiplier_.Resize(M);
117  math::Set<float, CPUContext>(M, static_cast<float>(1),
118  bias_multiplier_.mutable_data<float>(), &context_);
119  }
120 
121  for (int sample = 0; sample < M; ++sample) {
122  int word_id = labeldata[sample];
123  const PathProto& path = hierarchy[word_id];
124  for (const PathNodeProto& node : path.path_nodes()) {
125  //Offset of node's weight matrix in W
126  int w_offset = node.index();
127  //Number of output dimensions in node's weight matrix
128  int w_length = node.length();
129  int target = node.target();
130  //Adding log probabilities
131  Ydata[sample] += RunForwardSingle(X.data<float>() + sample*K,
132  W.data<float>() + w_offset*K, b.data<float>() + w_offset, target,
133  int_output_data, bias_multiplier_.data<float>()+sample, w_length, K,
134  int_output_offset);
135  }
136  }
137  return true;
138 }
139 
140 template <>
141 void HSoftmaxGradientOp<float, CPUContext>::RunBackwardSingle(const float* X,
142  const float* dY, const float* W, int target,
143  const float* int_output, float* dX, float* dW, float* db, float* dint_output,
144  int dim_in, int dim_out, int& int_output_offset) {
145 
146  //Cross entropy
147  // dX_entropy is the dX for the cross entropy layer
148  float* dX_entropy = dint_output + int_output_offset - dim_out;
149  // X_entropy is the X for the cross entropy layer and Y for the softmax layer
150  const float* X_entropy = int_output + int_output_offset - dim_out;
151 
152  math::Set<float, CPUContext>(dim_out, 0.f, dX_entropy, &context_);
153  dX_entropy[target] = - (*dY) / std::max(X_entropy[target], kLOG_THRESHOLD());
154 
155  int_output_offset -= dim_out;
156 
157  //Softmax
158  if (scale_.size() != 1) {
159  scale_.Resize(1);
160  }
161  float* scaledata = scale_.mutable_data<float>();
162 
163  if (sum_multiplier_.size() != dim_out) {
164  sum_multiplier_.Resize(dim_out);
165  math::Set<float, CPUContext>(dim_out, 1.f,
166  sum_multiplier_.mutable_data<float>(), &context_);
167  }
168 
169  float* dX_softmax = dint_output + int_output_offset - dim_out;
170  context_.Copy<float, CPUContext, CPUContext>(dim_out, dX_entropy, dX_softmax);
171 
172  math::Dot<float, CPUContext>(dim_out, X_entropy, dX_entropy, scaledata,
173  &context_);
174  math::Gemv<float, CPUContext>(CblasTrans, 1, dim_out, -1,
175  sum_multiplier_.data<float>(), scaledata , 1, dX_softmax, &context_);
176  math::Mul<float, CPUContext>(dim_out, dX_softmax, X_entropy, dX_softmax,
177  &context_);
178 
179  int_output_offset -= dim_out;
180 
181  //FC
182  if (bias_multiplier_.size() != 1) {
183  // If the helper bias multiplier has not been created, reshape and fill
184  // it with 1
185  bias_multiplier_.Resize(1);
186  math::Set<float, CPUContext>(1, static_cast<float>(1),
187  bias_multiplier_.template mutable_data<float>(), &context_);
188  }
189 
190  // Compute dW and add incrementally
191  // dW = dW + dX_softmax'*X
192  math::Gemm<float, CPUContext>(CblasTrans, CblasNoTrans, dim_out, dim_in, 1, 1,
193  dX_softmax, X, 1, dW, &context_);
194 
195  // Compute dB and add incrementally
196  // db = db + dX_softmax*bias_multiplier_
197  math::Gemv<float, CPUContext>(CblasTrans, 1, dim_out, 1, dX_softmax,
198  bias_multiplier_.template data<float>(), 1, db, &context_);
199 
200  // Compute dX and add incrementally
201  // dX = dX + W'dX_softmax
202  math::Gemv<float, CPUContext>(CblasTrans, dim_out, dim_in,
203  1, W, dX_softmax, 1, dX, &context_);
204 }
205 
206 // Implementation for the CPU context.
207 template <>
208 bool HSoftmaxGradientOp<float, CPUContext>::RunOnDevice() {
209  auto& X = Input(0);
210  const auto& W = Input(1);
211  const auto& b = Input(2);
212  auto& label = Input(3);
213  auto& intermediate_output = Input(4);
214  auto& dY = Input(5);
215  auto* dX = Output(0);
216  auto* dW = Output(1);
217  auto* db = Output(2);
218  auto* dX_intermediate_output = Output(3);
219  dX->ResizeLike(X);
220  dW->ResizeLike(W);
221  db->ResizeLike(b);
222  dX_intermediate_output->ResizeLike(intermediate_output);
223 
224  float* dX_data = dX->mutable_data<float>();
225  float* dW_data = dW->mutable_data<float>();
226  float* db_data = db->mutable_data<float>();
227  float* dOutput_data = dX_intermediate_output->mutable_data<float>();
228 
229  math::Set<float, CPUContext>(X.size(), 0.f, dX_data, &context_);
230  math::Set<float, CPUContext>(W.size(), 0.f, dW_data, &context_);
231  math::Set<float, CPUContext>(b.size(), 0.f, db_data, &context_);
232  math::Set<float, CPUContext>(intermediate_output.size(), 0.f, dOutput_data,
233  &context_);
234 
235  // Batch size
236  int M = X.ndim() > 1 ? X.dim32(0) : 1;
237  // Input feature dimension
238  int K = X.size() / M;
239  const auto* labeldata = label.data<int>();
240 
241  auto hierarchy = getHierarchyForLabels(M, labeldata, hierarchy_all_map_);
242  int output_offset = getIntermediateOutputSize(labeldata, M, hierarchy);
243 
244  //Traverse backward to access intermediate_output generated by HSoftmaxOp
245  // sequentially in reverse order
246  for (int sample = M-1; sample >= 0; sample--) {
247  int word_id = labeldata[sample];
248  PathProto path = hierarchy[word_id];
249  for (auto node = path.path_nodes().rbegin();
250  node != path.path_nodes().rend(); node++) {
251  int w_offset = node->index();
252  int w_length = node->length();
253  int target = node->target();
254  RunBackwardSingle(X.data<float>() + sample*K, dY.data<float>() + sample,
255  W.data<float>() + w_offset*K, target, intermediate_output.data<float>(),
256  dX_data + sample*K, dW_data + w_offset*K, db_data + w_offset,
257  dOutput_data, K, w_length, output_offset);
258  }
259  }
260  return true;
261 }
262 
263 // Implementation for the CPU context.
264 template <>
265 bool HSoftmaxSearchOp<float, CPUContext>::pruning(
266  const float* X,
267  int sample,
268  int K,
269  const float* W,
270  const float* b,
271  const NodeProto& src_node,
272  NodeProto& dst_node,
273  float parent_score,
274  float beam) {
275  int w_length = src_node.children_size() + src_node.word_ids_size();
276  Tensor<CPUContext> intermediate_data;
277  intermediate_data.Resize(2 * w_length);
278  float* int_output_data = intermediate_data.template mutable_data<float>();
279  int int_output_offset = 0;
280  int w_offset = src_node.offset();
281 
282  RunForwardSingle(
283  X + K * sample,
284  W + w_offset * K,
285  b + w_offset,
286  -1,
287  int_output_data,
288  bias_multiplier_.template data<float>() + sample,
289  w_length,
290  K,
291  int_output_offset);
292 
293  float* softmax_output_data = int_output_data + w_length;
294  // real probabilities
295  for (int i = 0; i < w_length; i++) {
296  softmax_output_data[i] =
297  -log(std::max(softmax_output_data[i], kLOG_THRESHOLD())) + parent_score;
298  }
299  for (int i = 0; i < src_node.children_size(); i++) {
300  if (softmax_output_data[i] < parent_score + beam) {
301  dst_node.add_children();
302  int idx = dst_node.children_size() - 1;
303  CAFFE_ENFORCE(
304  src_node.children(i).has_offset(),
305  "HSM Search require the field offset in NodeProte");
306  dst_node.mutable_children(idx)->set_offset(src_node.children(i).offset());
307  CAFFE_ENFORCE(
308  src_node.children(i).has_name(),
309  "HSM Search require the field name in NodeProte");
310  dst_node.mutable_children(idx)->set_name(src_node.children(i).name());
311  dst_node.add_scores(softmax_output_data[i]);
312  pruning(
313  X,
314  sample,
315  K,
316  W,
317  b,
318  src_node.children(i),
319  *dst_node.mutable_children(idx),
320  softmax_output_data[i],
321  beam);
322  }
323  }
324 
325  for (int i = src_node.children_size(); i < w_length; i++) {
326  if (softmax_output_data[i] < parent_score + beam) {
327  dst_node.add_word_ids(src_node.word_ids(i - src_node.children_size()));
328  dst_node.add_scores(softmax_output_data[i]);
329  }
330  }
331 
332  return true;
333 }
334 
335 template <>
336 bool HSoftmaxSearchOp<float, CPUContext>::extractNodes(
337  const NodeProto& node,
338  std::vector<std::pair<string, float>>& info) {
339  int i = 0;
340 
341  for (const auto& n : node.children()) {
342  info.emplace_back(std::make_pair(n.name(), node.scores(i++)));
343  }
344  for (const int n : node.word_ids()) {
345  info.emplace_back(std::make_pair(caffe2::to_string(n), node.scores(i++)));
346  }
347 
348  for (const auto& n : node.children()) {
349  extractNodes(n, info);
350  }
351  return true;
352 }
353 
354 // Implementation for the CPU context.
355 template <>
356 bool HSoftmaxSearchOp<float, CPUContext>::RunOnDevice() {
357  auto& X = Input(0);
358  const auto& W = Input(1);
359  const auto& b = Input(2);
360  auto* Y_names = Output(0);
361  auto* Y_scores = Output(1);
362  // Batch size
363  int M = X.ndim() > 1 ? X.dim32(0) : 1;
364  // Input feature dimension
365  int K = X.size() / M;
366  CAFFE_ENFORCE(W.ndim() == 2, "Weight must be a matrix."); // N*K
367  CAFFE_ENFORCE(b.ndim() == 1, "Bias must be a vector."); // N
368  CAFFE_ENFORCE(K == W.size() / (W.dim32(0)), "feature dimension mismatch.");
369  // Sum of output dimensions of all hierarchy nodes
370  int N = W.dim32(0);
371  CAFFE_ENFORCE(N == b.dim32(0), "mismatch between Weight and Bias.");
372  Y_names->Resize(M, top_n_);
373  Y_scores->Resize(M, top_n_);
374 
375  if (bias_multiplier_.size() != M) {
376  bias_multiplier_.Resize(M);
377  math::Set<float, CPUContext>(
378  M,
379  static_cast<float>(1),
380  bias_multiplier_.mutable_data<float>(),
381  &context_);
382  }
383 
384  for (int sample = 0; sample < M; ++sample) {
385  CAFFE_ENFORCE(
386  tree_.root_node().has_offset(),
387  "HSM Search require the field offset in NodeProte");
388  CAFFE_ENFORCE(
389  tree_.root_node().has_name(),
390  "HSM Search require the field name in NodeProte");
391 
392  NodeProto dst_node;
393  dst_node.set_offset(tree_.root_node().offset());
394  dst_node.set_name(tree_.root_node().name());
395 
396  pruning(
397  X.data<float>(),
398  sample,
399  K,
400  W.data<float>(),
401  b.data<float>(),
402  tree_.root_node(),
403  dst_node,
404  0,
405  beam_);
406 
407  std::vector<std::pair<string, float>> info;
408  extractNodes(dst_node, info);
409  // saving the results for each sample.
410  std::partial_sort(
411  info.begin(),
412  info.begin() + (top_n_ < info.size() ? top_n_ : info.size() - 1),
413  info.end(),
414  [&](std::pair<string, float> a, std::pair<string, float> b) {
415  return a.second < b.second;
416  });
417  auto* y_name_data = Y_names->mutable_data<string>() + sample * top_n_;
418  auto* y_score_data = Y_scores->mutable_data<float>() + sample * top_n_;
419  for (int i = 0; i < top_n_; i++) {
420  if (i < info.size()) {
421  y_name_data[i] = info[i].first;
422  y_score_data[i] = info[i].second;
423  } else {
424  y_score_data[i] = 0;
425  }
426  }
427  }
428 
429  return true;
430 }
431 
432 template <typename T, class Context>
433 bool HuffmanTreeHierarchyOp<T, Context>::RunOnDevice() {
434  const auto& Y = Input(0);
435  auto treeOutput = Output(0);
436  CAFFE_ENFORCE_EQ(Y.ndim(), 1, "Input labels must be a vector.");
437  const auto y_data = Y.template data<T>();
438  treeOutput->Resize(1);
439  std::vector<int> labelCounts;
440  labelCounts.resize(num_classes_, 0);
441  for (int i = 0; i < Y.dim32(0); ++i) {
442  // Labels are in range [0, num_classes]
443  const int label_index = y_data[i];
444  CAFFE_ENFORCE_LT(
445  label_index,
446  num_classes_,
447  "Found an input label ",
448  label_index,
449  " not in range [",
450  0,
451  ",",
452  num_classes_,
453  "]");
454  labelCounts[label_index]++;
455  }
456 
457  std::priority_queue<Node, std::vector<Node>, NodeComparator> nodes;
458  std::vector<Node> huffmanTree;
459  std::vector<int> labelIndices;
460  labelIndices.resize(num_classes_);
461 
462  int current_node_index = 0;
463  for (int i = 0; i < num_classes_; ++i) {
464  Node node(i, labelCounts[i]);
465  nodes.push(node);
466  }
467 
468  // Extract node with minimum count and insert it in the tree array.
469  auto get_next_node = [&nodes, &huffmanTree, &labelIndices]() {
470  auto node = nodes.top();
471  int node_index = huffmanTree.size();
472  if (node.label != -1) {
473  labelIndices[node.label] = node_index;
474  }
475  nodes.pop();
476  huffmanTree.push_back(node);
477  return std::pair<int, Node>(node_index, node);
478  };
479 
480  // Merge two nodes and insert the results in the queue.
481  auto merge_nodes = [&nodes](
482  const std::pair<int, Node>& node_l, const std::pair<int, Node>& node_r) {
483  Node node(-1, node_l.second.count + node_r.second.count);
484  node.left_ch_index = node_l.first;
485  node.right_ch_index = node_r.first;
486  nodes.push(node);
487  };
488 
489  // Main loop for buttom up huffman tree construction.
490  while (!nodes.empty()) {
491  auto lNode = get_next_node();
492  if (!nodes.empty()) {
493  auto rNode = get_next_node();
494  merge_nodes(lNode, rNode);
495  }
496  }
497 
498  auto is_leaf_node = [&huffmanTree](const int node_index) {
499  return huffmanTree[node_index].left_ch_index == -1 &&
500  huffmanTree[node_index].right_ch_index == -1;
501  };
502 
503  auto get_node_label = [&huffmanTree](const int node_index) {
504  return huffmanTree[node_index].label;
505  };
506 
507  // Build huffman tree.
508  int current_offset = 0;
509  std::function<void(int, NodeProto*)> build_tree = [&](
510  const int node_index, NodeProto* node) {
511  if (is_leaf_node(node_index) || node_index == -1) {
512  return;
513  }
514  const int left_ch_index = huffmanTree[node_index].left_ch_index;
515  const int right_ch_index = huffmanTree[node_index].right_ch_index;
516  if (left_ch_index != -1) {
517  if (is_leaf_node(left_ch_index)) {
518  node->add_word_ids(get_node_label(left_ch_index));
519  } else {
520  auto* ch_node = node->add_children();
521  ch_node->set_offset(current_offset);
522  current_offset += 2;
523  build_tree(left_ch_index, ch_node);
524  }
525  }
526  if (right_ch_index != -1) {
527  if (is_leaf_node(right_ch_index)) {
528  node->add_word_ids(get_node_label(right_ch_index));
529  current_offset++;
530  } else {
531  auto* ch_node = node->add_children();
532  ch_node->set_offset(current_offset);
533  current_offset += 2;
534  build_tree(right_ch_index, ch_node);
535  }
536  }
537  };
538 
539  // The last element inserted in the tree is the root.
540  const int rootNodeIndex = huffmanTree.size() - 1;
541  NodeProto rootNode;
542  rootNode.set_offset(current_offset);
543  current_offset += 2;
544  build_tree(rootNodeIndex, &rootNode);
545  TreeProto treeProto;
546  *treeProto.mutable_root_node() = rootNode;
547 
548  treeProto.SerializeToString(treeOutput->template mutable_data<string>());
549  return true;
550 }
551 
552 namespace {
553 REGISTER_CPU_OPERATOR(HSoftmax, HSoftmaxOp<float, CPUContext>);
554 REGISTER_CPU_OPERATOR(HSoftmaxGradient,
555  HSoftmaxGradientOp<float, CPUContext>);
556 REGISTER_CPU_OPERATOR(HSoftmaxSearch, HSoftmaxSearchOp<float, CPUContext>);
557 REGISTER_CPU_OPERATOR(
558  HuffmanTreeHierarchy,
559  HuffmanTreeHierarchyOp<int64_t, CPUContext>);
560 
561 OPERATOR_SCHEMA(HSoftmax)
562  .NumInputs(4)
563  .NumOutputs(2)
564  .SetDoc(R"DOC(
565 Hierarchical softmax is an operator which approximates the softmax operator
566 while giving significant training speed gains and reasonably comparable
567 performance. In this operator, instead of calculating the probabilities of all
568 the classes, we calculate the probability of each step in the path from root to
569 the target word in the hierarchy.
570 
571 The operator takes a 2-D tensor (Tensor<float>) containing a batch of layers, a
572 set of parameters represented by the weight matrix and bias terms, and a 1-D
573 tensor (Tensor<int>) holding labels, or the indices of the target class. The
574 hierarchy has to be specified as an argument to the operator.
575 
576 The operator returns a 1-D tensor holding the computed log probability of the
577 target class and a 2-D tensor of intermediate outputs (from the weight matrix
578 and softmax from each step in the path from root to target class) which will be
579 used by the gradient operator to compute gradients for all samples in the batch.
580 )DOC")
581  .Arg("hierarchy", "Serialized HierarchyProto string containing list of "
582  "vocabulary words and their paths from root of hierarchy to the leaf")
583  .Input(0, "X", "Input data from previous layer")
584  .Input(1, "W", "2D blob containing 'stacked' fully connected weight "
585  "matrices. Each node in the hierarchy contributes one FC weight matrix if "
586  "it has children nodes. Dimension is N*D, D is input dimension of data (X), "
587  "N is sum of all output dimensions, or total number of nodes (excl root)")
588  .Input(2, "b", "1D blob with N parameters")
589  .Input(3, "labels", "int word_id of the target word")
590  .Output(0, "Y", "1-D of log probability outputs, one per sample")
591  .Output(1, "intermediate_output", "Extra blob to store the intermediate "
592  "FC and softmax outputs for each node in the hierarchical path of a word. "
593  "The outputs from samples are stored in consecutive blocks in the forward "
594  "pass and are used in reverse order in the backward gradientOp pass");
595 
596 OPERATOR_SCHEMA(HSoftmaxGradient).NumInputs(6).NumOutputs(4);
597 
598 class GetHSoftmaxGradient : public GradientMakerBase {
599  using GradientMakerBase::GradientMakerBase;
600  vector<OperatorDef> GetGradientDefs() override {
601  return SingleGradientDef(
602  "HSoftmaxGradient", "",
603  //X, W, b, label, intermediate output, dY
604  vector<string>{I(0), I(1), I(2), I(3), O(1), GO(0)},
605  //dX, dW, db, dintermediate_output
606  vector<string>{GI(0), GI(1), GI(2), GO(1)});
607  }
608 };
609 REGISTER_GRADIENT(HSoftmax, GetHSoftmaxGradient);
610 
611 OPERATOR_SCHEMA(HSoftmaxSearch)
612  .NumInputs(3)
613  .NumOutputs(2)
614  .SetDoc(R"DOC(
615 HSoftmaxSearch is an operator to generate the most possible paths given a
616 well-trained model and input vector. Greedy algorithm is used for pruning the
617 search tree.
618 )DOC")
619  .Arg(
620  "tree",
621  "Serialized TreeProto string containing a tree "
622  "including all intermidate nodes and leafs. All nodes must have names "
623  "for correct outputs")
624  .Arg(
625  "beam",
626  "beam used for pruning tree. The pruning algorithm is that "
627  "only children, whose score is smaller than parent's score puls beam, "
628  "will be propagated. ")
629  .Arg("topN", "Number of nodes in outputs")
630  .Input(0, "X", "Input data from previous layer")
631  .Input(1, "W", "The matrix trained from Softmax Ops")
632  .Input(2, "b", "The bias traiend from Softmax Ops")
633  .Output(
634  0,
635  "Y_names",
636  "The name of selected nodes and leafs. "
637  "For nodes, it will be the name defined in the tree. "
638  "For leafs, it will be the index of the word in the tree.")
639  .Output(1, "Y_scores", "The corresponding scores of Y_names");
640 SHOULD_NOT_DO_GRADIENT(HSoftmaxSearch);
641 
642 OPERATOR_SCHEMA(HuffmanTreeHierarchy)
643  .NumInputs(1)
644  .NumOutputs(1)
645  .SetDoc(R"DOC(
646 HuffmanTreeHierarchy is an operator to generate huffman tree hierarchy given
647 the input labels. It returns the tree as seralized HierarchyProto
648 )DOC")
649  .Arg("num_classes", "The number of classes used to build the hierarchy.")
650  .Input(0, "Labels", "The labels vector")
651  .Output(0, "Hierarch", "Huffman coding hierarchy of the labels");
652 
653 SHOULD_NOT_DO_GRADIENT(HuffmanTreeHierarchyOp);
654 } // namespace
655 } // namespace caffe2
Copyright (c) 2016-present, Facebook, Inc.
Copyright (c) 2016-present, Facebook, Inc.