I wrote it because, it seemed less mystifying to read maths instead of a random class that supposedly does something. I wrote it from scratch in cpp (23) due to the fact that I wanted to emphasise on precision, however it's subtly built on top of xtensor.
check it out in action #include <usul.hpp> using namespace std;
int main() {
double LR = 0.1;
int EPOCH = 5000;
// lets write the forward pass
Tensor X = {
{0.0, 0.0}, // this is what we're feeding it
{0.0, 1.0},
{1.0, 0.0},
{1.0, 1.0},
};
// this is what we're after
Tensor Y_true = {
0.0, // xor{0.0, 0.0} = 0.0
1.0, // xor{0.0, 1.0} = 1.0
1.0, // xor{1.0, 0.0} = 1.0
0.0 // xor{1.0, 1.0} = 0.0
};
Y_true.reshape({4, 1});
// we are now going to create a single layer, double neuron network
// each layer will have a bias and weights tensor.
auto W1 = generate_random_matrix({2, 4}, -1.0, 1.0);
auto B1 = generate_random_matrix({1, 4}, -1.0, 1.0);
auto W2 = generate_random_matrix({4, 1}, -1.0, 1.0);
auto B2 = generate_random_matrix({1, 1}, -1.0, 1.0);
// this is the training loop
for (int i = 0; i < EPOCH; i++) {
// so lets do one forward pass
// first layer
auto Z1 = X % W1 + B1;
auto A = Z1.ReLU();
// second layer
auto Z2 = A % W2 + B2;
// activation
// should be - auto S = Z2.activate(Activate::Sigmoid);
auto S = Z2.Sigmoid();
// Loss
// should be - auto Loss = S.loss(Loss::MSE);
auto loss = (S - Y_true) * (S - Y_true);
loss = loss.mean();
cout << "Loss: " << loss << "\n";
cout << "guesses: " << "\n"
<< S << "\n";
cout << "should be: " << Y_true << "\n";
loss.backwards();
W1.step(LR);
B1.step(LR);
W2.step(LR);
B2.step(LR);
}
}
which will output Guesses:
Tensor({{ 0.027546},
{ 0.963592},
{ 0.964138},
{ 0.038964}}, shape=(4,1))
should be: Tensor({{ 0.},
{ 1.},
{ 1.},
{ 0.}}, shape=(4,1))
Loss: Tensor( 0.001222, shape=())
Just write the maths, and Usul-cpp will handle the rest, it is extremely lightweight, and the entire library is memory safe. And its called Usul, because I'm a huge dune fan.You can view the project at [https://github.com/crushr3sist/Usul-cpp](https://github.com/crushr3sist/Usul-cpp)
Any ideas for what to do next are all welcome, you can also pull and implement features that you think could improve this project. (no vibecoded prs please, this project is not vibe-coded)
ronny2fresh•7m ago
i'd like to share a bit of a background. maths has always stood in the way between me and finally learning how to write neural networks, I've always been fascinated by them ever since i saw that diagram of a deep neural network with all the node edges interconnecting with others.
Well I stopped letting highschool math class trauma stop me from understanding these concepts, i took a pen and paper out, taught myself numerical descent, analytical descent, backwards propagation, what a compute graph is, all of these subjects, taught myself, until I was manually backwards propagating the loss between a few arithmetics, until that moved into scalar level neural networks and doing simple calculus and being able to do xor gate problem with scalar, and then I taught myself matrix calculus, and well...
I taught myself neural networks, taught myself the maths, until I hand written the xor gate problem in matrix syntax using xtensor, and manually, by hand, no AI, did the xor gate problem manually backwards propagating error, automatically differentiating. This was also a kind of detox from LLM dependency, I don't tend to use it for code, but because its so central in my daily life and a migration from google to gemini, using LLMs for immediate information access had started to affect my mind and my ability to intake information. I really hope some of you find this library useful, as i did spend a pretty decent amount of time on it, and I would love it if any of you had any ideas on what to do next with this library.
Thank you for reading, I really appreciate it.