2023-07-20 22:34:56 +02:00
|
|
|
#include <stdio.h>
|
2023-07-27 22:43:11 +02:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <inttypes.h>
|
2023-07-20 22:34:56 +02:00
|
|
|
|
|
|
|
#include "clm.h"
|
|
|
|
|
2023-07-27 22:43:11 +02:00
|
|
|
float train_data_x[4][2] = {
|
|
|
|
{0, 0},
|
|
|
|
{0, 1},
|
|
|
|
{1, 0},
|
|
|
|
{1, 1}
|
|
|
|
};
|
|
|
|
|
|
|
|
float train_data_y[4][1] = {
|
|
|
|
{0},
|
|
|
|
{1},
|
|
|
|
{1},
|
|
|
|
{0}
|
|
|
|
};
|
|
|
|
|
|
|
|
float *predict(clm_NN nn, float *x, unsigned int length) {
|
|
|
|
clm_Matrix xM = clm_matrixFromArray(x, length);
|
|
|
|
|
|
|
|
for(unsigned int i = 0; i < nn.numLayers; i++) {
|
|
|
|
clm_Linear layer = nn.layers[i];
|
|
|
|
clm_Matrix newX = clm_matrixMultiplyMatrix(layer.weights, xM);
|
|
|
|
|
|
|
|
if(clm_matrixIsInvalid(newX)) {
|
|
|
|
printf("Failed to predict\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
clm_matrixAddMatrix(newX, layer.bias);
|
|
|
|
clm_matrixSigmoid(newX);
|
|
|
|
clm_freeMatrix(xM);
|
|
|
|
xM = newX;
|
|
|
|
}
|
|
|
|
|
|
|
|
return xM.values;
|
|
|
|
}
|
|
|
|
|
|
|
|
void train(clm_NN nn, float *x, unsigned int xL, float *y, unsigned int yL) {
|
|
|
|
clm_Matrix xM = clm_matrixFromArray(x, xL);
|
|
|
|
clm_Matrix yM = clm_matrixFromArray(y, yL);
|
|
|
|
|
|
|
|
// TODO: potential compute/memory tradeoff? (recalculate matrices every time <-> keep everything cached)
|
|
|
|
|
|
|
|
// Forward pass
|
|
|
|
clm_Matrix *outputs = calloc(nn.numLayers + 1 /* 1 for input */, sizeof(clm_Matrix));
|
|
|
|
outputs[0] = xM;
|
|
|
|
for(unsigned int i = 0; i < nn.numLayers; i++) {
|
|
|
|
clm_Linear layer = nn.layers[i];
|
|
|
|
clm_Matrix newX = clm_matrixMultiplyMatrix(layer.weights, xM);
|
|
|
|
if(clm_matrixIsInvalid(newX)) {
|
|
|
|
printf("Forward pass failed\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
clm_matrixAddMatrix(newX, layer.bias);
|
|
|
|
clm_matrixSigmoid(newX);
|
|
|
|
xM = newX;
|
|
|
|
outputs[i + 1] = xM;
|
|
|
|
}
|
|
|
|
|
|
|
|
clm_Matrix dError = clm_matrixSubtractMatrix(yM, outputs[nn.numLayers]); // yhat - y
|
|
|
|
|
|
|
|
clm_Matrix lastGradient = clm_matrixDSigmoid(clm_matrixCopy(outputs[nn.numLayers])); // dsig(yhat)
|
|
|
|
clm_matrixMultiplyMatrixElements(lastGradient, dError); // (yhat - y) . dsig(yhat)
|
|
|
|
clm_matrixMultiplyScalar(lastGradient, nn.learnRate);
|
|
|
|
|
|
|
|
clm_Matrix lastInputT = clm_matrixTranspose(outputs[nn.numLayers - 1]);
|
|
|
|
clm_Matrix lastDeltaW = clm_matrixMultiplyMatrix(lastGradient, lastInputT);
|
|
|
|
clm_freeMatrix(lastInputT);
|
|
|
|
|
|
|
|
clm_matrixAddMatrix(nn.layers[nn.numLayers - 1].weights, lastDeltaW);
|
|
|
|
clm_matrixAddMatrix(nn.layers[nn.numLayers - 1].bias, lastGradient);
|
|
|
|
|
|
|
|
clm_freeMatrix(lastDeltaW);
|
|
|
|
clm_freeMatrix(lastGradient);
|
|
|
|
|
|
|
|
for(int i = nn.numLayers - 2; i >= 0; i--) {
|
|
|
|
clm_Linear layer = nn.layers[i];
|
|
|
|
clm_Matrix inputToThisLayer = outputs[i];
|
|
|
|
clm_Matrix outputOfThisLayer = outputs[i + 1];
|
|
|
|
|
|
|
|
clm_Matrix weightsT = clm_matrixTranspose(nn.layers[i + 1].weights);
|
|
|
|
clm_Matrix newDError = clm_matrixMultiplyMatrix(weightsT, dError);
|
|
|
|
clm_freeMatrix(weightsT);
|
|
|
|
clm_freeMatrix(dError);
|
|
|
|
dError = newDError;
|
|
|
|
|
|
|
|
clm_Matrix gradient = clm_matrixDSigmoid(clm_matrixCopy(outputOfThisLayer));
|
|
|
|
clm_matrixMultiplyMatrixElements(gradient, dError);
|
|
|
|
clm_matrixMultiplyScalar(gradient, nn.learnRate);
|
|
|
|
|
|
|
|
clm_Matrix inputT = clm_matrixTranspose(inputToThisLayer);
|
|
|
|
clm_Matrix deltaW = clm_matrixMultiplyMatrix(gradient, inputT);
|
|
|
|
clm_freeMatrix(inputT);
|
|
|
|
|
|
|
|
clm_matrixAddMatrix(layer.weights, deltaW);
|
|
|
|
clm_matrixAddMatrix(layer.bias, gradient);
|
|
|
|
|
|
|
|
clm_freeMatrix(deltaW);
|
|
|
|
clm_freeMatrix(gradient);
|
|
|
|
}
|
|
|
|
|
|
|
|
clm_freeMatrix(dError);
|
|
|
|
|
|
|
|
for(unsigned int i = 0; i <= nn.numLayers; i++) {
|
|
|
|
clm_freeMatrix(outputs[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(outputs);
|
|
|
|
}
|
|
|
|
|
|
|
|
void loadLabels(clm_Vector **labelsOut, unsigned int *labelsCountOut) {
|
|
|
|
FILE *file = fopen("data/train-labels.idx1-ubyte", "r");
|
|
|
|
if(!file) {
|
|
|
|
perror("Failed to open labels\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char magicBytes[4];
|
|
|
|
fread(magicBytes, sizeof(magicBytes), 1, file);
|
|
|
|
|
|
|
|
printf("%d\n", (magicBytes[0] << 24) | (magicBytes[1] << 16) | (magicBytes[2] << 8) | magicBytes[3]);
|
|
|
|
|
|
|
|
unsigned char lengthBytes[4];
|
|
|
|
fread(lengthBytes, sizeof(lengthBytes), 1, file);
|
|
|
|
|
|
|
|
uint32_t length = (lengthBytes[0] << 24) | (lengthBytes[1] << 16) | (lengthBytes[2] << 8) | lengthBytes[3];
|
|
|
|
printf("%" PRId32 "\n", length);
|
|
|
|
|
|
|
|
clm_Vector *vectors = calloc(length, sizeof(clm_Vector));
|
|
|
|
|
|
|
|
for(unsigned int i = 0; i < length; i++) {
|
|
|
|
unsigned char label;
|
|
|
|
fread(&label, sizeof(unsigned char), 1, file);
|
|
|
|
|
|
|
|
clm_Vector vector = clm_vectorCreate(10);
|
|
|
|
for(unsigned int j = 0; j < 10; j++) {
|
|
|
|
vector.values[j] = label == j ? 1 : 0;
|
|
|
|
}
|
|
|
|
vectors[i] = vector;
|
|
|
|
}
|
|
|
|
|
|
|
|
*labelsOut = vectors;
|
|
|
|
*labelsCountOut = length;
|
|
|
|
}
|
|
|
|
|
|
|
|
void loadImages(clm_Vector **imagesOut, unsigned int *imageCountOut) {
|
|
|
|
FILE *file = fopen("data/train-images.idx3-ubyte", "r");
|
|
|
|
if(!file) {
|
|
|
|
perror("Failed to open images\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char magicBytes[4];
|
|
|
|
fread(magicBytes, sizeof(magicBytes), 1, file);
|
|
|
|
|
|
|
|
printf("%d\n", (magicBytes[0] << 24) | (magicBytes[1] << 16) | (magicBytes[2] << 8) | magicBytes[3]);
|
|
|
|
|
|
|
|
unsigned char lengthBytes[4];
|
|
|
|
fread(lengthBytes, sizeof(lengthBytes), 1, file);
|
|
|
|
uint32_t length = (lengthBytes[0] << 24) | (lengthBytes[1] << 16) | (lengthBytes[2] << 8) | lengthBytes[3];
|
|
|
|
printf("%" PRId32 "\n", length);
|
|
|
|
|
|
|
|
unsigned char rowsBytes[4];
|
|
|
|
fread(rowsBytes, sizeof(rowsBytes), 1, file);
|
|
|
|
uint32_t rows = (rowsBytes[0] << 24) | (rowsBytes[1] << 16) | (rowsBytes[2] << 8) | rowsBytes[3];
|
|
|
|
printf("%" PRId32 "\n", rows);
|
|
|
|
|
|
|
|
unsigned char colsBytes[4];
|
|
|
|
fread(colsBytes, sizeof(colsBytes), 1, file);
|
|
|
|
uint32_t cols = (colsBytes[0] << 24) | (colsBytes[1] << 16) | (colsBytes[2] << 8) | colsBytes[3];
|
|
|
|
printf("%" PRId32 "\n", cols);
|
|
|
|
|
|
|
|
clm_Vector *images = calloc(length, sizeof(clm_Vector));
|
|
|
|
for(unsigned int i = 0; i < length; i++) {
|
|
|
|
clm_Vector vec = clm_vectorCreate(cols * rows);
|
|
|
|
unsigned char image[cols * rows];
|
|
|
|
fread(image, sizeof(image), 1, file);
|
|
|
|
for(unsigned int j = 0; j < cols * rows; j++) {
|
|
|
|
vec.values[j] = (float) image[j];
|
|
|
|
}
|
|
|
|
images[i] = vec;
|
|
|
|
}
|
|
|
|
|
|
|
|
*imagesOut = images;
|
|
|
|
*imageCountOut = length;
|
|
|
|
}
|
|
|
|
|
2023-07-20 22:34:56 +02:00
|
|
|
int main() {
|
2023-07-27 22:43:11 +02:00
|
|
|
clm_Vector *labels = NULL;
|
|
|
|
unsigned int labelCount;
|
|
|
|
loadLabels(&labels, &labelCount);
|
|
|
|
printf("LENGTH: %u\n", labelCount);
|
|
|
|
|
|
|
|
clm_Vector *images = NULL;
|
|
|
|
unsigned int imageCount;
|
|
|
|
loadImages(&images, &imageCount);
|
|
|
|
|
|
|
|
imageCount = 60000;
|
|
|
|
|
|
|
|
printf("%f\n", images[0].values[0]);
|
|
|
|
|
|
|
|
srand(1);
|
|
|
|
|
|
|
|
unsigned int
|
|
|
|
i = 784,
|
|
|
|
h = 30,
|
|
|
|
o = 10;
|
|
|
|
|
|
|
|
clm_Linear layer1;
|
|
|
|
layer1.weights = clm_createMatrixRandom(h, i);
|
|
|
|
layer1.bias = clm_createMatrixRandom(h, 1);
|
|
|
|
|
|
|
|
clm_Linear layer2;
|
|
|
|
layer2.weights = clm_createMatrixRandom(o, h);
|
|
|
|
layer2.bias = clm_createMatrixRandom(o, 1);
|
|
|
|
|
|
|
|
clm_Linear layers[] = {layer1, layer2};
|
|
|
|
clm_NN nn = { layers, sizeof(layers) / sizeof(clm_Linear), 0.01 };
|
|
|
|
|
|
|
|
for(unsigned int epoch = 0; epoch < 10; epoch++) {
|
|
|
|
printf("Epoch %u\n", epoch);
|
|
|
|
|
|
|
|
for(unsigned int idx = 0; idx < imageCount; idx++) { // Each train sample
|
|
|
|
if(idx % 1000 == 0) {
|
|
|
|
printf("%.2f%%\n", idx / (float) imageCount * 100);
|
|
|
|
}
|
|
|
|
//printf("%u\n", idx);
|
|
|
|
//train(nn, train_data_x[idx], 2, train_data_y[idx], 1);
|
|
|
|
/*for(unsigned int f = 0; f < images[idx].length; f++) {
|
|
|
|
printf("%.2f ", images[idx].values[f]);
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
for(unsigned int f = 0; f < labels[idx].length; f++) {
|
|
|
|
printf("%.2f ", labels[idx].values[f]);
|
|
|
|
}
|
|
|
|
printf("\n");*/
|
|
|
|
//printf("%.2f\n", labels.values[idx]);
|
|
|
|
|
|
|
|
train(nn, images[idx].values, images[idx].length, labels[idx].values, labels[idx].length);
|
|
|
|
//train(nn, test, 784, target, 10);
|
|
|
|
//predict(nn, test, 784);
|
|
|
|
}
|
|
|
|
}
|
2023-07-20 22:34:56 +02:00
|
|
|
|
2023-07-27 22:43:11 +02:00
|
|
|
unsigned int correct = 0;
|
|
|
|
for(unsigned int idx = 0; idx < imageCount; idx++) { // Each train sample
|
|
|
|
//printf("pred(%.2f, %.2f) = %.2f\n", train_data_x[idx][0], train_data_x[idx][1], predict(nn, train_data_x[idx], 2)[0]);
|
|
|
|
float *pred = predict(nn, images[idx].values, images[idx].length);
|
|
|
|
unsigned int predDigit = 0;
|
|
|
|
float max = -1;
|
|
|
|
for(unsigned int j = 0; j < 10; j++) {
|
|
|
|
//printf("%.2f ", pred[j]);
|
|
|
|
if(pred[j] > max || max < 0) {
|
|
|
|
max = pred[j];
|
|
|
|
predDigit = j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(idx < 100) printf("%u (confidence: %.2f)\n", predDigit, max);
|
2023-07-20 22:34:56 +02:00
|
|
|
|
2023-07-27 22:43:11 +02:00
|
|
|
unsigned int actDigit = 0;
|
|
|
|
float maxA = -1;
|
|
|
|
for(unsigned int j = 0; j < 10; j++) {
|
|
|
|
//printf("%.2f ", pred[j]);
|
|
|
|
if(labels[idx].values[j] > maxA || maxA < 0) {
|
|
|
|
maxA = labels[idx].values[j];
|
|
|
|
actDigit = j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(idx < 100) printf("Actual: %u\n", actDigit);
|
|
|
|
//printf("\n");
|
2023-07-20 22:34:56 +02:00
|
|
|
|
2023-07-27 22:43:11 +02:00
|
|
|
if(predDigit == actDigit) correct++;
|
|
|
|
}
|
2023-07-20 22:34:56 +02:00
|
|
|
|
2023-07-27 22:43:11 +02:00
|
|
|
printf("Correct: %u -> %.2f", correct, (float) correct / imageCount * 100);
|
2023-07-20 22:34:56 +02:00
|
|
|
|
2023-07-27 22:43:11 +02:00
|
|
|
printf("\n");
|
2023-07-20 22:34:56 +02:00
|
|
|
}
|