20 Commits
Author SHA1 Message Date
Kristin 7aa57191da neuralNetworkTests mit Kommentaren 2025-11-23 17:16:18 +01:00
Kristin da4eaa718d Merge branch 'main' of https://git.efi.th-nuernberg.de/gitea/kachelto100370/info2Praktikum-NeuronalesNetz into Krisp2 2025-11-23 16:44:13 +01:00
Kristin 56d59b1b50 neuralNetworkTests 2025-11-23 16:41:57 +01:00
Kristin 86a9d16c4f addmatrix mit broadcasten 2025-11-22 12:41:41 +01:00
Kristin ede0bd8bd8 addmatrix mit broadcasten 2025-11-22 12:15:04 +01:00
kachelto100370 7f3c6d1d3f first pass matrix add, ohne broadcasting 2025-11-20 16:04:01 +01:00
hofmannkr98890 97df88c0ab Merge pull request 'Krisp2' (#1) from Krisp2 into main
Reviewed-on: kachelto100370/info2Praktikum-NeuronalesNetz#1
2025-11-20 13:48:43 +00:00
hofmannkr98890 be07dcffcf Merge branch 'main' into Krisp2 2025-11-20 13:48:23 +00:00
Kristin 858673bdca makefile für alle Betriebssysteme 2025-11-20 14:46:14 +01:00
kachelto100370 619cd95a5c start wroking on image Input 2025-11-18 10:45:50 +01:00
Kristin 6b9711e47d Merge branch 'main' of https://git.efi.th-nuernberg.de/gitea/kachelto100370/info2Praktikum-NeuronalesNetz into Krisp2 2025-11-18 10:15:56 +01:00
kachelto100370 653312f8a6 merge upstream 2025-11-18 09:15:45 +00:00
Kristin 2b751ef931 Merge branch 'main' of https://git.efi.th-nuernberg.de/gitea/kachelto100370/info2Praktikum-NeuronalesNetz into Krisp2 2025-11-18 10:13:09 +01:00
Kristin 0081e8f89e data in buffer 2025-11-18 10:11:38 +01:00
kachelto100370 d8e759b436 merge upstream 2025-11-16 15:37:40 +00:00
Kristin 3c49920613 matrix.c und matrix.h, makefile für windows 2025-11-13 20:28:48 +01:00
kachelto100370 28944bd871 added createMatrix() + matrix data type 2025-11-11 11:15:40 +01:00
kachelto100370 29b2966c63 defined struct matrix 2025-11-11 09:54:31 +01:00
kachelto100370 41c164d3b2 kleine config änderung 2025-11-11 09:20:18 +01:00
kachelto100370 b4bc2fae8a add Readme 2025-11-11 09:20:18 +01:00
9 changed files with 785 additions and 469 deletions
+3
View File
@@ -0,0 +1,3 @@
{
"makefile.configureOnOpen": false
}
+2
View File
@@ -0,0 +1,2 @@
# Projekt 2 für Informatik 2 Praktikum
+8 -1
View File
@@ -7,16 +7,23 @@
#define FILE_HEADER_STRING "__info2_image_file_format__" #define FILE_HEADER_STRING "__info2_image_file_format__"
// 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
GrayScaleImage readImage()
{
}
// TODO Vervollständigen Sie die Funktion readImages unter Benutzung Ihrer Hilfsfunktionen // TODO Vervollständigen Sie die Funktion readImages unter Benutzung Ihrer Hilfsfunktionen
GrayScaleImageSeries *readImages(const char *path) GrayScaleImageSeries *readImages(const char *path)
{ {
GrayScaleImageSeries *series = NULL; GrayScaleImageSeries *series = NULL;
FILE *file = fopen("mnist_test.info2","rb");
char headOfFile;
series = malloc();
return series; return series;
} }
// TODO Vervollständigen Sie die Funktion clearSeries, welche eine Bildserie vollständig aus dem Speicher freigibt // TODO Vervollständigen Sie die Funktion clearSeries, welche eine Bildserie vollständig aus dem Speicher freigibt
void clearSeries(GrayScaleImageSeries *series) void clearSeries(GrayScaleImageSeries *series)
{ {
} }
+1 -2
View File
@@ -62,5 +62,4 @@ ifeq ($(OS),Windows_NT)
del /f *.o *.exe del /f *.o *.exe
else else
rm -f *.o mnist runMatrixTests runNeuralNetworkTests runImageInputTests rm -f *.o mnist runMatrixTests runNeuralNetworkTests runImageInputTests
endif endif
+231 -21
View File
@@ -1,35 +1,245 @@
#include "matrix.h"
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "matrix.h"
// TODO Matrix-Funktionen implementieren // TODO Matrix-Funktionen implementieren
/*typedef struct {
unsigned int rows; //Zeilen
unsigned int cols; //Spalten
MatrixType *buffer; //Zeiger auf Speicherbereich Reihen*Spalten
} Matrix;*/
Matrix createMatrix(unsigned int rows, unsigned int cols) {
Matrix createMatrix(unsigned int rows, unsigned int cols) Matrix errorMatrix = {0, 0, NULL};
{ if (rows == 0 || cols == 0) {
return errorMatrix;
}
MatrixType *buffer =
malloc(rows * cols * sizeof(MatrixType)); // Speicher reservieren, malloc
// liefert Zeiger auf Speicher
Matrix newMatrix = {rows, cols, buffer}; // neue Matrix nach struct
return newMatrix;
}
void clearMatrix(Matrix *matrix) {
matrix->buffer = UNDEFINED_MATRIX_VALUE;
matrix->rows = UNDEFINED_MATRIX_VALUE;
matrix->cols = UNDEFINED_MATRIX_VALUE;
free((*matrix).buffer); // Speicher freigeben
} }
void clearMatrix(Matrix *matrix) void setMatrixAt(const MatrixType value, Matrix matrix,
{ const unsigned int rowIdx, // Kopie der Matrix wird übergeben
const unsigned int colIdx) {
if (rowIdx >= matrix.rows ||
colIdx >= matrix.cols) { // Speichergröße nicht überschreiten
return;
}
matrix.buffer[rowIdx * matrix.cols + colIdx] =
value; // rowIdx * matrix.cols -> Beginn der Zeile colIdx ->Spalte
// innerhalb der Zeile
}
MatrixType getMatrixAt(const Matrix matrix,
unsigned int rowIdx, // Kopie der Matrix wird übergeben
unsigned int colIdx) {
if (rowIdx >= matrix.rows ||
colIdx >= matrix.cols) { // Speichergröße nicht überschreiten
return 0;
}
MatrixType value = matrix.buffer[rowIdx * matrix.cols + colIdx];
return value;
} }
void setMatrixAt(MatrixType value, Matrix matrix, unsigned int rowIdx, unsigned int colIdx) Matrix broadCastCols(const Matrix matrix, const unsigned int rows,
{ const unsigned int cols) {
Matrix copy = createMatrix(
rows, cols); // Matrix 1 Kopie erstellen mit Dimensionen von Matrix2
for (int r = 0; r < rows; r++) {
MatrixType value = getMatrixAt(matrix, r, 0);
for (int c = 0; c < cols; c++) {
setMatrixAt(value, copy, r, c);
}
}
return copy;
}
Matrix broadCastRows(const Matrix matrix, const unsigned int rows,
const unsigned int cols) {
Matrix copy = createMatrix(rows, cols);
for (int c = 0; c < cols; c++) {
MatrixType value = getMatrixAt(matrix, 0, c);
for (int r = 0; r < rows; r++) {
setMatrixAt(value, copy, r, c);
}
}
return copy;
} }
MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx, unsigned int colIdx) Matrix add(const Matrix matrix1, const Matrix matrix2) {
{
const unsigned int rows1 = matrix1.rows;
const unsigned int rows2 = matrix2.rows;
const unsigned int cols1 = matrix1.cols;
const unsigned int cols2 = matrix2.cols;
const int rowsEqual = ((rows1 == rows2) ? 1 : 0);
const int colsEqual = ((cols1 == cols2) ? 1 : 0);
if (rowsEqual && colsEqual) // addieren
{
Matrix result = createMatrix(rows1, cols1); // Speicher reservieren
for (int i = 0; i < (rows1 * cols1); i++) { // addieren
result.buffer[i] =
(matrix1.buffer[i] +
matrix2.buffer[i]); // buffer[i] ⇔ *(buffer + i) Adresse =
// Startadresse + (i * sizeof(MatrixType))
}
return result; // zurückgeben
}
else if (rowsEqual && !colsEqual) {
if (cols1 == 1) {
Matrix result = createMatrix(rows2, cols2);
Matrix copy1 = broadCastCols(matrix1, rows2, cols2);
for (int i = 0; i < (rows2 * cols2); i++) { // addieren
result.buffer[i] =
(copy1.buffer[i] +
matrix2.buffer[i]); // buffer[i] ⇔ *(buffer + i) Adresse =
// Startadresse + (i * sizeof(MatrixType))
}
return result;
// add und return
} else if (cols2 == 1) {
Matrix result = createMatrix(rows1, cols1);
Matrix copy2 = broadCastCols(matrix2, rows1, cols1);
for (int i = 0; i < (rows1 * cols1); i++) { // addieren
result.buffer[i] =
(matrix1.buffer[i] +
copy2.buffer[i]); // buffer[i] ⇔ *(buffer + i) Adresse =
// Startadresse + (i * sizeof(MatrixType))
}
return result;
// add und return
}
else {
printf("Fehlermeldung"); // vielleicht Fehlermeldung ändern zu
// Programmabbruch
Matrix error = {0, 0, NULL};
return error;
}
}
else if (!rowsEqual && colsEqual) {
if (rows1 == 1) {
Matrix result = createMatrix(rows2, cols2);
Matrix copy1 = broadCastRows(matrix1, rows2, cols2);
for (int i = 0; i < (rows2 * cols2); i++) { // addieren
result.buffer[i] =
(copy1.buffer[i] +
matrix2.buffer[i]); // buffer[i] ⇔ *(buffer + i) Adresse =
// Startadresse + (i * sizeof(MatrixType))
}
return result;
// add und return
} else if (rows2 == 1) {
Matrix result = createMatrix(rows1, cols1);
Matrix copy2 = broadCastCols(matrix2, rows1, cols1);
// add und return
for (int i = 0; i < (rows1 * cols1); i++) { // addieren
result.buffer[i] =
(matrix1.buffer[i] +
copy2.buffer[i]); // buffer[i] ⇔ *(buffer + i) Adresse =
// Startadresse + (i * sizeof(MatrixType))
}
return result;
}
else {
printf("Fehlermeldung"); // vielleicht Fehlermeldung ändern zu
// Programmabbruch
Matrix error = {0, 0, NULL};
return error;
}
}
else {
printf("Fehlermeldung"); // vielleicht Fehlermeldung ändern zu
// Programmabbruch
Matrix error = {0, 0, NULL};
return error;
}
} }
Matrix add(const Matrix matrix1, const Matrix matrix2) Matrix multiply(const Matrix matrix1, const Matrix matrix2) {
{ // Spalten1 müssen gleich zeilen2 sein! dann multiplizieren
if (matrix1.cols == matrix2.rows) {
} Matrix multMatrix = createMatrix(matrix1.rows, matrix2.cols);
// durch neue matrix iterieren
Matrix multiply(const Matrix matrix1, const Matrix matrix2) for (int r = 0; r < matrix1.rows; r++) {
{ for (int c = 0; c < matrix2.cols; c++) {
MatrixType sum = 0.0;
// skalarprodukte berechnen, k damit die ganze zeile mal die ganze
// spalte genommen wird quasi
for (int k = 0; k < matrix1.cols; k++) {
// sum+=
// matrix1.buffer[r*matrix1.cols+k]*matrix2.buffer[k*matrix2.cols+c];
sum += getMatrixAt(matrix1, r, k) * getMatrixAt(matrix2, k, c);
}
// Ergebnisse in neue matrix speichern
setMatrixAt(sum, multMatrix, r, c);
}
}
return multMatrix;
}
// sonst fehler, kein multiply möglich
else {
Matrix errorMatrix = {0, 0, NULL};
return errorMatrix;
}
} }
+14 -3
View File
@@ -6,14 +6,25 @@
typedef float MatrixType; typedef float MatrixType;
// TODO Matrixtyp definieren // TODO Matrixtyp definieren
typedef struct {
unsigned int rows;
unsigned int cols;
MatrixType *buffer;
} Matrix;
Matrix createMatrix(unsigned int rows, unsigned int cols); Matrix createMatrix(unsigned int rows, unsigned int cols);
void clearMatrix(Matrix *matrix); void clearMatrix(Matrix *matrix);
void setMatrixAt(MatrixType value, Matrix matrix, unsigned int rowIdx, unsigned int colIdx); void setMatrixAt(MatrixType value, Matrix matrix, unsigned int rowIdx,
MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx, unsigned int colIdx); unsigned int colIdx);
MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx,
unsigned int colIdx);
Matrix broadCastCols(const Matrix matrix, const unsigned int rows,
const unsigned int cols);
Matrix broadCastRows(const Matrix matrix, const unsigned int rows,
const unsigned int cols);
Matrix add(const Matrix matrix1, const Matrix matrix2); Matrix add(const Matrix matrix1, const Matrix matrix2);
Matrix multiply(const Matrix matrix1, const Matrix matrix2); Matrix multiply(const Matrix matrix1, const Matrix matrix2);
#endif #endif
+29
View File
@@ -0,0 +1,29 @@
Inhalte: Dynamische Speicherverwaltung, Strukturen, Dateien lesen.
Ziel: Die Bilder aus mnist_test.info 2 auslesen
Struktur für einlesen des Strings am Anfang der Datei:
int AnzahlBilder
int breiteBilder
int LaengeBilder
Struktur für Bilder:
unsinged int array Breite * Höhe
unsigned int Klasse (Label 0 - 9)
Speicher für Bilder dynamisch allokieren
GrayScaleImageSeries:
datei einlesen
header String aus der Datei lesen
mit header String den benötigten Speicher freigeben
in den Speicher die Datei einschreiben (mit Hilfsfunktion)
Hilfsfunktion (saveFile)
gehe zum Anfang des Strings
speicher alles der Reihe nach ein
clearSeries:
pointer der be malloc kommt nehemen
free()
+201 -234
View File
@@ -1,268 +1,235 @@
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include "neuralNetwork.h" #include "neuralNetwork.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 100 #define BUFFER_SIZE 100
#define FILE_HEADER_STRING "__info2_neural_network_file_format__" #define FILE_HEADER_STRING "__info2_neural_network_file_format__"
static void softmax(Matrix *matrix) static void softmax(Matrix *matrix) {
{ if (matrix->cols > 0) {
if(matrix->cols > 0) double *colSums = (double *)calloc(matrix->cols, sizeof(double));
{
double *colSums = (double *)calloc(matrix->cols, sizeof(double));
if(colSums != NULL) 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++) {
{ MatrixType expValue = exp(getMatrixAt(*matrix, rowIdx, colIdx));
for(int rowIdx = 0; rowIdx < matrix->rows; rowIdx++) setMatrixAt(expValue, *matrix, rowIdx, colIdx);
{ colSums[colIdx] += expValue;
MatrixType expValue = 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);
} }
} }
}
static void relu(Matrix *matrix) for (int colIdx = 0; colIdx < matrix->cols; colIdx++) {
{ for (int rowIdx = 0; rowIdx < matrix->rows; rowIdx++) {
for(int i = 0; i < matrix->rows * matrix->cols; i++) MatrixType normalizedValue =
{ getMatrixAt(*matrix, rowIdx, colIdx) / colSums[colIdx];
matrix->buffer[i] = matrix->buffer[i] >= 0 ? matrix->buffer[i] : 0; setMatrixAt(normalizedValue, *matrix, rowIdx, colIdx);
}
}
static int checkFileHeader(FILE *file)
{
int isValid = 0;
int fileHeaderLen = strlen(FILE_HEADER_STRING);
char buffer[BUFFER_SIZE] = {0};
if(BUFFER_SIZE-1 < fileHeaderLen)
fileHeaderLen = BUFFER_SIZE-1;
if(fread(buffer, sizeof(char), fileHeaderLen, file) == fileHeaderLen)
isValid = strcmp(buffer, FILE_HEADER_STRING) == 0;
return isValid;
}
static unsigned int readDimension(FILE *file)
{
int dimension = 0;
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)
clearMatrix(&matrix);
}
return matrix;
}
static Layer readLayer(FILE *file, unsigned int inputDimension, unsigned int outputDimension)
{
Layer layer;
layer.weights = readMatrix(file, outputDimension, inputDimension);
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;
}
static void clearLayer(Layer *layer)
{
if(layer != NULL)
{
clearMatrix(&layer->weights);
clearMatrix(&layer->biases);
layer->activation = NULL;
}
}
static void assignActivations(NeuralNetwork model)
{
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;
}
NeuralNetwork loadModel(const char *path)
{
NeuralNetwork model = {NULL, 0};
FILE *file = fopen(path, "rb");
if(file != NULL)
{
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); }
free(colSums);
assignActivations(model);
} }
}
return model;
} }
static Matrix imageBatchToMatrixOfImageVectors(const GrayScaleImage images[], unsigned int count) static void relu(Matrix *matrix) {
{ for (int i = 0; i < matrix->rows * matrix->cols; i++) {
Matrix matrix = {NULL, 0, 0}; matrix->buffer[i] = matrix->buffer[i] >= 0 ? matrix->buffer[i] : 0;
}
}
if(count > 0 && images != NULL) static int checkFileHeader(FILE *file) {
{ int isValid = 0;
matrix = createMatrix(images[0].height * images[0].width, count); int fileHeaderLen = strlen(FILE_HEADER_STRING);
char buffer[BUFFER_SIZE] = {0};
if(matrix.buffer != NULL) if (BUFFER_SIZE - 1 < fileHeaderLen)
{ fileHeaderLen = BUFFER_SIZE - 1;
for(int i = 0; i < count; i++)
{ if (fread(buffer, sizeof(char), fileHeaderLen, file) == fileHeaderLen)
for(int j = 0; j < images[i].width * images[i].height; j++) isValid = strcmp(buffer, FILE_HEADER_STRING) == 0;
{
setMatrixAt((MatrixType)images[i].buffer[j], matrix, j, i); return isValid;
} }
}
static unsigned int readDimension(FILE *file) {
int dimension = 0;
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)
clearMatrix(&matrix);
}
return matrix;
}
static Layer readLayer(FILE *file, unsigned int inputDimension,
unsigned int outputDimension) {
Layer layer;
layer.weights = readMatrix(file, outputDimension, inputDimension);
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;
}
static void clearLayer(Layer *layer) {
if (layer != NULL) {
clearMatrix(&layer->weights);
clearMatrix(&layer->biases);
layer->activation = NULL;
}
}
static void assignActivations(NeuralNetwork model) {
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;
}
NeuralNetwork loadModel(const char *path) {
NeuralNetwork model = {NULL, 0};
FILE *file = fopen(path, "rb");
if (file != NULL) {
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;
} }
}
return matrix; layerBuffer = (Layer *)realloc(
} model.layers, (model.numberOfLayers + 1) * sizeof(Layer));
static Matrix forward(const NeuralNetwork model, Matrix inputBatch) if (layerBuffer != NULL)
{ model.layers = layerBuffer;
Matrix result = inputBatch; else {
clearModel(&model);
if(result.buffer != NULL) break;
{
for(int i = 0; i < model.numberOfLayers; i++)
{
Matrix biasResult;
Matrix weightResult;
weightResult = multiply(model.layers[i].weights, result);
clearMatrix(&result);
biasResult = add(model.layers[i].biases, weightResult);
clearMatrix(&weightResult);
if(model.layers[i].activation != NULL)
model.layers[i].activation(&biasResult);
result = biasResult;
} }
}
return result; model.layers[model.numberOfLayers] = layer;
model.numberOfLayers++;
inputDimension = outputDimension;
outputDimension = readDimension(file);
}
}
fclose(file);
assignActivations(model);
}
return model;
} }
unsigned char *argmax(const Matrix matrix) static Matrix imageBatchToMatrixOfImageVectors(const GrayScaleImage images[],
{ unsigned int count) {
unsigned char *maxIdx = NULL; Matrix matrix = {0, 0, NULL}; // falsch herum
if(matrix.rows > 0 && matrix.cols > 0) if (count > 0 && images != NULL) {
{ matrix = createMatrix(images[0].height * images[0].width, count);
maxIdx = (unsigned char *)malloc(sizeof(unsigned char) * matrix.cols);
if(maxIdx != NULL) if (matrix.buffer != NULL) {
{ for (int i = 0; i < count; i++) {
for(int colIdx = 0; colIdx < matrix.cols; colIdx++) for (int j = 0; j < images[i].width * images[i].height; j++) {
{ setMatrixAt((MatrixType)images[i].buffer[j], matrix, j, i);
maxIdx[colIdx] = 0;
for(int rowIdx = 1; rowIdx < matrix.rows; rowIdx++)
{
if(getMatrixAt(matrix, rowIdx, colIdx) > getMatrixAt(matrix, maxIdx[colIdx], colIdx))
maxIdx[colIdx] = rowIdx;
}
}
} }
}
} }
}
return maxIdx; return matrix;
} }
unsigned char *predict(const NeuralNetwork model, const GrayScaleImage images[], unsigned int numberOfImages) static Matrix forward(const NeuralNetwork model, Matrix inputBatch) {
{ Matrix result = inputBatch;
Matrix inputBatch = imageBatchToMatrixOfImageVectors(images, numberOfImages);
Matrix outputBatch = forward(model, inputBatch);
unsigned char *result = argmax(outputBatch); if (result.buffer != NULL) {
for (int i = 0; i < model.numberOfLayers; i++) {
clearMatrix(&outputBatch); Matrix biasResult;
Matrix weightResult;
return result;
weightResult = multiply(model.layers[i].weights, result);
clearMatrix(&result);
biasResult = add(model.layers[i].biases, weightResult);
clearMatrix(&weightResult);
if (model.layers[i].activation != NULL)
model.layers[i].activation(&biasResult);
result = biasResult;
}
}
return result;
} }
void clearModel(NeuralNetwork *model) unsigned char *argmax(const Matrix matrix) {
{ unsigned char *maxIdx = NULL;
if(model != NULL)
{ if (matrix.rows > 0 && matrix.cols > 0) {
for(int i = 0; i < model->numberOfLayers; i++) maxIdx = (unsigned char *)malloc(sizeof(unsigned char) * matrix.cols);
{
clearLayer(&model->layers[i]); if (maxIdx != NULL) {
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;
} }
model->layers = NULL; }
model->numberOfLayers = 0;
} }
}
return maxIdx;
}
unsigned char *predict(const NeuralNetwork model, const GrayScaleImage images[],
unsigned int numberOfImages) {
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;
}
} }
+296 -208
View File
@@ -1,242 +1,330 @@
#include "neuralNetwork.h"
#include "unity.h"
#include <math.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <math.h>
#include "unity.h"
#include "neuralNetwork.h"
/*typedef struct
static void prepareNeuralNetworkFile(const char *path, const NeuralNetwork nn)
{ {
// TODO Matrix weights;
} Matrix biases;
ActivationFunctionType activation;
} Layer;
void test_loadModelReturnsCorrectNumberOfLayers(void) typedef struct
{ {
const char *path = "some__nn_test_file.info2"; Layer *layers;
MatrixType buffer1[] = {1, 2, 3, 4, 5, 6}; unsigned int numberOfLayers;
MatrixType buffer2[] = {1, 2, 3, 4, 5, 6}; } NeuralNetwork;*/
Matrix weights1 = {.buffer=buffer1, .rows=3, .cols=2};
Matrix weights2 = {.buffer=buffer2, .rows=2, .cols=3};
MatrixType buffer3[] = {1, 2, 3};
MatrixType buffer4[] = {1, 2};
Matrix biases1 = {.buffer=buffer3, .rows=3, .cols=1};
Matrix biases2 = {.buffer=buffer4, .rows=2, .cols=1};
Layer layers[] = {{.weights=weights1, .biases=biases1}, {.weights=weights2, .biases=biases2}};
NeuralNetwork expectedNet = {.layers=layers, .numberOfLayers=2}; /*Layer: Ebene im neuronalen Netzwerk, besteht aus mehreren Neuronen
NeuralNetwork netUnderTest; Input-Layer: Eingabedatei
Hidden-Layer: verarbeiten die Daten
Output-Layer: Ergebnis
prepareNeuralNetworkFile(path, expectedNet);
netUnderTest = loadModel(path); Gewichte: bestimmen, wie stark ein Eingangssignal auf ein Neuron wirkt
remove(path);
TEST_ASSERT_EQUAL_INT(expectedNet.numberOfLayers, netUnderTest.numberOfLayers); Dimension: Form der Matrizen für einen Layer*/
clearModel(&netUnderTest);
}
void test_loadModelReturnsCorrectWeightDimensions(void) // speichert NeuralNetwork nn in binäre Datei->erzeugt Dateiformat
{ static void prepareNeuralNetworkFile(const char *path, const NeuralNetwork nn) {
const char *path = "some__nn_test_file.info2"; FILE *f = fopen(path, "wb"); // Binärdatei zum Schreiben öffnen
MatrixType weightBuffer[] = {1, 2, 3, 4, 5, 6}; if (f == NULL)
Matrix weights = {.buffer=weightBuffer, .rows=3, .cols=2}; return;
MatrixType biasBuffer[] = {7, 8, 9};
Matrix biases = {.buffer=biasBuffer, .rows=3, .cols=1};
Layer layers[] = {{.weights=weights, .biases=biases}};
NeuralNetwork expectedNet = {.layers=layers, .numberOfLayers=1}; // Header ist Erkennungsstring am Anfang der Datei, loadmodel erkennt
NeuralNetwork netUnderTest; // Dateiformat
const char header[] = "__info2_neural_network_file_format__";
fwrite(header, sizeof(char), strlen(header), f);
prepareNeuralNetworkFile(path, expectedNet); // Wenn es keine Layer gibt, 0 eintragen, LoadModel gibt 0 zurück
if (nn.numberOfLayers == 0) {
int zero = 0;
fwrite(&zero, sizeof(int), 1, f);
fclose(f);
return;
}
netUnderTest = loadModel(path); // Layer 0, inputDimension: Anzahl Input-Neuronen, outputDimension: Anzahl
remove(path); // Output-Neuronen
int inputDim = (int)nn.layers[0].weights.cols;
int outputDim = (int)nn.layers[0].weights.rows;
fwrite(&inputDim, sizeof(int), 1, f);
fwrite(&outputDim, sizeof(int), 1, f);
TEST_ASSERT_TRUE(netUnderTest.numberOfLayers > 0); /* 3) Für jede Layer in Reihenfolge: Gewichte (output x input), Biases (output
TEST_ASSERT_EQUAL_INT(expectedNet.layers[0].weights.rows, netUnderTest.layers[0].weights.rows); x 1). Zwischen Layern wird nur die nächste outputDimension (int)
TEST_ASSERT_EQUAL_INT(expectedNet.layers[0].weights.cols, netUnderTest.layers[0].weights.cols); geschrieben. */
clearModel(&netUnderTest); for (int i = 0; i < nn.numberOfLayers; i++) {
} Layer layer = nn.layers[i];
void test_loadModelReturnsCorrectBiasDimensions(void) int wrows = (int)layer.weights.rows;
{ int wcols = (int)layer.weights.cols;
const char *path = "some__nn_test_file.info2"; int wcount = wrows * wcols;
MatrixType weightBuffer[] = {1, 2, 3, 4, 5, 6}; int bcount =
Matrix weights = {.buffer=weightBuffer, .rows=3, .cols=2}; layer.biases.rows * layer.biases.cols; /* normalerweise rows * 1 */
MatrixType biasBuffer[] = {7, 8, 9};
Matrix biases = {.buffer=biasBuffer, .rows=3, .cols=1};
Layer layers[] = {{.weights=weights, .biases=biases}};
NeuralNetwork expectedNet = {.layers=layers, .numberOfLayers=1}; /* Gewichte (MatrixType binär) */
NeuralNetwork netUnderTest; if (wcount > 0 && layer.weights.buffer != NULL) {
fwrite(layer.weights.buffer, sizeof(MatrixType), (size_t)wcount, f);
prepareNeuralNetworkFile(path, expectedNet);
netUnderTest = loadModel(path);
remove(path);
TEST_ASSERT_TRUE(netUnderTest.numberOfLayers > 0);
TEST_ASSERT_EQUAL_INT(expectedNet.layers[0].biases.rows, netUnderTest.layers[0].biases.rows);
TEST_ASSERT_EQUAL_INT(expectedNet.layers[0].biases.cols, netUnderTest.layers[0].biases.cols);
clearModel(&netUnderTest);
}
void test_loadModelReturnsCorrectWeights(void)
{
const char *path = "some__nn_test_file.info2";
MatrixType weightBuffer[] = {1, 2, 3, 4, 5, 6};
Matrix weights = {.buffer=weightBuffer, .rows=3, .cols=2};
MatrixType biasBuffer[] = {7, 8, 9};
Matrix biases = {.buffer=biasBuffer, .rows=3, .cols=1};
Layer layers[] = {{.weights=weights, .biases=biases}};
NeuralNetwork expectedNet = {.layers=layers, .numberOfLayers=1};
NeuralNetwork netUnderTest;
prepareNeuralNetworkFile(path, expectedNet);
netUnderTest = loadModel(path);
remove(path);
TEST_ASSERT_TRUE(netUnderTest.numberOfLayers > 0);
TEST_ASSERT_EQUAL_INT(expectedNet.layers[0].weights.rows, netUnderTest.layers[0].weights.rows);
TEST_ASSERT_EQUAL_INT(expectedNet.layers[0].weights.cols, netUnderTest.layers[0].weights.cols);
int n = netUnderTest.layers[0].weights.rows * netUnderTest.layers[0].weights.cols;
TEST_ASSERT_EQUAL_INT_ARRAY(expectedNet.layers[0].weights.buffer, netUnderTest.layers[0].weights.buffer, n);
clearModel(&netUnderTest);
}
void test_loadModelReturnsCorrectBiases(void)
{
const char *path = "some__nn_test_file.info2";
MatrixType weightBuffer[] = {1, 2, 3, 4, 5, 6};
Matrix weights = {.buffer=weightBuffer, .rows=3, .cols=2};
MatrixType biasBuffer[] = {7, 8, 9};
Matrix biases = {.buffer=biasBuffer, .rows=3, .cols=1};
Layer layers[] = {{.weights=weights, .biases=biases}};
NeuralNetwork expectedNet = {.layers=layers, .numberOfLayers=1};
NeuralNetwork netUnderTest;
prepareNeuralNetworkFile(path, expectedNet);
netUnderTest = loadModel(path);
remove(path);
TEST_ASSERT_TRUE(netUnderTest.numberOfLayers > 0);
TEST_ASSERT_EQUAL_INT(expectedNet.layers[0].weights.rows, netUnderTest.layers[0].weights.rows);
TEST_ASSERT_EQUAL_INT(expectedNet.layers[0].weights.cols, netUnderTest.layers[0].weights.cols);
int n = netUnderTest.layers[0].biases.rows * netUnderTest.layers[0].biases.cols;
TEST_ASSERT_EQUAL_INT_ARRAY(expectedNet.layers[0].biases.buffer, netUnderTest.layers[0].biases.buffer, n);
clearModel(&netUnderTest);
}
void test_loadModelFailsOnWrongFileTag(void)
{
const char *path = "some_nn_test_file.info2";
NeuralNetwork netUnderTest;
FILE *file = fopen(path, "wb");
if(file != NULL)
{
const char *fileTag = "info2_neural_network_file_format";
fwrite(fileTag, sizeof(char), strlen(fileTag), file);
fclose(file);
} }
netUnderTest = loadModel(path); /* Biases (MatrixType binär) */
if (bcount > 0 && layer.biases.buffer != NULL) {
remove(path); fwrite(layer.biases.buffer, sizeof(MatrixType), (size_t)bcount, f);
TEST_ASSERT_NULL(netUnderTest.layers);
TEST_ASSERT_EQUAL_INT(0, netUnderTest.numberOfLayers);
}
void test_clearModelSetsMembersToNull(void)
{
const char *path = "some__nn_test_file.info2";
MatrixType weightBuffer[] = {1, 2, 3, 4, 5, 6};
Matrix weights = {.buffer=weightBuffer, .rows=3, .cols=2};
MatrixType biasBuffer[] = {7, 8, 9};
Matrix biases = {.buffer=biasBuffer, .rows=3, .cols=1};
Layer layers[] = {{.weights=weights, .biases=biases}};
NeuralNetwork expectedNet = {.layers=layers, .numberOfLayers=1};
NeuralNetwork netUnderTest;
prepareNeuralNetworkFile(path, expectedNet);
netUnderTest = loadModel(path);
remove(path);
TEST_ASSERT_NOT_NULL(netUnderTest.layers);
TEST_ASSERT_TRUE(netUnderTest.numberOfLayers > 0);
clearModel(&netUnderTest);
TEST_ASSERT_NULL(netUnderTest.layers);
TEST_ASSERT_EQUAL_INT(0, netUnderTest.numberOfLayers);
}
static void someActivation(Matrix *matrix)
{
for(int i = 0; i < matrix->rows * matrix->cols; i++)
{
matrix->buffer[i] = fabs(matrix->buffer[i]);
} }
/* Für die nächste Layer: falls vorhanden, schreibe deren outputDimension */
if (i + 1 < nn.numberOfLayers) {
int nextOutput = (int)nn.layers[i + 1].weights.rows;
fwrite(&nextOutput, sizeof(int), 1, f);
} else {
/* Letzte Layer: wir können das Ende signalisieren, indem wir ein 0
schreiben. loadModel liest dann outputDimension = 0 und beendet die
Schleife. */
int zero = 0;
fwrite(&zero, sizeof(int), 1, f);
}
}
fclose(f);
} }
void test_predictReturnsCorrectLabels(void) void test_loadModelReturnsCorrectNumberOfLayers(void) {
{ const char *path = "some__nn_test_file.info2";
const unsigned char expectedLabels[] = {4, 2}; MatrixType buffer1[] = {1, 2, 3, 4, 5, 6};
GrayScalePixelType imageBuffer1[] = {10, 30, 25, 17}; MatrixType buffer2[] = {1, 2, 3, 4, 5, 6};
GrayScalePixelType imageBuffer2[] = {20, 40, 10, 128}; Matrix weights1 = {.buffer = buffer1, .rows = 3, .cols = 2};
GrayScaleImage inputImages[] = {{.buffer=imageBuffer1, .width=2, .height=2}, {.buffer=imageBuffer2, .width=2, .height=2}}; Matrix weights2 = {.buffer = buffer2, .rows = 2, .cols = 3};
MatrixType weightsBuffer1[] = {1, -2, 3, -4, 5, -6, 7, -8}; MatrixType buffer3[] = {1, 2, 3};
MatrixType weightsBuffer2[] = {-9, 10, 11, 12, 13, 14}; MatrixType buffer4[] = {1, 2};
MatrixType weightsBuffer3[] = {-15, 16, 17, 18, -19, 20, 21, 22, 23, -24, 25, 26, 27, -28, -29}; Matrix biases1 = {.buffer = buffer3, .rows = 3, .cols = 1};
Matrix weights1 = {.buffer=weightsBuffer1, .rows=2, .cols=4}; Matrix biases2 = {.buffer = buffer4, .rows = 2, .cols = 1};
Matrix weights2 = {.buffer=weightsBuffer2, .rows=3, .cols=2}; Layer layers[] = {{.weights = weights1, .biases = biases1},
Matrix weights3 = {.buffer=weightsBuffer3, .rows=5, .cols=3}; {.weights = weights2, .biases = biases2}};
MatrixType biasBuffer1[] = {200, 0};
MatrixType biasBuffer2[] = {0, -100, 0}; NeuralNetwork expectedNet = {.layers = layers, .numberOfLayers = 2};
MatrixType biasBuffer3[] = {0, -1000, 0, 2000, 0}; NeuralNetwork netUnderTest;
Matrix biases1 = {.buffer=biasBuffer1, .rows=2, .cols=1};
Matrix biases2 = {.buffer=biasBuffer2, .rows=3, .cols=1}; prepareNeuralNetworkFile(path, expectedNet);
Matrix biases3 = {.buffer=biasBuffer3, .rows=5, .cols=1};
Layer layers[] = {{.weights=weights1, .biases=biases1, .activation=someActivation}, \ netUnderTest = loadModel(path);
{.weights=weights2, .biases=biases2, .activation=someActivation}, \ remove(path);
{.weights=weights3, .biases=biases3, .activation=someActivation}};
NeuralNetwork netUnderTest = {.layers=layers, .numberOfLayers=3}; TEST_ASSERT_EQUAL_INT(expectedNet.numberOfLayers,
unsigned char *predictedLabels = predict(netUnderTest, inputImages, 2); netUnderTest.numberOfLayers);
TEST_ASSERT_NOT_NULL(predictedLabels); clearModel(&netUnderTest);
int n = (int)(sizeof(expectedLabels) / sizeof(expectedLabels[0])); }
TEST_ASSERT_EQUAL_UINT8_ARRAY(expectedLabels, predictedLabels, n);
free(predictedLabels); void test_loadModelReturnsCorrectWeightDimensions(void) {
const char *path = "some__nn_test_file.info2";
MatrixType weightBuffer[] = {1, 2, 3, 4, 5, 6};
Matrix weights = {.buffer = weightBuffer, .rows = 3, .cols = 2};
MatrixType biasBuffer[] = {7, 8, 9};
Matrix biases = {.buffer = biasBuffer, .rows = 3, .cols = 1};
Layer layers[] = {{.weights = weights, .biases = biases}};
NeuralNetwork expectedNet = {.layers = layers, .numberOfLayers = 1};
NeuralNetwork netUnderTest;
prepareNeuralNetworkFile(path, expectedNet);
netUnderTest = loadModel(path);
remove(path);
TEST_ASSERT_TRUE(netUnderTest.numberOfLayers > 0);
TEST_ASSERT_EQUAL_INT(expectedNet.layers[0].weights.rows,
netUnderTest.layers[0].weights.rows);
TEST_ASSERT_EQUAL_INT(expectedNet.layers[0].weights.cols,
netUnderTest.layers[0].weights.cols);
clearModel(&netUnderTest);
}
void test_loadModelReturnsCorrectBiasDimensions(void) {
const char *path = "some__nn_test_file.info2";
MatrixType weightBuffer[] = {1, 2, 3, 4, 5, 6};
Matrix weights = {.buffer = weightBuffer, .rows = 3, .cols = 2};
MatrixType biasBuffer[] = {7, 8, 9};
Matrix biases = {.buffer = biasBuffer, .rows = 3, .cols = 1};
Layer layers[] = {{.weights = weights, .biases = biases}};
NeuralNetwork expectedNet = {.layers = layers, .numberOfLayers = 1};
NeuralNetwork netUnderTest;
prepareNeuralNetworkFile(path, expectedNet);
netUnderTest = loadModel(path);
remove(path);
TEST_ASSERT_TRUE(netUnderTest.numberOfLayers > 0);
TEST_ASSERT_EQUAL_INT(expectedNet.layers[0].biases.rows,
netUnderTest.layers[0].biases.rows);
TEST_ASSERT_EQUAL_INT(expectedNet.layers[0].biases.cols,
netUnderTest.layers[0].biases.cols);
clearModel(&netUnderTest);
}
void test_loadModelReturnsCorrectWeights(void) {
const char *path = "some__nn_test_file.info2";
MatrixType weightBuffer[] = {1, 2, 3, 4, 5, 6};
Matrix weights = {.buffer = weightBuffer, .rows = 3, .cols = 2};
MatrixType biasBuffer[] = {7, 8, 9};
Matrix biases = {.buffer = biasBuffer, .rows = 3, .cols = 1};
Layer layers[] = {{.weights = weights, .biases = biases}};
NeuralNetwork expectedNet = {.layers = layers, .numberOfLayers = 1};
NeuralNetwork netUnderTest;
prepareNeuralNetworkFile(path, expectedNet);
netUnderTest = loadModel(path);
remove(path);
TEST_ASSERT_TRUE(netUnderTest.numberOfLayers > 0);
TEST_ASSERT_EQUAL_INT(expectedNet.layers[0].weights.rows,
netUnderTest.layers[0].weights.rows);
TEST_ASSERT_EQUAL_INT(expectedNet.layers[0].weights.cols,
netUnderTest.layers[0].weights.cols);
int n =
netUnderTest.layers[0].weights.rows * netUnderTest.layers[0].weights.cols;
TEST_ASSERT_EQUAL_INT_ARRAY(expectedNet.layers[0].weights.buffer,
netUnderTest.layers[0].weights.buffer, n);
clearModel(&netUnderTest);
}
void test_loadModelReturnsCorrectBiases(void) {
const char *path = "some__nn_test_file.info2";
MatrixType weightBuffer[] = {1, 2, 3, 4, 5, 6};
Matrix weights = {.buffer = weightBuffer, .rows = 3, .cols = 2};
MatrixType biasBuffer[] = {7, 8, 9};
Matrix biases = {.buffer = biasBuffer, .rows = 3, .cols = 1};
Layer layers[] = {{.weights = weights, .biases = biases}};
NeuralNetwork expectedNet = {.layers = layers, .numberOfLayers = 1};
NeuralNetwork netUnderTest;
prepareNeuralNetworkFile(path, expectedNet);
netUnderTest = loadModel(path);
remove(path);
TEST_ASSERT_TRUE(netUnderTest.numberOfLayers > 0);
TEST_ASSERT_EQUAL_INT(expectedNet.layers[0].weights.rows,
netUnderTest.layers[0].weights.rows);
TEST_ASSERT_EQUAL_INT(expectedNet.layers[0].weights.cols,
netUnderTest.layers[0].weights.cols);
int n =
netUnderTest.layers[0].biases.rows * netUnderTest.layers[0].biases.cols;
TEST_ASSERT_EQUAL_INT_ARRAY(expectedNet.layers[0].biases.buffer,
netUnderTest.layers[0].biases.buffer, n);
clearModel(&netUnderTest);
}
void test_loadModelFailsOnWrongFileTag(void) {
const char *path = "some_nn_test_file.info2";
NeuralNetwork netUnderTest;
FILE *file = fopen(path, "wb");
if (file != NULL) {
const char *fileTag = "info2_neural_network_file_format";
fwrite(fileTag, sizeof(char), strlen(fileTag), file);
fclose(file);
}
netUnderTest = loadModel(path);
remove(path);
TEST_ASSERT_NULL(netUnderTest.layers);
TEST_ASSERT_EQUAL_INT(0, netUnderTest.numberOfLayers);
}
void test_clearModelSetsMembersToNull(void) {
const char *path = "some__nn_test_file.info2";
MatrixType weightBuffer[] = {1, 2, 3, 4, 5, 6};
Matrix weights = {.buffer = weightBuffer, .rows = 3, .cols = 2};
MatrixType biasBuffer[] = {7, 8, 9};
Matrix biases = {.buffer = biasBuffer, .rows = 3, .cols = 1};
Layer layers[] = {{.weights = weights, .biases = biases}};
NeuralNetwork expectedNet = {.layers = layers, .numberOfLayers = 1};
NeuralNetwork netUnderTest;
prepareNeuralNetworkFile(path, expectedNet);
netUnderTest = loadModel(path);
remove(path);
TEST_ASSERT_NOT_NULL(netUnderTest.layers);
TEST_ASSERT_TRUE(netUnderTest.numberOfLayers > 0);
clearModel(&netUnderTest);
TEST_ASSERT_NULL(netUnderTest.layers);
TEST_ASSERT_EQUAL_INT(0, netUnderTest.numberOfLayers);
}
static void someActivation(Matrix *matrix) {
for (int i = 0; i < matrix->rows * matrix->cols; i++) {
matrix->buffer[i] = fabs(matrix->buffer[i]);
}
}
void test_predictReturnsCorrectLabels(void) {
const unsigned char expectedLabels[] = {4, 2};
GrayScalePixelType imageBuffer1[] = {10, 30, 25, 17};
GrayScalePixelType imageBuffer2[] = {20, 40, 10, 128};
GrayScaleImage inputImages[] = {
{.buffer = imageBuffer1, .width = 2, .height = 2},
{.buffer = imageBuffer2, .width = 2, .height = 2}};
MatrixType weightsBuffer1[] = {1, -2, 3, -4, 5, -6, 7, -8};
MatrixType weightsBuffer2[] = {-9, 10, 11, 12, 13, 14};
MatrixType weightsBuffer3[] = {-15, 16, 17, 18, -19, 20, 21, 22,
23, -24, 25, 26, 27, -28, -29};
Matrix weights1 = {.buffer = weightsBuffer1, .rows = 2, .cols = 4};
Matrix weights2 = {.buffer = weightsBuffer2, .rows = 3, .cols = 2};
Matrix weights3 = {.buffer = weightsBuffer3, .rows = 5, .cols = 3};
MatrixType biasBuffer1[] = {200, 0};
MatrixType biasBuffer2[] = {0, -100, 0};
MatrixType biasBuffer3[] = {0, -1000, 0, 2000, 0};
Matrix biases1 = {.buffer = biasBuffer1, .rows = 2, .cols = 1};
Matrix biases2 = {.buffer = biasBuffer2, .rows = 3, .cols = 1};
Matrix biases3 = {.buffer = biasBuffer3, .rows = 5, .cols = 1};
Layer layers[] = {
{.weights = weights1, .biases = biases1, .activation = someActivation},
{.weights = weights2, .biases = biases2, .activation = someActivation},
{.weights = weights3, .biases = biases3, .activation = someActivation}};
NeuralNetwork netUnderTest = {.layers = layers, .numberOfLayers = 3};
unsigned char *predictedLabels = predict(netUnderTest, inputImages, 2);
TEST_ASSERT_NOT_NULL(predictedLabels);
int n = (int)(sizeof(expectedLabels) / sizeof(expectedLabels[0]));
TEST_ASSERT_EQUAL_UINT8_ARRAY(expectedLabels, predictedLabels, n);
free(predictedLabels);
} }
void setUp(void) { void setUp(void) {
// Falls notwendig, kann hier Vorbereitungsarbeit gemacht werden // Falls notwendig, kann hier Vorbereitungsarbeit gemacht werden
} }
void tearDown(void) { void tearDown(void) {
// Hier kann Bereinigungsarbeit nach jedem Test durchgeführt werden // Hier kann Bereinigungsarbeit nach jedem Test durchgeführt werden
} }
int main() int main() {
{ UNITY_BEGIN();
UNITY_BEGIN();
printf("\n============================\nNeural network tests\n============================\n"); printf("\n============================\nNeural network "
RUN_TEST(test_loadModelReturnsCorrectNumberOfLayers); "tests\n============================\n");
RUN_TEST(test_loadModelReturnsCorrectWeightDimensions); RUN_TEST(test_loadModelReturnsCorrectNumberOfLayers);
RUN_TEST(test_loadModelReturnsCorrectBiasDimensions); RUN_TEST(test_loadModelReturnsCorrectWeightDimensions);
RUN_TEST(test_loadModelReturnsCorrectWeights); RUN_TEST(test_loadModelReturnsCorrectBiasDimensions);
RUN_TEST(test_loadModelReturnsCorrectBiases); RUN_TEST(test_loadModelReturnsCorrectWeights);
RUN_TEST(test_loadModelFailsOnWrongFileTag); RUN_TEST(test_loadModelReturnsCorrectBiases);
RUN_TEST(test_clearModelSetsMembersToNull); RUN_TEST(test_loadModelFailsOnWrongFileTag);
RUN_TEST(test_predictReturnsCorrectLabels); RUN_TEST(test_clearModelSetsMembersToNull);
RUN_TEST(test_predictReturnsCorrectLabels);
return UNITY_END(); return UNITY_END();
} }