Caffe2 - C++ API
A deep learning, cross platform ML framework
softsign_op.cc
1 #include "caffe2/operators/softsign_op.h"
2 
3 #include "caffe2/utils/eigen_utils.h"
4 
5 #include <algorithm>
6 #include <functional>
7 
8 namespace caffe2 {
9 
10 template <>
11 template <typename T>
12 bool SoftsignFunctor<CPUContext>::
13 operator()(const int N, const T* X, T* Y, CPUContext* /* context */) const {
14  ConstEigenVectorArrayMap<T> X_arr(X, N);
15  EigenVectorMap<T>(Y, N) = (T(1) + X_arr.abs()).inverse() * X_arr;
16  return true;
17 }
18 
19 template <>
20 template <typename T>
21 bool SoftsignGradientFunctor<CPUContext>::Forward(
22  const std::vector<int>& X_dims,
23  const std::vector<int>& /* dY_dims */,
24  const T* X,
25  const T* dY,
26  T* dX,
27  CPUContext* /* context */) const {
28  const int size = std::accumulate(
29  X_dims.cbegin(), X_dims.cend(), 1, std::multiplies<int>());
30  ConstEigenVectorArrayMap<T> dY_arr(dY, size);
31  ConstEigenVectorArrayMap<T> X_arr(X, size);
32  EigenVectorMap<T>(dX, size) =
33  dY_arr * (T(1) + X_arr.abs()).square().inverse();
34  return true;
35 }
36 
37 REGISTER_CPU_OPERATOR(
38  Softsign,
39  UnaryElementwiseOp<
40  TensorTypes<float>,
41  CPUContext,
42  SoftsignFunctor<CPUContext>>);
43 REGISTER_CPU_GRADIENT_OPERATOR(
44  SoftsignGradient,
45  BinaryElementwiseOp<
46  TensorTypes<float>,
47  CPUContext,
48  SoftsignGradientFunctor<CPUContext>>);
49 
50 OPERATOR_SCHEMA(Softsign)
51  .NumInputs(1)
52  .NumOutputs(1)
53  .AllowInplace({{0, 0}})
54  .IdenticalTypeAndShape()
55  .SetDoc(R"DOC(
56 *Softsign* takes one input data tensor $X$ and produces one output data $Y,$ where the softsign function, $y = \frac{x}{1+ |x|}$, is applied to $X$ elementwise. This operation can be done in an in-place fashion too, by providing the same input and output blobs.
57 
58 Github Links:
59 
60 - https://github.com/pytorch/pytorch/blob/master/caffe2/operators/softsign_op.cc
61 
62 
63 <details>
64 
65 <summary> <b>Example</b> </summary>
66 
67 **Code**
68 
69 ```
70 
71 workspace.ResetWorkspace()
72 
73 op = core.CreateOperator(
74  "Softsign",
75  ["X"],
76  ["Y"],
77 )
78 
79 workspace.FeedBlob("X", np.random.randn(3, 3).astype(np.float32))
80 print("X:\n", workspace.FetchBlob("X"), "\n")
81 
82 workspace.RunOperatorOnce(op)
83 print("Y:\n", workspace.FetchBlob("Y"))
84 
85 ```
86 
87 **Result**
88 
89 ```
90 
91 X:
92  [[-1.3060539 0.7242748 -1.9907674 ]
93  [-0.64802396 -0.03244735 0.7455406 ]
94  [-0.298492 -0.5774271 2.8364444 ]]
95 
96 Y:
97  [[-0.5663588 0.420046 -0.6656376 ]
98  [-0.39321268 -0.03142761 0.4271116 ]
99  [-0.2298759 -0.36605626 0.739342 ]]
100 
101 ```
102 
103 </details>
104 
105 
106 )DOC")
107  .Input(0, "input", "Input data blob to be operated on.")
108  .Output(0, "output", "Output data blob with same shape as input")
109  .InheritOnnxSchema();
110 
111 GRADIENT_OPERATOR_SCHEMA(SoftsignGradient)
112  .NumInputs(2)
113  .NumOutputs(1)
114  .AllowInplace({{1, 0}})
115  .SetDoc(R"DOC(
116 Calculates the softsign gradient (sgn(x)/(1+|x|)^2) of the given input tensor
117 element-wise.
118 )DOC")
119  .Input(0, "input", "1-D input tensor")
120  .Input(1, "input", "1-D input tensor")
121  .Output(
122  0,
123  "output",
124  "The softsign gradient (sgn(x)/(1+|x|)^2) values of the input tensor "
125  "computed element-wise");
126 
127 namespace {
128 
129 class GetSoftsignGradient : public GradientMakerBase {
130  using GradientMakerBase::GradientMakerBase;
131  std::vector<OperatorDef> GetGradientDefs() override {
132  CAFFE_ENFORCE(
133  I(0) != O(0),
134  "Cannot compute softsign gradient "
135  "if you choose to do an in-place calculation.");
136 
137  return SingleGradientDef(
138  "SoftsignGradient",
139  "",
140  std::vector<std::string>{I(0), GO(0)},
141  std::vector<std::string>{GI(0)});
142  }
143 };
144 
145 } // namespace
146 
147 REGISTER_GRADIENT(Softsign, GetSoftsignGradient);
148 
149 } // namespace caffe2
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
Definition: blob.h:13