TrackChain / ml /scripts /train_fault_classifier.py
PRiyanshu0-1's picture
Add ml/ codebase and configs
2adb41a verified
Raw History Blame Contribute Delete
3.82 kB
"""
ml/scripts/train_fault_classifier.py
Trains the Bi-LSTM Geometry Fault Classifier (tc.v1 SOTA).
"""
import os
import sys
import argparse
from pathlib import Path
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
from ml.data.synthetic_geometry import SyntheticGeometryDataset
from ml.models.geometry.fault_classifier import BiLSTMAttention
def train(
num_samples: int = 5000,
val_samples: int = 1000,
epochs: int = 20,
batch_size: int = 64,
lr: float = 1e-3,
save_path: str = "artifacts/checkpoints/geometry/bilstm_fault_typing.pt",
):
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")
# 1. Data
train_ds = SyntheticGeometryDataset(num_samples=num_samples)
val_ds = SyntheticGeometryDataset(num_samples=val_samples)
train_loader = DataLoader(train_ds, batch_size=batch_size, shuffle=True)
val_loader = DataLoader(val_ds, batch_size=batch_size)
# 2. Model
model = BiLSTMAttention().to(device)
# 3. SOTA Loss & Optimizer
criterion = nn.CrossEntropyLoss(label_smoothing=0.1)
optimizer = optim.AdamW(model.parameters(), lr=lr, weight_decay=1e-4)
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=epochs)
# 4. Training Loop
print(f"Starting training for {epochs} epochs on {len(train_ds)} samples...")
for epoch in range(epochs):
model.train()
train_loss = 0.0
for X, y in train_loader:
X, y = X.to(device), y.to(device)
optimizer.zero_grad()
logits, _ = model(X)
loss = criterion(logits, y)
loss.backward()
optimizer.step()
train_loss += loss.item()
scheduler.step()
# Validation
model.eval()
correct = 0
total = 0
with torch.no_grad():
for X, y in val_loader:
X, y = X.to(device), y.to(device)
logits, _ = model(X)
preds = torch.argmax(logits, dim=1)
correct += (preds == y).sum().item()
total += y.size(0)
val_acc = correct / max(1, total)
print(f"Epoch {epoch+1:02d}/{epochs:02d} | Loss: {train_loss/len(train_loader):.4f} | Val Acc: {val_acc * 100:.2f}%")
# 5. Save Checkpoint
out_dir = os.path.dirname(save_path)
os.makedirs(out_dir, exist_ok=True)
torch.save(model.state_dict(), save_path)
print(f"[OK] Model saved to {save_path}")
# Mirror to ml/models/geometry/weights for unified resolution
mirror_path = "ml/models/geometry/weights/fault_classifier.pt"
os.makedirs(os.path.dirname(mirror_path), exist_ok=True)
torch.save({"model_state_dict": model.state_dict(), "temperature": 1.5}, mirror_path)
print(f"[OK] Mirrored weights to {mirror_path}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Train Bi-LSTM Geometry Classifier.")
parser.add_argument("--epochs", type=int, default=20, help="Training epochs")
parser.add_argument("--batch_size", "--batch-size", "--batch", type=int, default=64, dest="batch_size", help="Batch size")
parser.add_argument("--lr", type=float, default=1e-3, help="Learning rate")
parser.add_argument("--save_path", "--save-path", default="artifacts/checkpoints/geometry/bilstm_fault_typing.pt", dest="save_path")
parser.add_argument("--num_samples", "--num-samples", type=int, default=5000, dest="num_samples")
args = parser.parse_args()
train(
epochs=args.epochs,
batch_size=args.batch_size,
lr=args.lr,
save_path=args.save_path,
num_samples=args.num_samples,
)