JobAnalyze_6k / model /Model.py
akshaybabu06's picture
Files
ab331fa verified
Raw
History Blame Contribute Delete
3.95 kB
"""
Model.py - DO NOT RUN THIS SCRIPT!
Run this script, prep/data_prep.py and the notebooks through pipeline.py
"""
import json
import torch
import numpy as np
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
torch.manual_seed(42)
DATA_DIR = Path(__file__).resolve().parent / 'prep'
assert (DATA_DIR / 'prepared_data.npz').exists(), f"Missing {DATA_DIR / 'prepared_data.npz'}"
assert (DATA_DIR / 'label_vocab.json').exists(), f"Missing {DATA_DIR / 'label_vocab.json'}"
data = np.load(DATA_DIR / 'prepared_data.npz')
X_train = data['X_train']
X_test = data['X_test']
y_train = data['y_train']
y_test = data['y_test']
with open(DATA_DIR / 'label_vocab.json', encoding='utf-8') as f:
VOCAB = json.load(f)
DIM = X_train.shape[1]
NUM_LABELS = len(VOCAB)
class SkillData(Dataset):
def __init__(self, X, y) -> torch.tensor:
self.X = torch.tensor(X, dtype=torch.float32)
self.y = torch.tensor(y, dtype=torch.float32)
def __len__(self) -> len:
return len(self.X)
def __getitem__(self, idx) -> int:
return self.X[idx], self.y[idx]
train_ds = SkillData(X_train, y_train)
test_ds = SkillData(X_test, y_test)
train_loader = DataLoader(train_ds, batch_size=64, shuffle=True)
test_loader = DataLoader(test_ds, batch_size=64, shuffle=False)
class SkillClassifier(nn.Module):
def __init__(self, input_dim, num_labels, hidden_dim=32, dropout=0.3) -> None:
super().__init__()
self.net = nn.Sequential(
nn.Linear(input_dim, hidden_dim),
nn.ReLU(),
nn.Dropout(dropout),
nn.Linear(hidden_dim, num_labels)
)
def forward(self, x) -> float:
return self.net(x)
jobanalyze_6k = SkillClassifier(DIM, NUM_LABELS)
total_params = sum(p.numel() for p in jobanalyze_6k.parameters())
trainable_params = sum(p.numel() for p in jobanalyze_6k.parameters() if p.requires_grad)
print(f"Total Parameters: {total_params:,}")
print(f"Trainable Parameters: {trainable_params:,}")
pos_counts = y_train.sum(axis=0)
neg_counts = len(y_train) - pos_counts
pos_weight = torch.tensor(neg_counts / (pos_counts + 1e-6), dtype=torch.float32)
pos_weight = torch.clamp(pos_weight, max=10.0)
criterion = nn.BCEWithLogitsLoss(pos_weight=pos_weight)
optimizer = torch.optim.Adam(jobanalyze_6k.parameters(), lr=1e-3, weight_decay=1e-4)
EPOCHS = 300
history = {'train_loss' : [], 'test_loss' : []}
for epoch in range(1, EPOCHS + 1):
jobanalyze_6k.train()
train_losses = []
for xb, yb in train_loader:
optimizer.zero_grad()
logits = jobanalyze_6k(xb)
loss = criterion(logits, yb)
loss.backward()
optimizer.step()
train_losses.append(loss.item())
jobanalyze_6k.eval()
test_losses = []
with torch.no_grad():
for xb, yb in test_loader:
logits = jobanalyze_6k(xb)
loss = criterion(logits, yb)
test_losses.append(loss.item())
train_loss = sum(train_losses) / len(train_losses)
test_loss = sum(test_losses) / len(test_losses)
history['train_loss'].append(train_loss)
history['test_loss'].append(test_loss)
if epoch % 10 == 0 or epoch == 1:
print(f"Epoch {epoch:3d} | train_loss {train_loss:.4f} | test_loss {test_loss:.4f}")
out_dir = REPO_ROOT / 'model_out'
out_dir.mkdir(parents=True, exist_ok=True)
torch.save(jobanalyze_6k.state_dict(), out_dir / 'skill_classifier.pt')
with open(out_dir / 'training_history.json', 'w') as f:
json.dump(history, f)
print("\n Saved Model")
print(f"Final train_loss : {history['train_loss'][-1]:.4f} | "
f"Final test_loss : {history['test_loss'][-1]:.4f}")