File size: 5,459 Bytes
8b41169 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | /* mnist.c β SnapKitty MLC: MNIST digit classifier example
*
* Architecture:
* Input (784) β W0(16Γ784) ReLU β W1(16Γ16) ReLU [+ skip] β W2(10Γ16) Softmax
* Loss: cross-entropy
* Init: Xavier uniform (Glorot)
* Train: SGD, 10 epochs, batch=50, lr=0.01
*
* Data: run examples/data_convert.py first to generate .mat files.
*
* Authors: Ahmad Ali Parr, Jessica L. Williams (SNAPKITTYWEST)
* Source: SNAPKITTYAGENT9NOVA/MLC
* License: BSL-1.1 / AGPL-3.0 / MPL-2.0
*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "../include/sk_defs.h"
#include "../include/sk_arena.h"
#include "../include/sk_random.h"
#include "../include/sk_matrix.h"
#include "../include/sk_model.h"
/* ββ ANSI terminal: render digit as coloured blocks ββββββββββββββββ */
static void draw_mnist_digit(const f32* data) {
for (u32 y = 0; y < 28; y++) {
for (u32 x = 0; x < 28; x++) {
f32 num = data[x + y * 28];
u32 col = 232 + (u32)(num * 23);
printf("\x1b[48;5;%dm ", col);
}
printf("\x1b[0m\n");
}
printf("\x1b[0m");
}
/* ββ Build the MNIST model ββββββββββββββββββββββββββββββββββββββββββ */
static void create_mnist_model(sk_arena* arena, sk_model* model) {
/* Xavier uniform init bounds */
f32 b0 = sqrtf(6.0f / (784 + 16));
f32 b1 = sqrtf(6.0f / ( 16 + 16));
f32 b2 = sqrtf(6.0f / ( 16 + 10));
sk_model_var* input = sk_mv_create(arena, model, 784, 1, SK_MV_INPUT);
sk_model_var* W0 = sk_mv_create(arena, model, 16, 784, SK_MV_REQUIRES_GRAD | SK_MV_PARAMETER);
sk_model_var* W1 = sk_mv_create(arena, model, 16, 16, SK_MV_REQUIRES_GRAD | SK_MV_PARAMETER);
sk_model_var* W2 = sk_mv_create(arena, model, 10, 16, SK_MV_REQUIRES_GRAD | SK_MV_PARAMETER);
sk_mat_fill_rand(W0->val, -b0, b0);
sk_mat_fill_rand(W1->val, -b1, b1);
sk_mat_fill_rand(W2->val, -b2, b2);
sk_model_var* b0v = sk_mv_create(arena, model, 16, 1, SK_MV_REQUIRES_GRAD | SK_MV_PARAMETER);
sk_model_var* b1v = sk_mv_create(arena, model, 16, 1, SK_MV_REQUIRES_GRAD | SK_MV_PARAMETER);
sk_model_var* b2v = sk_mv_create(arena, model, 10, 1, SK_MV_REQUIRES_GRAD | SK_MV_PARAMETER);
/* Layer 0: W0 Γ input + b0 β ReLU */
sk_model_var* z0 = sk_mv_matmul(arena, model, W0, input, 0);
sk_model_var* z0b = sk_mv_add(arena, model, z0, b0v, 0);
sk_model_var* a0 = sk_mv_relu(arena, model, z0b, 0);
/* Layer 1: W1 Γ a0 + b1 β ReLU + skip */
sk_model_var* z1 = sk_mv_matmul(arena, model, W1, a0, 0);
sk_model_var* z1b = sk_mv_add(arena, model, z1, b1v, 0);
sk_model_var* z1r = sk_mv_relu(arena, model, z1b, 0);
sk_model_var* a1 = sk_mv_add(arena, model, a0, z1r, 0); /* skip connection */
/* Layer 2: W2 Γ a1 + b2 β Softmax */
sk_model_var* z2 = sk_mv_matmul(arena, model, W2, a1, 0);
sk_model_var* z2b = sk_mv_add(arena, model, z2, b2v, 0);
sk_model_var* out = sk_mv_softmax(arena, model, z2b, SK_MV_OUTPUT);
/* Labels + loss */
sk_model_var* y = sk_mv_create(arena, model, 10, 1, SK_MV_DESIRED_OUTPUT);
sk_model_var* cost = sk_mv_cross_entropy(arena, model, y, out, SK_MV_COST);
(void)cost;
}
/* ββ main βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
int main(void) {
sk_arena* perm = sk_arena_create(GiB(1), MiB(1));
sk_matrix* train_images = sk_mat_load(perm, 60000, 784, "train_images.mat");
sk_matrix* test_images = sk_mat_load(perm, 10000, 784, "test_images.mat");
sk_matrix* train_labels = sk_mat_create(perm, 60000, 10);
sk_matrix* test_labels = sk_mat_create(perm, 10000, 10);
{
sk_matrix* tl = sk_mat_load(perm, 60000, 1, "train_labels.mat");
sk_matrix* tst = sk_mat_load(perm, 10000, 1, "test_labels.mat");
for (u32 i = 0; i < 60000; i++)
train_labels->data[i * 10 + (u32)tl->data[i]] = 1.0f;
for (u32 i = 0; i < 10000; i++)
test_labels->data[i * 10 + (u32)tst->data[i]] = 1.0f;
}
draw_mnist_digit(test_images->data);
printf("Label: ");
for (u32 i = 0; i < 10; i++) printf("%.0f ", test_labels->data[i]);
printf("\n\n");
sk_model* model = sk_model_create(perm);
create_mnist_model(perm, model);
sk_model_compile(perm, model);
memcpy(model->input->val->data, test_images->data, sizeof(f32) * 784);
sk_model_feedforward(model);
printf("Pre-training: ");
for (u32 i = 0; i < 10; i++) printf("%.2f ", model->output->val->data[i]);
printf("\n");
sk_training_desc desc = {
.train_images = train_images,
.train_labels = train_labels,
.test_images = test_images,
.test_labels = test_labels,
.epochs = 10,
.batch_size = 50,
.learning_rate = 0.01f,
};
sk_model_train(model, &desc);
memcpy(model->input->val->data, test_images->data, sizeof(f32) * 784);
sk_model_feedforward(model);
printf("Post-training: ");
for (u32 i = 0; i < 10; i++) printf("%.4f ", model->output->val->data[i]);
printf("\nPredicted: %llu\n", (unsigned long long)sk_mat_argmax(model->output->val));
sk_arena_destroy(perm);
return 0;
}
|