| import os |
| import torch |
| import re |
| from collections import defaultdict |
| from sentence_transformers import SentenceTransformer, util |
| from sklearn.metrics import classification_report, confusion_matrix |
| import matplotlib.pyplot as plt |
| import seaborn as sns |
| import warnings |
| warnings.filterwarnings("ignore") |
|
|
| |
| THIS_DIR = os.path.dirname(os.path.abspath(__file__)) |
| PROJECT_ROOT = os.path.abspath(os.path.join(THIS_DIR, "..", "..")) |
|
|
| TEST_DIR_1 = os.path.join(PROJECT_ROOT, "datasets", "text_dataset", "test", "test_data") |
| TEST_DIR_2 = os.path.join(PROJECT_ROOT, "datasets", "text_dataset", "test", "expanded_test_dataset") |
| TRAIN_DIR = os.path.join(PROJECT_ROOT, "datasets", "text_dataset", "train", "train_dataset") |
| MODEL_PATH = os.path.join(THIS_DIR, "marine_text_reasoning_model_v4") |
|
|
| |
| corpus_texts = [] |
| species_labels = [] |
|
|
| for file in os.listdir(TRAIN_DIR): |
| if file.endswith(".txt") and not file.startswith("_"): |
| name = file.replace(".txt", "") |
| if name == "orca": |
| name = "killer_whale" |
| with open(os.path.join(TRAIN_DIR, file), "r", encoding="utf-8") as f: |
| corpus_texts.append(f.read().strip()) |
| species_labels.append(name) |
|
|
| |
| test_samples = [] |
|
|
| def parse_filename(filename): |
| match = re.match(r"(.+?)_test_\d+_(.+)\.txt", filename) |
| if match: |
| species = match.group(1) |
| test_type = match.group(2) |
| return species, test_type |
| else: |
| return filename.replace(".txt", ""), "Standard" |
|
|
| for test_dir in [TEST_DIR_1, TEST_DIR_2]: |
| for file in os.listdir(test_dir): |
| if not file.endswith(".txt"): |
| continue |
| if file.startswith("_"): |
| continue |
| |
| species, test_type = parse_filename(file) |
| if species == "orca": |
| species = "killer_whale" |
| |
| with open(os.path.join(test_dir, file), "r", encoding="utf-8") as f: |
| text = f.read().strip() |
| |
| if test_type == "FeatureOnly": |
| text = text.replace("[MASK]", "").replace(" ", " ") |
| |
| |
| term_to_remove = species.replace('_', ' ') |
| pattern = re.compile(re.escape(term_to_remove), re.IGNORECASE) |
| text = pattern.sub("the species", text) |
| |
| test_samples.append({ |
| "text": text, |
| "true_species": species, |
| "test_type": test_type |
| }) |
|
|
| print(f"Loaded {len(corpus_texts)} reference species.") |
| print(f"Loaded {len(test_samples)} test samples.") |
|
|
| |
| print("Loading model and encoding...") |
| model = SentenceTransformer(MODEL_PATH) |
| corpus_embeddings = model.encode(corpus_texts, convert_to_tensor=True, show_progress_bar=False) |
|
|
| query_texts = [sample["text"] for sample in test_samples] |
| query_embeddings = model.encode(query_texts, convert_to_tensor=True, show_progress_bar=True) |
|
|
| |
| correct_total = 0 |
| species_correct = defaultdict(int) |
| species_total = defaultdict(int) |
| test_type_correct = defaultdict(int) |
| test_type_total = defaultdict(int) |
|
|
| scores = util.cos_sim(query_embeddings, corpus_embeddings) |
| predictions = torch.argmax(scores, dim=1) |
|
|
| for idx, sample in enumerate(test_samples): |
| true_species = sample["true_species"] |
| test_type = sample["test_type"] |
| |
| pred_idx = predictions[idx].item() |
| pred_species = species_labels[pred_idx] |
| |
| is_correct = (pred_species == true_species) |
| |
| if is_correct: |
| correct_total += 1 |
| species_correct[true_species] += 1 |
| test_type_correct[test_type] += 1 |
| |
| species_total[true_species] += 1 |
| test_type_total[test_type] += 1 |
|
|
| |
| print("\n" + "="*50) |
| print("EVALUATION METRICS") |
| print("="*50) |
|
|
| overall_acc = (correct_total / len(test_samples)) * 100 |
| print(f"\n1. Overall Accuracy") |
| print(f"Accuracy: {overall_acc:.2f}% ({correct_total}/{len(test_samples)})\n") |
|
|
| y_true = [sample["true_species"] for sample in test_samples] |
| y_pred = [species_labels[predictions[idx].item()] for idx in range(len(test_samples))] |
|
|
| print("2. Classification Report (Precision, Recall, F1)") |
| print(classification_report(y_true, y_pred, zero_division=0)) |
|
|
| |
| cm = confusion_matrix(y_true, y_pred, labels=sorted(list(set(y_true)))) |
| plt.figure(figsize=(20, 18)) |
| sns.heatmap(cm, xticklabels=sorted(list(set(y_true))), yticklabels=sorted(list(set(y_true))), cmap="Blues", cbar=False) |
| plt.title("Text Classification Confusion Matrix") |
| plt.xlabel("Predicted") |
| plt.ylabel("Actual") |
| plt.tight_layout() |
| plt.savefig(os.path.join(os.path.dirname(__file__), "confusion_matrix.png")) |
| plt.close() |
| print("Confusion matrix saved to confusion_matrix.png") |
|
|
| print("\n2. Per-Species Accuracy") |
| for species in sorted(species_total.keys()): |
| acc = (species_correct[species] / species_total[species]) * 100 |
| print(f" - {species}: {acc:.2f}% ({species_correct[species]}/{species_total[species]})") |
|
|
| print("\n3. Accuracy by Test Type") |
| for t_type in sorted(test_type_total.keys()): |
| acc = (test_type_correct[t_type] / test_type_total[t_type]) * 100 |
| print(f" - {t_type}: {acc:.2f}% ({test_type_correct[t_type]}/{test_type_total[t_type]})") |
|
|