+ New

utensil

Public
← utensil / examples / cifar10_loader.h
#ifndef CIFAR10_LOADER_H
#define CIFAR10_LOADER_H

// Minimal loader for the CIFAR-10 "binary version" (cifar-10-binary.tar.gz):
// each record is 1 label byte + 3072 pixel bytes (1024 R, 1024 G, 1024 B; each
// plane row-major 32x32). Include after utensil.h (cifar10_argmax takes a
// ut_tensor*). Normalises with the standard CIFAR-10 per-channel mean/std.

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

#define CIFAR10_PIX (3 * 32 * 32)
#define CIFAR10_REC (1 + CIFAR10_PIX)

typedef struct {
  int n;
  float* imgs;      // [n,3,32,32] NCHW, normalised
  uint8_t* labels;  // [n]
} cifar10_t;

static const float CIFAR10_MEAN[3] = {0.4914f, 0.4822f, 0.4465f};
static const float CIFAR10_STD[3] = {0.2470f, 0.2435f, 0.2616f};

// paths: nfiles binary batch files (data_batch_1..5.bin, or test_batch.bin),
// each holding exactly 10000 records
static cifar10_t cifar10_load(const char** paths, int nfiles) {
  cifar10_t d = {.n = nfiles * 10000};
  d.imgs = malloc((size_t)d.n * CIFAR10_PIX * sizeof(float));
  d.labels = malloc((size_t)d.n);
  uint8_t rec[CIFAR10_REC];
  int idx = 0;
  for (int f = 0; f < nfiles; f++) {
    FILE* fp = fopen(paths[f], "rb");
    for (int i = 0; i < 10000; i++) {
      fread(rec, 1, CIFAR10_REC, fp);
      d.labels[idx] = rec[0];
      for (int p = 0; p < CIFAR10_PIX; p++) {
        int ch = p / (32 * 32);
        d.imgs[idx * CIFAR10_PIX + p] = (rec[1 + p] / 255.f - CIFAR10_MEAN[ch]) / CIFAR10_STD[ch];
      }
      idx++;
    }
    fclose(fp);
  }
  return d;
}

static void cifar10_free(cifar10_t* d) {
  free(d->imgs);
  free(d->labels);
}

static void cifar10_shuffle(int* a, int n) {
  for (int i = n - 1; i > 0; i--) {
    int j = rand() % (i + 1);
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
  }
}

static int cifar10_argmax(ut_tensor* t, int row) {
  int C = t->shape.shape[1], best = 0;
  float* r = t->data + row * C;
  for (int j = 1; j < C; j++)
    if (r[j] > r[best]) best = j;
  return best;
}

#endif