MartialTerran commited on
Commit
42a5011
1 Parent(s): 8d9edc5

Create Modular_Pytorch_Sine-x_formula_model.py

Browse files

See How to use tensorflow lite to export model to Arduino: https://github.com/Azacus1/Modelling-for-sin-wave-function/tree/main
Intro to TinyML Part 1: Training a Neural Network for Arduino in TensorFlow | Digi-Key Electronics
https://www.youtube.com/watch?v=BzzqYNYOcWc&t=0s

Intro to TinyML Part 2: Deploying a TensorFlow Lite Model to Arduino | Digi-Key Electronics
https://www.youtube.com/watch?v=dU01M61RW8s&t

Modular_Pytorch_Sine-x_formula_model.py ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #Modular Sine-x Model with hyperparameter dictionary-control.
2
+ #Based on Tensorflow example published by githubusercontent Azacus1 Modelling-for-sin-wave-function Model.py
3
+
4
+ Hyperparameters = {
5
+ 'hidden_layers': [32, 32, 32], # Example: 3 hidden layers with 32 neurons each
6
+ 'activation': 'relu', # Activation function for hidden layers ('relu', 'sigmoid', 'tanh', 'elu', 'leakyrelu')
7
+ 'epochs': 500,
8
+ 'batch_size': 64,
9
+ 'learning_rate': 0.001,
10
+ }
11
+
12
+ print("load libraries")
13
+ import torch
14
+ import torch.nn as nn
15
+ import torch.optim as optim
16
+ import numpy as np
17
+ import pandas as pd
18
+ import matplotlib.pyplot as plt
19
+ import math
20
+ print("done loading libraries")
21
+
22
+ # Set seed for experiment reproducibility
23
+ seed = 1
24
+ np.random.seed(seed)
25
+ torch.manual_seed(seed)
26
+
27
+ # Number of sample datapoints
28
+ SAMPLES = 400
29
+
30
+ def generate_sine_data(samples):
31
+ print("# --- Synthetic Data Generation ---")
32
+ x_values = np.random.uniform(low=0, high=2 * math.pi, size=samples).astype(np.float32)
33
+ np.random.shuffle(x_values)
34
+ y_values = np.sin(x_values).astype(np.float32)
35
+ y_values += 0.01 * np.random.randn(*y_values.shape)
36
+ return x_values, y_values
37
+
38
+ # --- Data Splitting ---
39
+ def split_data(x_values, y_values, train_split=0.6, test_split=0.2):
40
+ print("# --- Train/Test Data Splitting ---")
41
+ train_split_index = int(train_split * SAMPLES)
42
+ test_split_index = int(test_split * SAMPLES) + train_split_index
43
+ x_train, x_test, x_validate = np.split(x_values, [train_split_index, test_split_index])
44
+ y_train, y_test, y_validate = np.split(y_values, [train_split_index, test_split_index])
45
+ assert (x_train.size + x_validate.size + x_test.size) == SAMPLES
46
+ return (x_train, y_train), (x_test, y_test), (x_validate, y_validate)
47
+
48
+ # --- Data Conversion to Tensors ---
49
+ def convert_to_tensors(x_train, y_train, x_test, y_test, x_validate, y_validate):
50
+ print("# --- Data Conversion to Tensors ---")
51
+ x_train_tensor = torch.from_numpy(x_train).unsqueeze(1)
52
+ y_train_tensor = torch.from_numpy(y_train).unsqueeze(1)
53
+ x_test_tensor = torch.from_numpy(x_test).unsqueeze(1)
54
+ y_test_tensor = torch.from_numpy(y_test).unsqueeze(1)
55
+ x_validate_tensor = torch.from_numpy(x_validate).unsqueeze(1)
56
+ y_validate_tensor = torch.from_numpy(y_validate).unsqueeze(1)
57
+ return x_train_tensor, y_train_tensor, x_test_tensor, y_test_tensor, x_validate_tensor, y_validate_tensor
58
+
59
+ # --- Plotting Utilities ---
60
+ def plot_data(x_train, y_train, x_test, y_test, x_validate, y_validate):
61
+ print("# --- Plotting Utilities ---")
62
+ plt.plot(x_train, y_train, 'b.', label="Train")
63
+ plt.plot(x_test, y_test, 'r.', label="Test")
64
+ plt.plot(x_validate, y_validate, 'y.', label="Validate")
65
+ plt.legend()
66
+ plt.show()
67
+
68
+ def plot_loss(train_losses, val_losses, skip=0):
69
+ print("# --- Plotting train_losses, val_losses ---")
70
+ epochs_range = range(1, len(train_losses) + 1)
71
+ plt.figure(figsize=(10, 4))
72
+ plt.subplot(1, 2, 1)
73
+ plt.plot(epochs_range[skip:], train_losses[skip:], 'g.', label='Training loss')
74
+ plt.plot(epochs_range[skip:], val_losses[skip:], 'b.', label='Validation loss')
75
+ plt.title('Training and validation loss')
76
+ plt.xlabel('Epochs')
77
+ plt.ylabel('Loss')
78
+ plt.legend()
79
+ plt.show()
80
+
81
+ def plot_mae(epochs_range, train_mae, val_mae):
82
+ print("# --- Plotting MAE ---")
83
+ plt.subplot(1, 2, 2)
84
+ plt.plot([epochs_range[-1]], [train_mae], 'g.', label='Training MAE')
85
+ plt.plot([epochs_range[-1]], [val_mae], 'b.', label='Validation MAE')
86
+ plt.title('Training and validation mean absolute error')
87
+ plt.xlabel('Epochs (only final epoch shown for MAE)')
88
+ plt.ylabel('MAE')
89
+ plt.legend()
90
+ plt.tight_layout()
91
+ plt.show()
92
+
93
+ def plot_predictions(x_test, y_test, y_test_pred_tensor):
94
+ print("# --- Plotting Predictions ---")
95
+ plt.clf()
96
+ plt.title('Comparison of predictions and actual values')
97
+ plt.plot(x_test, y_test, 'b.', label='Actual values')
98
+ plt.plot(x_test, y_test_pred_tensor.detach().numpy(), 'r.', label='PyTorch predicted')
99
+ plt.legend()
100
+ plt.show()
101
+
102
+ # --- Model Definition ---
103
+ class DynamicSineModel(nn.Module):
104
+ def __init__(self, config):
105
+ super(DynamicSineModel, self).__init__()
106
+ self.layers = nn.ModuleList()
107
+ self.config = config
108
+ input_dim = 1
109
+
110
+ # Build hidden layers
111
+ for i, num_neurons in enumerate(self.config['hidden_layers']):
112
+ self.layers.append(nn.Linear(input_dim, num_neurons))
113
+ input_dim = num_neurons # Update input dimension for the next layer
114
+
115
+ # Output layer
116
+ self.layers.append(nn.Linear(input_dim, 1))
117
+
118
+ # Determine activation functions
119
+ self.activation_functions = []
120
+ for _ in range(len(self.config['hidden_layers'])):
121
+ activation_name = self.config.get('activation', 'relu').lower()
122
+ if activation_name == 'relu':
123
+ self.activation_functions.append(nn.ReLU())
124
+ elif activation_name == 'sigmoid':
125
+ self.activation_functions.append(nn.Sigmoid())
126
+ elif activation_name == 'tanh':
127
+ self.activation_functions.append(nn.Tanh())
128
+ elif activation_name == 'elu':
129
+ self.activation_functions.append(nn.ELU())
130
+ elif activation_name == 'leakyrelu':
131
+ self.activation_functions.append(nn.LeakyReLU())
132
+ else:
133
+ raise ValueError(f"Activation function '{activation_name}' not supported.")
134
+
135
+ def forward(self, x):
136
+ for i, (layer, activation) in enumerate(zip(self.layers[:-1], self.activation_functions)):
137
+ x = activation(layer(x))
138
+ x = self.layers[-1](x) # Output layer without activation
139
+ return x
140
+
141
+ # --- Training and Evaluation Functions ---
142
+ def train_model(model, optimizer, loss_fn, x_train_tensor, y_train_tensor, x_validate_tensor, y_validate_tensor, config):
143
+ epochs = config['epochs']
144
+ batch_size = config['batch_size']
145
+ train_losses = []
146
+ val_losses = []
147
+
148
+ for epoch in range(1, epochs + 1):
149
+ model.train()
150
+ permutation = torch.randperm(x_train_tensor.size()[0])
151
+ epoch_train_loss = 0.0
152
+ for i in range(0, x_train_tensor.size()[0], batch_size):
153
+ indices = permutation[i:i + batch_size]
154
+ x_batch, y_batch = x_train_tensor[indices], y_train_tensor[indices]
155
+ optimizer.zero_grad()
156
+ y_pred = model(x_batch)
157
+ loss = loss_fn(y_pred, y_batch)
158
+ loss.backward()
159
+ optimizer.step()
160
+ epoch_train_loss += loss.item() * x_batch.size(0)
161
+ train_losses.append(epoch_train_loss / x_train_tensor.size(0))
162
+
163
+ model.eval()
164
+ with torch.no_grad():
165
+ y_val_pred = model(x_validate_tensor)
166
+ val_loss = loss_fn(y_val_pred, y_validate_tensor).item()
167
+ val_losses.append(val_loss)
168
+
169
+ if epoch % 100 == 0:
170
+ print(f'Epoch {epoch}/{epochs}, Training Loss: {train_losses[-1]:.4f}, Validation Loss: {val_loss:.4f}')
171
+ return train_losses, val_losses
172
+
173
+ def evaluate_model(model, loss_fn, x_test_tensor, y_test_tensor, x_train_tensor, y_train_tensor, x_validate_tensor, y_validate_tensor):
174
+ model.eval()
175
+ with torch.no_grad():
176
+ y_test_pred_tensor = model(x_test_tensor)
177
+ test_loss = loss_fn(y_test_pred_tensor, y_test_tensor).item()
178
+ test_mae = torch.mean(torch.abs(y_test_pred_tensor - y_test_tensor)).item()
179
+ train_mae = torch.mean(torch.abs(model(x_train_tensor) - y_train_tensor)).item()
180
+ val_mae = torch.mean(torch.abs(model(x_validate_tensor) - y_validate_tensor)).item()
181
+
182
+ print(f'Test Loss: {test_loss:.4f}')
183
+ print(f'Test MAE: {test_mae:.4f}')
184
+ return test_loss, test_mae, train_mae, val_mae, y_test_pred_tensor
185
+
186
+ # --- Main Execution ---
187
+ def main():
188
+ # Hyperparameters
189
+ config = Hyperparameters
190
+
191
+ # Generate and split data
192
+ x_values, y_values = generate_sine_data(SAMPLES)
193
+ (x_train, y_train), (x_test, y_test), (x_validate, y_validate) = split_data(x_values, y_values)
194
+ plot_data(x_train, y_train, x_test, y_test, x_validate, y_validate)
195
+ x_train_tensor, y_train_tensor, x_test_tensor, y_test_tensor, x_validate_tensor, y_validate_tensor = convert_to_tensors(
196
+ x_train, y_train, x_test, y_test, x_validate, y_validate
197
+ )
198
+
199
+ # Model, Optimizer, and Loss Function
200
+ model = DynamicSineModel(config)
201
+ optimizer = optim.Adam(model.parameters(), lr=config['learning_rate'])
202
+ loss_fn = nn.MSELoss()
203
+
204
+ # Training
205
+ train_losses, val_losses = train_model(
206
+ model, optimizer, loss_fn, x_train_tensor, y_train_tensor, x_validate_tensor, y_validate_tensor, config
207
+ )
208
+ plot_loss(train_losses, val_losses)
209
+
210
+ # Evaluation
211
+ test_loss, test_mae, train_mae, val_mae, y_test_pred_tensor = evaluate_model(
212
+ model, loss_fn, x_test_tensor, y_test_tensor, x_train_tensor, y_train_tensor, x_validate_tensor, y_validate_tensor
213
+ )
214
+ plot_mae(range(1, len(train_losses) + 1), train_mae, val_mae)
215
+ plot_predictions(x_test, y_test, y_test_pred_tensor)
216
+
217
+ if __name__ == "__main__":
218
+ main()