188 lines
5.3 KiB
Python
188 lines
5.3 KiB
Python
import numpy as np
|
|
import torch
|
|
import torch.nn as nn
|
|
import argparse
|
|
from torch.utils.data import random_split, DataLoader, TensorDataset
|
|
from torch.optim.lr_scheduler import ReduceLROnPlateau
|
|
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
|
|
import matplotlib.pyplot as plt
|
|
|
|
parser = argparse.ArgumentParser(
|
|
prog="train_nn"
|
|
)
|
|
parser.add_argument("-g", "--graph", action="store_true", default=False, help="Graph losses")
|
|
parser.add_argument("-s", "--save", default="", help="Graph save location")
|
|
args = parser.parse_args()
|
|
graph = args.graph
|
|
save = args.save
|
|
|
|
|
|
class NeuralNetwork(nn.Module):
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
self.features = nn.Sequential(
|
|
nn.Linear(8, 512),
|
|
nn.ReLU(),
|
|
nn.BatchNorm1d(512),
|
|
nn.Dropout(p=0.4),
|
|
|
|
nn.Linear(512, 256),
|
|
nn.ReLU(),
|
|
nn.BatchNorm1d(256),
|
|
nn.Dropout(p=0.4),
|
|
|
|
nn.Linear(256, 128),
|
|
nn.ReLU(),
|
|
nn.BatchNorm1d(128),
|
|
nn.Dropout(p=0.4),
|
|
|
|
nn.Linear(128, 64),
|
|
nn.ReLU(),
|
|
nn.BatchNorm1d(64),
|
|
nn.Dropout(p=0.4),
|
|
|
|
nn.Linear(64, 4)
|
|
)
|
|
|
|
def forward(self, x):
|
|
return self.features(x)
|
|
|
|
|
|
data = np.load("clean.npy")
|
|
data_x = np.append(data[:, 0:2], data[:, 6:12], axis=1) # grade, sex, ses, occupation, living, commute, sleep, absence
|
|
data_y = data[:, 2:6] # gpa, math, slovak, english
|
|
print(f"Loaded data of shape {data.shape}")
|
|
print(f"\tx: {data_x.shape}")
|
|
print(f"\ty: {data_y.shape}")
|
|
print("")
|
|
|
|
train_size = int(len(data) * 0.8)
|
|
test_size = len(data) - train_size
|
|
torch.manual_seed(42)
|
|
|
|
batch_size = 32
|
|
epochs = 200
|
|
lr = 0.001
|
|
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
|
|
dataset = TensorDataset(torch.Tensor(data_x), torch.Tensor(data_y))
|
|
train_dataset, test_dataset = random_split(dataset, [train_size, test_size])
|
|
train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
|
|
test_dataloader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
|
|
|
|
print(f"Initialized dataloaders")
|
|
print(f"\ttrain: {len(train_dataloader)} batches ({len(train_dataset)} samples)")
|
|
print(f"\ttest: {len(test_dataloader)} batches ({len(test_dataset)} samples)")
|
|
print("")
|
|
|
|
model = NeuralNetwork()
|
|
model.to(device)
|
|
|
|
print(f"Initialized model")
|
|
print(model)
|
|
print("")
|
|
|
|
loss_fn = nn.MSELoss()
|
|
optimizer = torch.optim.AdamW(model.parameters(), lr=lr, weight_decay=1e-5)
|
|
scheduler = ReduceLROnPlateau(optimizer, mode='min', patience=5, factor=0.5)
|
|
|
|
train_losses = []
|
|
test_losses = []
|
|
|
|
for epoch in range(epochs):
|
|
print(f"Epoch {epoch + 1}/{epochs}...\r", end="")
|
|
|
|
model.train()
|
|
train_loss = 0.0
|
|
|
|
for X, y in train_dataloader:
|
|
X, y = X.to(device), y.to(device)
|
|
optimizer.zero_grad()
|
|
|
|
pred = model(X)
|
|
loss = loss_fn(pred, y)
|
|
|
|
loss.backward()
|
|
optimizer.step()
|
|
|
|
train_loss += loss.item() * X.size(0)
|
|
|
|
train_loss /= len(train_dataset)
|
|
train_losses.append(train_loss)
|
|
|
|
model.eval()
|
|
test_loss = 0.0
|
|
|
|
with torch.no_grad():
|
|
for X, y in test_dataloader:
|
|
X, y = X.to(device), y.to(device)
|
|
|
|
pred = model(X)
|
|
loss = loss_fn(pred, y)
|
|
|
|
test_loss += loss.item() * X.size(0)
|
|
|
|
test_loss /= len(test_dataset)
|
|
test_losses.append(test_loss)
|
|
|
|
print(
|
|
f"Epoch {epoch + 1}/{epochs}\n"
|
|
f"Train loss: {train_loss:.4f}\n"
|
|
f"Test loss: {test_loss:.4f}\n"
|
|
)
|
|
scheduler.step(test_loss)
|
|
|
|
print(f"Average train loss: {sum(train_losses) / len(train_losses):.4f}")
|
|
print(f"Average test loss: {sum(test_losses) / len(test_losses):.4f}")
|
|
|
|
torch.save(model.state_dict(), "model.pth")
|
|
print("Model saved to model.pth")
|
|
|
|
model.eval()
|
|
all_preds = []
|
|
all_labels = []
|
|
|
|
with torch.no_grad():
|
|
for X, y in test_dataloader:
|
|
X, y = X.to(device), y.to(device)
|
|
pred = model(X)
|
|
all_preds.append(pred.argmax(dim=1).cpu().numpy())
|
|
all_labels.append(y.argmax(dim=1).cpu().numpy())
|
|
|
|
all_preds = np.concatenate(all_preds)
|
|
all_labels = np.concatenate(all_labels)
|
|
|
|
accuracy = accuracy_score(all_labels, all_preds)
|
|
precision = precision_score(all_labels, all_preds, average='weighted', zero_division=0)
|
|
recall = recall_score(all_labels, all_preds, average='weighted', zero_division=0)
|
|
f1 = f1_score(all_labels, all_preds, average='weighted')
|
|
|
|
print("\nEvaluation Metrics:")
|
|
print(f"Accuracy: {accuracy:.4f}")
|
|
print(f"Precision: {precision:.4f}")
|
|
print(f"Recall: {recall:.4f}")
|
|
print(f"F1 Score: {f1:.4f}")
|
|
|
|
if graph:
|
|
x = np.arange(1, epochs + 1, 1)
|
|
|
|
plt.figure(figsize=(8, 6))
|
|
plt.plot(x, train_losses, color="red", label="Strata trénovania")
|
|
plt.plot(x, test_losses, color="blue", label="Strata testovania")
|
|
|
|
plt.xlabel("Epocha", fontweight="bold", fontsize=14)
|
|
plt.ylabel("Strata", fontweight="bold", fontsize=14)
|
|
plt.title("Priebeh trénovania", fontsize=20)
|
|
|
|
plt.text(0.99, 0.99,
|
|
f"Presnosť: {accuracy:.4f}\nPrecíznosť: {precision:.4f}\nOdvolanie: {recall:.4f}\nF1 skóre: {f1:.4f}",
|
|
ha="right", va="top", transform=plt.gca().transAxes, fontweight="bold", fontsize=14)
|
|
|
|
plt.legend()
|
|
plt.tight_layout()
|
|
if save != "":
|
|
plt.savefig(save)
|
|
else:
|
|
plt.show()
|