train_teddy.c

← Back to explorer
train_teddy/ train_teddy.c
Raw
C 218 lines · UTF-8
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
// Created by AG on 13-08-2026
#include "compute_backend.h"
#include "matrix_ops.h"
#include "computation_engine.h"
#include "model_train.h"
#include "dataset_ops.h"

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// Model Defitions
#define model_dimm 784
#define model_num_classes 10
#define model_training_data_count 60000
#define model_test_data_count 10000
#define model_hidden_layer 16
#define model_batch_size 500
#define model_epochs 10
#define model_learning_rate 0.25f

static void model_draw_digit(const float *pixels) {
  for (int row = 0; row < 28; row++) {
    for (int column = 0; column < 28; column++) {
      int grayscale_image_data = (int) pixels[row * 28 + column] * 23.0f;
      printf("\033[48;5;%dm \033[0m", 232 +  grayscale_image_data);
    }
    printf("\n");
  }
}

static ComputationGraph *model_build(int batch_size) {
  ComputationGraph *graph = computation_graph_create();
  GraphNode *input_node = computation_graph_variable(graph, model_dimm, batch_size, GRAPH_NODE_INPUT);
  GraphNode *initial_weight = computation_graph_variable(graph, model_hidden_layer, model_dimm, GRAPH_NODE_REQUIRES_GRAD | GRAPH_NODE_PARAMETER);
  GraphNode *initial_bias_value = computation_graph_variable(graph, model_hidden_layer, 1, GRAPH_NODE_REQUIRES_GRAD | GRAPH_NODE_PARAMETER);
  GraphNode *initial_preactivation_value = computation_graph_matrix_multiply(graph, initial_weight, input_node, 0);
  GraphNode *biased_value = computation_graph_add_bias(graph, initial_preactivation_value, initial_bias_value, 0);
  GraphNode *activation_value = computation_graph_reLU(graph, biased_value, 0);

  GraphNode *w1 = computation_graph_variable(graph, model_hidden_layer, model_hidden_layer, GRAPH_NODE_REQUIRES_GRAD | GRAPH_NODE_PARAMETER);
  GraphNode *bias1 = computation_graph_variable(graph, model_hidden_layer, 1, GRAPH_NODE_REQUIRES_GRAD | GRAPH_NODE_PARAMETER);
  GraphNode *preactivation_value1 = computation_graph_matrix_multiply(graph, w1, activation_value, 0);
  GraphNode *biased_value_1 = computation_graph_add_bias(graph, preactivation_value1, bias1, 0);
  GraphNode *preresidual1 = computation_graph_reLU(graph, biased_value_1, 0);

  GraphNode *residual_sum_value = computation_graph_add(graph, preresidual1, activation_value, 0);

  GraphNode *w2 = computation_graph_variable(graph, model_num_classes, model_hidden_layer, GRAPH_NODE_REQUIRES_GRAD | GRAPH_NODE_PARAMETER);
  GraphNode *bias2 = computation_graph_variable(graph, model_num_classes, 1, GRAPH_NODE_REQUIRES_GRAD | GRAPH_NODE_PARAMETER);
  GraphNode *preactivation_value2 = computation_graph_matrix_multiply(graph, w2, residual_sum_value, 0);
  GraphNode *biased_value_2 = computation_graph_add_bias(graph, preactivation_value2, bias2, 0);

  GraphNode *output = computation_graph_softmax(graph, biased_value_2, GRAPH_NODE_OUTPUT);
  GraphNode *target_value = computation_graph_variable(graph, model_num_classes, batch_size, GRAPH_NODE_TARGET);
  GraphNode *loss_value = computation_graph_cross_entropy(graph, output, target_value, GRAPH_NODE_LOSS);


  (void) loss_value;
  model_weight_matrix(initial_weight);
  model_weight_matrix(w1);
  model_weight_matrix(w2);

  computation_graph_compile(graph);

  return graph;
}


static void output_distribution(ComputationGraph *graph, int batch_size) {
  float *output_values = (float *) malloc(sizeof(float) * model_num_classes * batch_size);
  matrix_download(graph->output_node->value, output_values);

  printf("Teddy: output probabilities: [");
  for (int i = 0; i < model_num_classes; i++) {

    if (i > 0){
      printf (", ");
    }

    printf("%.3f", output_values[i * batch_size]);
  }
  printf("]\n");

  int predicted_value = 0;
  float best_confidence = output_values[0];
  for (int i = 1; i < model_num_classes; i++) {
    float confidence = output_values[i * batch_size];
    if (confidence > best_confidence) {
      best_confidence = confidence;
      predicted_value = i;
    }
  }

  printf("Teddy: Predicted digit: %d (%.1f%% confidence)\n", predicted_value, best_confidence * 100.0f);
  free(output_values);
}


int main(int argc, char **argv) {
  const char *kernel_path = "kernel/opencl.c";
  const char *data_directory = "data";

  if (argc > 1) {
    kernel_path = argv[1];
  }
  
  if (argc > 2) {
    data_directory = argv[2];
  }
  printf("\n\033[1mTeddy: A Machine Learning Library in C by AalbatrossGuy (AG).\033[0m\n");
  printf("\033[1mAG: Check out my homelab at https://vargoseus.com/\033[0m\n\n");

  ComputeBackend *teddy_backend = compute_backend_init(kernel_path);
  if (!teddy_backend) {
    fprintf(stderr, "Teddy: Couldn't initialize OpenCL backend. Exiting...\n");
    return 1;
  }

  compute_backend_set_global(teddy_backend);

  char path_buffer[512];

  snprintf(path_buffer, sizeof(path_buffer), "%s/training_images.bin", data_directory);
  float *raw_training_images = load_dataset_binary_f32(path_buffer, model_training_data_count * model_dimm);

  snprintf(path_buffer, sizeof(path_buffer), "%s/training_labels.bin", data_directory);
  float *raw_training_labels = load_dataset_binary_f32(path_buffer, model_training_data_count);

  snprintf(path_buffer, sizeof(path_buffer), "%s/test_images.bin", data_directory);
  float *raw_test_images = load_dataset_binary_f32(path_buffer, model_test_data_count * model_dimm);

  snprintf(path_buffer, sizeof(path_buffer), "%s/test_labels.bin", data_directory);
  float *raw_test_labels = load_dataset_binary_f32(path_buffer, model_test_data_count);

  if (!raw_training_images || !raw_training_labels || !raw_test_images || !raw_test_labels) {
    fprintf(stderr, "Teddy: Failed to load dataset. Download it via the python downloader script.");
    compute_backend_destroy(teddy_backend);
    return 1;
  }

  float *encoded_training_labels = (float *) malloc(sizeof(float) * model_training_data_count * model_num_classes);
  float *encoded_test_labels = (float *) malloc(sizeof(float) * model_test_data_count * model_num_classes);

  one_hot_encode(encoded_training_labels, raw_training_labels, model_training_data_count, model_num_classes);
  one_hot_encode(encoded_test_labels, raw_test_labels, model_test_data_count, model_num_classes);

  srand((unsigned int) time(NULL));
  int demo_sample_index = rand() % model_training_data_count;
  const float *demo_sample_image = raw_training_images + (size_t) demo_sample_index * model_dimm;

  unsigned int rng_seed = 1337u;
  const char *seed_override = getenv("TEDDY_SEED");
  if (seed_override) {
    rng_seed = (unsigned int) strtoul(seed_override, NULL, 10);
  }
  printf("Teddy: RNG seed: %u \n", rng_seed);
  srand(rng_seed);

  printf("\n======== Sample Training Digit ==========\n");
  model_draw_digit(demo_sample_image);
  printf("Teddy: label: %d\n\n", (int) raw_training_labels[demo_sample_index]);

  ComputationGraph *teddy = model_build(model_batch_size);

  printf("\n======== Pre-training Inference ==========\n");
  get_model_prediction(teddy, demo_sample_image, model_dimm, model_batch_size);
  compute_backend_finish(teddy_backend);
  output_distribution(teddy, model_batch_size);

  printf("\n======== Training ===========\n");
  printf("Teddy: training samples: %d | test samples: %d\n", model_training_data_count, model_test_data_count);
  printf("Teddy: batch size: %d | epochs: %d | learning rate: %.3f\n", model_batch_size, model_epochs, model_learning_rate);

  TrainingParams training_parameters = {
    raw_training_images,
    encoded_training_labels,
    raw_test_images,
    encoded_test_labels,
    model_training_data_count,
    model_test_data_count,
    model_dimm,
    model_num_classes,
    model_epochs,
    model_batch_size,
    model_learning_rate
  };

  struct timespec train_start, train_end;
  clock_gettime(CLOCK_MONOTONIC, &train_start);
  train_model(teddy, &training_parameters);
  clock_gettime(CLOCK_MONOTONIC, &train_end);
  compute_backend_finish(teddy_backend);

  double train_seconds = (train_end.tv_sec - train_start.tv_sec) + (train_end.tv_nsec - train_start.tv_nsec) / 1e9;

  printf("\n======== Post-training Inference=======\n");
  get_model_prediction(teddy, demo_sample_image, model_dimm, model_batch_size);
  compute_backend_finish(teddy_backend);
  output_distribution(teddy, model_batch_size);

  printf("\n=========Teddy Evaluation=========\n");
  evaluate_model_prediction(teddy, &training_parameters);

  computation_graph_destroy(teddy);
  free(raw_training_images);
  free(raw_training_labels);
  free(raw_test_images);
  free(raw_test_labels);
  free(encoded_training_labels);
  free(encoded_test_labels);

  compute_backend_destroy(teddy_backend);

  printf("\nTeddy: training took %.2fs\n", train_seconds);
  printf("Teddy: Run finished. Au revoir!\n");

}