Coverage Report

Created: 2019-07-03 22:50

/home/liu/buildslave/linux-x64-runtests/build/lib/nnc/cmd/loss/ccv_nnc_categorical_crossentropy_cpu_ref.c
Line
Count
Source (jump to first uncovered line)
1
#include <ccv.h>
2
#include <ccv_internal.h>
3
#include <nnc/ccv_nnc.h>
4
#include <nnc/ccv_nnc_easy.h>
5
#include <nnc/ccv_nnc_internal.h>
6
#ifdef USE_OPENMP
7
#include <omp.h>
8
#endif
9
#ifdef USE_DISPATCH
10
#include <dispatch/dispatch.h>
11
#endif
12
13
static int _ccv_nnc_categorical_crossentropy_forw(const ccv_nnc_cmd_t cmd, const ccv_nnc_hint_t hint, const int flags, ccv_nnc_tensor_t* const* const inputs, const int input_size, ccv_nnc_tensor_t* const* const outputs, const int output_size, ccv_nnc_stream_context_t* const stream_context)
14
3
{
15
3
  assert(input_size == 2);
16
3
  const ccv_nnc_tensor_t* a = inputs[0];
17
3
  assert(!CCV_IS_TENSOR_VIEW(a));
18
3
  const ccv_nnc_tensor_t* b = inputs[1];
19
3
  assert(!CCV_IS_TENSOR_VIEW(b));
20
3
  assert(output_size == 1);
21
3
  ccv_nnc_tensor_t* c = outputs[0];
22
3
  assert(!CCV_IS_TENSOR_VIEW(c));
23
3
  const int axis_count = ccv_nnc_tensor_nd(a->info.dim);
24
3
  const int batch_size = axis_count < 2 ? 
10
: a->info.dim[0];
25
3
  const int count = ccv_nnc_tensor_count(a->info) / batch_size;
26
3
  int i;
27
3
  if (b->info.datatype == CCV_32F)
28
2
  {
29
2
    // If has more than 1 axis, then the range is the channel count. Otherwise, if our batch size is 1, then the range is
30
2
    // the channel count. Otherwise, the range is 1 (and the only axis is the batch size).
31
2
    const int range = ccv_nnc_tensor_nd(b->info.dim) > 1 ? 
ccv_nnc_tensor_get_c(b->info)0
: (batch_size == 1 ?
b->info.dim[0]0
: 1);
32
2
    if (range == 1)
33
2
    {
34
4
      for (i = 0; i < CCV_NNC_MAX_DIM_ALLOC && b->info.dim[i] > 0; 
i++2
)
35
2
        { assert(b->info.dim[i] == c->info.dim[i]); }
36
2
      parallel_for(i, batch_size) {
37
0
        const int label = (int)(b->data.f32[i] + 0.5);
38
0
        assert(label >= 0 && label < count);
39
6
        const float p = a->data.f32[i * count + label];
40
6
        c->data.f32[i] = -logf(p);
41
8
      } parallel_endfor
42
2
    } else {
43
0
      assert(range == count);
44
0
      parallel_for(i, batch_size) {
45
0
        int j;
46
0
        float p = 0;
47
0
        float* const bp = b->data.f32 + i * count;
48
0
        float* const ap = a->data.f32 + i * count;
49
0
        for (j = 0; j < count; j++)
50
0
          p += -bp[j] * logf(ap[j]);
51
0
        c->data.f32[i] = p;
52
0
      } parallel_endfor
53
0
    }
54
2
  } else 
if (1
b->info.datatype == CCV_32S1
) {
55
2
    for (i = 0; i < CCV_NNC_MAX_DIM_ALLOC && b->info.dim[i] > 0; 
i++1
)
56
1
      { assert(b->info.dim[i] == c->info.dim[i]); }
57
1
    parallel_for(i, batch_size) {
58
0
      const int label = b->data.i32[i];
59
0
      assert(label >= 0 && label < count);
60
2
      const float p = a->data.f32[i * count + label];
61
2
      c->data.f32[i] = -logf(p);
62
3
    } parallel_endfor
63
1
  }
64
3
  return CCV_NNC_EXEC_SUCCESS;
65
3
}
66
67
static int _ccv_nnc_categorical_crossentropy_back(const ccv_nnc_cmd_t cmd, const ccv_nnc_hint_t hint, const int flags, ccv_nnc_tensor_t* const* const inputs, const int input_size, ccv_nnc_tensor_t* const* const outputs, const int output_size, ccv_nnc_stream_context_t* const stream_context)
68
3
{
69
3
  assert(input_size >= 3);
70
3
  assert(output_size >= 1);
71
3
  const ccv_nnc_tensor_t* g = inputs[0];
72
3
  assert(!g || !CCV_IS_TENSOR_VIEW(g));
73
3
  const ccv_nnc_tensor_t* a = inputs[1];
74
3
  assert(!CCV_IS_TENSOR_VIEW(a));
75
3
  const ccv_nnc_tensor_t* b = inputs[2];
76
3
  assert(!CCV_IS_TENSOR_VIEW(b));
77
3
  ccv_nnc_tensor_t* h = outputs[0];
78
3
  assert(!CCV_IS_TENSOR_VIEW(h));
79
3
  const int axis_count = ccv_nnc_tensor_nd(a->info.dim);
80
3
  const int batch_size = axis_count < 2 ? 
10
: a->info.dim[0];
81
3
  const int count = ccv_nnc_tensor_count(a->info) / batch_size;
82
3
  int i;
83
3
  if (g)
84
3
  {
85
3
    if (b->info.datatype == CCV_32F)
86
1
    {
87
1
      // If has more than 1 axis, then the range is the channel count. Otherwise, if our batch size is 1, then the range is
88
1
      // the channel count. Otherwise, the range is 1 (and the only axis is the batch size).
89
1
      const int range = ccv_nnc_tensor_nd(b->info.dim) > 1 ? 
ccv_nnc_tensor_get_c(b->info)0
: (batch_size == 1 ?
b->info.dim[0]0
: 1);
90
1
      if (range == 1)
91
1
      {
92
3
        for (i = 0; i < CCV_NNC_MAX_DIM_ALLOC && a->info.dim[i] > 0; 
i++2
)
93
2
          { assert(a->info.dim[i] == h->info.dim[i]); }
94
1
        parallel_for(i, batch_size) {
95
0
          int j;
96
0
          const float gp = g->data.f32[i];
97
0
          const int label = (int)(b->data.f32[i] + 0.5);
98
0
          float* const hp = h->data.f32 + i * count;
99
116
          for (j = 0; j < count; j++)
100
116
            hp[j] = 0;
101
0
          const float p = a->data.f32[i * count + label];
102
0
          hp[label] = -gp / p;
103
1
        } parallel_endfor
104
1
      } else {
105
0
        assert(range == count);
106
0
        parallel_for(i, batch_size) {
107
0
          int j;
108
0
          const float gp = g->data.f32[i];
109
0
          float* const hp = h->data.f32 + i * count;
110
0
          float* const ap = a->data.f32 + i * count;
111
0
          float* const bp = b->data.f32 + i * count;
112
0
          for (j = 0; j < count; j++)
113
0
            hp[j] = -gp * bp[j] / ap[j];
114
0
        } parallel_endfor
115
0
      }
116
2
    } else if (b->info.datatype == CCV_32S) {
117
6
      for (i = 0; i < CCV_NNC_MAX_DIM_ALLOC && a->info.dim[i] > 0; 
i++4
)
118
4
        { assert(a->info.dim[i] == h->info.dim[i]); }
119
2
      parallel_for(i, batch_size) {
120
0
        int j;
121
0
        const float gp = g->data.f32[i];
122
0
        const int label = b->data.i32[i];
123
0
        float* const hp = h->data.f32 + i * count;
124
9
        for (j = 0; j < count; j++)
125
9
          hp[j] = 0;
126
0
        const float p = a->data.f32[i * count + label];
127
0
        hp[label] = -gp / p;
128
2
      } parallel_endfor
129
2
    }
130
3
  } else {
131
0
    if (b->info.datatype == CCV_32F)
132
0
    {
133
0
      // If has more than 1 axis, then the range is the channel count. Otherwise, if our batch size is 1, then the range is
134
0
      // the channel count. Otherwise, the range is 1 (and the only axis is the batch size).
135
0
      const int range = ccv_nnc_tensor_nd(b->info.dim) > 1 ? ccv_nnc_tensor_get_c(b->info) : (batch_size == 1 ? b->info.dim[0] : 1);
136
0
      if (range == 1)
137
0
      {
138
0
        for (i = 0; i < CCV_NNC_MAX_DIM_ALLOC && a->info.dim[i] > 0; i++)
139
0
          { assert(a->info.dim[i] == h->info.dim[i]); }
140
0
        parallel_for(i, batch_size) {
141
0
          int j;
142
0
          const int label = (int)(b->data.f32[i] + 0.5);
143
0
          float* const hp = h->data.f32 + i * count;
144
0
          for (j = 0; j < count; j++)
145
0
            hp[j] = 0;
146
0
          const float p = a->data.f32[i * count + label];
147
0
          hp[label] = -1. / p;
148
0
        } parallel_endfor
149
0
      } else {
150
0
        assert(range == count);
151
0
        parallel_for(i, batch_size) {
152
0
          int j;
153
0
          float* const hp = h->data.f32 + i * count;
154
0
          float* const ap = a->data.f32 + i * count;
155
0
          float* const bp = b->data.f32 + i * count;
156
0
          for (j = 0; j < count; j++)
157
0
            hp[j] = -bp[j] / ap[j];
158
0
        } parallel_endfor
159
0
      }
160
0
    } else if (b->info.datatype == CCV_32S) {
161
0
      for (i = 0; i < CCV_NNC_MAX_DIM_ALLOC && a->info.dim[i] > 0; i++)
162
0
        { assert(a->info.dim[i] == h->info.dim[i]); }
163
0
      parallel_for(i, batch_size) {
164
0
        int j;
165
0
        const int label = b->data.i32[i];
166
0
        float* const hp = h->data.f32 + i * count;
167
0
        for (j = 0; j < count; j++)
168
0
          hp[j] = 0;
169
0
        const float p = a->data.f32[i * count + label];
170
0
        hp[label] = -1. / p;
171
0
      } parallel_endfor
172
0
    }
173
0
  }
174
3
  return CCV_NNC_EXEC_SUCCESS;
175
3
}
176
177
REGISTER_COMMAND_BACKEND(CCV_NNC_CATEGORICAL_CROSSENTROPY_FORWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
178
1
{
179
1
  registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW;
180
1
  registry->tensor_datatypes = CCV_32F | CCV_32S;
181
1
  registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
182
1
  registry->algorithms = 1;
183
1
  registry->exec = _ccv_nnc_categorical_crossentropy_forw;
184
1
}
185
186
REGISTER_COMMAND_BACKEND(CCV_NNC_CATEGORICAL_CROSSENTROPY_BACKWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
187
1
{
188
1
  registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW;
189
1
  registry->tensor_datatypes = CCV_32F | CCV_32S;
190
1
  registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
191
1
  registry->algorithms = 1;
192
1
  registry->exec = _ccv_nnc_categorical_crossentropy_back;
193
1
}