-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathmlp_example.cpp
More file actions
153 lines (130 loc) · 4.36 KB
/
Copy pathmlp_example.cpp
File metadata and controls
153 lines (130 loc) · 4.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// mlp_example.cpp
// MLP learning example
#include <tt/data/dataset.h>
#include <tt/device.h>
#include <tt/grad_mode.h>
#include <tt/nn/linear.h>
#include <tt/nn/loss.h>
#include <tt/nn/module.h>
#include <tt/optim/adam.h>
#include <tt/random.h>
#include <tt/tensor.h>
#include <cstdint>
#include <format>
#include <iostream>
#include <ranges>
#include <string>
#include <tuple>
#include <utility>
using namespace tinytensor;
// Dataset representing sin(2*x) with noise
struct Dataset {
using DataType = std::tuple<Tensor, Tensor>;
Dataset(double start, double stop, int size, double noise_level, Device device)
: N(size), x(linspace(start, stop, true, {size}, TensorOptions().device(device))), y(sin(2 * x)) {
// Add noise to dataset
double noise_coef = (y.max() - y.min()).item<double>() * noise_level;
Tensor noise = zeros_like(y).normal_(0, 1) * noise_coef;
y = y + noise;
}
[[nodiscard]] auto size() const -> int {
return N;
}
[[nodiscard]] auto get(int idx) const -> DataType {
return {x[idx], y[idx]};
}
int N;
Tensor x;
Tensor y;
};
// Neural Net
class Net : public nn::Module {
public:
Net(int input_size, int output_size)
: layer1(input_size, 32), layer2(32, output_size) {
register_module(layer1);
register_module(layer2);
}
[[nodiscard]] auto name() const -> std::string override {
return "Net";
}
[[nodiscard]] auto forward(Tensor input) -> Tensor {
Tensor result = layer1.forward(input);
result = relu(result);
result = layer2.forward(result);
return result;
}
private:
nn::Linear layer1;
nn::Linear layer2;
};
int main() {
constexpr int dataset_size = 1000;
constexpr double dataset_start = -10;
constexpr double dataset_stop = 10;
constexpr double noise_level = 0.1;
constexpr double lr = 3e-4;
constexpr int epochs = 100;
constexpr int batch_size = 32;
constexpr uint64_t seed = 0;
constexpr Device device = kCPU;
set_default_generator_seed(seed);
// Dataset and splits
Dataset dataset(dataset_start, dataset_stop, dataset_size, noise_level, device);
auto [train_data, validate_data, test_data] = data::random_split(std::move(dataset), seed, 700, 200, 100);
auto train_loader = data::DataLoader(train_data, batch_size, true, seed);
auto validate_loader = data::DataLoader(validate_data, batch_size, false);
auto test_loader = data::DataLoader(test_data, batch_size, false);
Net net(1, 1);
optim::Adam optim(net.parameters_for_optimizer(), lr);
net.to(device);
std::cout << "start" << std::endl;
// Train/Validate
for ([[maybe_unused]] int epoch : std::views::iota(0, epochs)) {
// Train
net.train();
double epoch_train_loss = 0;
for (auto [x, y] : train_loader) {
x = x.to(device);
y = y.to(device);
optim.zero_grad();
Tensor y_hat = net.forward(x);
Tensor loss = nn::mse_loss(y_hat, y, nn::ReductionMode::mean);
epoch_train_loss += loss.item<double>() / train_loader.size();
loss.backward();
optim.step();
}
// Validate
net.eval();
double epoch_validate_loss = 0;
// Guard for no grads being tracked
{
const autograd::NoGradGuard guard;
for (auto [x, y] : validate_loader) {
x = x.to(device);
y = y.to(device);
Tensor y_hat = net.forward(x);
Tensor loss = nn::mse_loss(y_hat, y, nn::ReductionMode::mean);
epoch_validate_loss += loss.item<double>() / validate_loader.size();
}
std::cout << std::format(
"Epoch: {:d}, Train Loss: {:f}, Validate Loss: {:f}",
epoch,
epoch_train_loss,
epoch_validate_loss
) << std::endl;
}
}
// Test net on held out set
net.eval();
double test_loss = 0;
for (auto [x, y] : test_loader) {
x = x.to(device);
y = y.to(device);
Tensor y_hat = net.forward(x);
Tensor loss = nn::mse_loss(y_hat, y, nn::ReductionMode::mean);
test_loss += loss.item<double>() / test_loader.size();
}
std::cout << std::format("Test Loss: {:f}", test_loss) << std::endl;
return 0;
}