diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..39e9622 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,56 @@ +# This file is a template, and might need editing before it works on your project. +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# +# To contribute improvements to CI/CD templates, please follow the Development guide at: +# https://docs.gitlab.com/ee/development/cicd/templates.html +# This specific template is located at: +# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/C++.gitlab-ci.yml + +# use the official gcc image, based on debian +# can use versions as well, like gcc:5.2 +# see https://hub.docker.com/_/gcc/ + +image: gcc +stages: + - build + - release + +build: + stage: build + # instead of calling g++ directly you can also use some build toolkit like make + # install the necessary build tools when needed + before_script: + - apt update && apt -y install cmake + script: + - echo BUILD_JOB_ID=$CI_JOB_ID >> CI_JOB_ID.env + - echo "Compiling the code..." + - cmake . + - cmake --build . + + artifacts: + paths: + - c_net + reports: + dotenv: CI_JOB_ID.env + + + +release: + image: registry.gitlab.com/gitlab-org/release-cli:latest + stage: release + needs: + - job: build + + release: + tag_name: $CI_COMMIT_SHORT_SHA' + description: "latest" + + assets: + links: + - name: c_net linux download (precompiled) + url: '${CI_PROJECT_URL}/-/jobs/${BUILD_JOB_ID}/artifacts/file/c_net' + + script: echo "Define your deployment script!" + + diff --git a/CMakeLists.txt b/CMakeLists.txt index 5816779..461b2a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,5 +3,5 @@ project(c_net C) set(CMAKE_C_STANDARD 11) -add_executable(c_net main.c matrix/matrix.c image/image.c neuronal_network.c util.c matrix/operations.c) +add_executable(c_net main.c matrix.c image.c neuronal_network.c util.c util.h) target_link_libraries(c_net m) diff --git a/README.md b/README.md index 72ca9a6..5db1ec0 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # C-net ඞ ## Description -C-net ඞ is a Python project designed to read and predict numbers from the MNIST dataset using neural networks. +C-net ඞ is a C project designed to read and predict numbers from the MNIST dataset using neural networks. ## Visuals -![Insert GIF or Screenshot here](link_to_visual.gif) +![Insert GIF or Screenshot here](https://camo.githubusercontent.com/b308207b5c5ce0970b13c21609350fab21aa61a2fae56da2a6418d6fdcdbc079/68747470733a2f2f7777772e776f6c6672616d2e636f6d2f6d617468656d61746963612f6e65772d696e2d31302f656e68616e6365642d696d6167652d70726f63657373696e672f48544d4c496d616765732e656e2f68616e647772697474656e2d6469676974732d636c617373696669636174696f6e2f736d616c6c7468756d625f31302e676966) ## Roadmap @@ -23,8 +23,14 @@ This project was brought to you by the following contributors: - Dworski, Daniel - Walcher, Raphael -We would like to express our gratitude to the following project, which served as an inspiration and reference: +We would like to express our gratitude to the following sources, which served as an inspiration and reference: - [MNIST from Scratch](https://github.com/markkraay/mnist-from-scratch) by markkraay +- [Neural Network Framework in C](https://medium.com/analytics-vidhya/building-neural-network-framework-in-c-using-backpropagation-8ad589a0752d) +- [Simple Neural Network Implementation in C](https://towardsdatascience.com/simple-neural-network-implementation-in-c-663f51447547) +- [3Blue1Brown Neural Network Series](https://www.youtube.com/watch?v=aircAruvnKk&list=PLZHQObOWTQDNU6R1_67000Dx_ZCJB-3pi) +- [Brotcrunsher's YouTube Videos](https://www.youtube.com/watch?v=oCPT87SvkPM&pp=ygUbYnJvdCBjcnVzaGVyIG5ldXJhbCBuZXp3ZXJr), [Video 2](https://www.youtube.com/watch?v=YIqYBxpv53A&pp=ygUbYnJvdCBjcnVzaGVyIG5ldXJhbCBuZXp3ZXJr), [Video 3](https://youtu.be/EAtQCut6Qno) ## Project Status The project is considered finished, but ongoing optimizations and improvements may still be in progress. + +![amogus](https://media.tenor.com/7kpsm7kU330AAAAd/sussy-among-us.gif) \ No newline at end of file diff --git a/image/image.c b/image.c similarity index 90% rename from image/image.c rename to image.c index 419620d..f52fa2c 100644 --- a/image/image.c +++ b/image.c @@ -2,8 +2,8 @@ #include #include "image.h" -#include "../matrix/matrix.h" -#include "../util.h" +#include "matrix.h" +#include "util.h" void big_endian_to_c_uint(const char * bytes, void * target, int size) { char* helper = (char*)target; @@ -14,23 +14,27 @@ void big_endian_to_c_uint(const char * bytes, void * target, int size) { void read_until_space_or_newline(char * buff, int maxCount, FILE * fptr){ int bufferOffset = 0; - char c = -1; + char c; + int counter = 0; do{ c = (char)getc(fptr); buff[bufferOffset++] = c; - }while(!feof(fptr) && c != 0 && c != ' ' && c !='\n'); + }while(!feof(fptr) && c != 0 && c != ' ' && c !='\n' && counter++ < maxCount); buff[bufferOffset-1] = 0; } Image * load_pgm_image(char * image_file_string){ FILE * fptr = fopen(image_file_string, "r"); + if(!fptr){ + printf("could not open image file. exit\n"); + exit(1); + } Image *image = malloc(sizeof(Image)); image->label = -1; - char buffer[100]; - int magic_number = 0; + char buffer[2048]; fgets(buffer, 4, fptr); if(buffer[0] != 'P' || buffer[1] != '5'){ printf("Wrong file Format"); @@ -40,17 +44,16 @@ Image * load_pgm_image(char * image_file_string){ fgets(buffer, 1024, fptr); } - int image_width, image_height, image_length, image_white ; + int image_width, image_height, image_white ; read_until_space_or_newline(buffer, 10, fptr); - image_width = strtol(buffer, NULL, 10); + image_width = (int)strtol(buffer, NULL, 10); read_until_space_or_newline(buffer, 10, fptr); - image_height = strtol(buffer, NULL, 10); + image_height = (int)strtol(buffer, NULL, 10); read_until_space_or_newline(buffer, 10, fptr); - image_white = strtol(buffer, NULL, 10); + image_white = (int)strtol(buffer, NULL, 10); - image_length = image_width * image_height; image->pixel_values = matrix_create(image_height, image_width); for(int i = 0; i < image_height; i++){ @@ -159,6 +162,7 @@ Image** import_images(char* image_file_string, char* label_file_string, int* _nu } void img_print (Image* img) { + //print the image matrix_print(img->pixel_values); //print the number of the image @@ -182,7 +186,7 @@ void img_free (Image* img) { free(img); } -void images_free(Image** images, int quantity) { +void images_free (Image** images, int quantity){ //frees every single image for(int i=0;i -#include "image/image.h" +#include "image.h" #include "neuronal_network.h" +#include +#include +#include +#include "util.h" -int main() { - - const int amount_of_images_to_load = 60000; - const int amount_of_images_used_to_train = 30000; - const int amount_of_images_used_to_test = 1000; - const int input_size = 28*28; - const int hidden_layer_size = 50; - const int hidden_layer_count = 3; - const double learning_rate = 0.1; - - /* - * Loading Images from Dataset - */ - - Image** images = import_images("../data/train-images.idx3-ubyte", "../data/train-labels.idx1-ubyte", NULL, amount_of_images_to_load); - -// img_visualize(images[0]); -// img_print(images[0]); - - /* - * Create a new network and randomize the weights - */ - - Neural_Network* network = new_network(input_size, hidden_layer_size, hidden_layer_count, 10, learning_rate); - randomize_network(network, 1); - - /* - * Training - */ - - for (int i = 0; i < amount_of_images_used_to_train; i++) { - train_network(network, images[i], images[i]->label); - } - - // Batch training works if you change the train_network method, but the results are not that good (needs further testing) - // batch_train(nn, images, 30000, 2); - - printf("Trinaing Done!\n"); - - /* - * Saving and Loading - */ - -// save_network(network); -// Neural_Network* network = load_network("../networks/newest_network.txt"); - - /* - * Measure Accuracy & predict single images - */ - - printf("Accuracy: %lf\n", measure_network_accuracy(network, images, amount_of_images_used_to_test)); - -// matrix_print(predict_image(network, images[0])); - - images_free(images, amount_of_images_to_load); - free_network(network); - - return 0; +void parsingErrorPrintHelp(){ + printf("Syntax: c_net [train | predict]\n"); + printf("commands:\n"); + printf("train\t train the network\n"); + printf("predict\t load a pgm image and predict_demo the number\n"); + exit(1); } + +void parsingErrorTrain(){ + printf("invalid syntax\n"); + printf("Syntax: c_net train [path_to_train-images.idx3-ubyte] [path_to_train-labels.idx1-ubyte] [hidden_layer_count] [neurons_per_layer] [epochs] [learning_rate] [path_to_save_network]\n"); + exit(1); +} + +void parsingErrorDetect(){ + printf("invalid syntax\n"); + printf("Syntax: c_net predict_demo [path_to_network] [image_file]\n"); +} + +void predict_demo(int argc, char** arguments){ + if(argc != 2) parsingErrorDetect(); + char * network_file = arguments[0]; + char * image_file = arguments[1]; + + Neural_Network * nn = load_network(network_file); + Image * image = load_pgm_image(image_file); + Matrix * result = predict_image(nn, image); + int predicted = matrix_argmax(result); + printf("prediction result %d\n", predicted); + matrix_print(result); + matrix_free(result); +} + +void train(int argc, char** arguments) { + if (argc != 7) parsingErrorTrain(); + char *image_file = arguments[0]; + char *label_file = arguments[1]; + int hidden_count = (int) strtol(arguments[2], NULL, 10); + int neurons_per_layer = (int) strtol(arguments[3], NULL, 10); + int epochs = (int) strtol(arguments[4], NULL, 10); + if (errno != 0) { + printf("hidden_count, neurons_per_layer or epochs could not be parsed!\n"); + exit(1); + } + double learning_rate = strtod(arguments[5], NULL); + if (errno != 0) { + printf("learning_rate could not be parsed!\n"); + exit(1); + } + char *save_path = arguments[6]; + int imported = 0; + Image ** images = import_images(image_file, label_file, &imported, 60000); + Image ** evaluation_images = images+50000; + + int training_image_count = 50000; + int testing_image_count = 10000; + + Neural_Network *nn = new_network(28 * 28, neurons_per_layer, hidden_count, 10, learning_rate); + randomize_network(nn, 1); + printf("training_network\n"); + for(int epoch = 1; epoch <= epochs; epoch++){ + printf("epoch %d\n", epoch); + for (int i = 0; i < training_image_count; i++) { + if (i % 1000 == 0) { + updateBar(i * 100 / imported); + } + train_network(nn, images[i], images[i]->label); + } + updateBar(100); + printf("\n"); + printf("accuracy %lf\n", measure_network_accuracy(nn, evaluation_images, testing_image_count)); + } + printf("done training!\n"); + save_network(nn, save_path); +} + +int main(int argc, char** argv) { +// Image** images = import_images("../data/train-images.idx3-ubyte", "../data/train-labels.idx1-ubyte", NULL, 60000); +//// img_visualize(images[0]); +//// img_visualize(images[1]); +// +//// matrix_print(images[0]->pixel_values); +//// matrix_print(images[1]->pixel_values); +// +// Neural_Network* nn = new_network(28*28, 40, 5, 10, 0.08); +// randomize_network(nn, 1); +//// Neural_Network* nn = load_network("../networks/newest_network.txt"); +//// printf("Done loading!\n"); +// +//// batch_train(nn, images, 20000, 20); +// +// for (int i = 0; i < 30000; ++i) { +// train_network(nn, images[i], images[i]->label); +// } +// +// save_network(nn); +// +// printf("%lf\n", measure_network_accuracy(nn, images, 10000)); + if(argc < 2){ + parsingErrorPrintHelp(); + exit(1); + } + if(strcmp(argv[1], "train") == 0){ + train(argc-2, argv+2); + return 0; + } + if(strcmp(argv[1], "predict") == 0){ + predict_demo(argc - 2, argv + 2); + return 0; + } + parsingErrorPrintHelp(); + +} \ No newline at end of file diff --git a/matrix/operations.c b/matrix.c similarity index 64% rename from matrix/operations.c rename to matrix.c index d969e41..2a9acfc 100644 --- a/matrix/operations.c +++ b/matrix.c @@ -1,10 +1,92 @@ -#include +#include "matrix.h" #include +#include +#include #include -#include "math.h" -#include "operations.h" +#define MAX_BYTES 100 static int RANDOMIZED = 0; +// operational functions +Matrix* matrix_create(int rows, int columns) { + + // allocate memory for the matrix + Matrix* matrix = malloc(sizeof(Matrix)); + + // set size variables to the correct size + matrix->rows = rows; + matrix->columns = columns; + + // allocate memory for the numbers (2D-Array) + matrix->numbers = malloc(sizeof(double*) * rows); + for (int i = 0; i < rows; i++) { + matrix->numbers[i] = calloc(sizeof(double), columns); + } + + // return the pointer to the allocated memory + return matrix; +} + +void matrix_fill(Matrix* matrix, double value) { + + // simple for loop to populate the 2D-array with a value + for (int i = 0; i < matrix->rows; i++) { + for (int j = 0; j < matrix->columns; j++) { + matrix->numbers[i][j] = value; + } + } +} + +void matrix_free(Matrix* matrix) { + + // de-allocate every column + for (int i = 0; i < matrix->rows; i++) { + free(matrix->numbers[i]); + } + + // de-allocate the rows + free(matrix->numbers); + + // de-allocate the matrix + free(matrix); +} + +void matrix_print(Matrix *matrix) { + + // print the dimensions of the matrix + printf("Rows: %d, Columns: %d\n", matrix->rows, matrix->columns); + + // loop through all values and format them into the correct matrix representation + for (int i = 0; i < matrix->rows; i++) { + for (int j = 0; j < matrix->columns; j++) { + printf("%lf ", matrix->numbers[i][j]); + } + printf("\n"); + } +} + +Matrix* matrix_copy(Matrix *matrix) { + + // create another matrix of the same size + Matrix* copy_of_matrix = matrix_create(matrix->rows, matrix->columns); + + // copy the values from the original matrix into the copy + for (int i = 0; i < matrix->rows; i++) { + for (int j = 0; j < matrix->columns; j++) { + copy_of_matrix->numbers[i][j] = matrix->numbers[i][j]; + } + } + + // return the pointer to the copy + return copy_of_matrix; +} + +// mathematical functions + +/* + * These methods won't change or free the input matrix. + * It creates a new matrix, which is modified and then returned. + * If we don't need the original matrix, we should consider just changing the original matrix and changing the method signature to void. + */ Matrix* multiply(Matrix* matrix1, Matrix* matrix2) { @@ -134,6 +216,7 @@ Matrix* scale(Matrix* matrix, double value) { return result_matrix; } + Matrix* transpose(Matrix* matrix) { // create a new matrix of the size n-m, based on the original matrix of size m-n @@ -151,6 +234,67 @@ Matrix* transpose(Matrix* matrix) { } +void matrix_save(Matrix* matrix, char* file_string){ + + // open the file in append mode + FILE *file = fopen(file_string, "a"); + + // check if the file could be found + if(file == NULL) { + printf("ERROR: Unable to get handle for \"%s\"! (matrix_save)", file_string); + exit(1); + } + + // save the size of the matrix + fprintf(file, "%d\n", matrix->rows); + fprintf(file, "%d\n", matrix->columns); + + // save all the numbers of the matrix into the file + for(int i = 0; i < matrix->rows; i++){ + for(int j = 0; j < matrix->columns; j++){ + fprintf(file, "%.10f\n", matrix->numbers[i][j]); + } + } + + // close the file + fclose(file); +} + +Matrix* matrix_load(char* file_string){ + + FILE *fptr = fopen(file_string, "r"); + + if(!fptr){ + printf("Could not open \"%s\"", file_string); + exit(1); + } + + Matrix * m = load_next_matrix(fptr); + + fclose(fptr); + return m; +} + +Matrix* load_next_matrix(FILE *save_file){ + + char buffer[MAX_BYTES]; + + fgets(buffer, MAX_BYTES, save_file); + int rows = (int)strtol(buffer, NULL, 10); + fgets(buffer, MAX_BYTES, save_file); + int cols = (int)strtol(buffer, NULL, 10); + + Matrix *matrix = matrix_create(rows, cols); + + for(int i = 0; i < rows; i++){ + for(int j = 0; j < cols; j++){ + fgets(buffer, MAX_BYTES, save_file); + matrix->numbers[i][j] = strtod(buffer, NULL); + } + } + return matrix; +} + Matrix* matrix_flatten(Matrix* matrix, int axis) { // Axis = 0 -> Column Vector, Axis = 1 -> Row Vector Matrix* result_matrix; @@ -174,10 +318,10 @@ Matrix* matrix_flatten(Matrix* matrix, int axis) { return result_matrix; } -int argmax(Matrix* matrix) { +int matrix_argmax(Matrix* matrix) { // Expects a Mx1 matrix if (matrix->columns != 1){ - printf("ERROR: Matrix is not Mx1 (argmax)"); + printf("ERROR: Matrix is not Mx1 (matrix_argmax)"); exit(EXIT_FAILURE); } @@ -208,7 +352,7 @@ void matrix_randomize(Matrix* matrix, int n) { //move decimal int scaled_difference = (int)(difference * scaling_value); - + for (int i = 0; i < matrix->rows; i++) { for (int j = 0; j < matrix->columns; j++) { matrix->numbers[i][j] = min + (1.0 * (rand() % scaled_difference) / scaling_value); diff --git a/matrix.h b/matrix.h new file mode 100644 index 0000000..4ece217 --- /dev/null +++ b/matrix.h @@ -0,0 +1,39 @@ +#pragma once +#include + +typedef struct { + int rows, columns; + double **numbers; +} Matrix; + +static const int scaling_value = 10000; + +// operational functions +Matrix* matrix_create(int rows, int columns); +void matrix_fill(Matrix* matrix, double value); +void matrix_free(Matrix* matrix); +void matrix_print(Matrix *matrix); +Matrix* matrix_copy(Matrix *matrix); +void matrix_save(Matrix* matrix, char* file_string); +Matrix* matrix_load(char* file_string); +Matrix* load_next_matrix(FILE * save_file); + +void matrix_randomize(Matrix* matrix, int n); // don't understand the usage of the n +int matrix_argmax(Matrix* matrix); +Matrix* matrix_flatten(Matrix* matrix, int axis); +Matrix* matrix_add_bias(Matrix* matrix); + +/* + * These methods won't change or free the input matrix. + * It creates a new matrix, which is modified and then returned. + * If we don't need the original matrix, we should consider just changing the original matrix and changing the method signature to void. + */ + +// mathematical functions +Matrix* multiply(Matrix* matrix1, Matrix* matrix2); +Matrix* add(Matrix* matrix1, Matrix* matrix2); +Matrix* subtract(Matrix* matrix1, Matrix* matrix2); +Matrix* dot(Matrix* matrix1, Matrix* matrix2); +Matrix* apply(double (*function)(double), Matrix* matrix); +Matrix* scale(Matrix* matrix, double value); +Matrix* transpose(Matrix* matrix); diff --git a/matrix/matrix.c b/matrix/matrix.c deleted file mode 100644 index bec2425..0000000 --- a/matrix/matrix.c +++ /dev/null @@ -1,139 +0,0 @@ -#include "matrix.h" -#include -#include - -#define MAX_BYTES 100 - -Matrix* matrix_create(int rows, int columns) { - - // allocate memory for the matrix - Matrix* matrix = malloc(sizeof(Matrix)); - - // set size variables to the correct size - matrix->rows = rows; - matrix->columns = columns; - - // allocate memory for the numbers (2D-Array) - matrix->numbers = malloc(sizeof(double*) * rows); - for (int i = 0; i < rows; i++) { - matrix->numbers[i] = calloc(sizeof(double), columns); - } - - // return the pointer to the allocated memory - return matrix; -} - -void matrix_fill(Matrix* matrix, double value) { - - // simple for loop to populate the 2D-array with a value - for (int i = 0; i < matrix->rows; i++) { - for (int j = 0; j < matrix->columns; j++) { - matrix->numbers[i][j] = value; - } - } -} - -void matrix_free(Matrix* matrix) { - - // de-allocate every column - for (int i = 0; i < matrix->rows; i++) { - free(matrix->numbers[i]); - } - - // de-allocate the rows - free(matrix->numbers); - - // de-allocate the matrix - free(matrix); -} - -void matrix_print(Matrix *matrix) { - - // print the dimensions of the matrix - printf("Rows: %d, Columns: %d\n", matrix->rows, matrix->columns); - - // loop through all values and format them into the correct matrix representation - for (int i = 0; i < matrix->rows; i++) { - for (int j = 0; j < matrix->columns; j++) { - printf("%lf ", matrix->numbers[i][j]); - } - printf("\n"); - } -} - -Matrix* matrix_copy(Matrix *matrix) { - - // create another matrix of the same size - Matrix* copy_of_matrix = matrix_create(matrix->rows, matrix->columns); - - // copy the values from the original matrix into the copy - for (int i = 0; i < matrix->rows; i++) { - for (int j = 0; j < matrix->columns; j++) { - copy_of_matrix->numbers[i][j] = matrix->numbers[i][j]; - } - } - - // return the pointer to the copy - return copy_of_matrix; -} - -void matrix_save(Matrix* matrix, char* file_string){ - - // open the file in append mode - FILE *file = fopen(file_string, "a"); - - // check if the file could be found - if(file == NULL) { - printf("ERROR: Unable to get handle for \"%s\"! (matrix_save)", file_string); - exit(1); - } - - // save the size of the matrix - fprintf(file, "%d\n", matrix->rows); - fprintf(file, "%d\n", matrix->columns); - - // save all the numbers of the matrix into the file - for(int i = 0; i < matrix->rows; i++){ - for(int j = 0; j < matrix->columns; j++){ - fprintf(file, "%.10f\n", matrix->numbers[i][j]); - } - } - - // close the file - fclose(file); -} - -Matrix* matrix_load(char* file_string){ - - FILE *fptr = fopen(file_string, "r"); - - if(!fptr){ - printf("Could not open \"%s\"", file_string); - exit(1); - } - - Matrix * m = load_next_matrix(fptr); - - fclose(fptr); - return m; -} - -Matrix* load_next_matrix(FILE *save_file){ - - char buffer[MAX_BYTES]; - - fgets(buffer, MAX_BYTES, save_file); - int rows = (int)strtol(buffer, NULL, 10); - fgets(buffer, MAX_BYTES, save_file); - int cols = (int)strtol(buffer, NULL, 10); - - Matrix *matrix = matrix_create(rows, cols); - - for(int i = 0; i < rows; i++){ - for(int j = 0; j < cols; j++){ - fgets(buffer, MAX_BYTES, save_file); - matrix->numbers[i][j] = strtod(buffer, NULL); - } - } - return matrix; -} \ No newline at end of file diff --git a/matrix/matrix.h b/matrix/matrix.h deleted file mode 100644 index ea37f59..0000000 --- a/matrix/matrix.h +++ /dev/null @@ -1,15 +0,0 @@ -#include - -typedef struct { - int rows, columns; - double **numbers; -} Matrix; - -Matrix* matrix_create(int rows, int columns); -void matrix_fill(Matrix* matrix, double value); -void matrix_free(Matrix* matrix); -void matrix_print(Matrix *matrix); -Matrix* matrix_copy(Matrix *matrix); -void matrix_save(Matrix* matrix, char* file_string); -Matrix* matrix_load(char* file_string); -Matrix* load_next_matrix(FILE * save_file); \ No newline at end of file diff --git a/matrix/operations.h b/matrix/operations.h deleted file mode 100644 index e4f0220..0000000 --- a/matrix/operations.h +++ /dev/null @@ -1,25 +0,0 @@ -#include "matrix.h" - -static const int scaling_value = 10000; - -Matrix* multiply(Matrix* matrix1, Matrix* matrix2); - -Matrix* add(Matrix* matrix1, Matrix* matrix2); //only used in the batch_training method - -Matrix* subtract(Matrix* matrix1, Matrix* matrix2); - -Matrix* dot(Matrix* matrix1, Matrix* matrix2); - -Matrix* apply(double (*function)(double), Matrix* matrix); - -Matrix* scale(Matrix* matrix, double value); - -Matrix* transpose(Matrix* matrix); - -Matrix* matrix_flatten(Matrix* matrix, int axis); - -int argmax(Matrix* matrix); - -void matrix_randomize(Matrix* matrix, int n); - -Matrix* matrix_add_bias(Matrix* matrix); \ No newline at end of file diff --git a/neuronal_network.c b/neuronal_network.c index 650633c..756b71d 100644 --- a/neuronal_network.c +++ b/neuronal_network.c @@ -1,14 +1,14 @@ #include #include "neuronal_network.h" -#include "matrix\operations.h" #include #include +#include "util.h" double sigmoid(double input); Matrix* predict(Neural_Network* network, Matrix* image_data); Matrix* sigmoid_derivative(Matrix* matrix); -Matrix *calculate_weights_delta(Matrix *previous_layer_output, Matrix *delta_matrix); -void apply_weights(Neural_Network *network, Matrix *delta_weights_matrix, int index, double learning_rate); +Matrix *calculate_weights_delta(Matrix *previous_layer_output, Matrix *delta_matrix, double learning_rate); +void apply_weights(Neural_Network* network, Matrix* delta_weights_matrix, int index); Matrix* calculate_delta_hidden(Matrix* next_layer_delta, Matrix* weights, Matrix* current_layer_output); Neural_Network* new_network(int input_size, int hidden_size, int hidden_amount, int output_size, double learning_rate){ @@ -46,9 +46,7 @@ void free_network(Neural_Network* network){ free(network); } -void save_network(Neural_Network* network) { - - char* file_name = "../networks/newest_network.txt"; +void save_network(Neural_Network* network, char * file_name) { // create file FILE* save_file = fopen(file_name, "w"); @@ -118,10 +116,13 @@ void print_network(Neural_Network* network) { double measure_network_accuracy(Neural_Network* network, Image** images, int amount) { int num_correct = 0; + printf("evaluating network\n"); + if(amount > 10000) amount = 10000; for (int i = 0; i < amount; i++) { + updateBar(i*100/amount); Matrix* prediction = predict_image(network, images[i]); - int guess = argmax(prediction); + int guess = matrix_argmax(prediction); int answer = (unsigned char) images[i]->label; if (guess == answer) { @@ -130,6 +131,7 @@ double measure_network_accuracy(Neural_Network* network, Image** images, int amo matrix_free(prediction); } + updateBar(100); return ((double) num_correct) / amount; } @@ -168,26 +170,22 @@ Matrix* predict(Neural_Network* network, Matrix* image_data) { //void batch_train(Neural_Network* network, Image** images, int amount, int batch_size) { // -// if(amount % batch_size != 0) { -// printf("ERROR: Batch Size is not compatible with image amount! (batch_train)"); -// exit(1); -// } +// for (int i = 0; i < amount; ++i) { // -// int image_index = 0; -// -// for (int i = 0; i < amount / batch_size; ++i) { +// if(amount % 1000 == 0) { +// printf("1k pics!\n"); +// } // // Matrix* batch_weights[network->hidden_amount + 1]; // -// for (int j = 0; j < network->hidden_amount + 1; j++) { -// batch_weights[j] = matrix_create(network->weights[j]->rows, network->weights[j]->columns); -// matrix_fill(batch_weights[j], 0); -// } -// // for (int j = 0; j < batch_size; ++j) { -// Matrix** delta_weights = train_network(network, images[image_index], images[image_index]->label); +// Matrix** delta_weights = train_network(network, images[i], images[i]->label); // // for (int k = 0; k < network->hidden_amount + 1; k++) { +// if(j == 0) { +// batch_weights[k] = delta_weights[k]; +// continue; +// } // // Matrix* temp_result = add(batch_weights[k], delta_weights[k]); // @@ -198,16 +196,14 @@ Matrix* predict(Neural_Network* network, Matrix* image_data) { // } // // free(delta_weights); -// -// image_index++; // } // -// for (int j = 0; j < network->hidden_amount + 1; j++) { +// for (int j = 0; j < network->hidden_amount + 1; ++j) { // Matrix* average_delta_weight = scale(batch_weights[j], (1.0 / batch_size)); -// apply_weights(network, average_delta_weight, j, network->learning_rate); +// apply_weights(network, average_delta_weight, j); // -// matrix_free(batch_weights[j]); // matrix_free(average_delta_weight); +// matrix_free(batch_weights[j]); // } // } //} @@ -246,13 +242,13 @@ void train_network(Neural_Network* network, Image *image, int label) { Matrix* delta = multiply(sigmoid_prime, error); //calculate and apply the delta for all weights in out-put layer - delta_weights[network->hidden_amount] = calculate_weights_delta(output[network->hidden_amount - 1], delta); + delta_weights[network->hidden_amount] = calculate_weights_delta(output[network->hidden_amount - 1], delta, network->learning_rate); //hidden layers Matrix* previous_delta = delta; for (int i = network->hidden_amount; i > 1; i--) { delta = calculate_delta_hidden(previous_delta, network->weights[i], output[i - 1]); - delta_weights[i - 1] = calculate_weights_delta(output[i - 2], delta); + delta_weights[i - 1] = calculate_weights_delta(output[i - 2], delta, network->learning_rate); matrix_free(previous_delta); previous_delta = delta; @@ -260,16 +256,10 @@ void train_network(Neural_Network* network, Image *image, int label) { // Input Layer delta = calculate_delta_hidden(previous_delta, network->weights[1], output[0]); - delta_weights[0] = calculate_weights_delta(image_data, delta); - - - // if you want to use this method as a standalone method this part needs to be uncommented - for (int i = 0; i < network->hidden_amount + 1; ++i) { - apply_weights(network, delta_weights[i], i, network->learning_rate); - } + delta_weights[0] = calculate_weights_delta(image_data, delta, network->learning_rate); for (int i = 0; i < network->hidden_amount + 1; ++i) { - matrix_free(delta_weights[i]); + apply_weights(network, delta_weights[i], i); } // De-allocate stuff @@ -280,7 +270,9 @@ void train_network(Neural_Network* network, Image *image, int label) { matrix_free(output[i]); } - + for (int i = 0; i < network->hidden_amount + 1; ++i) { + matrix_free(delta_weights[i]); + } matrix_free(sigmoid_prime); matrix_free(wanted_output); @@ -319,7 +311,7 @@ Matrix* calculate_delta_hidden(Matrix* next_layer_delta, Matrix* weights, Matrix return new_deltas; } -void apply_weights(Neural_Network *network, Matrix *delta_weights_matrix, int index, double learning_rate) { +void apply_weights(Neural_Network* network, Matrix* delta_weights_matrix, int index) { if(index > network->hidden_amount || index < 0) { printf("ERROR: Index out of range! (apply_weights)"); @@ -331,28 +323,27 @@ void apply_weights(Neural_Network *network, Matrix *delta_weights_matrix, int in exit(1); } - // scale by learning rate - Matrix* scaled_delta_weights_matrix = scale(delta_weights_matrix, learning_rate); - for (int i = 0; i < delta_weights_matrix->rows; i++) { - for (int j = 0; j < scaled_delta_weights_matrix->columns; j++) { - network->weights[index]->numbers[i][j] += scaled_delta_weights_matrix->numbers[i][j]; // multiply delta_weights_matrix with learning rate AND - instead of + because soll-ist + for (int j = 0; j < delta_weights_matrix->columns; j++) { + network->weights[index]->numbers[i][j] += delta_weights_matrix->numbers[i][j]; // multiply delta_weights_matrix with learning rate AND - instead of + because soll-ist } } - - matrix_free(scaled_delta_weights_matrix); } -Matrix *calculate_weights_delta(Matrix *previous_layer_output, Matrix *delta_matrix) { +Matrix *calculate_weights_delta(Matrix *previous_layer_output, Matrix *delta_matrix, double learning_rate) { Matrix* previous_out_with_one = matrix_add_bias(previous_layer_output); Matrix* transposed_previous_out_with_bias = transpose(previous_out_with_one); Matrix* weights_delta_matrix = dot(delta_matrix, transposed_previous_out_with_bias); + // scale by learning rate + Matrix* result = scale(weights_delta_matrix, learning_rate); + matrix_free(previous_out_with_one); matrix_free(transposed_previous_out_with_bias); + matrix_free(weights_delta_matrix); - return weights_delta_matrix; + return result; } Matrix* sigmoid_derivative(Matrix* matrix) { diff --git a/neuronal_network.h b/neuronal_network.h index 80a48a2..34ccde0 100644 --- a/neuronal_network.h +++ b/neuronal_network.h @@ -1,6 +1,6 @@ -#include "matrix/matrix.h" -#include "image/image.h" +#include "matrix.h" +#include "image.h" typedef struct { int input_size; @@ -21,7 +21,7 @@ Neural_Network* new_network(int input_size, int hidden_size, int hidden_amount, void randomize_network(Neural_Network* network, int scope); void free_network(Neural_Network* network); -void save_network(Neural_Network* network); +void save_network(Neural_Network* network, char * file_name); Neural_Network* load_network(char* file); void print_network(Neural_Network* network);