240 lines
6.6 KiB
Python
240 lines
6.6 KiB
Python
"""
|
|
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()
|