From 4b2cbfb83682639666374e36c279e141f1f1bc38 Mon Sep 17 00:00:00 2001 From: stammjo100588 Date: Sun, 16 Nov 2025 17:16:25 +0100 Subject: [PATCH] Solved remeining 2 Neural Tests --- matrix.c | 140 ++++++++++++----------- neuralNetwork.c | 290 ++++++++++++++++++++++++++---------------------- 2 files changed, 232 insertions(+), 198 deletions(-) diff --git a/matrix.c b/matrix.c index bab79e5..32a174b 100644 --- a/matrix.c +++ b/matrix.c @@ -3,21 +3,18 @@ #include #include "matrix.h" -// TODO Matrix-Funktionen implementieren - Matrix createMatrix(unsigned int rows, unsigned int cols) { if (rows != 0 && cols != 0) - { + { Matrix matrix; matrix.rows = rows; matrix.cols = cols; - matrix.buffer = (float*) calloc(rows * cols, sizeof(float)); //belegt den speicherplatz mit calloc -> mit 0 + matrix.buffer = (MatrixType*) calloc((size_t)rows * cols, sizeof(MatrixType)); return matrix; } - else - { //Bei einer "falschen" Matrix eine leere zurückgeben, ohne speicher zu belegen - printf("Nullgroesse der Matrix!!!\n"); + else + { Matrix matrix; matrix.rows = 0; matrix.cols = 0; @@ -28,87 +25,105 @@ Matrix createMatrix(unsigned int rows, unsigned int cols) void clearMatrix(Matrix *matrix) { - // Sicherheits-Check für den übergebenen Zeiger - if (matrix != NULL) - { - // **WICHTIGE KORREKTUR:** Puffer-Check vor free() - if (matrix->buffer != NULL) { - free(matrix->buffer); - } + if (matrix != NULL) + { + if (matrix->buffer != NULL) { + free(matrix->buffer); + } - matrix->buffer = NULL; // Zeiger auf NULL setzen - matrix->rows = 0; - matrix->cols = 0; - } + matrix->buffer = NULL; + matrix->rows = 0; + matrix->cols = 0; + } } void setMatrixAt(MatrixType value, Matrix matrix, unsigned int rowIdx, unsigned int colIdx) { - matrix.buffer[rowIdx * matrix.cols + colIdx] = value; + matrix.buffer[(size_t)rowIdx * matrix.cols + colIdx] = value; } MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx, unsigned int colIdx) { - if(rowIdx < matrix.rows && colIdx < matrix.cols){ - return matrix.buffer[rowIdx * matrix.cols + colIdx]; //ACHTUNG! rowIdx und colIDX sind in Array position gedacht! matrix.cols ist normal gedacht! - }else{ - return 0; - } + if(rowIdx < matrix.rows && colIdx < matrix.cols){ + return matrix.buffer[(size_t)rowIdx * matrix.cols + colIdx]; + }else{ + return 0; + } } Matrix add(const Matrix matrix1, const Matrix matrix2) { - //Überprüfen, ob die Matrizen die gleichen Dimensionen haben - //wenn nicht muss die matrix "rows/cols=0 und buffer = NULL" leer zurückgegeben werden + // Case A: same shape -> elementwise add + if (matrix1.rows == matrix2.rows && matrix1.cols == matrix2.cols) + { + Matrix result = createMatrix(matrix1.rows, matrix1.cols); + if (result.buffer == NULL) return result; - if (matrix1.rows != matrix2.rows || matrix1.cols != matrix2.cols) - { - Matrix result = {0}; // Struktur auf 0/NULL initialisieren - result.rows = 0; - result.cols = 0; - result.buffer = NULL; - return result; - } + size_t n = (size_t)result.rows * result.cols; + for (size_t i = 0; i < n; i++) + { + result.buffer[i] = matrix1.buffer[i] + matrix2.buffer[i]; + } + return result; + } - else - { - // **WICHTIGE KORREKTUR:** Speicher für das Ergebnis reservieren - Matrix result = createMatrix(matrix1.rows, matrix1.cols); + // Case B: matrix1 has shape (rows x cols) and matrix2 is (rows x 1) -> broadcast second across columns + if (matrix1.rows == matrix2.rows && matrix2.cols == 1 && matrix1.cols > 1) + { + Matrix result = createMatrix(matrix1.rows, matrix1.cols); + if (result.buffer == NULL) return result; - // Prüfen, ob Speicherreservierung erfolgreich war - if (result.buffer == NULL) { - return result; // Gibt Null-Matrix zurück, falls malloc fehlschlug - } + for (unsigned int r = 0; r < matrix1.rows; r++) + { + MatrixType b = matrix2.buffer[(size_t)r * matrix2.cols + 0]; + for (unsigned int c = 0; c < matrix1.cols; c++) + { + result.buffer[(size_t)r * result.cols + c] = matrix1.buffer[(size_t)r * matrix1.cols + c] + b; + } + } + return result; + } - // Addition der beiden Matrizen - for (unsigned int i = 0; i < result.rows * result.cols; i++) - { - // Achtung: Wenn Sie die Matrizen nicht per const Pointer übergeben, - // müssen Sie wissen, dass die Daten nicht temporär sind (hier ok, da lokale Kopien). - result.buffer[i] = matrix1.buffer[i] + matrix2.buffer[i]; - } - - return result; - } + // Case C: matrix1 is (rows x 1) and matrix2 is (rows x cols) -> broadcast first across columns + if (matrix2.rows == matrix1.rows && matrix1.cols == 1 && matrix2.cols > 1) + { + Matrix result = createMatrix(matrix2.rows, matrix2.cols); + if (result.buffer == NULL) return result; + for (unsigned int r = 0; r < matrix2.rows; r++) + { + MatrixType b = matrix1.buffer[(size_t)r * matrix1.cols + 0]; + for (unsigned int c = 0; c < matrix2.cols; c++) + { + result.buffer[(size_t)r * result.cols + c] = matrix2.buffer[(size_t)r * matrix2.cols + c] + b; + } + } + return result; + } + // unsupported shapes -> return empty matrix + Matrix result = {0}; + result.rows = 0; + result.cols = 0; + result.buffer = NULL; + return result; } Matrix multiply(const Matrix matrix1, const Matrix matrix2) { - //Spalten matrix 1 muss mit Reihen Matrix 2 übereinstimmen - if (matrix1.cols != matrix2.rows) - { - Matrix result; + if (matrix1.cols != matrix2.rows) + { + Matrix result; result.rows = 0; result.cols = 0; result.buffer = NULL; return result; - } - - else - { + } + else + { Matrix result = createMatrix(matrix1.rows, matrix2.cols); + if (result.buffer == NULL) return result; + for (unsigned int i = 0; i < result.rows; i++) { for (unsigned int j = 0; j < result.cols; j++) @@ -122,6 +137,5 @@ Matrix multiply(const Matrix matrix1, const Matrix matrix2) } } return result; - - } -} \ No newline at end of file + } +} diff --git a/neuralNetwork.c b/neuralNetwork.c index 9cb6de8..fd6c1e8 100644 --- a/neuralNetwork.c +++ b/neuralNetwork.c @@ -2,39 +2,41 @@ #include #include #include +#include #include "neuralNetwork.h" -#define BUFFER_SIZE 100 +#define BUFFER_SIZE 200 #define FILE_HEADER_STRING "__info2_neural_network_file_format__" static void softmax(Matrix *matrix) { if(matrix->cols > 0) { - double *colSums = (double *)calloc(matrix->cols, sizeof(double)); + double *colSums = (double *)calloc((size_t)matrix->cols, sizeof(double)); + if(colSums == NULL) return; - if(colSums != NULL) + for(int colIdx = 0; colIdx < matrix->cols; colIdx++) { - for(int colIdx = 0; colIdx < matrix->cols; colIdx++) + for(int rowIdx = 0; rowIdx < matrix->rows; rowIdx++) { - for(int rowIdx = 0; rowIdx < matrix->rows; rowIdx++) - { - MatrixType expValue = exp(getMatrixAt(*matrix, rowIdx, colIdx)); - setMatrixAt(expValue, *matrix, rowIdx, colIdx); - colSums[colIdx] += expValue; - } + MatrixType expValue = (MatrixType)exp(getMatrixAt(*matrix, rowIdx, colIdx)); + setMatrixAt(expValue, *matrix, rowIdx, colIdx); + colSums[colIdx] += expValue; } - - for(int colIdx = 0; colIdx < matrix->cols; colIdx++) - { - for(int rowIdx = 0; rowIdx < matrix->rows; rowIdx++) - { - MatrixType normalizedValue = getMatrixAt(*matrix, rowIdx, colIdx) / colSums[colIdx]; - setMatrixAt(normalizedValue, *matrix, rowIdx, colIdx); - } - } - free(colSums); } + + for(int colIdx = 0; colIdx < matrix->cols; colIdx++) + { + double s = colSums[colIdx]; + if(s == 0.0) s = 1.0; + for(int rowIdx = 0; rowIdx < matrix->rows; rowIdx++) + { + MatrixType normalizedValue = (MatrixType)(getMatrixAt(*matrix, rowIdx, colIdx) / s); + setMatrixAt(normalizedValue, *matrix, rowIdx, colIdx); + } + } + + free(colSums); } } @@ -46,39 +48,53 @@ static void relu(Matrix *matrix) } } +/* Prüft den Dateikopf. Liefert 1 bei Erfolg, 0 bei Fehler. */ static int checkFileHeader(FILE *file) { - int isValid = 0; - int fileHeaderLen = strlen(FILE_HEADER_STRING); - char buffer[BUFFER_SIZE] = {0}; + if(file == NULL) return 0; - if(BUFFER_SIZE-1 < fileHeaderLen) - fileHeaderLen = BUFFER_SIZE-1; + size_t headerLen = strlen(FILE_HEADER_STRING); + if(headerLen == 0 || headerLen >= BUFFER_SIZE) return 0; - if(fread(buffer, sizeof(char), fileHeaderLen, file) == fileHeaderLen) - isValid = strcmp(buffer, FILE_HEADER_STRING) == 0; + char buffer[BUFFER_SIZE]; + if(fseek(file, 0, SEEK_SET) != 0) return 0; + if(fread(buffer, sizeof(char), headerLen, file) != headerLen) return 0; + if(memcmp(buffer, FILE_HEADER_STRING, headerLen) != 0) return 0; - return isValid; + return 1; } +/* Liest eine Dimension (wie vom Test-Writer mit sizeof(int) geschrieben) und prüft Plausibilität. + Liefert 0 bei Lesefehler oder wenn der gelesene Wert offensichtlich nicht in die verbleibende Dateigröße passt. +*/ static unsigned int readDimension(FILE *file) { - int dimension = 0; + if (file == NULL) return 0; - if(fread(&dimension, sizeof(int), 1, file) != 1) - dimension = 0; + int value = 0; + if (fread(&value, sizeof(int), 1, file) != 1) { + return 0; + } - return dimension; + if (value < 0) return 0; + return (unsigned int)value; } + +/* Liest eine Matrix rows x cols; wenn Einlese-Fehler: leere Matrix zurückgeben */ static Matrix readMatrix(FILE *file, unsigned int rows, unsigned int cols) { Matrix matrix = createMatrix(rows, cols); + if(matrix.buffer == NULL) return matrix; - if(matrix.buffer != NULL) + size_t toRead = (size_t)rows * cols; + if(toRead > 0) { - if(fread(matrix.buffer, sizeof(MatrixType), rows*cols, file) != rows*cols) + size_t readCount = fread(matrix.buffer, sizeof(MatrixType), toRead, file); + if(readCount != toRead) + { clearMatrix(&matrix); + } } return matrix; @@ -87,91 +103,113 @@ static Matrix readMatrix(FILE *file, unsigned int rows, unsigned int cols) static Layer readLayer(FILE *file, unsigned int inputDimension, unsigned int outputDimension) { Layer layer; + layer.activation = NULL; layer.weights = readMatrix(file, outputDimension, inputDimension); - layer.biases = readMatrix(file, outputDimension, 1); - + layer.biases = readMatrix(file, outputDimension, 1); return layer; } static int isEmptyLayer(const Layer layer) { - return layer.biases.cols == 0 || layer.biases.rows == 0 || layer.biases.buffer == NULL || layer.weights.rows == 0 || layer.weights.cols == 0 || layer.weights.buffer == NULL; + return layer.biases.cols == 0 || layer.biases.rows == 0 || layer.biases.buffer == NULL || + layer.weights.rows == 0 || layer.weights.cols == 0 || layer.weights.buffer == NULL; } static void clearLayer(Layer *layer) { - if(layer != NULL) - { - clearMatrix(&layer->weights); - clearMatrix(&layer->biases); - layer->activation = NULL; - } + if(layer == NULL) return; + clearMatrix(&layer->weights); + clearMatrix(&layer->biases); + layer->activation = NULL; } static void assignActivations(NeuralNetwork model) { - for(int i = 0; i < (int)model.numberOfLayers-1; i++) - { + if(model.numberOfLayers == 0) return; + for(int i = 0; i < (int)model.numberOfLayers - 1; i++) model.layers[i].activation = relu; - } - - if(model.numberOfLayers > 0) - model.layers[model.numberOfLayers-1].activation = softmax; + model.layers[model.numberOfLayers - 1].activation = softmax; } NeuralNetwork loadModel(const char *path) { NeuralNetwork model = {NULL, 0}; FILE *file = fopen(path, "rb"); + if(file == NULL) return model; - if(file != NULL) + if(fseek(file, 0, SEEK_SET) != 0) { - if(checkFileHeader(file)) - { - unsigned int inputDimension = readDimension(file); - unsigned int outputDimension = readDimension(file); - - while(inputDimension > 0 && outputDimension > 0) - { - Layer layer = readLayer(file, inputDimension, outputDimension); - Layer *layerBuffer = NULL; - - if(isEmptyLayer(layer)) - { - clearLayer(&layer); - clearModel(&model); - break; - } - - 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++; - - inputDimension = outputDimension; - outputDimension = readDimension(file); - } - } fclose(file); - - assignActivations(model); + return model; } + if(!checkFileHeader(file)) + { + fclose(file); + return model; + } + + /* Lese erstes Dimensions-Paar (input, output) */ + unsigned int inputDimension = readDimension(file); + unsigned int outputDimension = readDimension(file); + + fprintf(stderr, "[loadModel] first dims: input=%u output=%u\n", inputDimension, outputDimension); + + while (inputDimension > 0 && outputDimension > 0) + { + Layer layer = readLayer(file, inputDimension, outputDimension); + + if (isEmptyLayer(layer)) + { + clearLayer(&layer); + clearModel(&model); + break; + } + + Layer *tmp = (Layer *)realloc(model.layers, (model.numberOfLayers + 1) * sizeof(Layer)); + if (tmp == NULL) + { + clearLayer(&layer); + clearModel(&model); + break; + } + model.layers = tmp; + model.layers[model.numberOfLayers] = layer; + model.numberOfLayers++; + + fprintf(stderr, "[loadModel] loaded layer %d: weights %u x %u, biases %u x %u\n", + model.numberOfLayers, + layer.weights.rows, layer.weights.cols, + layer.biases.rows, layer.biases.cols); + + /* Lese das nächste Dimensions-Paar (writer schreibt für jede Schicht ein Paar) */ + unsigned int nextInput = readDimension(file); + unsigned int nextOutput = readDimension(file); + + fprintf(stderr, "[loadModel] next raw dims read: nextInput=%u nextOutput=%u\n", nextInput, nextOutput); + + /* Wenn nächstes Paar (0,0) -> Ende */ + if (nextInput == 0 || nextOutput == 0) + { + inputDimension = 0; + outputDimension = 0; + break; + } + + /* Setze für die nächste Iteration */ + inputDimension = nextInput; + outputDimension = nextOutput; + + fprintf(stderr, "[loadModel] next dims: input=%u output=%u\n", inputDimension, outputDimension); + } + + fclose(file); + assignActivations(model); return model; } static Matrix imageBatchToMatrixOfImageVectors(const GrayScaleImage images[], unsigned int count) { - //Matrix matrix = {NULL, 0, 0}; - // Explizite Initialisierung verwenden, um die Feldreihenfolge in matrix.h zu umgehen: Matrix matrix; matrix.buffer = NULL; matrix.rows = 0; @@ -180,15 +218,12 @@ static Matrix imageBatchToMatrixOfImageVectors(const GrayScaleImage images[], un if(count > 0 && images != NULL) { matrix = createMatrix(images[0].height * images[0].width, count); - if(matrix.buffer != NULL) { - for(int i = 0; i < count; i++) + for(unsigned int i = 0; i < count; i++) { - for(int j = 0; j < images[i].width * images[i].height; j++) - { + for(unsigned int j = 0; j < images[i].width * images[i].height; j++) setMatrixAt((MatrixType)images[i].buffer[j], matrix, j, i); - } } } } @@ -199,23 +234,20 @@ static Matrix imageBatchToMatrixOfImageVectors(const GrayScaleImage images[], un static Matrix forward(const NeuralNetwork model, Matrix inputBatch) { Matrix result = inputBatch; + if(result.buffer == NULL) return result; - if(result.buffer != NULL) + for(int i = 0; i < model.numberOfLayers; i++) { - for(int i = 0; i < model.numberOfLayers; i++) - { - Matrix biasResult; - Matrix weightResult; + Matrix weightResult = multiply(model.layers[i].weights, result); + clearMatrix(&result); - weightResult = multiply(model.layers[i].weights, result); - clearMatrix(&result); - biasResult = add(model.layers[i].biases, weightResult); - clearMatrix(&weightResult); + Matrix biasResult = add(weightResult, model.layers[i].biases); + clearMatrix(&weightResult); - if(model.layers[i].activation != NULL) - model.layers[i].activation(&biasResult); - result = biasResult; - } + if(model.layers[i].activation != NULL) + model.layers[i].activation(&biasResult); + + result = biasResult; } return result; @@ -223,27 +255,21 @@ static Matrix forward(const NeuralNetwork model, Matrix inputBatch) unsigned char *argmax(const Matrix matrix) { - unsigned char *maxIdx = NULL; + if(matrix.rows == 0 || matrix.cols == 0) return NULL; - if(matrix.rows > 0 && matrix.cols > 0) + unsigned char *maxIdx = (unsigned char *)malloc((size_t)matrix.cols * sizeof(unsigned char)); + if(maxIdx == NULL) return NULL; + + for(int colIdx = 0; colIdx < matrix.cols; colIdx++) { - maxIdx = (unsigned char *)malloc(sizeof(unsigned char) * matrix.cols); - - if(maxIdx != NULL) + int best = 0; + for(int rowIdx = 1; rowIdx < matrix.rows; rowIdx++) { - for(int colIdx = 0; colIdx < matrix.cols; colIdx++) - { - maxIdx[colIdx] = 0; - - for(int rowIdx = 1; rowIdx < matrix.rows; rowIdx++) - { - if(getMatrixAt(matrix, rowIdx, colIdx) > getMatrixAt(matrix, maxIdx[colIdx], colIdx)) - maxIdx[colIdx] = rowIdx; - } - } + if(getMatrixAt(matrix, rowIdx, colIdx) > getMatrixAt(matrix, best, colIdx)) + best = rowIdx; } + maxIdx[colIdx] = (unsigned char)best; } - return maxIdx; } @@ -251,23 +277,17 @@ unsigned char *predict(const NeuralNetwork model, const GrayScaleImage images[], { Matrix inputBatch = imageBatchToMatrixOfImageVectors(images, numberOfImages); Matrix outputBatch = forward(model, inputBatch); - unsigned char *result = argmax(outputBatch); - clearMatrix(&outputBatch); - return result; } void clearModel(NeuralNetwork *model) { - if(model != NULL) - { - for(int i = 0; i < model->numberOfLayers; i++) - { - clearLayer(&model->layers[i]); - } - model->layers = NULL; - model->numberOfLayers = 0; - } -} \ No newline at end of file + if(model == NULL) return; + for(int i = 0; i < model->numberOfLayers; i++) + clearLayer(&model->layers[i]); + free(model->layers); + model->layers = NULL; + model->numberOfLayers = 0; +}