From 8eab98586c51fcfb7bd1509538d4ce948f82a518 Mon Sep 17 00:00:00 2001 From: vikshar Date: Sun, 12 Jan 2025 09:56:33 -0600 Subject: [PATCH] refactor. simplify code - only gonna use mnist --- cnn.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ cnn.h | 171 ------------------------------------------------------- 2 files changed, 179 insertions(+), 171 deletions(-) create mode 100644 cnn.c delete mode 100644 cnn.h diff --git a/cnn.c b/cnn.c new file mode 100644 index 0000000..feb45ac --- /dev/null +++ b/cnn.c @@ -0,0 +1,179 @@ +// convolutional neural network c header library +// inspired by euske's nn1 +// meant to be synthesized into RTL through Vitus HLS for an FPGA implementation + +#include +#include + +typedef enum { + input, + conv, + max_pool, + fully_connected +} ltype; + +typedef enum { + fc_input, + fc_hidden, + fc_output, +} fcpos; + +typedef enum { + sigmoid, + relu, + softmax, +} activation; + +typedef struct { + ltype type; + int height; + int width; + int channels; // in this case, "channels" are the number of filters that are coming in + + union { + struct { + int num_filters; + int filter_size; // single integer b/c filter will usually be square shaped + int stride; + int zero_padding; // single integer for how many layers of zero padding + float (*weights); + float (*biases); + } conv_params; + + struct { + int pool_size; // single integer again + int stride; + } pool_params; + + struct { + int output_size; + float (*weights); + float (*biases); + activation type; + } fc_params; + } params; + float *output; + float *delta; +} Layer; + +float he_init(int fan_in) { + float scale = sqrt(2.0f / fan_in); + float random = (float)rand() / RAND_MAX * 2 - 1; + return random * scale; +} + +float glorot_init(int fan_in, int fan_out) { + float limit = sqrt(6.0f / (fan_in + fan_out)); + float random = (float)rand() / RAND_MAX; + return random * 2 * limit - limit; +} + + +Layer* create_input(int height, int width, int channels) { + Layer* layer = (Layer*)malloc(sizeof(Layer)); + layer->type = input; + layer->height = height; + layer->width = width; + layer->channels = channels; + layer->output = (float*)calloc(height * width * channels, sizeof(float)); + return layer; +} + +Layer* create_conv(int input_height, int input_width, int input_channels, int num_filters, int filter_size, int stride, int padding) { + Layer* layer = (Layer*)malloc(sizeof(Layer)); + layer->type = conv; + layer->params.conv_params.num_filters = num_filters; + layer->params.conv_params.filter_size = filter_size; + layer->params.conv_params.stride = stride; + layer->params.conv_params.zero_padding = padding; + + // output dimensions + // https://cs231n.github.io/convolutional-networks/ + int output_h = (input_height + 2 * padding - filter_size) / stride + 1; + int output_w = (input_width + 2 * padding - filter_size) / stride + 1; + layer->height = output_h; + layer->width = output_w; + layer->channels = num_filters; + + // conv layer uses relu, use HE init + int weights_size = num_filters * input_channels * filter_size * filter_size; + int fan_in = input_channels * filter_size * filter_size; + layer->params.conv_params.weights = (float*)calloc(weights_size, sizeof(float)); + for (int i = 0; i < weights_size; i++) { + layer->params.conv_params.weights[i] = he_init(fan_in); + } + + layer->params.conv_params.biases = (float*)calloc(num_filters, sizeof(float)); + + layer->output = (float*) calloc(output_h * output_w * num_filters, sizeof(float)); + layer->delta = (float*) calloc(output_h * output_w * num_filters, sizeof(float)); + + return layer; +} + +Layer* create_maxpool(int input_height, int input_width, int input_channels, int pool_size, int stride) { + Layer* layer = (Layer*)malloc(sizeof(Layer)); + layer->type = max_pool; + layer->params.pool_params.pool_size = pool_size; + layer->params.pool_params.stride = stride; + + // output dimensions + // https://cs231n.github.io/convolutional-networks/ + int output_h = (input_height - pool_size) / stride + 1; + int output_w = (input_width - pool_size) / stride + 1; + layer->height = output_h; + layer->width = output_w; + layer->channels = input_channels; + + layer->output = (float*) calloc(output_h * output_w * input_channels, sizeof(float)); + layer->delta = (float*) calloc(output_h * output_w * input_channels, sizeof(float)); + + return layer; +} + +Layer* create_fc(int output_size, int input_size, activation type) { + Layer* layer = (Layer*)malloc(sizeof(Layer)); + layer->type = fully_connected; + layer->params.fc_params.output_size = output_size; + layer->params.fc_params.type = type; // activation type can either be sigmoid or softmax (output layer) + + // use glorot initalization + layer->params.fc_params.weights = (float*)calloc(output_size * input_size, sizeof(float)); + for (int i = 0; i < (output_size * input_size); i++) { + layer->params.fc_params.weights[i] = glorot_init(input_size, output_size); + } + + layer->params.fc_params.biases = (float*)calloc(output_size, sizeof(float)); + + layer->height = 1; + layer->width = 1; + layer->channels = output_size; + layer->output = (float*) calloc(output_size, sizeof(float)); + layer->delta = (float*) calloc(output_size, sizeof(float)); + + return layer; +} + +void free_layer(Layer* layer) { + switch (layer->type) { + case input: + free(layer->output); + free(layer); + case conv: + free(layer->params.conv_params.weights); + free(layer->params.conv_params.biases); + free(layer->output); + free(layer->delta); + free(layer); + case max_pool: + free(layer->output); + free(layer->delta); + free(layer); + case fully_connected: + free(layer->params.fc_params.weights); + free(layer->params.fc_params.biases); + free(layer->output); + free(layer->delta); + free(layer); + } +} diff --git a/cnn.h b/cnn.h deleted file mode 100644 index 510e78d..0000000 --- a/cnn.h +++ /dev/null @@ -1,171 +0,0 @@ -// convolutional neural network c header library -// inspired by euske's nn1 -// meant to be synthesized into RTL through Vitus HLS for an FPGA implementation - -#include -#include -#include - -typedef enum { - input, - conv, - max_pool, - fully_connected -} ltype; - -typedef enum { - fc_input, - fc_hidden, - fc_output, -} fcpos; - -typedef struct { - ltype type; - // spatial extent of layer- l,w,depth (color space) - int height; - int width; - int channels; - - // layer params - union { - struct { - int num_filters; - int filter_height; - int filter_width; - int stride; - int zero_padding; // how many layers of zero padding - float*** filters; // (width x height) x filters - } conv_params; - - struct { - int pool_height; - int pool_width; - int stride; - } pool_params; - - struct { - int input_neurons; - int output_neurons; - float** weights; - float* biases; - fcpos position; - } fc_params; - } params; -} Layer; - -float random_uniform(float min, float max) { - return min + (max - min) * ((float)rand() / RAND_MAX); -} - -float he_uniform(int fan_in) { - float limit = sqrt(6.0f / fan_in); - return random_uniform((limit * -1), limit); -} - -float glorot_uniform(int fan_in, int fan_out) { - float limit = sqrt(6.0f / (fan_in + fan_out)); - return random_uniform((limit * -1), limit); -} - - -Layer* create_input(int height, int width, int channels) { - Layer* layer = (Layer*)malloc(sizeof(Layer)); - layer->type = input; - layer->height = height; - layer->width = width; - layer->channels = channels; - return layer; -} - - Layer* create_conv(int height, int width, int channels, int num_filters, int filter_width, int filter_height, int stride, int zero_padding) { - Layer* layer = (Layer*)malloc(sizeof(Layer)); - layer->type = conv; - layer->height = height; - layer->width = width; - layer->channels = channels; - - layer->params.conv_params.num_filters = num_filters; - layer->params.conv_params.filter_height = filter_height; - layer->params.conv_params.filter_width = filter_width; - layer->params.conv_params.stride = stride; - layer->params.conv_params.zero_padding = zero_padding; - - // conv layer uses relu - use he init for weights - layer->params.conv_params.filters = (float***)malloc(num_filters * sizeof(float**)); - int fan_in = filter_height * filter_width * channels; - for (int f = 0; f < num_filters; f++) { - layer->params.conv_params.filters[f] = (float**)malloc(filter_height * sizeof(float*)); - for (int h = 0; h < filter_height; h++) { - layer->params.conv_params.filters[f][h] = (float*)malloc(filter_width * sizeof(float)); - for (int w = 0; w < filter_width; w++) { - layer->params.conv_params.filters[f][h][w] = he_uniform(fan_in); - } - } - } - - return layer; -} - -Layer* create_max_pool(int height, int width, int channels, int pool_height, int pool_width, int stride) { - Layer* layer = (Layer*)malloc(sizeof(Layer)); - layer->type = max_pool; - layer->height = height; - layer->width = width; - layer->channels = channels; - - layer->params.pool_params.pool_height = pool_height; - layer->params.pool_params.pool_width = pool_width; - layer->params.pool_params.stride = stride; - - return layer; -} - -Layer* create_fc(int input_neurons, int output_neurons, fcpos position) { - Layer* layer = (Layer*)malloc(sizeof(Layer)); - layer->type = fully_connected; - layer->height = 1; - layer->width = output_neurons; - layer->channels = 1; - - layer->params.fc_params.input_neurons = input_neurons; - layer->params.fc_params.output_neurons = output_neurons; - layer->params.fc_params.position = position; - - // use xav/glorot init b/c of sigmoid - layer->params.fc_params.weights = (float**)malloc(output_neurons * sizeof(float*)); - for (int i = 0; i < output_neurons; i++) { - layer->params.fc_params.weights[i] = (float*)malloc(input_neurons * sizeof(float)); - for (int j = 0; j < input_neurons; j++) { - layer->params.fc_params.weights[i][j] = glorot_uniform(input_neurons, output_neurons); - } - } - - return layer; -} - -void free_layer(Layer* layer) { - if (!layer) return; - switch (layer->type) { - case conv: - for (int f = 0; f < layer->params.conv_params.num_filters; f++) { - for (int h = 0; h < layer->params.conv_params.num_filters; h++) { - free(layer->params.conv_params.filters[f][h]); - } - free(layer->params.conv_params.filters[f]); - } - free(layer->params.conv_params.filters); - break; - case fully_connected: - for (int i = 0; i < layer->params.fc_params.output_neurons; i++) { - free(layer->params.fc_params.weights[i]); - } - free(layer->params.fc_params.weights); - free(layer->params.fc_params.biases); - break; - default: - break; - } - free(layer); -} - -