Compare commits

..

4 commits

Author SHA1 Message Date
Raphael Walcher
3a8ab15bf2 Merge remote-tracking branch 'origin/Delta-Error-Test' into Delta-Error-Test 2023-09-24 20:41:32 +02:00
Raphael Walcher
2a4fbf9bbd readme 2023-09-24 20:41:12 +02:00
Thomas
cf8b0a8b94 Clean up (1) 2023-09-24 12:22:28 +02:00
Thomas
f836c53711 Clean up (before drastic refactoring) 2023-09-24 11:54:55 +02:00
14 changed files with 330 additions and 460 deletions

View file

@ -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!"

View file

@ -3,5 +3,5 @@ project(c_net C)
set(CMAKE_C_STANDARD 11) 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) target_link_libraries(c_net m)

View file

@ -1,10 +1,10 @@
# C-net ඞ # C-net ඞ
## Description ## 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 ## Visuals
![Insert GIF or Screenshot here](https://camo.githubusercontent.com/b308207b5c5ce0970b13c21609350fab21aa61a2fae56da2a6418d6fdcdbc079/68747470733a2f2f7777772e776f6c6672616d2e636f6d2f6d617468656d61746963612f6e65772d696e2d31302f656e68616e6365642d696d6167652d70726f63657373696e672f48544d4c496d616765732e656e2f68616e647772697474656e2d6469676974732d636c617373696669636174696f6e2f736d616c6c7468756d625f31302e676966) ![Insert GIF or Screenshot here](link_to_visual.gif)
## Roadmap ## Roadmap
@ -23,14 +23,8 @@ This project was brought to you by the following contributors:
- Dworski, Daniel - Dworski, Daniel
- Walcher, Raphael - 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 - [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 ## Project Status
The project is considered finished, but ongoing optimizations and improvements may still be in progress. 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)

32
image.h
View file

@ -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);

View file

@ -2,8 +2,8 @@
#include <stdlib.h> #include <stdlib.h>
#include "image.h" #include "image.h"
#include "matrix.h" #include "../matrix/matrix.h"
#include "util.h" #include "../util.h"
void big_endian_to_c_uint(const char * bytes, void * target, int size) { void big_endian_to_c_uint(const char * bytes, void * target, int size) {
char* helper = (char*)target; 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){ void read_until_space_or_newline(char * buff, int maxCount, FILE * fptr){
int bufferOffset = 0; int bufferOffset = 0;
char c; char c = -1;
int counter = 0;
do{ do{
c = (char)getc(fptr); c = (char)getc(fptr);
buff[bufferOffset++] = c; 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; buff[bufferOffset-1] = 0;
} }
Image * load_pgm_image(char * image_file_string){ Image * load_pgm_image(char * image_file_string){
FILE * fptr = fopen(image_file_string, "r"); 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 *image = malloc(sizeof(Image));
image->label = -1; image->label = -1;
char buffer[2048]; char buffer[100];
int magic_number = 0;
fgets(buffer, 4, fptr); fgets(buffer, 4, fptr);
if(buffer[0] != 'P' || buffer[1] != '5'){ if(buffer[0] != 'P' || buffer[1] != '5'){
printf("Wrong file Format"); printf("Wrong file Format");
@ -44,16 +40,17 @@ Image * load_pgm_image(char * image_file_string){
fgets(buffer, 1024, fptr); 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); 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); 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); 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); image->pixel_values = matrix_create(image_height, image_width);
for(int i = 0; i < image_height; i++){ 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) { void img_print (Image* img) {
//print the image //print the image
matrix_print(img->pixel_values); matrix_print(img->pixel_values);
//print the number of the image //print the number of the image
@ -186,7 +182,7 @@ void img_free (Image* img) {
free(img); free(img);
} }
void images_free (Image** images, int quantity){ void images_free(Image** images, int quantity) {
//frees every single image //frees every single image
for(int i=0;i<quantity;i++){ for(int i=0;i<quantity;i++){
img_free(images[i]); img_free(images[i]);

21
image/image.h Normal file
View file

@ -0,0 +1,21 @@
#include "../matrix/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;
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);
void images_free (Image** images, int quantity);

160
main.c
View file

@ -1,122 +1,64 @@
#include <stdio.h> #include <stdio.h>
#include "image.h" #include "image/image.h"
#include "neuronal_network.h" #include "neuronal_network.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "util.h"
void parsingErrorPrintHelp(){ int main() {
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(){ const int amount_of_images_to_load = 60000;
printf("invalid syntax\n"); const int amount_of_images_used_to_train = 30000;
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"); const int amount_of_images_used_to_test = 1000;
exit(1); const int input_size = 28*28;
} const int hidden_layer_size = 50;
const int hidden_layer_count = 3;
const double learning_rate = 0.1;
void parsingErrorDetect(){ /*
printf("invalid syntax\n"); * Loading Images from Dataset
printf("Syntax: c_net predict_demo [path_to_network] [image_file]\n"); */
}
void predict_demo(int argc, char** arguments){ Image** images = import_images("../data/train-images.idx3-ubyte", "../data/train-labels.idx1-ubyte", NULL, amount_of_images_to_load);
if(argc != 2) parsingErrorDetect();
char * network_file = arguments[0];
char * image_file = arguments[1];
Neural_Network * nn = load_network(network_file); // img_visualize(images[0]);
Image * image = load_pgm_image(image_file); // img_print(images[0]);
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(); * Create a new network and randomize the weights
char *image_file = arguments[0]; */
char *label_file = arguments[1];
int hidden_count = (int) strtol(arguments[2], NULL, 10); Neural_Network* network = new_network(input_size, hidden_layer_size, hidden_layer_count, 10, learning_rate);
int neurons_per_layer = (int) strtol(arguments[3], NULL, 10); randomize_network(network, 1);
int epochs = (int) strtol(arguments[4], NULL, 10);
if (errno != 0) { /*
printf("hidden_count, neurons_per_layer or epochs could not be parsed!\n"); * Training
exit(1); */
for (int i = 0; i < amount_of_images_used_to_train; i++) {
train_network(network, images[i], images[i]->label);
} }
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; // Batch training works if you change the train_network method, but the results are not that good (needs further testing)
int testing_image_count = 10000; // batch_train(nn, images, 30000, 2);
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();
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;
} }

View file

@ -1,39 +0,0 @@
#pragma once
#include <stdio.h>
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);

139
matrix/matrix.c Normal file
View file

@ -0,0 +1,139 @@
#include "matrix.h"
#include <stdlib.h>
#include <stdio.h>
#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;
}

15
matrix/matrix.h Normal file
View file

@ -0,0 +1,15 @@
#include <stdio.h>
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);

View file

@ -1,92 +1,10 @@
#include "matrix.h" #include <process.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h> #include <time.h>
#define MAX_BYTES 100 #include "math.h"
#include "operations.h"
static int RANDOMIZED = 0; 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) { Matrix* multiply(Matrix* matrix1, Matrix* matrix2) {
@ -216,7 +134,6 @@ Matrix* scale(Matrix* matrix, double value) {
return result_matrix; return result_matrix;
} }
Matrix* transpose(Matrix* matrix) { Matrix* transpose(Matrix* matrix) {
// create a new matrix of the size n-m, based on the original matrix of size m-n // 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) { Matrix* matrix_flatten(Matrix* matrix, int axis) {
// Axis = 0 -> Column Vector, Axis = 1 -> Row Vector // Axis = 0 -> Column Vector, Axis = 1 -> Row Vector
Matrix* result_matrix; Matrix* result_matrix;
@ -318,10 +174,10 @@ Matrix* matrix_flatten(Matrix* matrix, int axis) {
return result_matrix; return result_matrix;
} }
int matrix_argmax(Matrix* matrix) { int argmax(Matrix* matrix) {
// Expects a Mx1 matrix // Expects a Mx1 matrix
if (matrix->columns != 1){ if (matrix->columns != 1){
printf("ERROR: Matrix is not Mx1 (matrix_argmax)"); printf("ERROR: Matrix is not Mx1 (argmax)");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }

25
matrix/operations.h Normal file
View file

@ -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);

View file

@ -1,14 +1,14 @@
#include <stdlib.h> #include <stdlib.h>
#include "neuronal_network.h" #include "neuronal_network.h"
#include "matrix\operations.h"
#include <stdio.h> #include <stdio.h>
#include <math.h> #include <math.h>
#include "util.h"
double sigmoid(double input); double sigmoid(double input);
Matrix* predict(Neural_Network* network, Matrix* image_data); Matrix* predict(Neural_Network* network, Matrix* image_data);
Matrix* sigmoid_derivative(Matrix* matrix); Matrix* sigmoid_derivative(Matrix* 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);
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);
Matrix* calculate_delta_hidden(Matrix* next_layer_delta, Matrix* weights, Matrix* current_layer_output); 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){ 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); 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 // create file
FILE* save_file = fopen(file_name, "w"); 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) { double measure_network_accuracy(Neural_Network* network, Image** images, int amount) {
int num_correct = 0; int num_correct = 0;
printf("evaluating network\n");
if(amount > 10000) amount = 10000;
for (int i = 0; i < amount; i++) { for (int i = 0; i < amount; i++) {
updateBar(i*100/amount);
Matrix* prediction = predict_image(network, images[i]); Matrix* prediction = predict_image(network, images[i]);
int guess = matrix_argmax(prediction); int guess = argmax(prediction);
int answer = (unsigned char) images[i]->label; int answer = (unsigned char) images[i]->label;
if (guess == answer) { if (guess == answer) {
@ -131,7 +130,6 @@ double measure_network_accuracy(Neural_Network* network, Image** images, int amo
matrix_free(prediction); matrix_free(prediction);
} }
updateBar(100);
return ((double) num_correct) / amount; 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) { //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) { // int image_index = 0;
// printf("1k pics!\n"); //
// } // for (int i = 0; i < amount / batch_size; ++i) {
// //
// Matrix* batch_weights[network->hidden_amount + 1]; // 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) { // 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++) { // 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]); // 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); // 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)); // 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(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); Matrix* delta = multiply(sigmoid_prime, error);
//calculate and apply the delta for all weights in out-put layer //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 //hidden layers
Matrix* previous_delta = delta; Matrix* previous_delta = delta;
for (int i = network->hidden_amount; i > 1; i--) { for (int i = network->hidden_amount; i > 1; i--) {
delta = calculate_delta_hidden(previous_delta, network->weights[i], output[i - 1]); 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); matrix_free(previous_delta);
previous_delta = delta; previous_delta = delta;
@ -256,10 +260,16 @@ void train_network(Neural_Network* network, Image *image, int label) {
// Input Layer // Input Layer
delta = calculate_delta_hidden(previous_delta, network->weights[1], output[0]); 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) { 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 // De-allocate stuff
@ -270,9 +280,7 @@ void train_network(Neural_Network* network, Image *image, int label) {
matrix_free(output[i]); 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(sigmoid_prime);
matrix_free(wanted_output); matrix_free(wanted_output);
@ -311,7 +319,7 @@ Matrix* calculate_delta_hidden(Matrix* next_layer_delta, Matrix* weights, Matrix
return new_deltas; 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) { if(index > network->hidden_amount || index < 0) {
printf("ERROR: Index out of range! (apply_weights)"); 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); 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 i = 0; i < delta_weights_matrix->rows; i++) {
for (int j = 0; j < delta_weights_matrix->columns; j++) { for (int j = 0; j < scaled_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 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* previous_out_with_one = matrix_add_bias(previous_layer_output);
Matrix* transposed_previous_out_with_bias = transpose(previous_out_with_one); Matrix* transposed_previous_out_with_bias = transpose(previous_out_with_one);
Matrix* weights_delta_matrix = dot(delta_matrix, transposed_previous_out_with_bias); 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(previous_out_with_one);
matrix_free(transposed_previous_out_with_bias); matrix_free(transposed_previous_out_with_bias);
matrix_free(weights_delta_matrix);
return result; return weights_delta_matrix;
} }
Matrix* sigmoid_derivative(Matrix* matrix) { Matrix* sigmoid_derivative(Matrix* matrix) {

View file

@ -1,6 +1,6 @@
#include "matrix.h" #include "matrix/matrix.h"
#include "image.h" #include "image/image.h"
typedef struct { typedef struct {
int input_size; 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 randomize_network(Neural_Network* network, int scope);
void free_network(Neural_Network* network); 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); Neural_Network* load_network(char* file);
void print_network(Neural_Network* network); void print_network(Neural_Network* network);