File: apmeter_op.cc

package info (click to toggle)
pytorch 1.13.1%2Bdfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 139,252 kB
  • sloc: cpp: 1,100,274; python: 706,454; ansic: 83,052; asm: 7,618; java: 3,273; sh: 2,841; javascript: 612; makefile: 323; xml: 269; ruby: 185; yacc: 144; objc: 68; lex: 44
file content (131 lines) | stat: -rw-r--r-- 3,747 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include "caffe2/operators/apmeter_op.h"

namespace caffe2 {

template <>
void APMeterOp<float, CPUContext>::BufferPredictions(
    const float* XData,
    const int* labelData,
    int N,
    int D) {
  if (buffers_.empty()) {
    // Initialize the buffer
    buffers_.resize(D, std::vector<BufferDataType>(buffer_size_));
  }
  TORCH_DCHECK_EQ(buffers_.size(), D);

  // Fill atmose buffer_size_ data at a time, so truncate the input if needed
  if (N > buffer_size_) {
    XData = XData + (N - buffer_size_) * D;
    labelData = labelData + (N - buffer_size_) * D;
    N = buffer_size_;
  }

  // Reclaim space if not enough space in the buffer to hold new data
  int space_to_reclaim = buffer_used_ + N - buffer_size_;
  if (space_to_reclaim > 0) {
    for (auto& buffer : buffers_) {
      std::rotate(
          buffer.begin(), buffer.begin() + space_to_reclaim, buffer.end());
    }
    buffer_used_ -= space_to_reclaim;
  }

  // Fill the buffer
  for (int i = 0; i < D; i++) {
    for (int j = 0; j < N; j++) {
      buffers_[i][buffer_used_ + j].first = XData[j * D + i];
      buffers_[i][buffer_used_ + j].second = labelData[j * D + i];
    }
  }

  buffer_used_ += N;
}

template <>
bool APMeterOp<float, CPUContext>::RunOnDevice() {
  auto& X = Input(PREDICTION);
  auto& label = Input(LABEL);

  // Check dimensions
  TORCH_DCHECK_EQ(X.dim(), 2);
  int N = X.dim32(0);
  int D = X.dim32(1);
  TORCH_DCHECK_EQ(label.dim(), 2);
  TORCH_DCHECK_EQ(label.dim32(0), N);
  TORCH_DCHECK_EQ(label.dim32(1), D);
  auto* Y = Output(0, {D}, at::dtype<float>());

  const auto* Xdata = X.data<float>();
  const auto* labelData = label.data<int>();
  auto* Ydata = Y->template mutable_data<float>();

  BufferPredictions(Xdata, labelData, N, D);

  // Calculate AP for each class
  for (int i = 0; i < D; i++) {
    auto& buffer = buffers_[i];
    // Sort predictions by score
    std::stable_sort(
        buffer.begin(),
        buffer.begin() + buffer_used_,
        [](const BufferDataType& p1, const BufferDataType& p2) {
          return p1.first > p2.first;
        });
    // Calculate cumulative precision for each sample
    float tp_sum = 0.0;
    float precision_sum = 0.0;
    int ntruth = 0;
    for (int j = 0; j < buffer_used_; j++) {
      tp_sum += buffer[j].second;
      if (buffer[j].second == 1) {
        ntruth += 1;
        // NOLINTNEXTLINE(cppcoreguidelines-narrowing-conversions,bugprone-narrowing-conversions)
        precision_sum += tp_sum / (j + 1);
      }
    }

    // Calculate AP
    Ydata[i] = precision_sum / std::max(1, ntruth);
  }

  return true;
}

namespace {
REGISTER_CPU_OPERATOR(APMeter, APMeterOp<float, CPUContext>);

OPERATOR_SCHEMA(APMeter)
    .NumInputs(2)
    .NumOutputs(1)
    .ScalarType(TensorProto::FLOAT)
    .SetDoc(R"DOC(
APMeter computes Average Precision for binary or multi-class classification.
It takes two inputs: prediction scores P of size (n_samples x n_classes), and
true labels Y of size (n_samples x n_classes). It returns a single float number
per class for the average precision of that class.
)DOC")
    .Arg(
        "buffer_size",
        "(int32_t) indicates how many predictions should the op buffer. "
        "defaults to 1000")
    .Input(
        0,
        "predictions",
        "2-D tensor (Tensor<float>) of size (num_samples x"
        "num_classes) containing prediction scores")
    .Input(
        1,
        "labels",
        "2-D tensor (Tensor<float>) of size (num_samples) "
        "containing true labels for each sample")
    .Output(
        0,
        "AP",
        "1-D tensor (Tensor<float>) of size num_classes containing "
        "average precision for each class");

SHOULD_NOT_DO_GRADIENT(APMeter);

} // namespace
} // namespace caffe2