#include <cstddef>#include <iostream>#include <vector> #include "../tinytorch.hpp" namespace tt = tinytorch;using namespace std; // NOLINT // let's create a neural network to approximate the following functionint classify_fruit(float color, float size, float weight) { // color in [0, 1] where 0 represents green and 1 represents red // size in [0, 1] where 0 represents small and 1 represents large // weight in [0, 1] where 0 represents light and 1 represents heavy // y in [0, 3] representing three types of fruits: apple, orange, and banana double y = 0.8 * color + 1.0 * size + 1.0 * weight; if (y < 0.9) { return 0; // Apple } else if (y < 1.8) { return 1; // Orange } else { return 2; // Banana }} tuple<vector<float>, int> generate_case() { float x0 = (float)rand() / RAND_MAX; float x1 = (float)rand() / RAND_MAX; float x2 = (float)rand() / RAND_MAX; int y = classify_fruit(x0, x1, x2); return {{x0, x1, x2}, y};} tuple<vector<vector<float>>, vector<int>, vector<vector<float>>, vector<int>> generate_dataset( int train_size, int val_size) { vector<vector<float>> train_inputs, val_inputs; vector<int> train_targets, val_targets; for (int i = 0; i < train_size; i++) { auto [x, y] = generate_case(); train_inputs.push_back(x); train_targets.push_back(y); } for (int i = 0; i < val_size; i++) { auto [x, y] = generate_case(); val_inputs.push_back(x); val_targets.push_back(y); } return {train_inputs, train_targets, val_inputs, val_targets};} int main() { // create a context to hold all the tensors tt::TensorContext ctx((size_t)1024 * 1024); // generate some random data int train_size = 10000, val_size = 100; auto [train_inputs, train_targets, val_inputs, val_targets] = generate_dataset(train_size, val_size); // create a simple one hidden layer neural network int variables = 3; // number of input variables int neurons = 32; // number of neurons in the hidden layer int classes = 3; // number of classes // input and target tensors auto &x = *ctx.NewTensor({1, variables}); auto &y = *ctx.NewTensor({1}, tt::kI32); // parameters auto &W1 = *ctx.NewTensor({neurons, variables})->RandomNorm(); auto &b1 = *ctx.NewTensor({neurons})->RandomNorm(); auto &W2 = *ctx.NewTensor({classes, neurons})->RandomNorm(); auto &b2 = *ctx.NewTensor({classes})->RandomNorm(); vector<tt::Tensor *> params = {&W1, &b1, &W2, &b2}; // create the computation graph auto &hidden = (x.MatMul(W1) + b1).Gelu(); auto &logits = hidden.MatMul(W2) + b2; auto &probs = logits.Softmax(); auto &loss = probs.CrossEntropy(y); float learning_rate = 0.001; float training_loss = 0; // train the model for (int step = 0; step < 30000; step++) { if (step % 1000 == 0) { // evaluate the model on the validation set float val_loss = 0; for (int i = 0; i < val_size; i++) { x.Fill(val_inputs[i]); y.Fill(val_targets[i]); loss.Forward(); val_loss += loss.Flatten()[0]; } std::cout << "step " << step << ", val_loss: " << val_loss / val_size << std::endl; } // pick a random training case int casei = rand() % train_size; x.Fill(train_inputs[casei]); y.Fill(train_targets[casei]); // forward and backward loss.Forward(); loss.ZeroGrad(); loss.Backward(); // update the parameters for (auto param : params) { auto weights = (float *)param->data(); auto grads = (float *)param->grad()->data(); for (int i = 0; i < param->NumElements(); i++) { weights[i] -= learning_rate * grads[i]; } } training_loss = loss.Flatten()[0]; } std::cout << "training loss: " << training_loss << std::endl; // test the model for (int i = 0; i < 30; i++) { auto [testx, y_truth] = generate_case(); x.Fill(testx); probs.Forward(); // find the class with the highest probability auto probs_data = probs.Flatten(); int y_pred = 0; for (int j = 0; j < classes; j++) { if (probs_data[j] > probs_data[y_pred]) { y_pred = j; } } std::cout << "test case " << i << ", x: " << testx[0] << ", " << testx[1] << ", " << testx[2] << ", y_truth: " << y_truth << ", y_pred: " << y_pred; if (y_truth == y_pred) { std::cout << " (correct)"; } else { std::cout << " (wrong)"; } std::cout << endl; } return 0;}
#include <cstddef>
#include <iostream>
#include <vector>
#include "../tinytorch.hpp"
namespace tt = tinytorch;
using namespace std; // NOLINT
// let's create a neural network to approximate the following function
int classify_fruit(float color, float size, float weight) {
// color in [0, 1] where 0 represents green and 1 represents red
// size in [0, 1] where 0 represents small and 1 represents large
// weight in [0, 1] where 0 represents light and 1 represents heavy
// y in [0, 3] representing three types of fruits: apple, orange, and banana
double y = 0.8 * color + 1.0 * size + 1.0 * weight;
if (y < 0.9) {
return 0; // Apple
} else if (y < 1.8) {
return 1; // Orange
} else {
return 2; // Banana
}
}
tuple<vector<float>, int> generate_case() {
float x0 = (float)rand() / RAND_MAX;
float x1 = (float)rand() / RAND_MAX;
float x2 = (float)rand() / RAND_MAX;
int y = classify_fruit(x0, x1, x2);
return {{x0, x1, x2}, y};
}
tuple<vector<vector<float>>, vector<int>, vector<vector<float>>, vector<int>> generate_dataset(
int train_size, int val_size) {
vector<vector<float>> train_inputs, val_inputs;
vector<int> train_targets, val_targets;
for (int i = 0; i < train_size; i++) {
auto [x, y] = generate_case();
train_inputs.push_back(x);
train_targets.push_back(y);
}
for (int i = 0; i < val_size; i++) {
auto [x, y] = generate_case();
val_inputs.push_back(x);
val_targets.push_back(y);
}
return {train_inputs, train_targets, val_inputs, val_targets};
}
int main() {
// create a context to hold all the tensors
tt::TensorContext ctx((size_t)1024 * 1024);
// generate some random data
int train_size = 10000, val_size = 100;
auto [train_inputs, train_targets, val_inputs, val_targets] =
generate_dataset(train_size, val_size);
// create a simple one hidden layer neural network
int variables = 3; // number of input variables
int neurons = 32; // number of neurons in the hidden layer
int classes = 3; // number of classes
// input and target tensors
auto &x = *ctx.NewTensor({1, variables});
auto &y = *ctx.NewTensor({1}, tt::kI32);
// parameters
auto &W1 = *ctx.NewTensor({neurons, variables})->RandomNorm();
auto &b1 = *ctx.NewTensor({neurons})->RandomNorm();
auto &W2 = *ctx.NewTensor({classes, neurons})->RandomNorm();
auto &b2 = *ctx.NewTensor({classes})->RandomNorm();
vector<tt::Tensor *> params = {&W1, &b1, &W2, &b2};
// create the computation graph
auto &hidden = (x.MatMul(W1) + b1).Gelu();
auto &logits = hidden.MatMul(W2) + b2;
auto &probs = logits.Softmax();
auto &loss = probs.CrossEntropy(y);
float learning_rate = 0.001;
float training_loss = 0;
// train the model
for (int step = 0; step < 30000; step++) {
if (step % 1000 == 0) {
// evaluate the model on the validation set
float val_loss = 0;
for (int i = 0; i < val_size; i++) {
x.Fill(val_inputs[i]);
y.Fill(val_targets[i]);
loss.Forward();
val_loss += loss.Flatten()[0];
}
std::cout << "step " << step << ", val_loss: " << val_loss / val_size << std::endl;
}
// pick a random training case
int casei = rand() % train_size;
x.Fill(train_inputs[casei]);
y.Fill(train_targets[casei]);
// forward and backward
loss.Forward();
loss.ZeroGrad();
loss.Backward();
// update the parameters
for (auto param : params) {
auto weights = (float *)param->data();
auto grads = (float *)param->grad()->data();
for (int i = 0; i < param->NumElements(); i++) {
weights[i] -= learning_rate * grads[i];
}
}
training_loss = loss.Flatten()[0];
}
std::cout << "training loss: " << training_loss << std::endl;
// test the model
for (int i = 0; i < 30; i++) {
auto [testx, y_truth] = generate_case();
x.Fill(testx);
probs.Forward();
// find the class with the highest probability
auto probs_data = probs.Flatten();
int y_pred = 0;
for (int j = 0; j < classes; j++) {
if (probs_data[j] > probs_data[y_pred]) {
y_pred = j;
}
}
std::cout << "test case " << i << ", x: " << testx[0] << ", " << testx[1] << ", "
<< testx[2] << ", y_truth: " << y_truth << ", y_pred: " << y_pred;
if (y_truth == y_pred) {
std::cout << " (correct)";
} else {
std::cout << " (wrong)";
}
std::cout << endl;
}
return 0;
}