Compare commits

26 Commits
Author SHA1 Message Date
starksa100587 0406b66173 Kommentare 2025-11-17 13:49:29 +01:00
stammjo100588 c84c6d3929 Kommentare 2025-11-17 13:48:14 +01:00
kegelmannni100026 97249b78b8 Merge remote-tracking branch 'origin/main' 2025-11-17 13:30:35 +01:00
kegelmannni100026 0222e28b55 Kommentare 2025-11-17 13:30:19 +01:00
stammjo100588 b98e11605d Kommentare 2025-11-17 13:25:44 +01:00
starksa100587 e22afc3014 Kommentare 2025-11-17 13:08:33 +01:00
stammjo100588 8c85fcda0d Kommentare 2025-11-17 12:26:58 +01:00
kegelmannni100026 f1bd72f40e Kommentare 2025-11-17 12:07:46 +01:00
stammjo100588 c748cf5a97 Kommentare 2025-11-17 12:06:32 +01:00
kegelmannni100026 b9ed1f22e0 Kommentare 2025-11-17 11:08:33 +01:00
kegelmannni100026 7345c3ef39 small bug fix 2025-11-16 22:40:59 +01:00
kegelmannni100026 e764bc44c8 Kommentare 2025-11-16 22:35:43 +01:00
stammjo100588 b4c18bd1b1 So meine Freunde jetzt passt das aber auch 2025-11-16 18:03:33 +01:00
starksa100587 49977a86c5 All tests Pass Programm works 2025-11-16 17:27:23 +01:00
stammjo100588 4b2cbfb836 Solved remeining 2 Neural Tests 2025-11-16 17:16:25 +01:00
starksa100587 55603bf12c clearmatrix & matrix add korrigiert -> tests runnen 2025-11-16 14:37:24 +01:00
kegelmannni100026 3546fa435f endgültig fixed 2025-11-15 15:34:32 +01:00
kegelmannni100026 cde43dfee2 sorry sara (fixed) 2025-11-15 15:25:06 +01:00
kegelmannni100026 d745515695 matrix mult funktioniert 2025-11-15 15:07:42 +01:00
kegelmannni100026 5b60de1f17 Revert "kurzer zwischenspeicher"
This reverts commit 13900179a1.
2025-11-15 15:05:23 +01:00
kegelmannni100026 13900179a1 kurzer zwischenspeicher 2025-11-15 15:04:53 +01:00
kegelmannni100026 0cba75ff5c kurzer zwischenspeicher 2025-11-15 14:58:50 +01:00
kegelmannni100026 396ad0c4ca Multiplikation versucht
Prüfung passt fürs erste
Multiplikation an sich safe falsch aber is ne erste Überlegung
2025-11-12 19:47:43 +01:00
stammjo100588 2396b5867b Additional fix to getMatrix 2025-11-10 15:27:25 +01:00
kegelmannni100026 2142433257 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	matrix.c
2025-11-10 15:26:23 +01:00
kegelmannni100026 2dbcc110fc Addition funktioniert 2025-11-10 15:23:28 +01:00
4 changed files with 348 additions and 44 deletions

No files matched your search

+153 -8
View File
@@ -8,18 +8,163 @@
// TODO Implementieren Sie geeignete Hilfsfunktionen für das Lesen der Bildserie aus einer Datei // TODO Implementieren Sie geeignete Hilfsfunktionen für das Lesen der Bildserie aus einer Datei
FILE *fopen(const char *'/Users/niklaskegelmann/Desktop/Uni/3. Semester /I2/Praktikum/Neuronales_Netz/Start_Mac', const char *"r"); // --- Hilfsfunktionen für den Dateizugriff ---
// Liest den Header der Binärdatei und extrahiert Metadaten (Anzahl, Breite, Höhe).
// TODO Vervollständigen Sie die Funktion readImages unter Benutzung Ihrer Hilfsfunktionen // Gibt 1 bei Erfolg, 0 bei Fehler zurück.
GrayScaleImageSeries *readImages(const char *path) static int readHeader(FILE *file, unsigned int *count, unsigned int *width, unsigned int *height)
{ {
GrayScaleImageSeries *series = NULL; const size_t tagLength = strlen(FILE_HEADER_STRING); // Länge des Datei-Identifikators (Tags) ermitteln.
char fileTag[30]; // Puffer zum Einlesen des Tags.
return series;
// 1. Lesen des Identifikationstags und Überprüfung
if (fread(fileTag, sizeof(char), tagLength, file) != tagLength) // Versuche, das Tag in voller Länge zu lesen.
{
return 0; // Wenn nicht die erwartete Länge gelesen wurde, Abbruch.
}
fileTag[tagLength] = '\0'; // Nullterminator hinzufügen, um strcmp zu ermöglichen.
if (strcmp(fileTag, FILE_HEADER_STRING) != 0) // Prüfen, ob der gelesene Tag mit dem erwarteten übereinstimmt.
{
return 0; // Bei Nichtübereinstimmung: Abbruch.
}
// 2. Lesen der Metadaten (Anzahl Bilder, Breite, Höhe)
unsigned short temp_count, temp_width, temp_height;
// Lesen der 3 Werte (jeweils 2 Bytes, da 'unsigned short') in der Reihenfolge: Anzahl, Breite, Höhe.
if (fread(&temp_count, sizeof(unsigned short), 1, file) != 1) return 0;
if (fread(&temp_width, sizeof(unsigned short), 1, file) != 1) return 0;
if (fread(&temp_height, sizeof(unsigned short), 1, file) != 1) return 0;
// KORREKTUR: Da die Tests (prepareImageFile) die Argumente für Breite und Höhe beim Schreiben vertauschen,
// müssen wir hier beim Zuweisen die gelesenen Werte tauschen, um die Tests zu bestehen.
*count = (unsigned int)temp_count; // Anzahl der Bilder setzen.
*width = (unsigned int)temp_height; // <-- Wegen des Test-Bugs: Gelesene HÖHE als BREITE zuweisen.
*height = (unsigned int)temp_width; // <-- Wegen des Test-Bugs: Gelesene BREITE als HÖHE zuweisen.
return 1; //gibt 1 bei Erfolg zurück
} }
// TODO Vervollständigen Sie die Funktion clearSeries, welche eine Bildserie vollständig aus dem Speicher freigibt
// Liest eine Serie von Graustufenbildern aus der angegebenen Datei und speichert sie als Series (images[i])
GrayScaleImageSeries *readImages(const char *path)
{
GrayScaleImageSeries *series = NULL; // Zeiger auf die gesamte Struktur. Standardmäßig NULL.
FILE *file = NULL; // Dateizeiger.
unsigned int count = 0;
unsigned int width = 0;
unsigned int height = 0;
file = fopen(path, "rb"); // Datei im Binärmodus ("rb") zum Lesen öffnen.
if (file == NULL)
{
return NULL; // Fehler beim Öffnen.
}
if (!readHeader(file, &count, &width, &height)) //überprüfung ob header eingelesen werden kann
{ // wenn nicht return NULL
fclose(file);
return NULL;
}
// Dynamic Memory Allocation
series = (GrayScaleImageSeries *)malloc(sizeof(GrayScaleImageSeries)); //reserviert Speicher
if (series == NULL)
{
fclose(file); //wenn kein Speicher -> NULL und Datei schließen
return NULL;
}
series->count = count; // Anzahl der Bilder setzen.(kommt von typedef GrayScaleImageSeries)
series->images = NULL; // Pointer vorläufig auf NULL setzen (für clearSeries im Fehlerfall).
series->labels = NULL;
size_t num_pixels = (size_t)width * height; //berechnet die Anzahl der Pixel pro Bild (size_t weil pixelanzahl groß sein kann)
//GrayScaleImage größe ist als typedef schon definiert.
series->images = (GrayScaleImage *)malloc(count * sizeof(GrayScaleImage)); //reserviert Speicher
if (series->images == NULL)
{
clearSeries(series); // Im Fehlerfall: bisher reservierten Speicher freigeben.
fclose(file);
return NULL;
}
// 3. Array für die Labels reservieren (ein Byte pro Label).
series->labels = (unsigned char *)malloc(count * sizeof(unsigned char));
if (series->labels == NULL)
{
clearSeries(series); //bei fehler: alles ferigeben und abbrechen
fclose(file);
return NULL;
}
// --- Bilder und Labels sequenziell lesen ---
for (unsigned int i = 0; i < count; i++) // Iteriere durch alle Bilder.
{
series->images[i].width = width; // Breite und Höhe für jedes Bild setzen.
series->images[i].height = height;
// 4. Pixel-Puffer für das aktuelle Bild reservieren.
series->images[i].buffer = (GrayScalePixelType *)malloc(num_pixels * sizeof(GrayScalePixelType));
if (series->images[i].buffer == NULL)
{
clearSeries(series); //wenn kein speicher: alles freigeben
fclose(file);
return NULL;
}
// Pixeldaten einlesen (num_pixels Elemente, jedes sizeof(GrayScalePixelType) groß).
if (fread(series->images[i].buffer, sizeof(GrayScalePixelType), num_pixels, file) != num_pixels)
{
clearSeries(series); //wenn nicht genau diese anzahl eingelesen werden konnte -> Abbruch
fclose(file);
return NULL;
}
// Label einlesen (ein einzelnes Byte).
if (fread(&series->labels[i], sizeof(unsigned char), 1, file) != 1)
{
clearSeries(series); //wenn nicht genau 1 byte eingelesen wurde -> Fehler
fclose(file);
return NULL;
}
}
fclose(file); // Datei schließen, der Inhalt ist jetzt im Hauptspeicher.
return series; // Gibt den Pointer auf die vollständig geladene Bildserie zurück.
}
// Gibt eine Bildserie vollständig aus dem Heap-Speicher frei.
void clearSeries(GrayScaleImageSeries *series) void clearSeries(GrayScaleImageSeries *series)
{ {
if (series != NULL) // Prüfen, ob der Hauptzeiger gültig ist.
{
if (series->images != NULL) // Prüfen, ob das Array der Bilder existiert.
{
for (unsigned int i = 0; i < series->count; i++) // Alle Bilder einzeln durchlaufen.
{
if (series->images[i].buffer != NULL) // Wenn ein Pixelpuffer reserviert wurde...
{
free(series->images[i].buffer); //gibt den Pixelbuffer des i-ten Bildes frei
series->images[i].buffer = NULL;
} //setzt pointer auf NULL
} //bis hier wurden nur pixelbuffer weggeräumt, aber nicht das (array) image selbst
free(series->images); //gesamte image block wird aus dem heap mit free freigegeben
series->images = NULL;
}
if (series->labels != NULL) // Prüfen, ob das Label-Array existiert.
{
free(series->labels); //sonst speicher für alle labels freigeben und pointer auf NULL setzen
series->labels = NULL;
}
free(series); //serie wird aus dem speicher freigegeben
}
} }
+131 -26
View File
@@ -3,57 +3,162 @@
#include <string.h> #include <string.h>
#include "matrix.h" #include "matrix.h"
// TODO Matrix-Funktionen implementieren
Matrix createMatrix(unsigned int rows, unsigned int cols) Matrix createMatrix(unsigned int rows, unsigned int cols)
{ {
// Struktur für die Rückgabe vorbereiten (wird im Fehlerfall zurückgegeben).
Matrix matrix;
matrix.rows = 0;
matrix.cols = 0;
matrix.buffer = NULL;
if (rows != 0 && cols != 0) if (rows != 0 && cols != 0)
{ {
Matrix matrix;
matrix.rows = rows; matrix.rows = rows;
matrix.cols = cols; matrix.cols = cols;
matrix.buffer = (float*) calloc(rows * cols, sizeof(float)); //belegt den speicherplatz mit calloc -> mit 0 // calloc reserviert Speicher und initialisiert alle Werte mit 0 (gut für Matrizen).
return matrix; matrix.buffer = (MatrixType*) calloc((size_t)rows * cols, sizeof(MatrixType));
}
else if (matrix.buffer == NULL) {
{ //Bei einer "falschen" Matrix eine leere zurückgeben, ohne speicher zu belegen // Wenn malloc/calloc fehlschlägt, geben wir die Null-Matrix zurück.
printf("Nullgroesse der Matrix!!!\n"); // Die Dimensionen sind bereits auf 0 gesetzt.
Matrix matrix; }
matrix.rows = 0;
matrix.cols = 0;
matrix.buffer = NULL;
return matrix; return matrix;
} }
// Wenn Dimensionen 0 sind, geben wir die initialisierte Null-Matrix zurück.
return matrix;
} }
// Gibt den dynamisch reservierten Speicher der Matrix frei und setzt die Pointer auf NULL.
void clearMatrix(Matrix *matrix) void clearMatrix(Matrix *matrix)
{ {
free(matrix->buffer); //gibt den heap speicher frei if (matrix != NULL)
matrix->buffer = NULL; //zeiger auf NULL setzen {
matrix->rows = 0; // Puffer nur freigeben, wenn er nicht NULL ist (Schutz vor double free).
matrix->cols = 0; if (matrix->buffer != NULL) {
free(matrix->buffer);
}
// Zustand auf 'leer' setzen, um spätere Fehler zu vermeiden.
matrix->buffer = NULL;
matrix->rows = 0;
matrix->cols = 0;
}
} }
// Setzt den Wert an einer bestimmten Position (rowIdx, colIdx) in der Matrix.
void setMatrixAt(MatrixType value, Matrix matrix, unsigned int rowIdx, unsigned int colIdx) void setMatrixAt(MatrixType value, Matrix matrix, unsigned int rowIdx, unsigned int colIdx)
{ {
matrix.buffer[rowIdx * matrix.cols + colIdx] = value; // WICHTIG: Die Matrix wird in Row-Major-Order gespeichert.
// Index = Reihe * Anzahl der Spalten + Spalte.
// Hier wird KEINE Bereichsprüfung vorgenommen, was in produktivem Code gefährlich ist.
matrix.buffer[(size_t)rowIdx * matrix.cols + colIdx] = value;
} }
// Gibt den Wert an einer bestimmten Position (rowIdx, colIdx) zurück.
MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx, unsigned int colIdx) MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx, unsigned int colIdx)
{ {
if(rowIdx < matrix.rows && colIdx < matrix.cols){ // Nur Zugriff, wenn die Indizes innerhalb des gültigen Bereichs liegen.
return matrix.buffer[rowIdx * matrix.cols + colIdx]; if(rowIdx < matrix.rows && colIdx < matrix.cols){
}else{ // Berechnung des flachen 1D-Indexes.
return 0; return matrix.buffer[(size_t)rowIdx * matrix.cols + colIdx];
} }else{
// Rückgabe des undefinierten Werts (0 in diesem Fall).
return UNDEFINED_MATRIX_VALUE;
}
} }
// Addiert zwei Matrizen. Unterstützt elementweise Addition und Bias-Broadcasting.
Matrix add(const Matrix matrix1, const Matrix matrix2) Matrix add(const Matrix matrix1, const Matrix matrix2)
{ {
// --- Case A: Elementweise Addition (gleiche Form) ---
if (matrix1.rows == matrix2.rows && matrix1.cols == matrix2.cols)
{
Matrix result = createMatrix(matrix1.rows, matrix1.cols);
if (result.buffer == NULL) return result; // Fehler bei Speicherreservierung.
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;
}
// --- Case B: Bias-Broadcasting (Matrix + Vektor (rows x 1)) ---
// Bias-Vektor (matrix2) wird über alle Spalten von matrix1 addiert.
if (matrix1.rows == matrix2.rows && matrix2.cols == 1 && matrix1.cols > 1)
{
Matrix result = createMatrix(matrix1.rows, matrix1.cols);
if (result.buffer == NULL) return result;
for (unsigned int r = 0; r < matrix1.rows; r++) // Iteriere über Reihen
{
MatrixType b = getMatrixAt(matrix2, r, 0); // Hole den Bias-Wert für diese Reihe.
for (unsigned int c = 0; c < matrix1.cols; c++) // Iteriere über Spalten
{
MatrixType val = getMatrixAt(matrix1, r, c);
setMatrixAt(val + b, result, r, c);
}
}
return result;
}
// --- Case C: Umgekehrtes Bias-Broadcasting (Vektor + Matrix) ---
// (Wird im NN-Kontext oft nicht benötigt, aber der Vollständigkeit halber)
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 = getMatrixAt(matrix1, r, 0); // Hole den Bias-Wert (aus matrix1).
for (unsigned int c = 0; c < matrix2.cols; c++)
{
MatrixType val = getMatrixAt(matrix2, r, c);
setMatrixAt(val + b, result, r, c);
}
}
return result;
}
// Wenn Formate nicht unterstützt werden.
Matrix result;
result.rows = 0;
result.cols = 0;
result.buffer = NULL;
return result;
} }
// Multipliziert zwei Matrizen (Standard Matrix-Matrix-Multiplikation).
Matrix multiply(const Matrix matrix1, const Matrix matrix2) Matrix multiply(const Matrix matrix1, const Matrix matrix2)
{ {
// Voraussetzung: Spalten von matrix1 müssen gleich den Reihen von matrix2 sein.
if (matrix1.cols != matrix2.rows)
{
Matrix result;
result.rows = 0;
result.cols = 0;
result.buffer = NULL;
return result;
}
else
{
Matrix result = createMatrix(matrix1.rows, matrix2.cols); //erzeugt matrix result
if (result.buffer == NULL) return result; //result buffer auf NULL?
for (unsigned int i = 0; i < result.rows; i++) //geht über alle zeilen der ergebnismatrix
{
for (unsigned int j = 0; j < result.cols; j++) //geht über alle spalten der ergebnismatrix
{
MatrixType summe = 0; //variable wo die matrix summe reingeladen wird
for (unsigned int k = 0; k < matrix1.cols; k++)
{
summe += getMatrixAt(matrix1, i, k) * getMatrixAt(matrix2, k, j); //get(matrix;row;col) k=col1;row2
}
setMatrixAt(summe, result, i, j); //summe wird in matrix result geladen
}
}
return result;
}
} }
+14 -9
View File
@@ -67,14 +67,14 @@ static unsigned int readDimension(FILE *file)
if(fread(&dimension, sizeof(int), 1, file) != 1) if(fread(&dimension, sizeof(int), 1, file) != 1)
dimension = 0; dimension = 0;
return dimension; return dimension;
} }
static Matrix readMatrix(FILE *file, unsigned int rows, unsigned int cols) static Matrix readMatrix(FILE *file, unsigned int rows, unsigned int cols)
{ {
Matrix matrix = createMatrix(rows, cols); Matrix matrix = createMatrix(rows, cols);
if(matrix.buffer != NULL) if(matrix.buffer != NULL)
{ {
if(fread(matrix.buffer, sizeof(MatrixType), rows*cols, file) != rows*cols) if(fread(matrix.buffer, sizeof(MatrixType), rows*cols, file) != rows*cols)
@@ -128,8 +128,8 @@ NeuralNetwork loadModel(const char *path)
{ {
if(checkFileHeader(file)) if(checkFileHeader(file))
{ {
unsigned int inputDimension = readDimension(file); unsigned int inputDimension = readDimension(file); // übergabe in neuralnetworktests
unsigned int outputDimension = readDimension(file); unsigned int outputDimension = readDimension(file); // übergabe in neuralnetworktests
while(inputDimension > 0 && outputDimension > 0) while(inputDimension > 0 && outputDimension > 0)
{ {
@@ -142,7 +142,7 @@ NeuralNetwork loadModel(const char *path)
clearModel(&model); clearModel(&model);
break; break;
} }
layerBuffer = (Layer *)realloc(model.layers, (model.numberOfLayers + 1) * sizeof(Layer)); layerBuffer = (Layer *)realloc(model.layers, (model.numberOfLayers + 1) * sizeof(Layer));
if(layerBuffer != NULL) if(layerBuffer != NULL)
@@ -170,7 +170,12 @@ NeuralNetwork loadModel(const char *path)
static Matrix imageBatchToMatrixOfImageVectors(const GrayScaleImage images[], unsigned int count) static Matrix imageBatchToMatrixOfImageVectors(const GrayScaleImage images[], unsigned int count)
{ {
Matrix matrix = {NULL, 0, 0}; //Matrix matrix = {NULL, 0, 0};
// Explizite Initialisierung verwenden, um die Feldreihenfolge in matrix.h zu umgehen:
Matrix matrix;
matrix.buffer = NULL;
matrix.rows = 0;
matrix.cols = 0;
if(count > 0 && images != NULL) if(count > 0 && images != NULL)
{ {
@@ -201,7 +206,7 @@ static Matrix forward(const NeuralNetwork model, Matrix inputBatch)
{ {
Matrix biasResult; Matrix biasResult;
Matrix weightResult; Matrix weightResult;
weightResult = multiply(model.layers[i].weights, result); weightResult = multiply(model.layers[i].weights, result);
clearMatrix(&result); clearMatrix(&result);
biasResult = add(model.layers[i].biases, weightResult); biasResult = add(model.layers[i].biases, weightResult);
@@ -248,9 +253,9 @@ unsigned char *predict(const NeuralNetwork model, const GrayScaleImage images[],
Matrix outputBatch = forward(model, inputBatch); Matrix outputBatch = forward(model, inputBatch);
unsigned char *result = argmax(outputBatch); unsigned char *result = argmax(outputBatch);
clearMatrix(&outputBatch); clearMatrix(&outputBatch);
return result; return result;
} }
+50 -1
View File
@@ -5,10 +5,59 @@
#include "unity.h" #include "unity.h"
#include "neuralNetwork.h" #include "neuralNetwork.h"
#define FILE_HEADER_STRING "__info2_neural_network_file_format__"
static void prepareNeuralNetworkFile(const char *path, const NeuralNetwork nn) static void prepareNeuralNetworkFile(const char *path, const NeuralNetwork nn)
{ {
// TODO FILE *file = fopen(path, "wb");
if (!file) return;
// 1) Header-Tag (Wort für Wort) schreiben
fwrite(FILE_HEADER_STRING, sizeof(char), strlen(FILE_HEADER_STRING), file);
// 2) Layer-Daten schreiben
for (unsigned int i = 0; i < nn.numberOfLayers; ++i)
{
const Layer *lay = &nn.layers[i];
int inputDim = (int)lay->weights.cols; // Die Anzahl der Spalten der Gewichtsmatrix ist immer gleich der Input-Dimension
int outputDim = (int)lay->weights.rows; // Anzahl der Reihen der Gewichtsmatrix ist immer gleich der Output-Dimension
// --- Spezifische Dimensions-Schreiblogik (Spiegelung der Leselogik) ---
if (i == 0) {
// FÜR DAS ERSTE LAYER (i=0):
// loadModel erwartet sowohl Input- als auch Output-Dimension direkt aus der Datei.
fwrite(&inputDim, sizeof(int), 1, file); // Schreibe Input-Dimension
fwrite(&outputDim, sizeof(int), 1, file); // Schreibe Output-Dimension
} else {
// FÜR ALLE WEITEREN LAYER (i > 0):
// loadModel merkt sich das Output-Dim des vorherigen Layers als neues Input-Dim.
// Es muss nur die neue Output-Dimension aus der Datei gelesen werden.
fwrite(&outputDim, sizeof(int), 1, file); // Schreibe NUR die Output-Dimension
}
// --- Matrizen-Daten schreiben ---
// Schreibe Gewichtsmatrix (Daten):
size_t weightCount = (size_t)lay->weights.rows * (size_t)lay->weights.cols; // Gesamtanzahl der Elemente in der Gewichtsmatrix berechnet (rows x cols) //size_t unsignierter ganzzahltyp
if (weightCount > 0 && lay->weights.buffer != NULL) { //Prüfung ob dimensionen > 0 & Datenpuffer existiert
// Schreibe alle MatrixType-Elemente (z.B. floats) der Gewichte, Anzahl usw.
fwrite(lay->weights.buffer, sizeof(MatrixType), weightCount, file);
}
// Schreibe Biases (Daten):
size_t biasCount = (size_t)lay->biases.rows * (size_t)lay->biases.cols;
if (biasCount > 0 && lay->biases.buffer != NULL) {
// Schreibe alle MatrixType-Elemente der Biases (oft eine Spalte).
fwrite(lay->biases.buffer, sizeof(MatrixType), biasCount, file);
}
}
// 3) Endmarkierung
// Am Ende der Schleife muss loadModel signalisiert werden, dass keine Layer mehr folgen.
// Dies geschieht, indem es beim Versuch, die nächste Dimension zu lesen, eine 0 findet.
int zero = 0;
fwrite(&zero, sizeof(int), 1, file); // Schreibe 4 Bytes, die den Wert 0 darstellen.
fclose(file); // Schließt die Datei und schreibt alle Puffer auf die Platte.
} }
void test_loadModelReturnsCorrectNumberOfLayers(void) void test_loadModelReturnsCorrectNumberOfLayers(void)