def eval_metrics_one_dataset(model, test_dataloader, device, k_vals, opt):
    """
    Compute evaluation metrics on test-dataset, e.g. NMI, F1 and Recall @ k.

    Args:
        model:              PyTorch network, network to compute evaluation metrics for.
        test_dataloader:    PyTorch Dataloader, dataloader for test dataset, should have no shuffling and correct processing.
        device:             torch.device, Device to run inference on.
        k_vals:             list of int, Recall values to compute
        opt:                argparse.Namespace, contains all training-specific parameters.
    Returns:
        F1 score (float), NMI score (float), recall_at_k (list of float), data embedding (np.ndarray)
    """
    torch.cuda.empty_cache()

    _ = model.eval()
    n_classes = len(test_dataloader.dataset.avail_classes)
    with torch.no_grad():
        ### For all test images, extract features
        target_labels, feature_coll = [], []
        final_iter = tqdm(
            test_dataloader, desc="Computing Evaluation Metrics..."
        )
        image_paths = [x[0] for x in test_dataloader.dataset.image_list]
        for idx, inp in enumerate(final_iter):
            input_img, target = inp[-1], inp[0]
            target_labels.extend(target.numpy().tolist())
            out = model(input_img.to(device), feature=True)
            feature_coll.extend(out.cpu().detach().numpy().tolist())
        # pdb.set_trace()
        target_labels = np.hstack(target_labels).reshape(-1, 1)
        feature_coll = np.vstack(feature_coll).astype("float32")

        torch.cuda.empty_cache()

        ### Set Faiss CPU Cluster index
        cpu_cluster_index = faiss.IndexFlatL2(feature_coll.shape[-1])
        kmeans = faiss.Clustering(feature_coll.shape[-1], n_classes)
        kmeans.niter = 20
        kmeans.min_points_per_centroid = 1
        kmeans.max_points_per_centroid = 1000000000

        ### Train Kmeans
        kmeans.train(feature_coll, cpu_cluster_index)
        computed_centroids = faiss.vector_float_to_array(
            kmeans.centroids
        ).reshape(n_classes, feature_coll.shape[-1])

        ### Assign feature points to clusters
        faiss_search_index = faiss.IndexFlatL2(computed_centroids.shape[-1])
        faiss_search_index.add(computed_centroids)
        _, model_generated_cluster_labels = faiss_search_index.search(
            feature_coll, 1
        )

        ### Compute NMI
        NMI = metrics.cluster.normalized_mutual_info_score(
            model_generated_cluster_labels.reshape(-1),
            target_labels.reshape(-1),
        )

        ### Recover max(k_vals) nehbours to use for recall computation
        faiss_search_index = faiss.IndexFlatL2(feature_coll.shape[-1])
        faiss_search_index.add(feature_coll)
        _, k_closest_points = faiss_search_index.search(
            feature_coll, int(np.max(k_vals) + 1)
        )
        k_closest_classes = target_labels.reshape(-1)[k_closest_points[:, 1:]]
        print("computing recalls")
        ### Compute Recall
        recall_all_k = []
        for k in k_vals:
            recall_at_k = np.sum(
                [
                    1
                    for target, recalled_predictions in zip(
                        target_labels, k_closest_classes
                    )
                    if target in recalled_predictions[:k]
                ]
            ) / len(target_labels)
            recall_all_k.append(recall_at_k)
        print("finished recalls")
        print("computing F1")
        ### Compute F1 Score
        F1 = 0
        # F1 = f1_score(model_generated_cluster_labels, target_labels, feature_coll, computed_centroids)
        print("finished computing f1")

    return F1, NMI, recall_all_k, feature_coll

    Compute evaluation metrics on test-dataset, e.g. NMI, F1 and Recall @ k.

    Args:
        model:              PyTorch network, network to compute evaluation metrics for.
        test_dataloader:    PyTorch Dataloader, dataloader for test dataset, should have no shuffling and correct processing.
        device:             torch.device, Device to run inference on.
        k_vals:             list of int, Recall values to compute
        opt:                argparse.Namespace, contains all training-specific parameters.
    Returns:
        F1 score (float), NMI score (float), recall_at_k (list of float), data embedding (np.ndarray)
    
examples/pytorch/hilander/PSS/Smooth_AP/src/auxiliaries.py