diff --git a/Neuronales_Netz/.vscode/c_cpp_properties.json b/Neuronales_Netz/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c2098a2 --- /dev/null +++ b/Neuronales_Netz/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "linux-gcc-x64", + "includePath": [ + "${workspaceFolder}/**" + ], + "compilerPath": "/usr/bin/gcc", + "cStandard": "${default}", + "cppStandard": "${default}", + "intelliSenseMode": "linux-gcc-x64", + "compilerArgs": [ + "" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/Neuronales_Netz/.vscode/launch.json b/Neuronales_Netz/.vscode/launch.json new file mode 100644 index 0000000..d6a52c5 --- /dev/null +++ b/Neuronales_Netz/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "C/C++ Runner: Debug Session", + "type": "cppdbg", + "request": "launch", + "args": [], + "stopAtEntry": false, + "externalConsole": false, + "cwd": "/home/tommy/Desktop/Studium/3.Semester/I2-Praktikum/Playground/Neuronales_Netz", + "program": "/home/tommy/Desktop/Studium/3.Semester/I2-Praktikum/Playground/Neuronales_Netz/build/Debug/outDebug", + "MIMode": "gdb", + "miDebuggerPath": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/Neuronales_Netz/.vscode/settings.json b/Neuronales_Netz/.vscode/settings.json new file mode 100644 index 0000000..3e5eb95 --- /dev/null +++ b/Neuronales_Netz/.vscode/settings.json @@ -0,0 +1,59 @@ +{ + "C_Cpp_Runner.cCompilerPath": "gcc", + "C_Cpp_Runner.cppCompilerPath": "g++", + "C_Cpp_Runner.debuggerPath": "gdb", + "C_Cpp_Runner.cStandard": "", + "C_Cpp_Runner.cppStandard": "", + "C_Cpp_Runner.msvcBatchPath": "", + "C_Cpp_Runner.useMsvc": false, + "C_Cpp_Runner.warnings": [ + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wshadow", + "-Wformat=2", + "-Wcast-align", + "-Wconversion", + "-Wsign-conversion", + "-Wnull-dereference" + ], + "C_Cpp_Runner.msvcWarnings": [ + "/W4", + "/permissive-", + "/w14242", + "/w14287", + "/w14296", + "/w14311", + "/w14826", + "/w44062", + "/w44242", + "/w14905", + "/w14906", + "/w14263", + "/w44265", + "/w14928" + ], + "C_Cpp_Runner.enableWarnings": true, + "C_Cpp_Runner.warningsAsError": false, + "C_Cpp_Runner.compilerArgs": [], + "C_Cpp_Runner.linkerArgs": [], + "C_Cpp_Runner.includePaths": [], + "C_Cpp_Runner.includeSearch": [ + "*", + "**/*" + ], + "C_Cpp_Runner.excludeSearch": [ + "**/build", + "**/build/**", + "**/.*", + "**/.*/**", + "**/.vscode", + "**/.vscode/**" + ], + "C_Cpp_Runner.useAddressSanitizer": false, + "C_Cpp_Runner.useUndefinedSanitizer": false, + "C_Cpp_Runner.useLeakSanitizer": false, + "C_Cpp_Runner.showCompilationTime": false, + "C_Cpp_Runner.useLinkTimeOptimization": false, + "C_Cpp_Runner.msvcSecureNoWarnings": false +} \ No newline at end of file diff --git a/Neuronales_Netz/imageInput.c b/Neuronales_Netz/imageInput.c index bb30de1..4892870 100644 --- a/Neuronales_Netz/imageInput.c +++ b/Neuronales_Netz/imageInput.c @@ -6,17 +6,157 @@ #define BUFFER_SIZE 100 #define FILE_HEADER_STRING "__info2_image_file_format__" + + // TODO Implementieren Sie geeignete Hilfsfunktionen für das Lesen der Bildserie aus einer Datei // TODO Vervollständigen Sie die Funktion readImages unter Benutzung Ihrer Hilfsfunktionen GrayScaleImageSeries *readImages(const char *path) { - GrayScaleImageSeries *series = NULL; + FILE* file = fopen(path, "rb"); + + if(file == NULL) + { + puts("could not open file."); + + return NULL; + } + + if(!checkFileHeaderString(file)) + { + puts("wrong File."); + + return NULL; + } + + unsigned short count = getCount(file); + if(!count) + { + puts("could not read number of images."); + + return NULL; + } + + unsigned short width = getWidth(file); + if(!width) + { + puts("could not read Width of images."); + + return NULL; + } + + unsigned short height = getHeight(file); + if(!height) + { + puts("could not read height of images."); + + return NULL; + } + + GrayScaleImageSeries *series = createSeries(count); + + series->count = count; + unsigned short resolution = width * height; + + for(unsigned int i = 0; i < series->count; i++) + { + series->images[i].width = width; + series->images[i].height = height; + + series->images[i].buffer = getPixels(file, resolution); + series->labels[i] = getLabel(file); + } + + fclose(file); return series; } + + // TODO Vervollständigen Sie die Funktion clearSeries, welche eine Bildserie vollständig aus dem Speicher freigibt void clearSeries(GrayScaleImageSeries *series) +{ + for(int i = 0; i < series->count; i++) + { + free(series->images[i].buffer); + } + + free(series->images); + free(series->labels); + free(series); +} + + +int checkFileHeaderString(FILE *file) { + char buffer[strlen(FILE_HEADER_STRING)]; + + fread(buffer, sizeof(char), strlen(FILE_HEADER_STRING), file); + + for(int i = 0; i < strlen(FILE_HEADER_STRING); i++) + { + if(buffer[i] != FILE_HEADER_STRING[i]) + { + return 0; + } + } + return 1; +} + +unsigned short getCount(FILE *file) +{ + unsigned short count = 0; + + fread(&count, sizeof(unsigned short), 1, file); + + return count; +} + +unsigned short getWidth(FILE *file) +{ + unsigned short width = 0; + + fread(&width, sizeof(unsigned short), 1, file); + + return width; +} + +unsigned short getHeight(FILE *file) +{ + unsigned short height = 0; + + fread(&height, sizeof(unsigned short), 1, file); + + return height; +} + +GrayScaleImageSeries *createSeries(unsigned short count) +{ + GrayScaleImage *images = (GrayScaleImage*) calloc(count, sizeof(GrayScaleImage)); + unsigned char *labels = (unsigned char*) calloc(count, sizeof(unsigned char)); + + GrayScaleImageSeries *series = (GrayScaleImageSeries*) calloc(1, sizeof(GrayScaleImageSeries)); + series->images = images; + series->labels = labels; + + return series; +} + +GrayScalePixelType *getPixels(FILE *file, unsigned short resolution) +{ + GrayScalePixelType *pixels = (GrayScalePixelType*) calloc(resolution, sizeof(GrayScalePixelType)); + + fread(pixels, sizeof(GrayScalePixelType), resolution, file); + + return pixels; +} + +unsigned char getLabel(FILE *file) +{ + unsigned char label = '\0'; + + fread(&label, sizeof(unsigned char), 1, file); + + return label; } \ No newline at end of file diff --git a/Neuronales_Netz/imageInput.h b/Neuronales_Netz/imageInput.h index 656e213..15aacd8 100644 --- a/Neuronales_Netz/imageInput.h +++ b/Neuronales_Netz/imageInput.h @@ -19,5 +19,12 @@ typedef struct GrayScaleImageSeries *readImages(const char *path); void clearSeries(GrayScaleImageSeries *series); +int checkFileHeaderString(FILE *file); +unsigned short getCount(FILE *file); +unsigned short getWidth(FILE *file); +unsigned short getHeight(FILE *file); +GrayScaleImageSeries *createSeries(unsigned short count); +GrayScalePixelType *getPixels(FILE *file, unsigned short resolution); +unsigned char getLabel(FILE *file); #endif diff --git a/Neuronales_Netz/imageInput.o b/Neuronales_Netz/imageInput.o new file mode 100644 index 0000000..55c7c5b Binary files /dev/null and b/Neuronales_Netz/imageInput.o differ diff --git a/Neuronales_Netz/imageInputTests.c b/Neuronales_Netz/imageInputTests.c index 03240ab..788db23 100644 --- a/Neuronales_Netz/imageInputTests.c +++ b/Neuronales_Netz/imageInputTests.c @@ -119,6 +119,26 @@ void test_readImagesFailsOnWrongFileTag(void) remove(path); } +void test_readImagesReturnsCorrectPixels(void) +{ + GrayScalePixelType expectedResult = 0; + + GrayScaleImageSeries *series = NULL; + const char *path = "testFile.info2"; + prepareImageFile(path, 8, 8, 2, 0); + series = readImages(path); + + for (int i = 0; i < 2; i++) + { + for(int j = 0; j < 8 * 8; j++) + { + TEST_ASSERT_EQUAL_UINT8(expectedResult, series->images[i].buffer[j]); + } + } + clearSeries(series); + remove(path); +} + void setUp(void) { // Falls notwendig, kann hier Vorbereitungsarbeit gemacht werden } @@ -138,6 +158,8 @@ int main() RUN_TEST(test_readImagesReturnsCorrectLabels); RUN_TEST(test_readImagesReturnsNullOnNotExistingPath); RUN_TEST(test_readImagesFailsOnWrongFileTag); + RUN_TEST(test_readImagesReturnsCorrectPixels); + return UNITY_END(); } \ No newline at end of file diff --git a/Neuronales_Netz/main.o b/Neuronales_Netz/main.o new file mode 100644 index 0000000..669c1e5 Binary files /dev/null and b/Neuronales_Netz/main.o differ diff --git a/Neuronales_Netz/matrix.c b/Neuronales_Netz/matrix.c index ad00628..4edc624 100644 --- a/Neuronales_Netz/matrix.c +++ b/Neuronales_Netz/matrix.c @@ -3,33 +3,135 @@ #include "matrix.h" // TODO Matrix-Funktionen implementieren +// typedef struct +// { +// unsigned int *buffer; +// unsigned int rows; +// unsigned int cols; +// } Matrix; + Matrix createMatrix(unsigned int rows, unsigned int cols) { - + if(rows == 0 || cols == 0) + { + Matrix noMatrix = {NULL, 0, 0}; + + return noMatrix; + } + + Matrix matrix = {(MatrixType *)calloc(rows * cols, sizeof(int)) ,rows, cols}; + + return matrix; } void clearMatrix(Matrix *matrix) { - + free(matrix->buffer); + matrix->rows = 0; + matrix->cols = 0; + matrix->buffer = NULL; } void setMatrixAt(MatrixType value, Matrix matrix, unsigned int rowIdx, unsigned int colIdx) { - + if(rowIdx >= matrix.rows || colIdx >= matrix.cols) + { + return; + } + + matrix.buffer[matrix.cols * rowIdx + colIdx] = value; } MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx, unsigned int colIdx) { - + if(rowIdx >= matrix.rows || colIdx >= matrix.cols) + { + return UNDEFINED_MATRIX_VALUE; + } + + MatrixType matrixAt = matrix.buffer[matrix.cols * rowIdx + colIdx]; + + return matrixAt; } Matrix add(const Matrix matrix1, const Matrix matrix2) { - + if(matrix1.rows == matrix2.rows && matrix1.cols == matrix2.cols) + { + Matrix matrixSum = createMatrix(matrix1.rows, matrix1.cols); + + for(int i = 0; i < matrix1.rows; i++) + { + for(int j = 0; j < matrix1.cols; j++) + { + MatrixType value = getMatrixAt(matrix1, i, j) + getMatrixAt(matrix2, i, j); + + setMatrixAt(value, matrixSum, i, j); + } + + } + return matrixSum; + } + + if((matrix1.cols == 1 || matrix2.cols == 1) && matrix1.rows == matrix2.rows) + { + if(matrix1.cols == 1) + { + return broadcasting(matrix2, matrix1); + } + else + { + return broadcasting(matrix1, matrix2); + } + } + + Matrix failedMatrix = createMatrix(0, 0); + + return failedMatrix; +} + +Matrix broadcasting(const Matrix matrix, const Matrix vector) +{ + Matrix matrixSum = createMatrix(matrix.rows, matrix.cols); + + for(int i = 0; i < vector.rows; i++) + { + for(int j = 0; j < matrix.cols; j++) + { + MatrixType value = getMatrixAt(matrix, i, j) + getMatrixAt(vector, i, 0); + + setMatrixAt(value, matrixSum, i, j); + } + } + return matrixSum; } Matrix multiply(const Matrix matrix1, const Matrix matrix2) { - + if(matrix1.cols != matrix2.rows) + { + Matrix failedMatrix = createMatrix(0, 0); + + return failedMatrix; + } + + Matrix matrixRes = createMatrix(matrix1.rows, matrix2.cols); + + for(int i = 0; i < matrixRes.rows; i++) + { + for(int j = 0; j < matrixRes.cols; j++) + { + MatrixType sum = 0; + + for(int k = 0; k < matrix1.cols; k++) + { + MatrixType product = getMatrixAt(matrix1, i, k) * getMatrixAt(matrix2, k, j); + + sum += product; + } + setMatrixAt(sum, matrixRes, i, j); + } + } + return matrixRes; } \ No newline at end of file diff --git a/Neuronales_Netz/matrix.h b/Neuronales_Netz/matrix.h index cc640d1..3d1a4c6 100644 --- a/Neuronales_Netz/matrix.h +++ b/Neuronales_Netz/matrix.h @@ -6,6 +6,13 @@ typedef float MatrixType; // TODO Matrixtyp definieren +typedef struct +{ + MatrixType *buffer; + unsigned int rows; + unsigned int cols; + +} Matrix; Matrix createMatrix(unsigned int rows, unsigned int cols); @@ -13,6 +20,7 @@ void clearMatrix(Matrix *matrix); void setMatrixAt(MatrixType value, Matrix matrix, unsigned int rowIdx, unsigned int colIdx); MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx, unsigned int colIdx); Matrix add(const Matrix matrix1, const Matrix matrix2); +Matrix broadcasting(const Matrix matrix, const Matrix vector); Matrix multiply(const Matrix matrix1, const Matrix matrix2); diff --git a/Neuronales_Netz/matrix.o b/Neuronales_Netz/matrix.o new file mode 100644 index 0000000..854da1d Binary files /dev/null and b/Neuronales_Netz/matrix.o differ diff --git a/Neuronales_Netz/mnist b/Neuronales_Netz/mnist new file mode 100755 index 0000000..e406c24 Binary files /dev/null and b/Neuronales_Netz/mnist differ diff --git a/Neuronales_Netz/mnistVisualization.o b/Neuronales_Netz/mnistVisualization.o new file mode 100644 index 0000000..5d5a9b9 Binary files /dev/null and b/Neuronales_Netz/mnistVisualization.o differ diff --git a/Neuronales_Netz/neuralNetwork.c b/Neuronales_Netz/neuralNetwork.c index bd8f164..632235a 100644 --- a/Neuronales_Netz/neuralNetwork.c +++ b/Neuronales_Netz/neuralNetwork.c @@ -80,7 +80,6 @@ static Matrix readMatrix(FILE *file, unsigned int rows, unsigned int cols) if(fread(matrix.buffer, sizeof(MatrixType), rows*cols, file) != rows*cols) clearMatrix(&matrix); } - return matrix; } @@ -146,13 +145,16 @@ NeuralNetwork loadModel(const char *path) layerBuffer = (Layer *)realloc(model.layers, (model.numberOfLayers + 1) * sizeof(Layer)); if(layerBuffer != NULL) + { model.layers = layerBuffer; + } else { clearModel(&model); break; } + model.layers[model.numberOfLayers] = layer; model.numberOfLayers++; diff --git a/Neuronales_Netz/neuralNetwork.o b/Neuronales_Netz/neuralNetwork.o new file mode 100644 index 0000000..42147c3 Binary files /dev/null and b/Neuronales_Netz/neuralNetwork.o differ diff --git a/Neuronales_Netz/neuralNetworkTests.c b/Neuronales_Netz/neuralNetworkTests.c index 21ab370..9be0a7b 100644 --- a/Neuronales_Netz/neuralNetworkTests.c +++ b/Neuronales_Netz/neuralNetworkTests.c @@ -9,6 +9,31 @@ static void prepareNeuralNetworkFile(const char *path, const NeuralNetwork nn) { // TODO + FILE *file = fopen(path, "wb"); + + if(file != NULL) + { + const char *fileTag = "__info2_neural_network_file_format__"; + fwrite(fileTag, sizeof(fileTag[0]), strlen(fileTag), file); + fwrite(&nn.layers[0].weights.cols, sizeof(int), 1, file); + fwrite(&nn.layers[0].weights.rows, sizeof(int), 1, file); + + for(int i = 0; i < nn.numberOfLayers; i++) + { + unsigned int sizeWeights = nn.layers[i].weights.rows * nn.layers[i].weights.cols; + unsigned int sizeBiases = nn.layers[i].biases.rows; + + fwrite(nn.layers[i].weights.buffer, sizeof(MatrixType), sizeWeights, file); + fwrite(nn.layers[i].biases.buffer, sizeof(MatrixType), sizeBiases, file); + + if(i < nn.numberOfLayers - 1) + { + fwrite(&nn.layers[i].weights.cols, sizeof(unsigned int), 1, file); + } + } + + fclose(file); + } } void test_loadModelReturnsCorrectNumberOfLayers(void) diff --git a/Neuronales_Netz/runImageInputTests b/Neuronales_Netz/runImageInputTests new file mode 100755 index 0000000..e662c61 Binary files /dev/null and b/Neuronales_Netz/runImageInputTests differ diff --git a/Neuronales_Netz/runMatrixTests b/Neuronales_Netz/runMatrixTests new file mode 100755 index 0000000..a9ee406 Binary files /dev/null and b/Neuronales_Netz/runMatrixTests differ diff --git a/Neuronales_Netz/runNeuralNetworkTests b/Neuronales_Netz/runNeuralNetworkTests new file mode 100755 index 0000000..7d11d2d Binary files /dev/null and b/Neuronales_Netz/runNeuralNetworkTests differ