Это microcrad — реимплементация micrograd Андрея Карпаты на чистом C. Никаких тензоров, GPU или векторизации. Только скаляры и граф вычислений. Каждое число — узел в графе. Каждая операция запоминает, откуда взялся результат. Один проход value_backward() проходит граф в обратном порядке и считает производную для каждого входа. Всё ради обучения: как работает обратное распространение.
В основе лежит тип Value. Он оборачивает обычный double и хранит ссылки на предыдущие узлы (операнды) через указатели. У каждого Value есть счётчик ссылок ref_count. Это главный механизм управления памятью. Нет сборщика мусора — только value_retain() и value_release(). Когда счётчик падает до нуля, Value освобождается и рекурсивно отпускает свои операнды.
Операции — value_add, value_mul, value_pow, value_exp, value_log, value_relu. Каждая возвращает новый Value и автоматически увеличивает счётчик ссылок операндов. То есть результат «совладеет» своими операндами. value_pow принимает только константный показатель, хранит его в extra_data. Вычитание и деление не выделены отдельно — первое делается через сложение с отрицательным значением, второе через value_pow(x, -1.0) или через константу на выбор.
value_backward() делает топологическую сортировку графа (чтобы не пройти по одному узлу дважды) и затем проходится по списку в обратном порядке, применяя цепное правило. Результат — градиенты записываются в поля grad каждого узла. Важно: градиенты накапливаются (оператором +=). Так что в цикле обучения перед каждым backward нужно обнулять их вручную.
Всё ради сборки нейросети. Сверху лежит MLP (многослойный перцептрон), внутри — Layer и Neuron. Каждый Neuron содержит nin весов и смещение, инициализированные случайно. Форвард — relu(w·x + b). Каждый слой применяет ReLU и к выходным нейронам тоже — из-за этого сеть всегда выдаёт неотрицательные значения. Для обучения есть mlp_parameters() — плоский список всех тренируемых параметров.
Пример: сетка 2 → 8 → 1. В цикле: скармливаешь входы, собираешь loss, обнуляешь градиенты по всей таблице параметров, вызываешь value_backward(loss), потом проходишь по параметрам и делаешь param->data -= learning_rate * param->grad. И так далее.
Код откровенно учебный. Никакой production-надёжности. Вся документация намеренно прозрачная. Библиотека зависит только от libm. В комплекте два примера: простейшая регрессия на синтетических данных (запускается сразу) и демо с MNIST (качает данные). Начинать советуют с train_on_toy_regression.c — это самый короткий полный цикл обучения. Тесты в директории test/ работают как документация. Makefile собирает всё одной командой.