4
4
#include <assert.h>
5
5
#include <math.h>
6
6
#include <stddef.h>
7
+ #include <stdlib.h>
8
+ #include <time.h>
9
+ #include <stdio.h>
7
10
8
11
Tensor nn_linear (Tensor input , Tensor weight , Tensor bias ) {
9
12
Tensor tmp = Tensor_matmul (input , weight );
@@ -16,14 +19,14 @@ static Tensor GradFn_relu(Tensor self, int i) {
16
19
Tensor input = self .node -> inputs [i ];
17
20
Tensor res = Tensor_new (input .shape , false);
18
21
for (int i = 0 ; i < input .data -> numel ; i ++ ) {
19
- res .data -> flex [i ] = input .data -> flex [i ] > 0 ? 1 : 0 ;
22
+ res .data -> flex [i ] = input .data -> flex [i ] > 0 ? 1.0f : 0.0f ;
20
23
}
21
24
return res ;
22
25
}
23
26
24
27
Tensor nn_relu (Tensor self ) {
25
28
bool requires_grad = !cten_is_eval () && self .node != NULL ;
26
- Tensor res = Tensor_new (self .shape , requires_grad );
29
+ Tensor res = Tensor_zeros (self .shape , requires_grad );
27
30
for (int i = 0 ; i < self .data -> numel ; i ++ ) {
28
31
res .data -> flex [i ] = fmaxf (0 , self .data -> flex [i ]);
29
32
}
@@ -32,23 +35,52 @@ Tensor nn_relu(Tensor self) {
32
35
res .node -> grad_fn = GradFn_relu ;
33
36
res .node -> inputs [0 ] = self ;
34
37
res .node -> n_inputs = 1 ;
38
+ res .node -> name = "Relu" ;
39
+
40
+ }
41
+ return res ;
42
+ }
43
+
44
+ Tensor Glorot_init (TensorShape shape , bool requires_grad ) {
45
+ Tensor res = Tensor_new (shape , requires_grad );
46
+ int fan_in = shape [0 ];
47
+ int fan_out = shape [1 ];
48
+ float scale = sqrtf (6.0f / (fan_in + fan_out ));
49
+
50
+ for (int i = 0 ; i < res .data -> numel ; i ++ ) {
51
+ float r = (float )rand () / RAND_MAX * 2.0f - 1.0f ;
52
+ res .data -> flex [i ] = r * scale ;
35
53
}
36
54
return res ;
37
55
}
38
56
39
- /* nn.softmax */
40
57
static Tensor GradFn_softmax (Tensor self , int i ) {
41
58
Tensor input = self .node -> inputs [i ];
42
- Tensor res = Tensor_new (input .shape , false);
43
- for (int j = 0 ; j < input .data -> numel ; j ++ ) {
44
- float softmax_j = self .data -> flex [j ];
45
- for (int k = 0 ; k < input .data -> numel ; k ++ ) {
46
- float softmax_k = self .data -> flex [k ];
47
- float delta_jk = (j == k ) ? 1.0f : 0.0f ;
48
- res .data -> flex [j * input .data -> numel + k ] = softmax_j * (delta_jk - softmax_k );
59
+ Tensor grad = Tensor_new (input .shape , false);
60
+
61
+ int dim = TensorShape_dim (self .shape );
62
+ int batch_size = self .shape [0 ];
63
+ int num_classes = self .shape [1 ];
64
+ for (int b = 0 ; b < batch_size ; b ++ ){
65
+ for (int i = 0 ; i < num_classes ; i ++ ) {
66
+ for (int j = 0 ; j < num_classes ; j ++ ) {
67
+ float softmax_i = self .data -> flex [b * num_classes + i ];
68
+ float softmax_j = self .data -> flex [b * num_classes + j ];
69
+ float value ;
70
+ if (i == j ){
71
+ value = softmax_i * (1.0f - softmax_i );
72
+ }
73
+ else {
74
+ value = - softmax_i * softmax_j ;
75
+ }
76
+
77
+ if (i == j ){
78
+ grad .data -> flex [b * num_classes + i ] = value ;
79
+ }
80
+ }
49
81
}
50
82
}
51
- return res ;
83
+ return grad ;
52
84
}
53
85
54
86
Tensor nn_softmax (Tensor self ) {
@@ -83,12 +115,38 @@ Tensor nn_softmax(Tensor self) {
83
115
if (requires_grad ) {
84
116
res .node -> grad_fn = GradFn_softmax ;
85
117
res .node -> inputs [0 ] = self ;
86
- res .node -> n_inputs = 1 ;
118
+ res .node -> n_inputs = 1 ;
119
+ res .node -> name = "Softmax" ;
87
120
}
88
121
return res ;
89
122
}
90
123
91
124
/* nn.cross_entropy */
125
+ static Tensor GradFn_crossentropy (Tensor self , int i ) {
126
+ if (i == 1 ) { // Gradient w.r.t. y_pred
127
+ Tensor y_true = self .node -> inputs [0 ];
128
+ Tensor y_pred = self .node -> inputs [1 ];
129
+ int n_samples = y_true .shape [0 ];
130
+ int n_classes = y_true .shape [1 ];
131
+
132
+ Tensor grad = Tensor_new (y_pred .shape , false);
133
+
134
+ for (int i = 0 ; i < n_samples ; i ++ ) {
135
+ for (int j = 0 ; j < n_classes ; j ++ ) {
136
+ float y_true_val = y_true .data -> flex [i * n_classes + j ];
137
+ float y_pred_val = y_pred .data -> flex [i * n_classes + j ];
138
+ if (y_true_val == 0 ) {
139
+ grad .data -> flex [i * n_classes + j ] = 0 ;
140
+ } else {
141
+ grad .data -> flex [i * n_classes + j ] = - y_true_val / y_pred_val ;
142
+ }
143
+ }
144
+ }
145
+ return grad ;
146
+ }
147
+ return Tensor_zeros ((TensorShape ){1 }, false);
148
+ }
149
+
92
150
Tensor nn_crossentropy (Tensor y_true , Tensor y_pred ) {
93
151
// y_true: [None, n_classes]
94
152
// y_pred: [None, n_classes]
@@ -100,15 +158,101 @@ Tensor nn_crossentropy(Tensor y_true, Tensor y_pred) {
100
158
assert (n_samples == y_pred .shape [0 ]);
101
159
assert (n_classes == y_pred .shape [1 ]);
102
160
103
- bool requires_grad = !cten_is_eval () && (y_true .node != NULL || y_pred .node != NULL );
104
- Tensor res = Tensor_new ((TensorShape ){n_samples }, requires_grad );
161
+ bool requires_grad = !cten_is_eval () && (y_true .node != NULL || y_pred .node != NULL ); //No eval but rather training so requires grad is True
162
+ Tensor res = Tensor_zeros ((TensorShape ){1 }, requires_grad );
163
+
164
+ // Calculate cross-entropy loss
165
+ float total_loss = 0.0f ;
105
166
for (int i = 0 ; i < n_samples ; i ++ ) {
106
- float loss = 0 ;
167
+ float sample_loss = 0.0f ;
107
168
for (int j = 0 ; j < n_classes ; j ++ ) {
108
- loss +=
109
- y_true .data -> flex [i * n_classes + j ] * logf (y_pred .data -> flex [i * n_classes + j ]);
169
+ float true_val = y_true .data -> flex [i * n_classes + j ];
170
+ float pred_val = y_pred .data -> flex [i * n_classes + j ];
171
+ float epsilon = 1e-8f ; // avoid log(0) so we add a small epsilon
172
+ if (true_val > 0 ) { // one-hot encoding
173
+ sample_loss -= true_val * logf (pred_val + epsilon );
174
+ }
110
175
}
111
- res .data -> flex [i ] = - loss ;
176
+ total_loss += sample_loss ;
177
+ }
178
+
179
+ res .data -> flex [0 ] = total_loss / n_samples ;
180
+
181
+ if (requires_grad ) {
182
+ res .node -> grad_fn = GradFn_crossentropy ;
183
+ res .node -> inputs [0 ] = y_true ;
184
+ res .node -> inputs [1 ] = y_pred ;
185
+ res .node -> n_inputs = 2 ;
186
+ res .node -> name = "Cross-entropy" ;
112
187
}
113
- return Tensor_mean (res );
188
+
189
+ return res ;
190
+ }
191
+
192
+ static Tensor GradFn_softmax_crossentropy (Tensor self , int i ) {
193
+ if (i == 1 ) {
194
+ Tensor y_true = self .node -> inputs [0 ];
195
+ Tensor logits = self .node -> inputs [1 ];
196
+
197
+ Tensor y_pred = Tensor_new (logits .shape , false);
198
+ int self_dim = TensorShape_dim (logits .shape );
199
+ int last_dim_size = logits .shape [self_dim - 1 ];
200
+ int outer_size = logits .data -> numel / last_dim_size ;
201
+
202
+ for (int outer = 0 ; outer < outer_size ; outer ++ ) {
203
+ float max_val = - INFINITY ;
204
+ float sum = 0 ;
205
+
206
+ for (int d = 0 ; d < last_dim_size ; d ++ ) {
207
+ int index = outer * last_dim_size + d ;
208
+ max_val = fmaxf (max_val , logits .data -> flex [index ]);
209
+ }
210
+
211
+ for (int d = 0 ; d < last_dim_size ; d ++ ) {
212
+ int index = outer * last_dim_size + d ;
213
+ y_pred .data -> flex [index ] = expf (logits .data -> flex [index ] - max_val );
214
+ sum += y_pred .data -> flex [index ];
215
+ }
216
+
217
+ for (int d = 0 ; d < last_dim_size ; d ++ ) {
218
+ int index = outer * last_dim_size + d ;
219
+ y_pred .data -> flex [index ] /= sum ;
220
+ }
221
+ }
222
+
223
+ Tensor grad = Tensor_new (y_pred .shape , false);
224
+ int n_samples = y_pred .shape [0 ];
225
+ int n_classes = y_pred .shape [1 ];
226
+
227
+ for (int i = 0 ; i < n_samples ; i ++ ) {
228
+ for (int j = 0 ; j < n_classes ; j ++ ) {
229
+ grad .data -> flex [i * n_classes + j ] =
230
+ y_pred .data -> flex [i * n_classes + j ] - y_true .data -> flex [i * n_classes + j ];
231
+ }
232
+ }
233
+
234
+ return grad ;
235
+ }
236
+ return Tensor_zeros ((TensorShape ){1 }, false);
237
+ }
238
+
239
+ Tensor nn_softmax_crossentropy (Tensor y_true , Tensor logits ) {
240
+ bool requires_grad = !cten_is_eval () && logits .node != NULL ;
241
+ //disable gradient computation
242
+ cten_begin_eval ();
243
+ Tensor y_pred = nn_softmax (logits );
244
+ Tensor loss = nn_crossentropy (y_true , y_pred );
245
+ cten_end_eval ();
246
+ Tensor res = Tensor_zeros ((TensorShape ){1 }, requires_grad );
247
+ res .data -> flex [0 ] = loss .data -> flex [0 ];
248
+
249
+ if (requires_grad ) {
250
+ res .node -> grad_fn = GradFn_softmax_crossentropy ;
251
+ res .node -> inputs [0 ] = y_true ;
252
+ res .node -> inputs [1 ] = logits ;
253
+ res .node -> n_inputs = 2 ;
254
+ res .node -> name = "SoftmaxCrossEntropy" ;
255
+ }
256
+
257
+ return res ;
114
258
}
0 commit comments