Skip to content

Commit fe60955

Browse files
committed
Create project2pytorch.py
1 parent f1d5093 commit fe60955

File tree

1 file changed

+136
-0
lines changed

1 file changed

+136
-0
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import math
2+
import numpy as np
3+
import torch
4+
import torch.nn as nn
5+
from torch.utils.data import TensorDataset, DataLoader
6+
import matplotlib.pyplot as plt
7+
8+
# ----------------------------
9+
# Problem setup: Runge function
10+
# ----------------------------
11+
def runge(x):
12+
# x: numpy array
13+
return 1.0 / (1.0 + 25.0 * x**2)
14+
15+
# ----------------------------
16+
# MLP: two hidden layers
17+
# ----------------------------
18+
class MLP(nn.Module):
19+
def __init__(self, in_dim=1, hidden1=64, hidden2=64, out_dim=1):
20+
super().__init__()
21+
self.net = nn.Sequential(
22+
nn.Linear(in_dim, hidden1),
23+
nn.Sigmoid(),
24+
nn.Linear(hidden1, hidden2),
25+
nn.Sigmoid(),
26+
nn.Linear(hidden2, out_dim),
27+
)
28+
29+
def forward(self, x):
30+
return self.net(x)
31+
32+
# ----------------------------
33+
# Training utilities
34+
# ----------------------------
35+
def train(model, loader, optimizer, loss_fn, device):
36+
model.train()
37+
running = 0.0
38+
for xb, yb in loader:
39+
xb = xb.to(device)
40+
yb = yb.to(device)
41+
optimizer.zero_grad(set_to_none=True)
42+
pred = model(xb)
43+
loss = loss_fn(pred, yb)
44+
loss.backward()
45+
optimizer.step()
46+
running += loss.item() * xb.size(0)
47+
return running / len(loader.dataset)
48+
49+
@torch.no_grad()
50+
def evaluate(model, loader, loss_fn, device):
51+
model.eval()
52+
running = 0.0
53+
for xb, yb in loader:
54+
xb = xb.to(device)
55+
yb = yb.to(device)
56+
pred = model(xb)
57+
loss = loss_fn(pred, yb)
58+
running += loss.item() * xb.size(0)
59+
return running / len(loader.dataset)
60+
61+
# ----------------------------
62+
# Main
63+
# ----------------------------
64+
def main():
65+
# Reproducibility
66+
torch.manual_seed(7)
67+
np.random.seed(7)
68+
69+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
70+
print("Device:", device)
71+
72+
# Generate training data on [-1, 1]
73+
n_train = 256
74+
X_train = np.random.uniform(-1.0, 1.0, size=(n_train, 1)).astype(np.float32)
75+
y_train = runge(X_train).astype(np.float32)
76+
77+
# Small validation set
78+
n_val = 128
79+
X_val = np.random.uniform(-1.0, 1.0, size=(n_val, 1)).astype(np.float32)
80+
y_val = runge(X_val).astype(np.float32)
81+
82+
# Torch datasets/loaders
83+
train_ds = TensorDataset(torch.from_numpy(X_train), torch.from_numpy(y_train))
84+
val_ds = TensorDataset(torch.from_numpy(X_val), torch.from_numpy(y_val))
85+
train_loader = DataLoader(train_ds, batch_size=64, shuffle=True)
86+
val_loader = DataLoader(val_ds, batch_size=128, shuffle=False)
87+
88+
# Model, loss, optimizer (Adam)
89+
model = MLP(1, 128, 128, 1).to(device)
90+
loss_fn = nn.MSELoss()
91+
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
92+
93+
# Optional: mild cosine LR schedule for smooth convergence
94+
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=1500)
95+
96+
# Train
97+
epochs = 1500
98+
best_val = math.inf
99+
best_state = None
100+
for ep in range(1, epochs + 1):
101+
tr_loss = train(model, train_loader, optimizer, loss_fn, device)
102+
val_loss = evaluate(model, val_loader, loss_fn, device)
103+
scheduler.step()
104+
105+
if val_loss < best_val:
106+
best_val = val_loss
107+
best_state = {k: v.cpu().clone() for k, v in model.state_dict().items()}
108+
109+
if ep % 100 == 0 or ep == 1 or ep == epochs:
110+
print(f"Epoch {ep:4d} | train MSE: {tr_loss:.4e} | val MSE: {val_loss:.4e}")
111+
112+
# Restore best model
113+
if best_state is not None:
114+
model.load_state_dict(best_state)
115+
116+
# Evaluate on a dense grid for plotting
117+
xs = np.linspace(-1.0, 1.0, 500, dtype=np.float32).reshape(-1, 1)
118+
ys_true = runge(xs).reshape(-1)
119+
with torch.no_grad():
120+
yhat = model(torch.from_numpy(xs).to(device)).cpu().numpy().reshape(-1)
121+
122+
# Plot
123+
plt.figure()
124+
plt.plot(xs, ys_true, label="Runge function (true)")
125+
plt.plot(xs, yhat, linestyle="--", label="Neural net (prediction)")
126+
# also scatter training points (optional)
127+
plt.scatter(X_train, y_train, s=10, alpha=0.3, label="Train samples")
128+
plt.xlabel("x")
129+
plt.ylabel("f(x)")
130+
plt.title("Approximating the Runge function with a 2-hidden-layer MLP (Adam)")
131+
plt.legend()
132+
plt.tight_layout()
133+
plt.show()
134+
135+
if __name__ == "__main__":
136+
main()

0 commit comments

Comments
 (0)