Files
kelompok3-deeplearning/experiments/06_cnn_deeper.py
T

198 lines
7.1 KiB
Python

"""
Tugas 2 (Week 2): Eksplorasi Kedalaman Arsitektur CNN
Memodifikasi CNN dengan menambahkan satu blok Conv2D + MaxPooling ketiga.
Mengevaluasi:
1. Apakah model yang lebih dalam menghasilkan akurasi lebih tinggi?
2. Bagaimana perbandingan waktu training per epoch?
3. Catatan temuan dalam satu paragraf analitis.
"""
import os
import sys
import time
# Sembunyikan log TensorFlow yang berlebihan
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
# Tambahkan root directory ke path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from src.data_loader import load_mnist_for_cnn
from src.models import create_cnn_mnist_model, create_deep_cnn_mnist_model
from src.utils import EpochTimerCallback, ensure_dir
def main():
SEED = 42
EPOCHS = 10
BATCH_SIZE = 128
VAL_SPLIT = 0.1
tf.random.set_seed(SEED)
np.random.seed(SEED)
print("=" * 75)
print("TUGAS 2: EKSPLORASI KEDALAMAN ARSITEKTUR CNN (2 BLOK vs 3 BLOK)")
print("=" * 75)
# 1. Load Data
print("\nMemuat dataset MNIST...")
(x_train, y_train), (x_test, y_test) = load_mnist_for_cnn()
architectures = [
{
"name": "CNN 2 Blok (Baseline)",
"builder": create_cnn_mnist_model,
"desc": "Conv(32) + MaxPool -> Conv(64) + MaxPool -> Dense(64)"
},
{
"name": "CNN 3 Blok (Lebih Dalam)",
"builder": create_deep_cnn_mnist_model,
"desc": "Conv(32) + MaxPool -> Conv(64) + MaxPool -> Conv(128) + MaxPool -> Dense(64)"
}
]
results = []
histories = {}
for arch in architectures:
model_name = arch["name"]
print("\n" + "=" * 75)
print(f"PELATIHAN: {model_name}")
print(f"Konfigurasi: {arch['desc']}")
print("=" * 75)
model = arch["builder"]()
model.summary()
timer_cb = EpochTimerCallback()
start_train_time = time.time()
history = model.fit(
x_train,
y_train,
validation_split=VAL_SPLIT,
epochs=EPOCHS,
batch_size=BATCH_SIZE,
verbose=1,
callbacks=[timer_cb]
)
total_train_time = time.time() - start_train_time
avg_epoch_time = float(np.mean(timer_cb.epoch_times)) if timer_cb.epoch_times else 0.0
# Evaluasi pada test set
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=0)
n_params = model.count_params()
results.append({
"name": model_name,
"params": n_params,
"test_acc": test_acc * 100,
"test_loss": test_loss,
"epoch_time": avg_epoch_time,
"total_time": total_train_time,
"model_obj": model
})
histories[model_name] = history
# Simpan model 3 blok
models_dir = os.path.join("results", "models")
ensure_dir(models_dir)
results[1]["model_obj"].save(os.path.join(models_dir, "model_cnn_3blocks.keras"))
# Tabel Perbandingan Kuantitatif
print("\n\n" + "=" * 75)
print("HASIL PERBANDINGAN: KEDALAMAN CNN PADA MNIST")
print("=" * 75)
print(f"{'Arsitektur Model':<28}{'Parameter':>14}{'Test Acc':>14}{'Waktu/Epoch':>18}")
print("-" * 75)
for r in results:
print(f"{r['name']:<28}{r['params']:>14,}{r['test_acc']:>13.2f}%{r['epoch_time']:>16.2f}s")
print("-" * 75)
diff_acc = results[1]["test_acc"] - results[0]["test_acc"]
time_increase = ((results[1]["epoch_time"] - results[0]["epoch_time"]) / results[0]["epoch_time"]) * 100
param_diff = results[1]["params"] - results[0]["params"]
print(f"Selisih Akurasi (3 Blok vs 2 Blok) : {diff_acc:+.2f}% poin")
print(f"Perubahan Waktu Training / Epoch : {time_increase:+.1f}%")
print(f"Selisih Jumlah Parameter : {param_diff:+,} parameter")
print("=" * 75)
# Visualisasi
figures_dir = os.path.join("results", "figures")
ensure_dir(figures_dir)
fig_path = os.path.join(figures_dir, "perbandingan_kedalaman_cnn.png")
fig, axes = plt.subplots(1, 2, figsize=(13, 5))
epochs_range = range(1, EPOCHS + 1)
# Plot Accuracy Perbandingan
for name, hist in histories.items():
axes[0].plot(epochs_range, hist.history["val_accuracy"], label=f"{name} (Val Acc)", linewidth=2)
axes[0].set_title("Validation Accuracy per Epoch", fontsize=12, fontweight="bold")
axes[0].set_xlabel("Epoch", fontsize=10)
axes[0].set_ylabel("Accuracy", fontsize=10)
axes[0].legend()
axes[0].grid(True, alpha=0.3)
# Bar chart perbandingan Waktu vs Akurasi
labels = [r["name"] for r in results]
accs = [r["test_acc"] for r in results]
times = [r["epoch_time"] for r in results]
x = np.arange(len(labels))
width = 0.35
ax2 = axes[1].twinx()
p1 = axes[1].bar(x - width/2, accs, width, label="Test Accuracy (%)", color="#3498db")
p2 = ax2.bar(x + width/2, times, width, label="Waktu / Epoch (s)", color="#e67e22")
axes[1].set_ylabel("Accuracy (%)", color="#3498db", fontsize=10)
ax2.set_ylabel("Detik per Epoch", color="#e67e22", fontsize=10)
axes[1].set_xticks(x)
axes[1].set_xticklabels(labels, fontsize=10)
axes[1].set_ylim(97, 100)
axes[1].set_title("Trade-off: Test Accuracy vs Waktu Training", fontsize=12, fontweight="bold")
# Nilai di atas bar
for bar in p1:
axes[1].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.05, f"{bar.get_height():.2f}%", ha='center', va='bottom', fontsize=9)
for bar in p2:
ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.2, f"{bar.get_height():.2f}s", ha='center', va='bottom', fontsize=9)
plt.tight_layout()
plt.savefig(fig_path, dpi=150)
plt.close()
print(f"\n[INFO] Grafik perbandingan tersimpan di: {fig_path}")
# Catatan Analisis Satu Paragraf (Sesuai Instruksi Tugas 2)
print("\n" + "=" * 75)
print("CATATAN TEMUAN (SATU PARAGRAF ANALISIS):")
print("=" * 75)
paragraf = (
f"Penambahan blok ketiga (Conv2D 128 filter + MaxPooling2D) pada citra MNIST (28x28 piksel) "
f"menghasilkan akurasi test sebesar {results[1]['test_acc']:.2f}% dibandingkan dengan {results[0]['test_acc']:.2f}% "
f"pada baseline 2 blok (selisih {diff_acc:+.2f}% poin), namun meningkatkan waktu komputasi training "
f"sebesar {time_increase:+.1f}% per epoch ({results[1]['epoch_time']:.2f}s vs {results[0]['epoch_time']:.2f}s) "
f"serta menambah kompleksitas parameter. Pada dataset sederhana dengan resolusi spasial kecil seperti MNIST, "
f"tiga kali operasi pooling mereduksi resolusi spasial representasi secara drastis (hingga 3x3 piksel), "
f"sehingga arsitektur yang lebih dalam mengalami fenomena diminishing returns di mana penambahan layer "
f"tidak memberikan peningkatan performa yang sebanding dengan biaya komputasi yang dikeluarkan; "
f"oleh karena itu, arsitektur CNN 2 blok tetap menjadi pilihan paling optimal dan efisien untuk MNIST."
)
print(paragraf)
print("=" * 75 + "\n")
if __name__ == "__main__":
main()