Compare commits

..
6 Commits
40 changed files with 183 additions and 428 deletions

No files matched your search

+60 -3
View File
@@ -1,4 +1,61 @@
mnist
runTests
# ---> C
# Prerequisites
*.d
# Object files
*.o
*.exe
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
Startcode/mnist
Startcode/runTests
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# IDE folders
.vscode/
.idea/
# macOS
.DS_Store
Binary file not shown.
+22
View File
@@ -0,0 +1,22 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "imageInput.h"
#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;
return series;
}
// TODO Vervollständigen Sie die Funktion clearSeries, welche eine Bildserie vollständig aus dem Speicher freigibt
void clearSeries(GrayScaleImageSeries *series)
{
}
+23
View File
@@ -0,0 +1,23 @@
#ifndef IMAGEINPUT_H
#define IMAGEINPUT_H
typedef unsigned char GrayScalePixelType;
typedef struct
{
GrayScalePixelType *buffer;
unsigned int width;
unsigned int height;
} GrayScaleImage;
typedef struct
{
GrayScaleImage *images;
unsigned char *labels;
unsigned int count;
} GrayScaleImageSeries;
GrayScaleImageSeries *readImages(const char *path);
void clearSeries(GrayScaleImageSeries *series);
#endif
@@ -1,3 +1,4 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+35
View File
@@ -0,0 +1,35 @@
#include <stdlib.h>
#include <string.h>
#include "matrix.h"
// TODO Matrix-Funktionen implementieren
Matrix createMatrix(unsigned int rows, unsigned int cols)
{
}
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 multiply(const Matrix matrix1, const Matrix matrix2)
{
}
+3 -11
View File
@@ -5,17 +5,8 @@
typedef float MatrixType;
/*
* Die Matrixstruktur hält:
* - Anzahl Zeilen
* - Anzahl Spalten
* - Ein Zeiger auf die gespeicherten Werte (Tests erwarten den Namen "buffer")
*/
typedef struct {
unsigned int rows;
unsigned int cols;
MatrixType *buffer; // <- Name an Tests angepasst
} Matrix;
// TODO Matrixtyp definieren
Matrix createMatrix(unsigned int rows, unsigned int cols);
void clearMatrix(Matrix *matrix);
@@ -24,4 +15,5 @@ MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx, unsigned int co
Matrix add(const Matrix matrix1, const Matrix matrix2);
Matrix multiply(const Matrix matrix1, const Matrix matrix2);
#endif
+28 -1
View File
@@ -71,6 +71,32 @@ 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};
@@ -138,7 +164,7 @@ void test_setMatrixAtFailsOnIndicesOutOfRange(void)
Matrix matrixToTest = {.rows=2, .cols=3, .buffer=buffer};
setMatrixAt(-1, matrixToTest, 2, 3);
TEST_ASSERT_EQUAL_FLOAT_ARRAY(expectedResults, matrixToTest.buffer, matrixToTest.cols * matrixToTest.rows);
TEST_ASSERT_EQUAL_FLOAT_ARRAY(expectedResults, matrixToTest.buffer, sizeof(buffer)/sizeof(MatrixType));
}
void setUp(void) {
@@ -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);
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+10 -32
View File
@@ -170,8 +170,7 @@ NeuralNetwork loadModel(const char *path)
static Matrix imageBatchToMatrixOfImageVectors(const GrayScaleImage images[], unsigned int count)
{
// Matrix matrix = {NULL, 0, 0};
Matrix matrix = {.rows = 0, .cols = 0, .buffer = NULL};
Matrix matrix = {NULL, 0, 0};
if(count > 0 && images != NULL)
{
@@ -191,47 +190,26 @@ static Matrix imageBatchToMatrixOfImageVectors(const GrayScaleImage images[], un
return matrix;
}
//
static void addBiasInPlace(Matrix *matrix, const Matrix *biases)
{
// biases: rows x 1, matrix: rows x cols
if(matrix == NULL || biases == NULL)
return;
if(matrix->buffer == NULL || biases->buffer == NULL)
return;
if(matrix->rows != biases->rows || biases->cols != 1)
return;
for(unsigned int rowIdx = 0; rowIdx < matrix->rows; rowIdx++)
{
MatrixType b = getMatrixAt(*biases, rowIdx, 0);
for(unsigned int colIdx = 0; colIdx < matrix->cols; colIdx++)
{
MatrixType v = getMatrixAt(*matrix, rowIdx, colIdx);
setMatrixAt(v + b, *matrix, rowIdx, colIdx);
}
}
}
//
static Matrix forward(const NeuralNetwork model, Matrix inputBatch)
{
Matrix result = inputBatch;
if(result.buffer != NULL)
{
for(int i = 0; i < (int)model.numberOfLayers; i++)
for(int i = 0; i < model.numberOfLayers; i++)
{
// 1) weights * aktuelles Ergebnis
Matrix weightResult = multiply(model.layers[i].weights, result);
Matrix biasResult;
Matrix weightResult;
weightResult = multiply(model.layers[i].weights, result);
clearMatrix(&result);
result = weightResult;
biasResult = add(model.layers[i].biases, weightResult);
clearMatrix(&weightResult);
// 2) Bias auf alle Spalten addieren (Broadcast)
addBiasInPlace(&result, &model.layers[i].biases);
// 3) Aktivierungsfunktion anwenden
if(model.layers[i].activation != NULL)
model.layers[i].activation(&result);
model.layers[i].activation(&biasResult);
result = biasResult;
}
}
File renamed without changes.
@@ -8,46 +8,7 @@
static void prepareNeuralNetworkFile(const char *path, const NeuralNetwork nn)
{
FILE *file = fopen(path, "wb"); //Datei öffnen im write binary modus
if (!file) //wenn fopen 0 zurück gibt
{
fprintf(stderr, "Fehler: Datei konnte nicht geöffnet werden.\n");
return;
}
// Header schreiben
const char *header = "__info2_neural_network_file_format__";
fwrite(header, sizeof(char), strlen(header), file); // Zeiger auf Daten, größe des ElementsChar (1 byte),
//länge des Stings ohen \0, Zieldatei
// Erste Dimension: Input-Dimension der ersten Schicht (Anzahl der Eingangsneuronen)Anzahl Spalten
unsigned int inputDim = nn.layers[0].weights.cols;
// Speichert die Input-Dimension in die Datei
fwrite(&inputDim, sizeof(unsigned int), 1, file);
// Für jede Schicht im Netzwerk
for (int i = 0; i < nn.numberOfLayers; i++)
{
Layer layer = nn.layers[i]; // Zugriff auf die aktuelle Schicht
// Output-Dimension: Anzahl der Neuronen in dieser Schicht (Anzahl Zeilen der Gewichtsmatrix)
unsigned int outputDim = layer.weights.rows;
// Speichert die Output-Dimension in die Datei
fwrite(&outputDim, sizeof(unsigned int), 1, file);
// Speichert die Gewichtsmatrix der Schicht in die Datei
// Anzahl der Elemente = rows * cols, Typ = MatrixType
fwrite(layer.weights.buffer, sizeof(MatrixType),
layer.weights.rows * layer.weights.cols, file);
// Speichert die Bias-Werte der Schicht in die Datei
// Anzahl der Elemente = rows * cols (meist rows x 1), Typ = MatrixType
fwrite(layer.biases.buffer, sizeof(MatrixType),
layer.biases.rows * layer.biases.cols, file);
}
// Schließt die Datei nach dem Schreiben
fclose(file);
// TODO
}
void test_loadModelReturnsCorrectNumberOfLayers(void)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
-174
View File
@@ -1,174 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "imageInput.h"
#define FILE_HEADER_STRING "__info2_image_file_format__"
/* -------------------------------------------------------------------------
* Hilfsfunktion: liest ein unsigned short (16-bit) aus Datei
* Rückgabe: 1 bei Erfolg, 0 bei Fehler
* ------------------------------------------------------------------------- */
static int readUInt16(FILE *f, unsigned short *out)
{
return fread(out, sizeof(unsigned short), 1, f) == 1;
}
/* -------------------------------------------------------------------------
* Header prüfen:
* - Tests schreiben den Header exakt als strlen(FILE_HEADER_STRING) Bytes
* (OHNE Nullterminator). Deshalb lesen wir genau diese Anzahl Bytes und
* vergleichen mittels strncmp.
* ------------------------------------------------------------------------- */
static int readHeader(FILE *f)
{
size_t len = strlen(FILE_HEADER_STRING);
char buf[64]; /* genügend groß für unseren Header */
if (len >= sizeof(buf))
return 0;
if (fread(buf, 1, len, f) != len)
return 0;
/* selbst terminieren, damit strcmp / strncmp sauber arbeiten können */
buf[len] = '\0';
return strcmp(buf, FILE_HEADER_STRING) == 0;
}
/* -------------------------------------------------------------------------
* readImages:
* Liest das Format wie in den Tests erzeugt:
*
* [header bytes (strlen(FILE_HEADER_STRING))]
* uint16_t numberOfImages
* uint16_t width
* uint16_t height
*
* für jedes Bild:
* width*height bytes pixel
* uint8_t label
* ------------------------------------------------------------------------- */
GrayScaleImageSeries *readImages(const char *path)
{
if (!path)
return NULL;
FILE *f = fopen(path, "rb");
if (!f)
return NULL;
/* Header prüfen */
if (!readHeader(f))
{
fclose(f);
return NULL;
}
/* Anzahl der Bilder (unsigned short in den Tests) */
unsigned short count16;
if (!readUInt16(f, &count16))
{
fclose(f);
return NULL;
}
/* Breite und Höhe (ebenfalls unsigned short in den Tests) */
unsigned short width16, height16;
if (!readUInt16(f, &width16) || !readUInt16(f, &height16))
{
fclose(f);
return NULL;
}
/* Sicherheitscheck: keine extremen Werte */
if (count16 == 0 || width16 == 0 || height16 == 0)
{
/* ungültige/metadaten -> als Fehler behandeln */
fclose(f);
return NULL;
}
/* Serie allokieren */
GrayScaleImageSeries *series = malloc(sizeof(GrayScaleImageSeries));
if (!series)
{
fclose(f);
return NULL;
}
series->count = (unsigned int)count16;
series->images = calloc(series->count, sizeof(GrayScaleImage));
series->labels = calloc(series->count, sizeof(unsigned char));
if (!series->images || !series->labels)
{
clearSeries(series);
fclose(f);
return NULL;
}
/* Für alle Bilder: Breite / Höhe sind gleich (wie im Test geschrieben) */
for (unsigned int i = 0; i < series->count; ++i)
{
series->images[i].width = (unsigned int)width16;
series->images[i].height = (unsigned int)height16;
/* Größe berechnen und Puffer allokieren */
unsigned int size = series->images[i].width * series->images[i].height;
series->images[i].buffer = malloc(size * sizeof(GrayScalePixelType));
if (!series->images[i].buffer)
{
clearSeries(series);
fclose(f);
return NULL;
}
/* Pixeldaten einlesen */
size_t readPixels = fread(series->images[i].buffer, sizeof(GrayScalePixelType), size, f);
if (readPixels != size)
{
clearSeries(series);
fclose(f);
return NULL;
}
/* Label einlesen (1 Byte) */
if (fread(&series->labels[i], sizeof(unsigned char), 1, f) != 1)
{
clearSeries(series);
fclose(f);
return NULL;
}
}
fclose(f);
return series;
}
/* -------------------------------------------------------------------------
* clearSeries: gibt alle Ressourcen einer Serie frei
* - ist NULL-sicher
* ------------------------------------------------------------------------- */
void clearSeries(GrayScaleImageSeries *series)
{
if (!series)
return;
if (series->images)
{
for (unsigned int i = 0; i < series->count; ++i)
{
free(series->images[i].buffer);
series->images[i].buffer = NULL;
}
free(series->images);
series->images = NULL;
}
free(series->labels);
series->labels = NULL;
free(series);
}
-47
View File
@@ -1,47 +0,0 @@
#ifndef IMAGEINPUT_H
#define IMAGEINPUT_H
#include <stddef.h> // für size_t
// Datentyp für ein einzelnes Pixel (0–255)
typedef unsigned char GrayScalePixelType;
// Struktur eines einzelnen Graustufenbildes
typedef struct
{
GrayScalePixelType *buffer; // Zeiger auf Pixel-Daten (width * height)
unsigned int width; // Bildbreite
unsigned int height; // Bildhöhe
} GrayScaleImage;
// Eine Serie von Bildern inklusive Labels
typedef struct
{
GrayScaleImage *images; // Array von Bildern
unsigned char *labels; // Array von Labels (0–255)
unsigned int count; // Anzahl der Bilder
} GrayScaleImageSeries;
/**
* Liest eine Bilderserie aus einer Datei.
* Erwartetes Format:
* uint32 count
* für jedes Bild:
* uint32 width
* uint32 height
* width*height Bytes Bilddaten
* uint8 label
*
* @param path Pfad zur Datei
* @return Pointer auf GrayScaleImageSeries oder NULL bei Fehler
*/
GrayScaleImageSeries *readImages(const char *path);
/**
* Gibt den Speicher einer Serie wieder frei.
*
* @param series Pointer auf die Serie, darf auch NULL sein
*/
void clearSeries(GrayScaleImageSeries *series);
#endif // IMAGEINPUT_H
-120
View File
@@ -1,120 +0,0 @@
#include <stdlib.h>
#include "matrix.h"
/*
* Erstellt eine Matrix. Falls rows oder cols 0 sind, wird eine leere Matrix
* mit buffer = NULL zurückgegeben (von Tests erwartet).
*/
Matrix createMatrix(unsigned int rows, unsigned int cols) {
Matrix m;
// Tests erwarten: Wenn eine Dimension 0 ist -> komplett leere Matrix
if (rows == 0 || cols == 0) {
m.rows = 0;
m.cols = 0;
m.buffer = NULL;
return m;
}
m.rows = rows;
m.cols = cols;
m.buffer = malloc(rows * cols * sizeof(MatrixType));
if (m.buffer == NULL) {
m.rows = 0;
m.cols = 0;
return m;
}
for (unsigned int i = 0; i < rows * cols; i++) {
m.buffer[i] = UNDEFINED_MATRIX_VALUE;
}
return m;
}
/*
* Gibt den Speicher der Matrix frei und setzt alle Felder wie von Tests erwartet.
*/
void clearMatrix(Matrix *matrix) {
if (matrix->buffer != NULL) {
free(matrix->buffer);
}
matrix->buffer = NULL;
matrix->rows = 0;
matrix->cols = 0;
}
/*
* Setzt einen Wert an Position (rowIdx, colIdx).
*/
void setMatrixAt(MatrixType value, Matrix matrix, unsigned int rowIdx, unsigned int colIdx) {
if (matrix.buffer == NULL) return;
if (rowIdx >= matrix.rows || colIdx >= matrix.cols) return;
matrix.buffer[rowIdx * matrix.cols + colIdx] = value;
}
/*
* Gibt einen Wert zurück oder UNDEFINED_MATRIX_VALUE bei ungültigen Indizes.
*/
MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx, unsigned int colIdx) {
if (matrix.buffer == NULL) return UNDEFINED_MATRIX_VALUE;
if (rowIdx >= matrix.rows || colIdx >= matrix.cols) return UNDEFINED_MATRIX_VALUE;
return matrix.buffer[rowIdx * matrix.cols + colIdx];
}
/*
* Addiert zwei Matrizen gleicher Dimension.
* Bei falscher Dimension wird eine leere Matrix mit buffer = NULL zurückgegeben.
*/
Matrix add(const Matrix matrix1, const Matrix matrix2) {
if (matrix1.rows != matrix2.rows || matrix1.cols != matrix2.cols) {
Matrix empty = {0, 0, NULL};
return empty;
}
Matrix result = createMatrix(matrix1.rows, matrix1.cols);
for (unsigned int r = 0; r < matrix1.rows; r++) {
for (unsigned int c = 0; c < matrix1.cols; c++) {
MatrixType value =
getMatrixAt(matrix1, r, c) +
getMatrixAt(matrix2, r, c);
setMatrixAt(value, result, r, c);
}
}
return result;
}
/*
* Multipliziert zwei Matrizen.
*/
Matrix multiply(const Matrix matrix1, const Matrix matrix2) {
if (matrix1.cols != matrix2.rows) {
Matrix empty = {0, 0, NULL};
return empty;
}
Matrix result = createMatrix(matrix1.rows, matrix2.cols);
for (unsigned int r = 0; r < matrix1.rows; r++) {
for (unsigned int c = 0; c < matrix2.cols; c++) {
MatrixType sum = 0;
for (unsigned int k = 0; k < matrix1.cols; k++) {
sum += getMatrixAt(matrix1, r, k) *
getMatrixAt(matrix2, k, c);
}
setMatrixAt(sum, result, r, c);
}
}
return result;
}