diff --git a/info2praktikum-neuronalesnetz/matrix.c b/info2praktikum-neuronalesnetz/matrix.c index 6d5ae0f..67ae23d 100644 --- a/info2praktikum-neuronalesnetz/matrix.c +++ b/info2praktikum-neuronalesnetz/matrix.c @@ -54,21 +54,30 @@ MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx, unsigned int co Matrix add(const Matrix matrix1, const Matrix matrix2) { - if (matrix1.rows != matrix2.rows || matrix1.cols != matrix2.cols) { // Matrixen können nur addiert werden, sofern sie jeweils die gleiche Anzahl Spalten und Zeilen haben - Matrix errorMatrix = createMatrix(0, 0); - errorMatrix.buffer = NULL; - return errorMatrix; + size_t rows = (matrix1.rows > matrix2.rows) ? matrix1.rows : matrix2.rows; + size_t cols = (matrix1.cols > matrix2.cols) ? matrix1.cols : matrix2.cols; + + // Prüfen, ob Broadcasting möglich ist + if (!((matrix1.rows == rows || matrix1.rows == 1) && + (matrix2.rows == rows || matrix2.rows == 1) && + (matrix1.cols == cols || matrix1.cols == 1) && + (matrix2.cols == cols || matrix2.cols == 1))) { + Matrix errorMatrix = createMatrix(0, 0); + return errorMatrix; } - Matrix result = createMatrix(matrix1.rows, matrix1.cols); - for (size_t i = 0; i < matrix1.rows; i++) { - for (size_t j = 0; j < matrix1.cols; j++) { - MatrixType sum = getMatrixAt(matrix1, i, j) + getMatrixAt(matrix2, i, j); - setMatrixAt(sum, result, i, j); - } + Matrix result = createMatrix(rows, cols); + + for (size_t i = 0; i < rows; i++) { + for (size_t j = 0; j < cols; j++) { + MatrixType val1 = matrix1.buffer[(i % matrix1.rows) * matrix1.cols + (j % matrix1.cols)]; + MatrixType val2 = matrix2.buffer[(i % matrix2.rows) * matrix2.cols + (j % matrix2.cols)]; + result.buffer[i * cols + j] = val1 + val2; + } } + return result; - } +} Matrix multiply(const Matrix matrix1, const Matrix matrix2) diff --git a/info2praktikum-neuronalesnetz/matrixTests.c b/info2praktikum-neuronalesnetz/matrixTests.c index 686db1e..aa75700 100644 --- a/info2praktikum-neuronalesnetz/matrixTests.c +++ b/info2praktikum-neuronalesnetz/matrixTests.c @@ -21,7 +21,7 @@ void test_createMatrixReturnsCorrectMatrixDimensions(void) { const unsigned int expectedRows = 15; const unsigned int expectedCols = 10; - + Matrix matrixToTest = createMatrix(expectedRows, expectedCols); TEST_ASSERT_EQUAL_UINT32(expectedRows, matrixToTest.rows); TEST_ASSERT_EQUAL_UINT32(expectedCols, matrixToTest.cols); @@ -63,7 +63,7 @@ void test_addFailsOnDifferentInputDimensions(void) MatrixType buffer2[] = {7, 8, 9, 10, 11, 12}; Matrix matrix1 = {.rows=2, .cols=3, .buffer=buffer1}; Matrix matrix2 = {.rows=3, .cols=2, .buffer=buffer2}; - + Matrix result = add(matrix1, matrix2); TEST_ASSERT_NULL(result.buffer); @@ -71,13 +71,39 @@ void test_addFailsOnDifferentInputDimensions(void) TEST_ASSERT_EQUAL_UINT32(0, result.cols); } +void test_addSupportsBroadcasting(void) +{ + MatrixType buffer1[] = {1, 2, 3, 4, 5, 6}; + MatrixType buffer2[] = {7, 8}; + Matrix matrix1 = {.rows=2, .cols=3, .buffer=buffer1}; + Matrix matrix2 = {.rows=2, .cols=1, .buffer=buffer2}; + + Matrix result1 = add(matrix1, matrix2); + Matrix result2 = add(matrix2, matrix1); + + float expectedResults[] = {8, 9, 10, 12, 13, 14}; + + TEST_ASSERT_EQUAL_UINT32(matrix1.rows, result1.rows); + TEST_ASSERT_EQUAL_UINT32(matrix1.cols, result1.cols); + TEST_ASSERT_EQUAL_UINT32(matrix1.rows, result2.rows); + TEST_ASSERT_EQUAL_UINT32(matrix1.cols, result2.cols); + + TEST_ASSERT_EQUAL_INT(sizeof(expectedResults)/sizeof(expectedResults[0]), result1.rows * result1.cols); + TEST_ASSERT_EQUAL_FLOAT_ARRAY(expectedResults, result1.buffer, result1.cols * result1.rows); + TEST_ASSERT_EQUAL_INT(sizeof(expectedResults)/sizeof(expectedResults[0]), result2.rows * result2.cols); + TEST_ASSERT_EQUAL_FLOAT_ARRAY(expectedResults, result2.buffer, result2.cols * result2.rows); + + free(result1.buffer); + free(result2.buffer); +} + void test_multiplyReturnsCorrectResults(void) { MatrixType buffer1[] = {1, 2, 3, 4, 5, 6}; MatrixType buffer2[] = {7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; Matrix matrix1 = {.rows=2, .cols=3, .buffer=buffer1}; Matrix matrix2 = {.rows=3, .cols=4, .buffer=buffer2}; - + Matrix result = multiply(matrix1, matrix2); float expectedResults[] = {74, 80, 86, 92, 173, 188, 203, 218}; @@ -159,6 +185,7 @@ int main() RUN_TEST(test_clearMatrixSetsMembersToNull); RUN_TEST(test_addReturnsCorrectResult); RUN_TEST(test_addFailsOnDifferentInputDimensions); + RUN_TEST(test_addSupportsBroadcasting); RUN_TEST(test_multiplyReturnsCorrectResults); RUN_TEST(test_multiplyFailsOnWrongInputDimensions); RUN_TEST(test_getMatrixAtReturnsCorrectResult); diff --git a/info2praktikum-neuronalesnetz/neuralNetwork.c b/info2praktikum-neuronalesnetz/neuralNetwork.c index bd8f164..937f1de 100644 --- a/info2praktikum-neuronalesnetz/neuralNetwork.c +++ b/info2praktikum-neuronalesnetz/neuralNetwork.c @@ -67,14 +67,14 @@ static unsigned int readDimension(FILE *file) if(fread(&dimension, sizeof(int), 1, file) != 1) dimension = 0; - + return dimension; } static Matrix readMatrix(FILE *file, unsigned int rows, unsigned int cols) { Matrix matrix = createMatrix(rows, cols); - + if(matrix.buffer != NULL) { if(fread(matrix.buffer, sizeof(MatrixType), rows*cols, file) != rows*cols) @@ -128,7 +128,7 @@ NeuralNetwork loadModel(const char *path) { if(checkFileHeader(file)) { - unsigned int inputDimension = readDimension(file); + unsigned int inputDimension = readDimension(file); unsigned int outputDimension = readDimension(file); while(inputDimension > 0 && outputDimension > 0) @@ -142,7 +142,7 @@ NeuralNetwork loadModel(const char *path) clearModel(&model); break; } - + layerBuffer = (Layer *)realloc(model.layers, (model.numberOfLayers + 1) * sizeof(Layer)); if(layerBuffer != NULL) @@ -201,7 +201,7 @@ static Matrix forward(const NeuralNetwork model, Matrix inputBatch) { Matrix biasResult; Matrix weightResult; - + weightResult = multiply(model.layers[i].weights, result); clearMatrix(&result); biasResult = add(model.layers[i].biases, weightResult); @@ -248,9 +248,9 @@ unsigned char *predict(const NeuralNetwork model, const GrayScaleImage images[], Matrix outputBatch = forward(model, inputBatch); unsigned char *result = argmax(outputBatch); - + clearMatrix(&outputBatch); - + return result; } diff --git a/info2praktikum-neuronalesnetz/neuralNetworkTests.c b/info2praktikum-neuronalesnetz/neuralNetworkTests.c index 21ab370..8ab3bee 100644 --- a/info2praktikum-neuronalesnetz/neuralNetworkTests.c +++ b/info2praktikum-neuronalesnetz/neuralNetworkTests.c @@ -8,7 +8,41 @@ static void prepareNeuralNetworkFile(const char *path, const NeuralNetwork nn) { - // TODO + FILE *file = fopen(path, "wb"); + if (!file) { + perror("Fehler beim Öffnen der Datei"); + return; + } + + // Header schreiben + const char *header = "__info2_neural_network_file_format__"; + fwrite(header, sizeof(char), strlen(header), file); + + // Alle Layer schreiben + for (unsigned int i = 0; i < nn.numberOfLayers; i++) { + Layer layer = nn.layers[i]; + + unsigned int inputDim = layer.weights.cols; // Anzahl Eingänge + unsigned int outputDim = layer.weights.rows; // Anzahl Ausgänge + + // Dimensionen schreiben + fwrite(&inputDim, sizeof(unsigned int), 1, file); + fwrite(&outputDim, sizeof(unsigned int), 1, file); + + // Gewichte schreiben + size_t weightCount = layer.weights.rows * layer.weights.cols; + fwrite(layer.weights.buffer, sizeof(MatrixType), weightCount, file); + + // Biases schreiben + size_t biasCount = layer.biases.rows * layer.biases.cols; + fwrite(layer.biases.buffer, sizeof(MatrixType), biasCount, file); + } + + // End-Marker (inputDim = 0) + unsigned int zero = 0; + fwrite(&zero, sizeof(unsigned int), 1, file); + + fclose(file); } void test_loadModelReturnsCorrectNumberOfLayers(void)