|
1 | | -""" |
2 | | -A simple feedforward neural network in PyTorch to illustrate |
3 | | -the basic features of a neural network. |
4 | | -
|
5 | | -Dev only: |
6 | | -- Data: add random noise to a time series |
7 | | -- Model: a tiny neural network with one hidden layer, using PyTorch nn.Module |
8 | | -- Training setup: mean squared error loss and Adam optimizer |
9 | | -- Training loop: runs for a fixed number of epochs, printing loss occasionally |
10 | | -- Save the model to disk after training |
11 | | -
|
12 | | -Production: |
13 | | -
|
14 | | -- Load the trained model |
15 | | -- Test the model for the argument given by the student (infer value and compare to underlying 'True' function) |
16 | | -
|
17 | | -""" |
18 | | - |
19 | | -import torch |
20 | | -import torch.nn as nn |
21 | | -import torch.optim as optim |
22 | | -import matplotlib.pyplot as plt |
23 | | - |
24 | | -from lf_toolkit.evaluation import Result, Params |
25 | | - |
26 | | -from pathlib import Path |
27 | | -import os |
28 | | - |
29 | | -# Setup paths for saving/loading model and data |
30 | | -BASE_DIR = Path(__file__).resolve().parent |
31 | | -MODEL_DIR = Path(os.environ.get("MODEL_DIR", BASE_DIR / "storage")) |
32 | | -MODEL_DIR.mkdir(parents=True, exist_ok=True) |
33 | | -MODEL_PATH = MODEL_DIR / "basic_nn.pt" |
34 | | - |
35 | | -def f(x): |
36 | | - """Target function with noise (sine wave).""" |
37 | | - return torch.sin(x) |
38 | | - |
39 | | -def x_on_model(v, dev): |
40 | | - """ Helper: put scalar value on same device as model. """ |
41 | | - return torch.tensor([[v]], device=dev, dtype=torch.float32) |
42 | | - |
43 | | -class TinyNet(nn.Module): |
44 | | - """A tiny feedforward neural network.""" |
45 | | - def __init__(self): |
46 | | - super().__init__() |
47 | | - self.hidden = nn.Linear(1, 16) |
48 | | - self.act = nn.Tanh() |
49 | | - self.out = nn.Linear(16, 1) |
50 | | - |
51 | | - def forward(self, x): |
52 | | - return self.out(self.act(self.hidden(x))) |
53 | | - |
54 | | -def train_model(device): |
55 | | - torch.manual_seed(0) |
56 | | - x = torch.linspace(-2*torch.pi, 2*torch.pi, 200).unsqueeze(1).to(device) |
57 | | - y = (f(x) + 0.1*torch.randn_like(x)).to(device) |
58 | | - |
59 | | - model = TinyNet().to(device) |
60 | | - loss_fn = nn.MSELoss() |
61 | | - opt = optim.Adam(model.parameters(), lr=0.01) |
62 | | - |
63 | | - for epoch in range(2000): |
64 | | - y_pred = model(x) |
65 | | - loss = loss_fn(y_pred, y) |
66 | | - opt.zero_grad() |
67 | | - loss.backward() |
68 | | - opt.step() |
69 | | - if epoch % 400 == 0: |
70 | | - print(f"Epoch {epoch}: loss={loss.item():.4f}") |
71 | | - |
72 | | - return model |
73 | | - |
74 | | -def run(response, answer, params: Params) -> Result: |
75 | | - print("GPU") if torch.backends.mps.is_available() else print("CPU") |
76 | | - device = torch.device("mps" if torch.backends.mps.is_available() else "cpu") |
77 | | - |
78 | | - refresh = params.get("refresh", False) |
79 | | - if refresh: |
80 | | - model = train_model(device) |
81 | | - MODEL_DIR.mkdir(parents=True, exist_ok=True) |
82 | | - torch.save(model.state_dict(), MODEL_PATH) |
83 | | - |
84 | | - else: |
85 | | - model = TinyNet().to(device) |
86 | | - model.load_state_dict(torch.load(MODEL_PATH, map_location=device)) |
87 | | - model.eval() |
88 | | - |
89 | | - with torch.no_grad(): |
90 | | - # For now just test one point |
91 | | - x_val = x_on_model(float(response), device) |
92 | | - y_pred = model(x_val).cpu().item() |
93 | | - |
94 | | - absolute_tolerance = params.get("absolute_tolerance", 0.1) |
95 | | - y_true = f(torch.tensor([[float(response)]])).item() |
96 | | - diff = abs(y_pred - y_true) |
97 | | - is_correct=diff < absolute_tolerance |
98 | | - return Result(is_correct=is_correct,feedback_items=[("general",f"Model({response}) = {y_pred:.4f}, f({response}) = {y_true:.4f} (this is the 'true' value), Diff = {diff:.4f} (tolerance {absolute_tolerance}). Valid model: {is_correct}")]) |
99 | | - |
100 | | -# --- runnable code only executes if script is run directly --- |
101 | | - |
102 | | -if __name__ == "__main__": |
103 | | - |
104 | | - result = run("some_response", "some_answer", Params()) |
105 | | - print(result) |
106 | | - |
107 | | -""" # 5. Plot results (eval mode, extended domain) |
108 | | - with torch.no_grad(): |
109 | | - # Make domain twice as wide as training range |
110 | | - x_plot = torch.linspace(2*x.min().item(), 2*x.max().item(), 800, device=x.device).unsqueeze(1) |
111 | | - y_plot = model(x_plot) |
112 | | -
|
113 | | - plt.scatter(x.cpu(), y.cpu(), s=10, label="Data") |
114 | | - plt.plot(x_plot.cpu(), y_plot.cpu(), color="red", label="Model") |
115 | | - plt.legend() |
116 | | - plt.show() """ |
| 1 | +# """ |
| 2 | +# A simple feedforward neural network in PyTorch to illustrate |
| 3 | +# the basic features of a neural network. |
| 4 | +# |
| 5 | +# Dev only: |
| 6 | +# - Data: add random noise to a time series |
| 7 | +# - Model: a tiny neural network with one hidden layer, using PyTorch nn.Module |
| 8 | +# - Training setup: mean squared error loss and Adam optimizer |
| 9 | +# - Training loop: runs for a fixed number of epochs, printing loss occasionally |
| 10 | +# - Save the model to disk after training |
| 11 | +# |
| 12 | +# Production: |
| 13 | +# |
| 14 | +# - Load the trained model |
| 15 | +# - Test the model for the argument given by the student (infer value and compare to underlying 'True' function) |
| 16 | +# |
| 17 | +# """ |
| 18 | +# |
| 19 | +# import torch |
| 20 | +# import torch.nn as nn |
| 21 | +# import torch.optim as optim |
| 22 | +# import matplotlib.pyplot as plt |
| 23 | +# |
| 24 | +# from lf_toolkit.evaluation import Result, Params |
| 25 | +# |
| 26 | +# from pathlib import Path |
| 27 | +# import os |
| 28 | +# |
| 29 | +# # Setup paths for saving/loading model and data |
| 30 | +# BASE_DIR = Path(__file__).resolve().parent |
| 31 | +# MODEL_DIR = Path(os.environ.get("MODEL_DIR", BASE_DIR / "storage")) |
| 32 | +# MODEL_DIR.mkdir(parents=True, exist_ok=True) |
| 33 | +# MODEL_PATH = MODEL_DIR / "basic_nn.pt" |
| 34 | +# |
| 35 | +# def f(x): |
| 36 | +# """Target function with noise (sine wave).""" |
| 37 | +# return torch.sin(x) |
| 38 | +# |
| 39 | +# def x_on_model(v, dev): |
| 40 | +# """ Helper: put scalar value on same device as model. """ |
| 41 | +# return torch.tensor([[v]], device=dev, dtype=torch.float32) |
| 42 | +# |
| 43 | +# class TinyNet(nn.Module): |
| 44 | +# """A tiny feedforward neural network.""" |
| 45 | +# def __init__(self): |
| 46 | +# super().__init__() |
| 47 | +# self.hidden = nn.Linear(1, 16) |
| 48 | +# self.act = nn.Tanh() |
| 49 | +# self.out = nn.Linear(16, 1) |
| 50 | +# |
| 51 | +# def forward(self, x): |
| 52 | +# return self.out(self.act(self.hidden(x))) |
| 53 | +# |
| 54 | +# def train_model(device): |
| 55 | +# torch.manual_seed(0) |
| 56 | +# x = torch.linspace(-2*torch.pi, 2*torch.pi, 200).unsqueeze(1).to(device) |
| 57 | +# y = (f(x) + 0.1*torch.randn_like(x)).to(device) |
| 58 | +# |
| 59 | +# model = TinyNet().to(device) |
| 60 | +# loss_fn = nn.MSELoss() |
| 61 | +# opt = optim.Adam(model.parameters(), lr=0.01) |
| 62 | +# |
| 63 | +# for epoch in range(2000): |
| 64 | +# y_pred = model(x) |
| 65 | +# loss = loss_fn(y_pred, y) |
| 66 | +# opt.zero_grad() |
| 67 | +# loss.backward() |
| 68 | +# opt.step() |
| 69 | +# if epoch % 400 == 0: |
| 70 | +# print(f"Epoch {epoch}: loss={loss.item():.4f}") |
| 71 | +# |
| 72 | +# return model |
| 73 | +# |
| 74 | +# def run(response, answer, params: Params) -> Result: |
| 75 | +# print("GPU") if torch.backends.mps.is_available() else print("CPU") |
| 76 | +# device = torch.device("mps" if torch.backends.mps.is_available() else "cpu") |
| 77 | +# |
| 78 | +# refresh = params.get("refresh", False) |
| 79 | +# if refresh: |
| 80 | +# model = train_model(device) |
| 81 | +# MODEL_DIR.mkdir(parents=True, exist_ok=True) |
| 82 | +# torch.save(model.state_dict(), MODEL_PATH) |
| 83 | +# |
| 84 | +# else: |
| 85 | +# model = TinyNet().to(device) |
| 86 | +# model.load_state_dict(torch.load(MODEL_PATH, map_location=device)) |
| 87 | +# model.eval() |
| 88 | +# |
| 89 | +# with torch.no_grad(): |
| 90 | +# # For now just test one point |
| 91 | +# x_val = x_on_model(float(response), device) |
| 92 | +# y_pred = model(x_val).cpu().item() |
| 93 | +# |
| 94 | +# absolute_tolerance = params.get("absolute_tolerance", 0.1) |
| 95 | +# y_true = f(torch.tensor([[float(response)]])).item() |
| 96 | +# diff = abs(y_pred - y_true) |
| 97 | +# is_correct=diff < absolute_tolerance |
| 98 | +# return Result(is_correct=is_correct,feedback_items=[("general",f"Model({response}) = {y_pred:.4f}, f({response}) = {y_true:.4f} (this is the 'true' value), Diff = {diff:.4f} (tolerance {absolute_tolerance}). Valid model: {is_correct}")]) |
| 99 | +# |
| 100 | +# # --- runnable code only executes if script is run directly --- |
| 101 | +# |
| 102 | +# if __name__ == "__main__": |
| 103 | +# |
| 104 | +# result = run("some_response", "some_answer", Params()) |
| 105 | +# print(result) |
| 106 | +# |
| 107 | +# """ # 5. Plot results (eval mode, extended domain) |
| 108 | +# with torch.no_grad(): |
| 109 | +# # Make domain twice as wide as training range |
| 110 | +# x_plot = torch.linspace(2*x.min().item(), 2*x.max().item(), 800, device=x.device).unsqueeze(1) |
| 111 | +# y_plot = model(x_plot) |
| 112 | +# |
| 113 | +# plt.scatter(x.cpu(), y.cpu(), s=10, label="Data") |
| 114 | +# plt.plot(x_plot.cpu(), y_plot.cpu(), color="red", label="Model") |
| 115 | +# plt.legend() |
| 116 | +# plt.show() """ |
0 commit comments