""" Tahap 5: Testing Kata Script ini menguji model yang sudah dilatih untuk mengenali kata. Sesuai README: - Input: dataset/test/kata/ - Model: models/kata/model_kata.pt - Output: - Confusion Matrix - Classification Report - Accuracy - Precision - Recall - F1 Score """ import sys import json import numpy as np from pathlib import Path import argparse import torch from sklearn.metrics import ( classification_report, confusion_matrix, accuracy_score, precision_recall_fscore_support ) import matplotlib.pyplot as plt import seaborn as sns # Add parent directory to path sys.path.insert(0, str(Path(__file__).parent.parent)) from utils.config import ( TEST_DIR, MODEL_DIR, BATCH_SIZE, OUTPUT_DIR, CONFUSION_MATRIX_DIR, CLASSIFICATION_REPORT_DIR ) class KataDataset: """Dataset untuk kata.""" def __init__(self, data_dir: Path): self.data_dir = data_dir / "test" / "kata" self.samples = [] self.labels = [] self.label_to_idx = {} self._load_data() def _load_data(self): if not self.data_dir.exists(): return label_dirs = sorted([d for d in self.data_dir.iterdir() if d.is_dir()]) self.label_to_idx = {d.name: idx for idx, d in enumerate(label_dirs)} for label_dir in label_dirs: label_idx = self.label_to_idx[label_dir.name] for npy_file in label_dir.glob("*.npy"): try: sequence = np.load(npy_file) self.samples.append(sequence) self.labels.append(label_idx) except: pass def __len__(self): return len(self.samples) def load_model(model_path: Path, device: str): model = torch.load(model_path, map_location=device, weights_only=False) model.eval() return model def plot_confusion_matrix(cm, labels, output_path, title): plt.figure(figsize=(14, 12)) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=labels, yticklabels=labels) plt.title(title) plt.ylabel('True Label') plt.xlabel('Predicted Label') plt.tight_layout() plt.savefig(output_path, dpi=150) plt.close() print(f"Confusion matrix saved to: {output_path}") def test_kata(data_dir: Path, model_dir: Path, output_dir: Path): """Testing model kata.""" device = 'cuda' if torch.cuda.is_available() else 'cpu' print(f"Using device: {device}") # Load model model_path = model_dir / "kata" / "model_kata.pt" encoder_path = model_dir / "kata" / "label_encoder.json" if not model_path.exists(): print(f"Error: Model tidak ditemukan di {model_path}") return if not encoder_path.exists(): print(f"Error: Label encoder tidak ditemukan di {encoder_path}") return print(f"\nLoading model from: {model_path}") model = load_model(model_path, device) with open(encoder_path, 'r') as f: encoder_data = json.load(f) idx_to_label = encoder_data["idx_to_label"] # Load test dataset print(f"Loading test dataset from: {data_dir}") dataset = KataDataset(data_dir) print(f"Test samples: {len(dataset)}") if len(dataset) == 0: print("No test samples found!") return # Prepare data sequences = torch.FloatTensor(np.array(dataset.samples)) true_labels = np.array(dataset.labels) # Predict print("\nGenerating predictions...") model.eval() predictions = [] with torch.no_grad(): for i in range(0, len(sequences), BATCH_SIZE): batch = sequences[i:i+BATCH_SIZE].to(device) outputs = model(batch) _, preds = torch.max(outputs, 1) predictions.extend(preds.cpu().numpy().tolist()) # Calculate metrics accuracy = accuracy_score(true_labels, predictions) precision, recall, f1, _ = precision_recall_fscore_support( true_labels, predictions, average='weighted', zero_division=0 ) # Confusion matrix cm = confusion_matrix(true_labels, predictions) # Classification report label_names = [idx_to_label[str(i)] for i in range(len(idx_to_label))] report = classification_report( true_labels, predictions, target_names=label_names, zero_division=0 ) # Print results print("\n" + "="*60) print("TESTING KATA MODEL") print("="*60) print(f"\nAccuracy: {accuracy*100:.2f}%") print(f"Precision: {precision*100:.2f}%") print(f"Recall: {recall*100:.2f}%") print(f"F1-Score: {f1*100:.2f}%") print("\n" + "-"*60) print("Classification Report:") print("-"*60) print(report) # Save confusion matrix cm_path = CONFUSION_MATRIX_DIR / "kata_confusion_matrix.png" plot_confusion_matrix(cm, label_names, cm_path, "Kata Confusion Matrix") # Save classification report report_path = CLASSIFICATION_REPORT_DIR / "kata_classification_report.txt" with open(report_path, 'w') as f: f.write("Classification Report - Kata\n") f.write("="*60 + "\n\n") f.write(f"Accuracy: {accuracy*100:.2f}%\n") f.write(f"Precision: {precision*100:.2f}%\n") f.write(f"Recall: {recall*100:.2f}%\n") f.write(f"F1-Score: {f1*100:.2f}%\n\n") f.write("-"*60 + "\n") f.write(report) print(f"Classification report saved to: {report_path}") # Save results JSON results = { "category": "kata", "accuracy": float(accuracy), "precision": float(precision), "recall": float(recall), "f1_score": float(f1), "num_test_samples": len(dataset), "num_classes": len(label_names) } results_path = output_dir / "kata_test_results.json" with open(results_path, 'w') as f: json.dump(results, f, indent=2) print(f"Results saved to: {results_path}") print("\n" + "="*60) print("TESTING COMPLETE") print("="*60) return results def main(): """Main entry point.""" parser = argparse.ArgumentParser(description="Testing model kata") parser.add_argument("--data-dir", type=str, default=None) parser.add_argument("--model-dir", type=str, default=None) parser.add_argument("--output-dir", type=str, default=None) args = parser.parse_args() data_dir = Path(args.data_dir) if args.data_dir else TEST_DIR.parent model_dir = Path(args.model_dir) if args.model_dir else MODEL_DIR output_dir = Path(args.output_dir) if args.output_dir else OUTPUT_DIR print("="*60) print("TAHAP 5: TESTING KATA") print("="*60) test_kata(data_dir, model_dir, output_dir) if __name__ == "__main__": main()