k23064919 commited on
Commit
96ddbe6
·
2 Parent(s): e9e7d3aed657fc

Merge branch 'develop' of https://github.kcl.ac.uk/K23064919/smallGroupProject into develop

Browse files
best_model.pt CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:23a4c08eaad4b40290eca84e6a8fa3e1d69bdf4312d5db6db5de96d1d8753024
3
- size 130261986
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d3c19d6a5fea8043e6fda261763b7909aaed487b83991f29ca395b2ce7c8e591
3
+ size 20532322
dataPrep/helpers/clearml_data.py CHANGED
@@ -11,7 +11,7 @@ Takes latest Data Prep ClearML task from project and reconstruct:
11
  - data loaders for both full and subset datasets
12
  - Aug settings used
13
  '''
14
- def extract_latest_data_task(project_name: str = "Small Group Project", num_workers: int = 8):
15
 
16
  # --------- Get latest Data Preparation task from ClearML ---------
17
 
 
11
  - data loaders for both full and subset datasets
12
  - Aug settings used
13
  '''
14
+ def extract_latest_data_task(project_name: str = "Small Group Project", num_workers: int = 0):
15
 
16
  # --------- Get latest Data Preparation task from ClearML ---------
17
 
dataPrep/helpers/transforms_loaders.py CHANGED
@@ -103,13 +103,15 @@ def make_dataset_loaders(dataset, seed, batch_size, test_size, aug_config, worke
103
  pin_memory=True,
104
  num_workers=workers
105
  )
 
106
 
107
  print(f"\nWorkers used in DataLoaders: {workers}\n")
108
 
109
  dataset_loaders = {
110
  "train": train_loader,
111
  "val": val_loader,
112
- "test": test_loader
 
113
  }
114
 
115
  return dataset_loaders
 
103
  pin_memory=True,
104
  num_workers=workers
105
  )
106
+ class_names = dataset.features['label'].names
107
 
108
  print(f"\nWorkers used in DataLoaders: {workers}\n")
109
 
110
  dataset_loaders = {
111
  "train": train_loader,
112
  "val": val_loader,
113
+ "test": test_loader,
114
+ "classNames": class_names
115
  }
116
 
117
  return dataset_loaders
testingModel/helpers/evaluation.py CHANGED
@@ -1,43 +1,88 @@
1
- import torch
2
- from torch.nn import CrossEntropyLoss
3
-
4
-
5
- """
6
- Evaluates a trained model on a dataloader that returns batches like:
7
- batch["image"] -> Tensor [B, 3, 256, 256]
8
- batch["label"] -> Tensor [B]
9
-
10
- Returns dict:
11
- { "accuracy": float, "loss": float }
12
- """
13
- def make_predictions(model, dataloader, device):
14
-
15
- model.eval()
16
- criterion = CrossEntropyLoss()
17
-
18
- total_loss = 0
19
- total_correct = 0
20
- total_samples = 0
21
-
22
- with torch.no_grad():
23
- for batch in dataloader:
24
-
25
- # Move tensors to device
26
- images = batch["image"].to(device)
27
- labels = batch["label"].to(device).long()
28
-
29
- # Forward pass
30
- outputs = model(images)
31
- loss = criterion(outputs, labels)
32
-
33
- total_loss += loss.item() * images.size(0)
34
- total_correct += (outputs.argmax(dim=1) == labels).sum().item()
35
- total_samples += labels.size(0)
36
-
37
- accuracy = total_correct / total_samples
38
- avg_loss = total_loss / total_samples
39
-
40
- return {
41
- "accuracy": accuracy,
42
- "loss": avg_loss,
43
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch.nn import CrossEntropyLoss
3
+ import numpy as np
4
+ import matplotlib.pyplot as plt
5
+
6
+
7
+ """
8
+ Evaluates a trained model on a dataloader that returns batches like:
9
+ batch["image"] -> Tensor [B, 3, 256, 256]
10
+ batch["label"] -> Tensor [B]
11
+ """
12
+ def make_predictions(model, dataloader, device):
13
+
14
+ model.eval()
15
+ criterion = CrossEntropyLoss()
16
+
17
+ total_loss = 0
18
+ total_correct = 0
19
+ total_samples = 0
20
+
21
+ all_preds = []
22
+ all_labels = []
23
+
24
+ with torch.no_grad():
25
+ for batch in dataloader:
26
+
27
+ # Move tensors to device
28
+ images = batch["image"].to(device)
29
+ labels = batch["label"].to(device).long()
30
+
31
+ # Forward pass
32
+ outputs = model(images)
33
+ loss = criterion(outputs, labels)
34
+ preds = outputs.argmax(dim=1)
35
+
36
+ total_loss += loss.item() * images.size(0)
37
+ total_correct += (preds == labels).sum().item()
38
+ total_samples += labels.size(0)
39
+
40
+ # Accumulate all predictions and labels
41
+ all_preds.extend(preds.tolist())
42
+ all_labels.extend(labels.tolist())
43
+
44
+ accuracy = total_correct / total_samples
45
+ avg_loss = total_loss / total_samples
46
+
47
+ return {
48
+ "accuracy": accuracy,
49
+ "loss": avg_loss,
50
+ "predictions": np.array(all_preds),
51
+ "labels": np.array(all_labels),
52
+ }
53
+
54
+
55
+ # Computes per-class accuracies
56
+ def class_accuracies(labels, preds, num_classes):
57
+ correct = np.zeros(num_classes, dtype=int)
58
+ counts = np.zeros(num_classes, dtype=int)
59
+ accuracies = np.zeros(num_classes, dtype=float)
60
+
61
+ for true, pred in zip(labels, preds):
62
+ counts[true] += 1
63
+ if true == pred:
64
+ correct[true] += 1
65
+
66
+ # Calculate accuracies
67
+ for i in range(num_classes):
68
+ if counts[i] > 0:
69
+ accuracies[i] = round(correct[i] / counts[i], 4)
70
+ else:
71
+ accuracies[i] = 0.0
72
+
73
+ return accuracies
74
+
75
+
76
+ def plot_class_accuracies(accuracies, class_names):
77
+ fig, ax = plt.subplots(figsize=(12, 6))
78
+
79
+ ax.set_title("Per-Class Accuracy")
80
+ ax.set_xlabel("Class")
81
+ ax.set_ylabel("Accuracy")
82
+ ax.set_ylim(0, 1.0)
83
+ ax.bar(class_names, accuracies)
84
+
85
+ plt.xticks(rotation=90)
86
+ plt.tight_layout()
87
+
88
+ return fig
testingModel/run_testing.py CHANGED
@@ -1,76 +1,98 @@
1
- from clearml import Task
2
- from dataPrep.helpers.clearml_data import extract_latest_data_task
3
-
4
- import torch
5
- from models.modelOne import modelOne
6
- from testingModel.helpers.evaluation import make_predictions
7
-
8
-
9
- # -------------- Load Data --------------
10
- project_name = "Small Group Project"
11
- subset_loaders, full_loaders, data_prep_metadata = extract_latest_data_task(project_name=project_name)
12
-
13
-
14
- # -------- ClearML Testing Task Setup --------
15
- testing_task = Task.init(
16
- project_name=f"{project_name}/Model Testing",
17
- task_name="Model Testing",
18
- task_type=Task.TaskTypes.testing,
19
- reuse_last_task_id=False,
20
- )
21
-
22
- # Reference the data prep task used
23
- testing_logger = testing_task.get_logger()
24
- testing_task.connect(data_prep_metadata, name="data_prep_metadata_READONLY")
25
-
26
- CLEARML_TRAINING_ID = "5bac154a885b4acbaa07d8588027bb27"
27
-
28
- # Testing parameters - Modify these when experimenting
29
- testing_config = {
30
- "model_train_id": CLEARML_TRAINING_ID,
31
- "num_classes": 39,
32
- "model_path": "best_model.pt",
33
- }
34
- testing_task.connect(testing_config)
35
-
36
- # Load the model weights from ClearML training task
37
- training_task = Task.get_task(task_id=testing_config["model_train_id"])
38
- model_artifact = training_task.artifacts.get("best_model")
39
- model_path = model_artifact.get_local_copy()
40
-
41
- # Reference training metadata
42
- training_hyperparams = training_task.get_parameters_as_dict()
43
- testing_task.connect(training_hyperparams['General'], name="training_metadata_READONLY")
44
-
45
-
46
- # -------- Rebuild the ML model --------
47
- model = modelOne()
48
- state_dict = torch.load(model_path, map_location="cpu") # Load to CPU first
49
- model.load_state_dict(state_dict)
50
- model.eval() # set dropout & batch norm layers to eval mode
51
-
52
- # Move model to GPU if available
53
- device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
54
- model.to(device)
55
-
56
-
57
- # -------------------- Test model on test set --------------------
58
- testing_logger.report_text("Starting evaluation on TEST SUBSET...\n")
59
- test_subset = subset_loaders['test']
60
-
61
- subset_results = make_predictions(model, test_subset, device)
62
-
63
-
64
- # Accuracy & Loss logging
65
- testing_logger.report_single_value(name="Test Subset Accuracy", value=subset_results["accuracy"])
66
- testing_logger.report_single_value(name="Test Subset Loss", value=subset_results["loss"])
67
-
68
-
69
- # --------- Complete -----------------
70
- print("\n------ Testing Complete ------")
71
- testing_logger.report_text(
72
- f"TEST SUBSET RESULTS:\n"
73
- f"Loss: {subset_results['loss']:.4f}\n"
74
- f"Accuracy: {subset_results['accuracy']:.4f}\n"
75
- )
76
- testing_task.close()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from clearml import Task
2
+ from dataPrep.helpers.clearml_data import extract_latest_data_task
3
+
4
+ import torch
5
+ from models.modelOne import modelOne
6
+ from models.modelTwo import BetterCNN
7
+ from testingModel.helpers.evaluation import make_predictions, class_accuracies, plot_class_accuracies
8
+
9
+
10
+ # -------------- Load Data --------------
11
+ project_name = "Small Group Project"
12
+ subset_loaders, full_loaders, data_prep_metadata = extract_latest_data_task(project_name=project_name)
13
+
14
+
15
+ # -------- ClearML Testing Task Setup --------
16
+ testing_task = Task.init(
17
+ project_name=f"{project_name}/Model Testing",
18
+ task_name="Model Testing",
19
+ task_type=Task.TaskTypes.testing,
20
+ reuse_last_task_id=False,
21
+ )
22
+
23
+ # Reference the data prep task used
24
+ testing_logger = testing_task.get_logger()
25
+ testing_task.connect(data_prep_metadata, name="data_prep_metadata_READONLY")
26
+
27
+ CLEARML_TRAINING_ID = "dca82d7c2f404c249f2e5325aaf77207"
28
+
29
+ # Testing parameters - Modify these when experimenting
30
+ testing_config = {
31
+ "model_train_id": CLEARML_TRAINING_ID,
32
+ "num_classes": 39,
33
+ "model_path": "best_model.pt",
34
+ }
35
+ testing_task.connect(testing_config)
36
+
37
+ # Load the model weights from ClearML training task
38
+ training_task = Task.get_task(task_id=testing_config["model_train_id"])
39
+ model_artifact = training_task.artifacts.get("best_model")
40
+ model_path = model_artifact.get_local_copy()
41
+
42
+ # Reference training metadata
43
+ training_hyperparams = training_task.get_parameters_as_dict()
44
+ testing_task.connect(training_hyperparams['General'], name="training_metadata_READONLY")
45
+
46
+
47
+ # -------- Rebuild the ML model --------
48
+ model = BetterCNN(noOfClasses=testing_config["num_classes"])
49
+ state_dict = torch.load(model_path, map_location="cpu") # Load to CPU first
50
+ model.load_state_dict(state_dict)
51
+ model.eval() # set dropout & batch norm layers to eval mode
52
+
53
+ # Move model to GPU if available
54
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
55
+ model.to(device)
56
+
57
+
58
+ # -------------------- Test model on test set --------------------
59
+ testing_logger.report_text("Starting evaluation on TEST SUBSET...\n")
60
+ test_subset = subset_loaders['test']
61
+
62
+ subset_results = make_predictions(model, test_subset, device)
63
+
64
+
65
+ # Accuracy & Loss logging
66
+ testing_logger.report_single_value(name="Test Subset Accuracy", value=subset_results["accuracy"])
67
+ testing_logger.report_single_value(name="Test Subset Loss", value=subset_results["loss"])
68
+
69
+ # Compute per-class accuracy
70
+ preds = subset_results["predictions"]
71
+ labels = subset_results["labels"]
72
+ class_acc = class_accuracies(
73
+ labels,
74
+ preds,
75
+ num_classes=testing_config["num_classes"]
76
+ )
77
+
78
+ # Plot with formatted class names
79
+ class_names = subset_loaders['classNames']
80
+ formatted_class_names = [" ".join(name.replace('_', ' ').split()) for name in class_names]
81
+ acc_fig = plot_class_accuracies(class_acc, formatted_class_names)
82
+
83
+ # Log accuracies plot to ClearML
84
+ testing_logger.report_matplotlib_figure(
85
+ title="Subset Per-Class Accuracy",
86
+ series="Class Accuracy",
87
+ figure=acc_fig
88
+ )
89
+
90
+
91
+ # --------- Complete -----------------
92
+ print("\n------ Testing Complete ------")
93
+ testing_logger.report_text(
94
+ f"TEST SUBSET RESULTS:\n"
95
+ f"Loss: {subset_results['loss']:.4f}\n"
96
+ f"Accuracy: {subset_results['accuracy']:.4f}\n"
97
+ )
98
+ testing_task.close()
trainingModel/run_training.py CHANGED
@@ -1,8 +1,8 @@
1
- import os
2
  from clearml import Task
3
  from dataPrep.helpers.clearml_data import extract_latest_data_task
4
 
5
  import torch
 
6
  from models.modelTwo import BetterCNN
7
  from trainingModel.helpers.Training import train_model
8
 
 
 
1
  from clearml import Task
2
  from dataPrep.helpers.clearml_data import extract_latest_data_task
3
 
4
  import torch
5
+ from models.modelOne import modelOne
6
  from models.modelTwo import BetterCNN
7
  from trainingModel.helpers.Training import train_model
8