History

initial release

Yusong Gao committed 2 years ago

Commit d0d49a9No parent (first commit)21 files · +8268 −0Browse files

Showing the changes to example/tinytorch_example.cpp (1 of 21 files). Show all

Unified · Split

example/tinytorch_example.cpp+149−0
@@ -0,0 +1,149 @@
1+#include <cstddef>
2+#include <iostream>
3+#include <vector>
4+
5+#include "../tinytorch.hpp"
6+
7+namespace tt = tinytorch;
8+using namespace std; // NOLINT
9+
10+// let's create a neural network to approximate the following function
11+int classify_fruit(float color, float size, float weight) {
12+ // color in [0, 1] where 0 represents green and 1 represents red
13+ // size in [0, 1] where 0 represents small and 1 represents large
14+ // weight in [0, 1] where 0 represents light and 1 represents heavy
15+ // y in [0, 3] representing three types of fruits: apple, orange, and banana
16+
17+ double y = 0.8 * color + 1.0 * size + 1.0 * weight;
18+
19+ if (y < 0.9) {
20+ return 0; // Apple
21+ } else if (y < 1.8) {
22+ return 1; // Orange
23+ } else {
24+ return 2; // Banana
25+ }
26+}
27+
28+tuple<vector<float>, int> generate_case() {
29+ float x0 = (float)rand() / RAND_MAX;
30+ float x1 = (float)rand() / RAND_MAX;
31+ float x2 = (float)rand() / RAND_MAX;
32+ int y = classify_fruit(x0, x1, x2);
33+ return {{x0, x1, x2}, y};
34+}
35+
36+tuple<vector<vector<float>>, vector<int>, vector<vector<float>>, vector<int>> generate_dataset(
37+ int train_size, int val_size) {
38+ vector<vector<float>> train_inputs, val_inputs;
39+ vector<int> train_targets, val_targets;
40+ for (int i = 0; i < train_size; i++) {
41+ auto [x, y] = generate_case();
42+ train_inputs.push_back(x);
43+ train_targets.push_back(y);
44+ }
45+ for (int i = 0; i < val_size; i++) {
46+ auto [x, y] = generate_case();
47+ val_inputs.push_back(x);
48+ val_targets.push_back(y);
49+ }
50+ return {train_inputs, train_targets, val_inputs, val_targets};
51+}
52+
53+int main() {
54+ // create a context to hold all the tensors
55+ tt::TensorContext ctx((size_t)1024 * 1024);
56+
57+ // generate some random data
58+ int train_size = 10000, val_size = 100;
59+ auto [train_inputs, train_targets, val_inputs, val_targets] =
60+ generate_dataset(train_size, val_size);
61+
62+ // create a simple one hidden layer neural network
63+ int variables = 3; // number of input variables
64+ int neurons = 32; // number of neurons in the hidden layer
65+ int classes = 3; // number of classes
66+
67+ // input and target tensors
68+ auto &x = *ctx.NewTensor({1, variables});
69+ auto &y = *ctx.NewTensor({1}, tt::kI32);
70+ // parameters
71+ auto &W1 = *ctx.NewTensor({neurons, variables})->RandomNorm();
72+ auto &b1 = *ctx.NewTensor({neurons})->RandomNorm();
73+ auto &W2 = *ctx.NewTensor({classes, neurons})->RandomNorm();
74+ auto &b2 = *ctx.NewTensor({classes})->RandomNorm();
75+ vector<tt::Tensor *> params = {&W1, &b1, &W2, &b2};
76+ // create the computation graph
77+ auto &hidden = (x.MatMul(W1) + b1).Gelu();
78+ auto &logits = hidden.MatMul(W2) + b2;
79+ auto &probs = logits.Softmax();
80+ auto &loss = probs.CrossEntropy(y);
81+
82+ float learning_rate = 0.001;
83+ float training_loss = 0;
84+
85+ // train the model
86+ for (int step = 0; step < 30000; step++) {
87+ if (step % 1000 == 0) {
88+ // evaluate the model on the validation set
89+ float val_loss = 0;
90+ for (int i = 0; i < val_size; i++) {
91+ x.Fill(val_inputs[i]);
92+ y.Fill(val_targets[i]);
93+ loss.Forward();
94+ val_loss += loss.Flatten()[0];
95+ }
96+ std::cout << "step " << step << ", val_loss: " << val_loss / val_size << std::endl;
97+ }
98+
99+ // pick a random training case
100+ int casei = rand() % train_size;
101+ x.Fill(train_inputs[casei]);
102+ y.Fill(train_targets[casei]);
103+
104+ // forward and backward
105+ loss.Forward();
106+ loss.ZeroGrad();
107+ loss.Backward();
108+
109+ // update the parameters
110+ for (auto param : params) {
111+ auto weights = (float *)param->data();
112+ auto grads = (float *)param->grad()->data();
113+ for (int i = 0; i < param->NumElements(); i++) {
114+ weights[i] -= learning_rate * grads[i];
115+ }
116+ }
117+
118+ training_loss = loss.Flatten()[0];
119+ }
120+
121+ std::cout << "training loss: " << training_loss << std::endl;
122+
123+ // test the model
124+ for (int i = 0; i < 30; i++) {
125+ auto [testx, y_truth] = generate_case();
126+ x.Fill(testx);
127+ probs.Forward();
128+
129+ // find the class with the highest probability
130+ auto probs_data = probs.Flatten();
131+ int y_pred = 0;
132+ for (int j = 0; j < classes; j++) {
133+ if (probs_data[j] > probs_data[y_pred]) {
134+ y_pred = j;
135+ }
136+ }
137+
138+ std::cout << "test case " << i << ", x: " << testx[0] << ", " << testx[1] << ", "
139+ << testx[2] << ", y_truth: " << y_truth << ", y_pred: " << y_pred;
140+ if (y_truth == y_pred) {
141+ std::cout << " (correct)";
142+ } else {
143+ std::cout << " (wrong)";
144+ }
145+ std::cout << endl;
146+ }
147+
148+ return 0;
149+}