| import numpy as np |
|
|
| def compute_metrics(scores, labels, threshold, score_is_distance=False): |
| """ |
| If score_is_distance, lower score means more similar. (e.g. Euclidean) |
| If not, higher score means more similar. (e.g. Cosine) |
| """ |
| scores = np.asarray(scores) |
| labels = np.asarray(labels) |
| |
| if len(scores) == 0: |
| return {} |
| |
| if score_is_distance: |
| preds = (scores < threshold).astype(int) |
| else: |
| preds = (scores >= threshold).astype(int) |
| |
| tp = np.sum((preds == 1) & (labels == 1)) |
| fp = np.sum((preds == 1) & (labels == 0)) |
| tn = np.sum((preds == 0) & (labels == 0)) |
| fn = np.sum((preds == 0) & (labels == 1)) |
| |
| acc = (tp + tn) / len(labels) |
| tpr = tp / (tp + fn) if (tp + fn) > 0 else 0 |
| fpr = fp / (fp + tn) if (fp + tn) > 0 else 0 |
| precision = tp / (tp + fp) if (tp + fp) > 0 else 0 |
| recall = tpr |
| f1 = 2 * (precision * recall) / (precision + recall) if (precision + recall) > 0 else 0 |
| |
| return { |
| "accuracy": float(acc), |
| "tpr": float(tpr), |
| "fpr": float(fpr), |
| "precision": float(precision), |
| "recall": float(recall), |
| "f1": float(f1), |
| "tp": int(tp), |
| "fp": int(fp), |
| "tn": int(tn), |
| "fn": int(fn) |
| } |
|
|
| def get_confusion_matrix(scores, labels, threshold, score_is_distance=False): |
| m = compute_metrics(scores, labels, threshold, score_is_distance) |
| if not m: |
| return {} |
| return { |
| "tp": m["tp"], |
| "fp": m["fp"], |
| "tn": m["tn"], |
| "fn": m["fn"] |
| } |
|
|
| def compute_confidence(score, threshold, k=10, score_is_distance=False): |
| """ |
| Computes a calibrated confidence score in [0.5, 1.0] for the decision. |
| Uses a sigmoid transformation centered at the threshold. |
| """ |
| if score_is_distance: |
| val = threshold - score |
| else: |
| val = score - threshold |
| |
| |
| prob = 1.0 / (1.0 + np.exp(-k * val)) |
| confidence = prob if prob >= 0.5 else 1.0 - prob |
| return float(confidence) |
|
|