Source browser
examples/shaders/ann_gradient.cpp
Read the complete file below or download its original bytes. Line numbers provide stable links to a declaration or processing step.
File fingerprint
1// Analytic binary cross-entropy gradients for every row and every ANN parameter.
2#ifdef __cplusplus // Expose host declarations only when a C++ compiler reads this shader.
3#include "shdr.h" // Include shdr.h declarations.
4#endif // Finish this conditional compilation branch.
5layout(local_size_x = 64) in; // Group 64 independent invocations along x.
6layout(std430, binding = 0) readonly buffer Inputs { // Read the model's sample-major input features through buffer binding 0.
7 float inputs[]; // Store the model's sample-major input features.
8}; // Finish this declaration.
9layout(std430, binding = 1) readonly buffer Targets { // Read one binary training label per sample through buffer binding 1.
10 float targets[]; // Store one binary training label per sample.
11}; // Finish this declaration.
12layout(std430, binding = 2) readonly buffer Parameters { // Read the complete flat learned parameter vector through buffer binding 2.
13 float parameters[]; // Store the complete flat learned parameter vector.
14}; // Finish this declaration.
15layout(std430, binding = 3) writeonly buffer Gradients { // Write sample-major derivatives for every trainable parameter through buffer binding 3.
16 float gradients[]; // Store sample-major derivatives for every trainable parameter.
17}; // Finish this declaration.
18uniform uint batch_count; // Set the number of independent samples in this model batch.
19float logistic(float value) { // Evaluate the sigmoid with a sign-selected, numerically stable exponential.
20 if (value >= 0.0) // Use the negative exponent when the input logit is nonnegative.
21 return 1.0 / (1.0 + exp(-value)); // Convert a nonnegative logit into its sigmoid probability.
22 float e = exp(value); // Use a nonpositive exponent for the negative logit branch.
23 return e / (1.0 + e); // Convert a negative logit into its sigmoid probability.
24} // End this block.
25void main() { // Differentiate one sample loss with respect to one ANN parameter.
26 uint index = gl_GlobalInvocationID.x, row = index / 33u, parameter = index % 33u; // Select one sample-major derivative among all 33 ANN parameters.
27 if (row >= batch_count) // Ignore invocations outside the sample batch.
28 return; // Stop this invocation before performing another resource access.
29 float x = inputs[2u * row], y = inputs[2u * row + 1u], logit = parameters[32]; // Read the two input features and initialize the output with its bias.
30 float activations[8], preactivations[8]; // Retain hidden preactivations and ReLU outputs for the chain rule.
31 for (uint neuron = 0u; neuron < 8u; ++neuron) { // Evaluate each of the eight hidden neurons.
32 preactivations[neuron] = parameters[2u * neuron] * x + parameters[2u * neuron + 1u] * y + // Multiply the two input features by this neuron's learned weights.
33 parameters[16u + neuron]; // Include this hidden neuron's bias before the ReLU.
34 activations[neuron] = max(0.0, preactivations[neuron]); // Form the hidden neuron's weighted input before its nonlinearity.
35 logit += activations[neuron] * parameters[24u + neuron]; // Keep positive preactivations and set the negative branch to zero.
36 } // End this block.
37 float delta = logistic(logit) - targets[row], gradient = delta; // Use prediction minus target as the cross-entropy derivative with respect to the logit.
38 if (parameter < 16u) { // Differentiate one of the sixteen hidden input weights.
39 uint neuron = parameter / 2u; // Find the hidden neuron owning this input weight.
40 gradient = preactivations[neuron] > 0.0 // Form the hidden neuron's weighted input before its nonlinearity.
41 ? delta * parameters[24u + neuron] * (parameter % 2u == 0u ? x : y) // Multiply the output derivative by its output weight and the selected input feature.
42 : 0.0; // Use zero derivative for the inactive ReLU branch.
43 } else if (parameter < 24u) { // Differentiate one of the eight hidden biases.
44 uint neuron = parameter - 16u; // Find the hidden neuron owning this bias.
45 gradient = preactivations[neuron] > 0.0 ? delta * parameters[24u + neuron] : 0.0; // Form the hidden neuron's weighted input before its nonlinearity.
46 } else if (parameter < 32u) // Differentiate one of the eight output weights.
47 gradient = delta * activations[parameter - 24u]; // Multiply the output logit derivative by this hidden activation.
48 gradients[index] = gradient; // Store this sample's derivative for the following batch SGD stage.
49} // End this block.