1 #include "spatial_softmax_with_loss_op.h" 2 #include "softmax_shared.h" 7 SpatialSoftmaxWithLoss,
8 SpatialSoftmaxWithLossOp<float, CPUContext>);
10 SpatialSoftmaxWithLossGradient,
11 SpatialSoftmaxWithLossGradientOp<float, CPUContext>);
14 OPERATOR_SCHEMA(SpatialSoftmaxWithLoss)
17 .TensorInferenceFunction([](
const OperatorDef& def,
18 const vector<TensorShape>& in) {
19 ArgumentHelper helper(def);
20 vector<TensorShape> out(2);
24 auto batch_size = logits.dims().Get(0);
25 auto num_classes = logits.dims().Get(1);
27 CAFFE_ENFORCE_EQ(logits.dims_size(), 4);
28 CAFFE_ENFORCE_EQ(labels.dims_size(), 3);
29 out[0].set_data_type(logits.data_type());
30 out[0].add_dims(batch_size);
31 out[0].add_dims(num_classes);
32 out[0].add_dims(in[0].dims(2));
33 out[0].add_dims(in[0].dims(3));
38 Combined Spatial Softmax and Cross-Entropy loss operator. 39 Similar to SoftmaxWithLoss, this operator computes the spatial softmax 40 normalized values for each layer in the batch of the given input, after which 41 cross-entropy loss is computed. This operator is numerically more stable than 42 separate Softmax and CrossEntropy ops. The inputs are a 2-D tensor 43 (Tensor) of size (batch_size x input_feature_dimensions) and tensor of 44 labels (ground truth). 45 Output is tensor with the probability for each label in a pixel for each example 46 (N x D x W x H) and averaged loss (scalar). 47 For spatial softmax, weighting is by x,y position of the input. 49 .Input(0, "logits",
"Unscaled log probabilities")
50 .Input(1,
"labels",
"Ground truth")
54 "Optional blob to be used to weight the samples for the loss. With\ 55 spatial set, weighting is by x,y of the input")
56 .Output(0,
"softmax",
"Tensor with softmax cross entropy loss")
57 .Output(1,
"loss",
"Average loss");
60 OPERATOR_SCHEMA(SpatialSoftmaxWithLossGradient).NumOutputs(1);
62 #define DONT_CARE (-1) 65 bool SpatialSoftmaxWithLossOp<float, CPUContext>::RunOnDevice() {
73 Output(0, X.sizes(), at::dtype<float>());
75 if (!sum_multiplier_.defined()) {
76 sum_multiplier_ = caffe2::empty({D}, at::dtype<float>().device(CPU));
77 math::Set<float, CPUContext>(
78 D, 1.f, sum_multiplier_.mutable_data<
float>(), &context_);
79 }
else if (sum_multiplier_.numel() != D) {
80 sum_multiplier_.Resize(D);
81 math::Set<float, CPUContext>(
82 D, 1.f, sum_multiplier_.mutable_data<
float>(), &context_);
85 float* Pdata = P->template mutable_data<float>();
86 const float* weights = (InputSize() > 2 ? Input(2).data<
float>() :
nullptr);
87 CAFFE_ENFORCE_EQ(X.dim(), 4);
88 CAFFE_ENFORCE_EQ(
T.dim(), 3);
89 CAFFE_ENFORCE_EQ(
T.dim32(0), N);
94 const float* Xdata = X.data<
float>();
96 for (
int i = 0; i < N; ++i) {
97 for (
int y = 0; y < H; ++y) {
98 for (
int x = 0; x < W; ++x) {
100 float max_val = (-1e20f);
101 for (
int c = 0; c < D; ++c) {
103 int idx = i * (H * W * D) + c * (H * W) + y * W + x;
104 max_val = std::max(max_val, Xdata[idx]);
109 for (
int c = 0; c < D; ++c) {
110 int idx = i * (H * W * D) + c * (H * W) + y * W + x;
111 float expx = exp(Xdata[idx] - max_val);
117 for (
int c = 0; c < D; ++c) {
118 int idx = i * (H * W * D) + c * (H * W) + y * W + x;
119 Pdata[idx] /= expsum;
127 Output(1, vector<int64_t>(), at::dtype<float>());
128 float* avg_loss_data = avg_loss->template mutable_data<float>();
129 const int* label_data =
T.data<
int>();
131 float sum_label_xent = 0.0f;
132 float total_weight = 0.0;
134 for (
int y = 0; y < H; y++) {
135 for (
int x = 0; x < W; x++) {
136 for (
int i = 0; i < N; i++) {
137 int label_idx = i * H * W + y * W + x;
138 int label = label_data[label_idx];
139 if (label != DONT_CARE) {
141 label < D && label >= 0,
142 "Label seems incorrect:label value larger than number of classes",
146 int idx = i * (H * W * D) + label * (H * W) + y * W + x;
147 float w = weights ? weights[label_idx] : 1.0;
149 sum_label_xent += -log(std::max(Pdata[idx], 1e-20f)) * w;
154 if (total_weight != 0.0) {
155 *avg_loss_data = sum_label_xent / total_weight;
157 *avg_loss_data = 0.0;
163 bool SpatialSoftmaxWithLossGradientOp<float, CPUContext>::RunOnDevice() {
167 auto& P = Input(InputSize() - 2);
168 auto& d_avg_loss = Input(InputSize() - 1);
170 const float* weights = (InputSize() > 4 ? Input(2).data<
float>() :
nullptr);
174 auto* dX = Output(0, X.sizes(), at::dtype<float>());
175 CAFFE_ENFORCE_EQ(
T.dim32(0), N);
176 CAFFE_ENFORCE_EQ(X.dim(), 4);
177 CAFFE_ENFORCE_EQ(
T.dim(), 3);
182 const float* Pdata = P.data<
float>();
183 float* dX_data = dX->template mutable_data<float>();
184 const int* label_data =
T.data<
int>();
189 context_.CopyFromCPU<
float>(P.numel(), Pdata, dX_data);
191 float total_weight = 0.0f;
192 for (
int y = 0; y < H; ++y) {
193 for (
int x = 0; x < W; ++x) {
194 for (
int i = 0; i < N; ++i) {
195 int label_idx = i * H * W + y * W + x;
196 int label = label_data[label_idx];
198 if (label != DONT_CARE) {
199 int idx = i * (H * W * D) + label * (H * W) + y * W + x;
201 dX_data[idx] = (dX_data[idx] - 1.0);
203 if (weights !=
nullptr) {
204 float weight = weights[label_idx];
205 for (
int c = 0; c < D; ++c) {
206 int k = i * (H * W * D) + c * (H * W) + y * W + x;
207 dX_data[k] *= weight;
209 total_weight += weight;
215 for (
int c = 0; c < D; ++c) {
216 int idx = i * (H * W * D) + c * (H * W) + y * W + x;
224 if (total_weight > 0) {
225 math::Scale<float, float, CPUContext>(
227 scale_ / total_weight,
232 math::Scale<float, float, CPUContext>(
234 d_avg_loss.data<
float>(),
236 dX->template mutable_data<float>(),
242 class GetSoftmaxWithLossGradient :
public GradientMakerBase {
243 using GradientMakerBase::GradientMakerBase;
244 vector<OperatorDef> GetGradientDefs()
override {
245 vector<string> blob_names{
246 {I(0), I(1), O(0), GO(1)},
250 if (def_.input_size() == 3) {
251 blob_names.emplace(blob_names.begin() + 2, I(2));
253 return SingleGradientDef(
254 "SpatialSoftmaxWithLossGradient",
257 vector<string>{GI(0)});
261 REGISTER_GRADIENT(SpatialSoftmaxWithLoss, GetSoftmaxWithLossGradient);
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...