Source browser
examples/shaders/cnn_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// Train convolution coefficients, biases, and the classifier head with analytic gradients.
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 image loss with respect to one CNN parameter.
26 uint index = gl_GlobalInvocationID.x, row = index / 23u, parameter = index % 23u; // Select one sample-major derivative among all 23 CNN parameters.
27 if (row >= batch_count) // Ignore invocations outside the sample batch.
28 return; // Stop this invocation before performing another resource access.
29 float logit = parameters[22], derivative = parameter == 22u ? 1.0 : 0.0; // Initialize the output bias and its derivative for this parameter.
30 for (uint channel = 0u; channel < 2u; ++channel) { // Evaluate each of the two learned convolution filters.
31 float pooled = 0.0, pooled_derivative = 0.0; // Start the mean feature and its derivative at zero.
32 for (uint y = 0u; y < 6u; ++y) // Visit the six valid output rows of the three-by-three filter.
33 for (uint x = 0u; x < 6u; ++x) { // Visit the six valid output columns of this filter.
34 float activation = parameters[18u + channel]; // Start this convolution window with the filter's learned bias.
35 for (uint ky = 0u; ky < 3u; ++ky) // Visit each row of the three-by-three learned kernel.
36 for (uint kx = 0u; kx < 3u; ++kx) // Visit each column of the three-by-three learned kernel.
37 activation += parameters[9u * channel + 3u * ky + kx] * // Multiply this kernel coefficient by its matching input image sample.
38 inputs[64u * row + 8u * (y + ky) + x + kx]; // Read the sample from this image's flattened eight-by-eight plane.
39 if (activation > 0.0) { // Pass feature derivatives only through the active ReLU branch.
40 pooled += activation / 36.0; // Accumulate the active convolution feature in the global mean.
41 if (parameter < 18u && parameter / 9u == channel) { // Differentiate a coefficient only when it belongs to this filter.
42 uint coefficient = parameter % 9u; // Locate the selected three-by-three coefficient within its filter.
43 pooled_derivative += // Accumulate this kernel coefficient's contribution to the pooled derivative.
44 inputs[64u * row + 8u * (y + coefficient / 3u) + x + coefficient % 3u] / // Read the sample from this image's flattened eight-by-eight plane.
45 36.0; // Normalize the derivative by the valid six-by-six output area.
46 } else if (parameter == 18u + channel) // Handle the derivative of this filter's additive bias.
47 pooled_derivative += 1.0 / 36.0; // Normalize the derivative by the valid six-by-six output area.
48 } // End this block.
49 } // End this block.
50 logit += parameters[20u + channel] * pooled; // Connect this mean convolution feature through its learned head weight.
51 derivative += parameters[20u + channel] * pooled_derivative; // Propagate the pooled feature derivative through the head weight.
52 if (parameter == 20u + channel) // Handle the derivative of the head weight for this filter.
53 derivative += pooled; // The head weight derivative is this filter's pooled feature.
54 } // End this block.
55 gradients[index] = (logistic(logit) - targets[row]) * derivative; // Multiply the output-loss derivative by the full parameter chain-rule derivative.
56} // End this block.