diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 39e9622..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,56 +0,0 @@ -# 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 461b2a6..5816779 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.c image.c neuronal_network.c util.c util.h) +add_executable(c_net main.c matrix/matrix.c image/image.c neuronal_network.c util.c matrix/operations.c) target_link_libraries(c_net m) diff --git a/README.md b/README.md index 5db1ec0..72ca9a6 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # C-net ඞ ## Description -C-net ඞ is a C project designed to read and predict numbers from the MNIST dataset using neural networks. +C-net ඞ is a Python project designed to read and predict numbers from the MNIST dataset using neural networks. ## Visuals -![Insert GIF or Screenshot here](https://camo.githubusercontent.com/b308207b5c5ce0970b13c21609350fab21aa61a2fae56da2a6418d6fdcdbc079/68747470733a2f2f7777772e776f6c6672616d2e636f6d2f6d617468656d61746963612f6e65772d696e2d31302f656e68616e6365642d696d6167652d70726f63657373696e672f48544d4c496d616765732e656e2f68616e647772697474656e2d6469676974732d636c617373696669636174696f6e2f736d616c6c7468756d625f31302e676966) +![Insert GIF or Screenshot here](link_to_visual.gif) ## Roadmap @@ -23,14 +23,8 @@ This project was brought to you by the following contributors: - Dworski, Daniel - Walcher, Raphael -We would like to express our gratitude to the following sources, which served as an inspiration and reference: +We would like to express our gratitude to the following project, 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.h b/image.h deleted file mode 100644 index 6d3caeb..0000000 --- a/image.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -#include "matrix.h" - -#include "matrix.h" - -typedef struct { - Matrix* pixel_values; - char label; -} Image; - -typedef struct { - const Image* image; - const size_t size; -} Image_Container; - -static const int MAGIC_NUMBER_LABEL = 2049; - -static const int MAGIC_NUMBER_IMAGES = 2051; - -/** - * reads a specified number of images out of the training dataset - * @param image_file_string Path to the file containing the image data - * @param label_file_string Path to the file containing the image labels - * @param ptr via this pointer, the images can be accessed - * @param count maximum number of images to be loaded. If it is 0, all available images are loaded. - * @return - */ -Image ** import_images(char* image_file_string, char* label_file_string, int* number_imported, int count); -Image * load_pgm_image(char * image_file_string); -void img_print (Image* image); -void img_visualize(Image*image); -void img_free (Image* image); \ No newline at end of file diff --git a/image.c b/image/image.c similarity index 90% rename from image.c rename to image/image.c index f52fa2c..419620d 100644 --- a/image.c +++ b/image/image.c @@ -2,8 +2,8 @@ #include #include "image.h" -#include "matrix.h" -#include "util.h" +#include "../matrix/matrix.h" +#include "../util.h" void big_endian_to_c_uint(const char * bytes, void * target, int size) { char* helper = (char*)target; @@ -14,27 +14,23 @@ 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; - int counter = 0; + char c = -1; do{ c = (char)getc(fptr); buff[bufferOffset++] = c; - }while(!feof(fptr) && c != 0 && c != ' ' && c !='\n' && counter++ < maxCount); + }while(!feof(fptr) && c != 0 && c != ' ' && c !='\n'); 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[2048]; + char buffer[100]; + int magic_number = 0; fgets(buffer, 4, fptr); if(buffer[0] != 'P' || buffer[1] != '5'){ printf("Wrong file Format"); @@ -44,16 +40,17 @@ Image * load_pgm_image(char * image_file_string){ fgets(buffer, 1024, fptr); } - int image_width, image_height, image_white ; + int image_width, image_height, image_length, image_white ; read_until_space_or_newline(buffer, 10, fptr); - image_width = (int)strtol(buffer, NULL, 10); + image_width = strtol(buffer, NULL, 10); read_until_space_or_newline(buffer, 10, fptr); - image_height = (int)strtol(buffer, NULL, 10); + image_height = strtol(buffer, NULL, 10); read_until_space_or_newline(buffer, 10, fptr); - image_white = (int)strtol(buffer, NULL, 10); + image_white = 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++){ @@ -162,7 +159,6 @@ 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 @@ -186,7 +182,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.h" +#include "image/image.h" #include "neuronal_network.h" -#include -#include -#include -#include "util.h" -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); +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 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.h b/matrix.h deleted file mode 100644 index 4ece217..0000000 --- a/matrix.h +++ /dev/null @@ -1,39 +0,0 @@ -#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 new file mode 100644 index 0000000..bec2425 --- /dev/null +++ b/matrix/matrix.c @@ -0,0 +1,139 @@ +#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 new file mode 100644 index 0000000..ea37f59 --- /dev/null +++ b/matrix/matrix.h @@ -0,0 +1,15 @@ +#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.c b/matrix/operations.c similarity index 64% rename from matrix.c rename to matrix/operations.c index 2a9acfc..d969e41 100644 --- a/matrix.c +++ b/matrix/operations.c @@ -1,92 +1,10 @@ -#include "matrix.h" +#include #include -#include -#include #include -#define MAX_BYTES 100 +#include "math.h" +#include "operations.h" 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) { @@ -216,7 +134,6 @@ 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 @@ -234,67 +151,6 @@ 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; @@ -318,10 +174,10 @@ Matrix* matrix_flatten(Matrix* matrix, int axis) { return result_matrix; } -int matrix_argmax(Matrix* matrix) { +int argmax(Matrix* matrix) { // Expects a Mx1 matrix if (matrix->columns != 1){ - printf("ERROR: Matrix is not Mx1 (matrix_argmax)"); + printf("ERROR: Matrix is not Mx1 (argmax)"); exit(EXIT_FAILURE); } @@ -352,7 +208,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/operations.h b/matrix/operations.h new file mode 100644 index 0000000..e4f0220 --- /dev/null +++ b/matrix/operations.h @@ -0,0 +1,25 @@ +#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 756b71d..650633c 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, double learning_rate); -void apply_weights(Neural_Network* network, Matrix* delta_weights_matrix, int index); +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_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,7 +46,9 @@ void free_network(Neural_Network* network){ free(network); } -void save_network(Neural_Network* network, char * file_name) { +void save_network(Neural_Network* network) { + + char* file_name = "../networks/newest_network.txt"; // create file FILE* save_file = fopen(file_name, "w"); @@ -116,13 +118,10 @@ 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 = matrix_argmax(prediction); + int guess = argmax(prediction); int answer = (unsigned char) images[i]->label; if (guess == answer) { @@ -131,7 +130,6 @@ double measure_network_accuracy(Neural_Network* network, Image** images, int amo matrix_free(prediction); } - updateBar(100); return ((double) num_correct) / amount; } @@ -170,22 +168,26 @@ Matrix* predict(Neural_Network* network, Matrix* image_data) { //void batch_train(Neural_Network* network, Image** images, int amount, int batch_size) { // -// for (int i = 0; i < amount; ++i) { +// if(amount % batch_size != 0) { +// printf("ERROR: Batch Size is not compatible with image amount! (batch_train)"); +// exit(1); +// } // -// if(amount % 1000 == 0) { -// printf("1k pics!\n"); -// } +// int image_index = 0; +// +// for (int i = 0; i < amount / batch_size; ++i) { // // 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[i], images[i]->label); +// Matrix** delta_weights = train_network(network, images[image_index], images[image_index]->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]); // @@ -196,14 +198,16 @@ 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); +// apply_weights(network, average_delta_weight, j, network->learning_rate); // -// matrix_free(average_delta_weight); // matrix_free(batch_weights[j]); +// matrix_free(average_delta_weight); // } // } //} @@ -242,13 +246,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, network->learning_rate); + delta_weights[network->hidden_amount] = calculate_weights_delta(output[network->hidden_amount - 1], delta); //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, network->learning_rate); + delta_weights[i - 1] = calculate_weights_delta(output[i - 2], delta); matrix_free(previous_delta); previous_delta = delta; @@ -256,10 +260,16 @@ 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, network->learning_rate); + 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); + } for (int i = 0; i < network->hidden_amount + 1; ++i) { - apply_weights(network, delta_weights[i], i); + matrix_free(delta_weights[i]); } // De-allocate stuff @@ -270,9 +280,7 @@ 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); @@ -311,7 +319,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) { +void apply_weights(Neural_Network *network, Matrix *delta_weights_matrix, int index, double learning_rate) { if(index > network->hidden_amount || index < 0) { printf("ERROR: Index out of range! (apply_weights)"); @@ -323,27 +331,28 @@ 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 < 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 + 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 } } + + matrix_free(scaled_delta_weights_matrix); } -Matrix *calculate_weights_delta(Matrix *previous_layer_output, Matrix *delta_matrix, double learning_rate) { +Matrix *calculate_weights_delta(Matrix *previous_layer_output, Matrix *delta_matrix) { 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 result; + return weights_delta_matrix; } Matrix* sigmoid_derivative(Matrix* matrix) { diff --git a/neuronal_network.h b/neuronal_network.h index 34ccde0..80a48a2 100644 --- a/neuronal_network.h +++ b/neuronal_network.h @@ -1,6 +1,6 @@ -#include "matrix.h" -#include "image.h" +#include "matrix/matrix.h" +#include "image/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, char * file_name); +void save_network(Neural_Network* network); Neural_Network* load_network(char* file); void print_network(Neural_Network* network);