MLP
PyTorch MLP: the simplest neural network. Module subclassing, optimizer, loss, training + eval loops.
PyTorch — MLP
EXAMPLE
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader, TensorDataset
# ===== Define =====
class MLP(nn.Module):
def __init__(self, in_features, hidden, num_classes):
super().__init__()
self.fc1 = nn.Linear(in_features, hidden)
self.fc2 = nn.Linear(hidden, hidden)
self.fc3 = nn.Linear(hidden, num_classes)
self.drop = nn.Dropout(0.2)
def forward(self, x):
x = F.relu(self.fc1(x))
x = self.drop(x)
x = F.relu(self.fc2(x))
return self.fc3(x) # raw logits; CE loss applies softmax
# ===== Data =====
X = torch.randn(1000, 4)
y = torch.randint(0, 3, (1000,))
Xv = torch.randn(200, 4)
yv = torch.randint(0, 3, (200,))
train_ds = TensorDataset(X, y)
val_ds = TensorDataset(Xv, yv)
train_loader = DataLoader(train_ds, batch_size=32, shuffle=True)
val_loader = DataLoader(val_ds, batch_size=64)
# ===== Setup =====
device = 'cuda' if torch.cuda.is_available() else 'cpu'
model = MLP(in_features=4, hidden=64, num_classes=3).to(device)
optim = torch.optim.Adam(model.parameters(), lr=1e-3)
loss_fn = nn.CrossEntropyLoss()
# ===== Training loop =====
def train_one_epoch():
model.train()
total_loss = 0
for Xb, yb in train_loader:
Xb = Xb.to(device); yb = yb.to(device)
optim.zero_grad()
out = model(Xb)
loss = loss_fn(out, yb)
loss.backward()
optim.step()
total_loss += loss.item() * yb.size(0)
return total_loss / len(train_loader.dataset)
@torch.no_grad()
def evaluate():
model.eval()
correct = total = 0
total_loss = 0
for Xb, yb in val_loader:
Xb = Xb.to(device); yb = yb.to(device)
out = model(Xb)
total_loss += loss_fn(out, yb).item() * yb.size(0)
pred = out.argmax(1)
correct += (pred == yb).sum().item()
total += yb.size(0)
return total_loss / total, correct / total
# ===== Run =====
best_acc = 0
for epoch in range(20):
train_loss = train_one_epoch()
val_loss, val_acc = evaluate()
if val_acc > best_acc:
best_acc = val_acc
torch.save(model.state_dict(), 'best.pt')
print(f'epoch {epoch}: train_loss={train_loss:.3f} val_loss={val_loss:.3f} val_acc={val_acc:.3f}')
# ===== Inference =====
model.load_state_dict(torch.load('best.pt'))
model.eval()
with torch.no_grad():
pred = model(Xv.to(device)).argmax(1)
# ===== Early stopping pattern =====
patience = 5
best_loss = float('inf')
bad_epochs = 0
for epoch in range(100):
train_loss = train_one_epoch()
val_loss, _ = evaluate()
if val_loss < best_loss:
best_loss = val_loss
bad_epochs = 0
torch.save(model.state_dict(), 'best.pt')
else:
bad_epochs += 1
if bad_epochs >= patience:
print('early stopping')
break
# ===== Patterns to internalise =====
# - .to(device) on model + every batch
# - zero_grad before backward; step after
# - model.train() / model.eval() toggles
# - Save the best state_dict; restore for inference
# ===== Pitfalls =====
# - Calling loss.item() inside the loop without detaching gradients (use no_grad in eval)
# - Forgetting .to(device) on a batch -> device mismatch error
# - Comparing eval / train losses without remembering dropout + bn differences
# - Mutating learning rate without a scheduler (use ReduceLROnPlateau or CosineAnnealing)
Why it matters
A PyTorch MLP is a small Module + an optimizer + a loss + a training loop. Manual loops are explicit, easy to debug, and form the basis of every more advanced model. Add early stopping + checkpointing + a scheduler when you go beyond toy datasets.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
import torch.nn as nn
mlp = nn.Sequential(
nn.Linear(784, 256), nn.ReLU(), nn.Dropout(0.3),
nn.Linear(256, 10),
)
Try it Yourself »
Discussion
Loading…