refactor. simplify code - only gonna use mnist
This commit is contained in:
parent
576cb9c490
commit
8eab98586c
179
cnn.c
Normal file
179
cnn.c
Normal file
@ -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 <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
171
cnn.h
171
cnn.h
@ -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 <cstdlib>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user