From 8af733ff12040b626d5498bc9889b660d6a4e5ec Mon Sep 17 00:00:00 2001 From: rijalhabibullah Date: Sun, 19 Apr 2026 04:17:06 +0700 Subject: [PATCH] ganti api ke laravel --- api_client.py | 262 --------- ...e_leaf_cnn_classification-checkpoint.ipynb | 372 ++++++------ rice leaf diseases dataset/api_server.py | 450 --------------- .../rice_leaf_cnn_classification.ipynb | 530 +++++++----------- setup.bat | 11 +- setup.sh | 15 +- test_api.py | 51 +- web_TA/.env.example | 4 + .../Controllers/ClassificationController.php | 258 +++++++-- .../Services/PythonClassificationService.php | 130 +++++ web_TA/routes/api.php | 7 +- web_TA/scripts/rice_inference.py | 293 ++++++++++ web_TA/~/.keras/keras.json | 6 + 13 files changed, 1088 insertions(+), 1301 deletions(-) delete mode 100644 api_client.py delete mode 100644 rice leaf diseases dataset/api_server.py create mode 100644 web_TA/app/Services/PythonClassificationService.php create mode 100644 web_TA/scripts/rice_inference.py create mode 100644 web_TA/~/.keras/keras.json diff --git a/api_client.py b/api_client.py deleted file mode 100644 index 826f834..0000000 --- a/api_client.py +++ /dev/null @@ -1,262 +0,0 @@ -""" -API Client Library - Python -Untuk mengakses API Classification dari Python -""" - -import requests -import base64 -from pathlib import Path -from typing import Dict, List, Optional, Tuple -import json - - -class RiceLeafClassificationAPI: - """Client untuk Rice Leaf Disease Classification API""" - - def __init__(self, base_url: str = "http://127.0.0.1:5000"): - """ - Initialize API client - - Args: - base_url: Base URL untuk API endpoint (default: Flask API on port 5000) - """ - self.base_url = base_url - self.session = requests.Session() - self.timeout = 30 - - def test_connection(self) -> Tuple[bool, str]: - """ - Test koneksi ke API - - Returns: - Tuple (success, message) - """ - try: - response = self.session.get(f"{self.base_url}/health", timeout=10) - if response.status_code == 200: - data = response.json() - if 'status' in data: # Flask API response - return data['status'] == 'ok', data.get('message', 'Connection OK') - else: - return data.get('success', True), data.get('message', 'Connection OK') - else: - return False, f"HTTP {response.status_code}" - except Exception as e: - return False, str(e) - - def classify_image( - self, - image_path: str, - save: bool = False, - notes: Optional[str] = None - ) -> Optional[Dict]: - """ - Klasifikasi gambar - - Args: - image_path: Path ke file gambar - save: Jika True, simpan gambar ke server - notes: Catatan tambahan (hanya jika save=True) - - Returns: - Dict dengan hasil klasifikasi atau None jika error - """ - # Validasi file - image_file = Path(image_path) - if not image_file.exists(): - print(f"Error: File tidak ditemukan: {image_path}") - return None - - if not image_file.suffix.lower() in ['.jpg', '.jpeg', '.png', '.gif']: - print(f"Error: Format file tidak didukung: {image_file.suffix}") - return None - - try: - # Tentukan endpoint - endpoint = "classify" if not save else "classify" - url = f"{self.base_url}/{endpoint}" - - # Baca dan kirim file - with open(image_file, 'rb') as f: - files = {'image': f} - data = {} - if save and notes: - data['notes'] = notes - - response = self.session.post( - url, - files=files, - data=data, - timeout=self.timeout - ) - - if response.status_code == 200: - response_data = response.json() - if response_data.get('success'): - return response_data.get('data') - else: - print(f"Error: {response_data.get('message')}") - return None - else: - print(f"Error: HTTP {response.status_code}") - return None - - except Exception as e: - print(f"Error during classification: {str(e)}") - return None - - def classify_from_base64( - self, - base64_image: str, - filename: str = "image.jpg" - ) -> Optional[Dict]: - """ - Klasifikasi dari base64 string - - Args: - base64_image: Base64 encoded image string - filename: Nama file (opsional) - - Returns: - Dict dengan hasil klasifikasi - """ - try: - url = f"{self.base_url}/classify" - payload = { - "image": base64_image, - "filename": filename - } - - response = self.session.post( - url, - json=payload, - timeout=self.timeout - ) - - if response.status_code == 200: - response_data = response.json() - if response_data.get('success'): - return response_data.get('data') - else: - print(f"Error: {response_data.get('message')}") - return None - else: - print(f"Error: HTTP {response.status_code}") - return None - - except Exception as e: - print(f"Error: {str(e)}") - return None - - def batch_classify( - self, - image_paths: List[str], - save: bool = False - ) -> List[Dict]: - """ - Klasifikasi multiple gambar sekaligus - - Args: - image_paths: List path-ke-gambar - save: Simpan ke server - - Returns: - List hasil klasifikasi - """ - results = [] - total = len(image_paths) - - for idx, path in enumerate(image_paths, 1): - print(f"Processing {idx}/{total}: {Path(path).name}...", end=" ") - result = self.classify_image(path, save=save) - - if result: - print("āœ“") - results.append({ - 'image': path, - 'result': result - }) - else: - print("āœ—") - results.append({ - 'image': path, - 'result': None - }) - - return results - - def print_result(self, result: Dict): - """Print hasil klasifikasi dalam format yang dapat dibaca""" - print("\n" + "="*60) - print("KLASIFIKASI HASIL") - print("="*60) - - print(f"\nšŸŽÆ DIAGNOSIS: {result['disease_info']['name']}") - print(f" Confidence: {result['confidence']}") - print(f" Severity: {result['disease_info']['severity']}") - - print(f"\nšŸ“Š PREDIKSI SEMUA KELAS:") - for class_name, score in result['all_predictions'].items(): - percentage = f"{score*100:.2f}%" - bar = "ā–ˆ" * int(score * 20) - print(f" {class_name:20} {percentage:>8} {bar}") - - print(f"\nšŸ”¬ GEJALA:") - for symptom in result['disease_info']['symptoms']: - print(f" • {symptom}") - - print(f"\nšŸ’Š PENANGANAN:") - for treatment in result['disease_info']['treatment']: - print(f" • {treatment}") - - print("\n" + "="*60 + "\n") - - -# ============================================================================ -# CONTOH PENGGUNAAN -# ============================================================================ - -if __name__ == "__main__": - # Initialize client - api = RiceLeafClassificationAPI() - - # Test connection - print("Testing API connection...") - success, message = api.test_connection() - if success: - print(f"āœ“ {message}\n") - else: - print(f"āœ— Failed: {message}\n") - exit(1) - - # Contoh 1: Klasifikasi single image - print("Example 1: Klasifikasi single image") - print("-" * 60) - image_path = "path/to/rice_leaf.jpg" # Ganti dengan path asli - result = api.classify_image(image_path, save=True, notes="Test dari script") - if result: - api.print_result(result) - - # Contoh 2: Batch classification - print("\nExample 2: Batch classification") - print("-" * 60) - image_list = [ - "path/to/image1.jpg", - "path/to/image2.jpg", - "path/to/image3.jpg", - ] - results = api.batch_classify(image_list, save=True) - - # Summary - successful = sum(1 for r in results if r['result'] is not None) - print(f"\nBatch Summary: {successful}/{len(results)} berhasil") - - # Contoh 3: Klasifikasi dari base64 - print("\nExample 3: Klasifikasi dari base64") - print("-" * 60) - with open("image.jpg", "rb") as f: - base64_image = base64.b64encode(f.read()).decode('utf-8') - - result = api.classify_from_base64(base64_image) - if result: - api.print_result(result) diff --git a/rice leaf diseases dataset/.ipynb_checkpoints/rice_leaf_cnn_classification-checkpoint.ipynb b/rice leaf diseases dataset/.ipynb_checkpoints/rice_leaf_cnn_classification-checkpoint.ipynb index b09054c..c4d8709 100644 --- a/rice leaf diseases dataset/.ipynb_checkpoints/rice_leaf_cnn_classification-checkpoint.ipynb +++ b/rice leaf diseases dataset/.ipynb_checkpoints/rice_leaf_cnn_classification-checkpoint.ipynb @@ -2,16 +2,19 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 4, "id": "5ca735e6", "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "TensorFlow Version: 2.20.0\n", - "GPU Available: []\n" + "ename": "ModuleNotFoundError", + "evalue": "No module named 'pandas'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[4], line 3\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mos\u001b[39;00m\n\u001b[0;32m 2\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mnumpy\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mnp\u001b[39;00m\n\u001b[1;32m----> 3\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mpandas\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mpd\u001b[39;00m\n\u001b[0;32m 4\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mmatplotlib\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mpyplot\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mplt\u001b[39;00m\n\u001b[0;32m 5\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mseaborn\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01msns\u001b[39;00m\n", + "\u001b[1;31mModuleNotFoundError\u001b[0m: No module named 'pandas'" ] } ], @@ -69,7 +72,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "63695071", "metadata": {}, "outputs": [ @@ -234,6 +237,9 @@ ], "source": [ "# Set dataset path - handle both relative and absolute paths\n", + "from pathlib import Path\n", + "import re\n", + "\n", "notebook_dir = Path('.').absolute()\n", "print(f\"Notebook working directory: {notebook_dir}\\n\")\n", "\n", @@ -258,47 +264,75 @@ "\n", "classes = ['Bacterialblight', 'Brownspot', 'Leafsmut']\n", "\n", - "# Load images and labels\n", + "# Heuristic filter to remove offline augmented/duplicate files.\n", + "def is_augmented_or_duplicate(filename: str) -> bool:\n", + " name = filename.lower()\n", + " patterns = [\n", + " r'rotated',\n", + " r'\\borig\\b',\n", + " r'_orig_',\n", + " r'\\(\\d+\\)',\n", + " r'copy',\n", + " ]\n", + " return any(re.search(p, name) for p in patterns)\n", + "\n", + "# Build group id so near-duplicate name variants do not leak across splits.\n", + "def build_group_id(class_name: str, filename: str) -> str:\n", + " stem = Path(filename).stem.lower()\n", + " stem = re.sub(r'\\(\\d+\\)', '', stem)\n", + " stem = re.sub(r'\\s+', '_', stem)\n", + " stem = re.sub(r'_+', '_', stem).strip('_')\n", + " return f\"{class_name}:{stem}\"\n", + "\n", + "# Load images, labels, and group ids\n", "images = []\n", "labels = []\n", + "groups = []\n", "class_to_idx = {class_name: idx for idx, class_name in enumerate(classes)}\n", "\n", - "print(\"Loading dataset...\")\n", + "print(\"Loading dataset (with anti-leakage filtering)...\")\n", "print(\"-\" * 50)\n", "\n", "total_images = 0\n", + "total_filtered_out = 0\n", + "\n", "for class_name in classes:\n", " class_path = dataset_base_path / class_name\n", - " \n", + "\n", " if class_path.exists():\n", - " # Count images first for progress bar\n", " image_files = [f for f in class_path.glob('*') if f.suffix.lower() in ['.jpg', '.jpeg', '.png']]\n", - " \n", - " # Load images with progress bar\n", + " kept_files = [f for f in image_files if not is_augmented_or_duplicate(f.name)]\n", + " filtered_count = len(image_files) - len(kept_files)\n", + "\n", " image_count = 0\n", - " with tqdm(image_files, desc=f\"Loading {class_name}\", position=classes.index(class_name), leave=True) as pbar:\n", + " with tqdm(kept_files, desc=f\"Loading {class_name}\", position=classes.index(class_name), leave=True) as pbar:\n", " for img_file in pbar:\n", " img = cv2.imread(str(img_file))\n", " if img is not None:\n", " img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n", " images.append(img_rgb)\n", " labels.append(class_to_idx[class_name])\n", + " groups.append(build_group_id(class_name, img_file.name))\n", " image_count += 1\n", - " \n", - " print(f\" āœ“ {class_name}: {image_count} images loaded\\n\")\n", + "\n", + " print(f\" āœ“ {class_name}: {image_count} images loaded | filtered out: {filtered_count}\\n\")\n", " total_images += image_count\n", + " total_filtered_out += filtered_count\n", " else:\n", " print(f\" āœ— {class_name} folder not found!\\n\")\n", "\n", "print(\"-\" * 50)\n", - "print(f\"Total images loaded: {total_images}\\n\")\n", + "print(f\"Total images loaded: {total_images}\")\n", + "print(f\"Total files filtered out: {total_filtered_out}\\n\")\n", "\n", "# Convert to numpy arrays with proper dtype\n", "X = np.array(images)\n", - "y = np.array(labels, dtype=np.int32) # Convert to int32 for bincount\n", + "y = np.array(labels, dtype=np.int32)\n", + "group_ids = np.array(groups)\n", "\n", "print(f\"Dataset shape: {X.shape}\")\n", "print(f\"Labels shape: {y.shape}\")\n", + "print(f\"Unique group ids: {len(np.unique(group_ids))}\")\n", "if len(y) > 0:\n", " print(f\"Class distribution: {np.bincount(y)}\")\n", "else:\n", @@ -307,7 +341,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "a9a6d821", "metadata": {}, "outputs": [ @@ -364,7 +398,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "f8de0b07", "metadata": {}, "outputs": [ @@ -416,35 +450,61 @@ " resized_img = cv2.resize(img, (IMG_SIZE, IMG_SIZE))\n", " X_resized.append(resized_img)\n", "\n", - "X_resized = np.array(X_resized)\n", + "X_resized = np.array(X_resized).astype('float32')\n", "\n", - "# Normalize pixel values to [0, 1]\n", - "print(\"Normalizing pixel values...\")\n", - "X_normalized = X_resized.astype('float32') / 255.0\n", + "# MobileNetV2-specific preprocessing ([-1, 1] range)\n", + "from tensorflow.keras.applications.mobilenet_v2 import preprocess_input\n", + "print(\"Applying MobileNetV2 preprocess_input...\")\n", + "X_preprocessed = preprocess_input(X_resized)\n", "\n", - "print(f\"\\nPreprocessed image shape: {X_normalized.shape}\")\n", - "print(f\"Pixel value range: [{X_normalized.min()}, {X_normalized.max()}]\")\n", + "print(f\"\\nPreprocessed image shape: {X_preprocessed.shape}\")\n", + "print(f\"Pixel value range: [{X_preprocessed.min():.3f}, {X_preprocessed.max():.3f}]\")\n", "\n", - "# Split dataset into train, validation, and test sets\n", - "print(\"\\nSplitting dataset...\")\n", - "X_train, X_temp, y_train, y_temp = train_test_split(\n", - " X_normalized, y, test_size=0.3, random_state=42, stratify=y\n", - ")\n", + "# Split dataset into train, validation, and test sets (group-aware anti-leakage split)\n", + "print(\"\\nSplitting dataset with GROUP-AWARE strategy (anti data leakage)...\")\n", + "from sklearn.model_selection import GroupShuffleSplit\n", "\n", - "X_val, X_test, y_val, y_test = train_test_split(\n", - " X_temp, y_temp, test_size=0.5, random_state=42, stratify=y_temp\n", - ")\n", + "# 70% train, 30% temp (val+test)\n", + "gss_outer = GroupShuffleSplit(n_splits=1, test_size=0.30, random_state=42)\n", + "train_idx, temp_idx = next(gss_outer.split(X_preprocessed, y, groups=group_ids))\n", + "\n", + "X_train, X_temp = X_preprocessed[train_idx], X_preprocessed[temp_idx]\n", + "y_train, y_temp = y[train_idx], y[temp_idx]\n", + "groups_train, groups_temp = group_ids[train_idx], group_ids[temp_idx]\n", + "\n", + "# Split temp equally into val and test: 15% val, 15% test\n", + "gss_inner = GroupShuffleSplit(n_splits=1, test_size=0.50, random_state=42)\n", + "val_rel_idx, test_rel_idx = next(gss_inner.split(X_temp, y_temp, groups=groups_temp))\n", + "\n", + "X_val, X_test = X_temp[val_rel_idx], X_temp[test_rel_idx]\n", + "y_val, y_test = y_temp[val_rel_idx], y_temp[test_rel_idx]\n", + "groups_val, groups_test = groups_temp[val_rel_idx], groups_temp[test_rel_idx]\n", + "\n", + "# Leakage sanity check: these must all be zero\n", + "leak_train_val = len(set(groups_train) & set(groups_val))\n", + "leak_train_test = len(set(groups_train) & set(groups_test))\n", + "leak_val_test = len(set(groups_val) & set(groups_test))\n", "\n", "print(\"-\" * 50)\n", - "print(f\"Training set: {X_train.shape[0]} images\")\n", + "print(f\"Training set: {X_train.shape[0]} images\")\n", "print(f\"Validation set: {X_val.shape[0]} images\")\n", - "print(f\"Test set: {X_test.shape[0]} images\")\n", - "print(\"-\" * 50)" + "print(f\"Test set: {X_test.shape[0]} images\")\n", + "print(\"-\" * 50)\n", + "print(\"Leakage check (should be 0):\")\n", + "print(f\" Train ∩ Val : {leak_train_val}\")\n", + "print(f\" Train ∩ Test: {leak_train_test}\")\n", + "print(f\" Val ∩ Test : {leak_val_test}\")\n", + "print(\"-\" * 50)\n", + "\n", + "# Show class distribution after split\n", + "for split_name, split_y in [('Train', y_train), ('Val', y_val), ('Test', y_test)]:\n", + " counts = np.bincount(split_y, minlength=len(classes))\n", + " print(f\"{split_name} distribution: {counts}\")" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "eba217e2", "metadata": {}, "outputs": [ @@ -465,22 +525,21 @@ } ], "source": [ - "# Data augmentation\n", + "# Data augmentation (moderate, realistic)\n", "print(\"Configuring data augmentation...\")\n", "print(\"-\" * 50)\n", "\n", "train_datagen = ImageDataGenerator(\n", - " rotation_range=20,\n", - " width_shift_range=0.2,\n", - " height_shift_range=0.2,\n", + " rotation_range=15,\n", + " width_shift_range=0.10,\n", + " height_shift_range=0.10,\n", + " zoom_range=0.10,\n", " horizontal_flip=True,\n", - " vertical_flip=True,\n", - " zoom_range=0.2,\n", - " shear_range=0.2,\n", " fill_mode='nearest'\n", ")\n", "\n", - "val_datagen = ImageDataGenerator() # No augmentation for validation\n", + "# No augmentation for validation/test\n", + "val_datagen = ImageDataGenerator()\n", "\n", "# Convert to one-hot encoding\n", "print(\"Converting labels to one-hot encoding...\")\n", @@ -506,7 +565,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "146d5d58", "metadata": {}, "outputs": [ @@ -756,69 +815,30 @@ } ], "source": [ - "# Option 1: Build custom CNN model from scratch\n", - "print(\"Building custom CNN model...\")\n", + "# Build Transfer Learning model (recommended to reduce overfitting)\n", + "print(\"Building MobileNetV2 transfer learning model...\")\n", "print(\"-\" * 50)\n", - "print(\"Architecture:\")\n", - "print(\" - 4 Convolutional Blocks (32→64→128→256 filters)\")\n", - "print(\" - Batch Normalization & Dropout for regularization\")\n", - "print(\" - Global Average Pooling + Dense layers\")\n", - "print()\n", "\n", - "def build_custom_cnn():\n", - " model = models.Sequential([\n", - " # Block 1\n", - " layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(IMG_SIZE, IMG_SIZE, 3)),\n", - " layers.BatchNormalization(),\n", - " layers.Conv2D(32, (3, 3), activation='relu', padding='same'),\n", - " layers.BatchNormalization(),\n", - " layers.MaxPooling2D((2, 2)),\n", - " layers.Dropout(0.25),\n", - " \n", - " # Block 2\n", - " layers.Conv2D(64, (3, 3), activation='relu', padding='same'),\n", - " layers.BatchNormalization(),\n", - " layers.Conv2D(64, (3, 3), activation='relu', padding='same'),\n", - " layers.BatchNormalization(),\n", - " layers.MaxPooling2D((2, 2)),\n", - " layers.Dropout(0.25),\n", - " \n", - " # Block 3\n", - " layers.Conv2D(128, (3, 3), activation='relu', padding='same'),\n", - " layers.BatchNormalization(),\n", - " layers.Conv2D(128, (3, 3), activation='relu', padding='same'),\n", - " layers.BatchNormalization(),\n", - " layers.MaxPooling2D((2, 2)),\n", - " layers.Dropout(0.25),\n", - " \n", - " # Block 4\n", - " layers.Conv2D(256, (3, 3), activation='relu', padding='same'),\n", - " layers.BatchNormalization(),\n", - " layers.Conv2D(256, (3, 3), activation='relu', padding='same'),\n", - " layers.BatchNormalization(),\n", - " layers.MaxPooling2D((2, 2)),\n", - " layers.Dropout(0.25),\n", - " \n", - " # Global Average Pooling\n", - " layers.GlobalAveragePooling2D(),\n", - " \n", - " # Dense layers\n", - " layers.Dense(512, activation='relu'),\n", - " layers.BatchNormalization(),\n", - " layers.Dropout(0.5),\n", - " \n", - " layers.Dense(256, activation='relu'),\n", - " layers.BatchNormalization(),\n", - " layers.Dropout(0.5),\n", - " \n", - " # Output layer\n", - " layers.Dense(len(classes), activation='softmax')\n", - " ])\n", - " \n", - " return model\n", + "from tensorflow.keras.applications import MobileNetV2\n", + "from tensorflow.keras import regularizers\n", + "\n", + "base_model = MobileNetV2(\n", + " input_shape=(IMG_SIZE, IMG_SIZE, 3),\n", + " include_top=False,\n", + " weights='imagenet'\n", + ")\n", + "base_model.trainable = False # freeze backbone at first stage\n", + "\n", + "model = models.Sequential([\n", + " base_model,\n", + " layers.GlobalAveragePooling2D(),\n", + " layers.Dropout(0.4),\n", + " layers.Dense(128, activation='relu', kernel_regularizer=regularizers.l2(1e-4)),\n", + " layers.BatchNormalization(),\n", + " layers.Dropout(0.4),\n", + " layers.Dense(len(classes), activation='softmax')\n", + "])\n", "\n", - "print(\"Creating model...\")\n", - "model = build_custom_cnn()\n", "print(\"āœ“ Model created!\\n\")\n", "print(\"Model Summary:\")\n", "print(\"-\" * 50)\n", @@ -828,7 +848,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "240316b9", "metadata": {}, "outputs": [ @@ -849,30 +869,18 @@ } ], "source": [ - "# Option 2: Transfer Learning with MobileNetV2 (uncomment to use)\n", - "# base_model = MobileNetV2(input_shape=(IMG_SIZE, IMG_SIZE, 3), include_top=False, weights='imagenet')\n", - "# base_model.trainable = False\n", - "# \n", - "# model = models.Sequential([\n", - "# base_model,\n", - "# layers.GlobalAveragePooling2D(),\n", - "# layers.Dense(256, activation='relu'),\n", - "# layers.Dropout(0.5),\n", - "# layers.Dense(len(classes), activation='softmax')\n", - "# ])\n", - "\n", - "# Compile the model\n", + "# Compile the model with stronger regularization\n", "print(\"Compiling model...\")\n", "print(\"-\" * 50)\n", "model.compile(\n", - " optimizer=keras.optimizers.Adam(learning_rate=0.001),\n", - " loss='categorical_crossentropy',\n", + " optimizer=keras.optimizers.Adam(learning_rate=1e-4),\n", + " loss=keras.losses.CategoricalCrossentropy(label_smoothing=0.1),\n", " metrics=['accuracy']\n", ")\n", "\n", "print(\"Configuration:\")\n", - "print(\" - Optimizer: Adam (lr=0.001)\")\n", - "print(\" - Loss: Categorical Crossentropy\")\n", + "print(\" - Optimizer: Adam (lr=0.0001)\")\n", + "print(\" - Loss: Categorical Crossentropy + Label Smoothing (0.1)\")\n", "print(\" - Metrics: Accuracy\")\n", "print(\"-\" * 50)\n", "print(\"āœ“ Model compiled successfully!\\n\")" @@ -888,7 +896,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "26699eff", "metadata": {}, "outputs": [ @@ -967,78 +975,126 @@ "source": [ "# Define callbacks with custom callback for progress display\n", "class ProgressCallback(keras.callbacks.Callback):\n", - " def __init__(self):\n", + " def __init__(self, total_epochs):\n", " self.start_time = None\n", " self.epoch_times = []\n", - " \n", + " self.total_epochs = total_epochs\n", + "\n", " def on_train_begin(self, logs=None):\n", " self.start_time = time.time()\n", " self.epoch_times = []\n", " print(\"\\nšŸ“Š Training Progress:\")\n", " print(\"-\" * 80)\n", - " \n", + "\n", " def on_epoch_end(self, epoch, logs=None):\n", " epoch_time = time.time() - self.start_time\n", " self.epoch_times.append(epoch_time)\n", " avg_epoch_time = epoch_time / (epoch + 1)\n", - " remaining_epochs = EPOCHS - (epoch + 1)\n", - " eta_seconds = avg_epoch_time * remaining_epochs\n", - " \n", + " remaining_epochs = self.total_epochs - (epoch + 1)\n", + " eta_seconds = avg_epoch_time * max(remaining_epochs, 0)\n", + "\n", " if logs:\n", " loss = logs.get('loss', 0)\n", " val_loss = logs.get('val_loss', 0)\n", " acc = logs.get('accuracy', 0)\n", " val_acc = logs.get('val_accuracy', 0)\n", - " \n", - " progress = \"ā–ˆ\" * (epoch + 1) + \"ā–‘\" * (EPOCHS - epoch - 1)\n", - " print(f\"[{progress}] Epoch {epoch + 1}/{EPOCHS} | \"\n", + "\n", + " progress = \"ā–ˆ\" * (epoch + 1) + \"ā–‘\" * (self.total_epochs - epoch - 1)\n", + " print(f\"[{progress}] Epoch {epoch + 1}/{self.total_epochs} | \"\n", " f\"Loss: {loss:.4f} | Val Loss: {val_loss:.4f} | \"\n", " f\"Acc: {acc:.4f} | Val Acc: {val_acc:.4f} | \"\n", " f\"ETA: {int(eta_seconds)}s\")\n", - " \n", + "\n", " def on_train_end(self, logs=None):\n", " total_time = time.time() - self.start_time\n", " print(\"-\" * 80)\n", " print(f\"āœ“ Training completed in {int(total_time)}s\\n\")\n", "\n", "import time\n", + "import numpy as np\n", + "from sklearn.utils.class_weight import compute_class_weight\n", "\n", + "# Balanced class weights to reduce bias to dominant class\n", + "class_weights = compute_class_weight(\n", + " class_weight='balanced',\n", + " classes=np.unique(y_train),\n", + " y=y_train\n", + ")\n", + "class_weight_dict = {i: w for i, w in enumerate(class_weights)}\n", + "print(\"Class weights:\", class_weight_dict)\n", + "\n", + "# Stage 1 callbacks\n", "early_stop = keras.callbacks.EarlyStopping(\n", " monitor='val_loss',\n", - " patience=10,\n", + " patience=6,\n", " restore_best_weights=True\n", ")\n", "\n", "reduce_lr = keras.callbacks.ReduceLROnPlateau(\n", " monitor='val_loss',\n", " factor=0.5,\n", - " patience=5,\n", - " min_lr=1e-7\n", + " patience=3,\n", + " min_lr=1e-7,\n", + " verbose=1\n", ")\n", "\n", - "# Train the model\n", - "EPOCHS = 50\n", + "checkpoint = keras.callbacks.ModelCheckpoint(\n", + " 'best_rice_leaf_model.keras',\n", + " monitor='val_loss',\n", + " save_best_only=True,\n", + " verbose=1\n", + ")\n", + "\n", + "# Stage 1: train head only\n", + "EPOCHS_STAGE1 = 20\n", "BATCH_SIZE = 32\n", "\n", "print(\"=\" * 80)\n", - "print(\"šŸš€ STARTING MODEL TRAINING\")\n", + "print(\"šŸš€ STAGE 1 TRAINING (Frozen Backbone)\")\n", "print(\"=\" * 80)\n", - "print(f\"Epochs: {EPOCHS} | Batch Size: {BATCH_SIZE}\")\n", + "print(f\"Epochs: {EPOCHS_STAGE1} | Batch Size: {BATCH_SIZE}\")\n", "print(f\"Training samples: {len(X_train)} | Validation samples: {len(X_val)}\")\n", - "print(f\"Optimizer: Adam (lr=0.001)\")\n", - "print(f\"Loss Function: Categorical Crossentropy\")\n", + "print(f\"Optimizer: Adam (lr=0.0001)\")\n", "print(\"=\" * 80)\n", "\n", - "history = model.fit(\n", + "history_stage1 = model.fit(\n", " train_datagen.flow(X_train, y_train_cat, batch_size=BATCH_SIZE),\n", - " epochs=EPOCHS,\n", - " batch_size=BATCH_SIZE,\n", + " epochs=EPOCHS_STAGE1,\n", " validation_data=(X_val, y_val_cat),\n", - " callbacks=[early_stop, reduce_lr, ProgressCallback()],\n", - " verbose=0 # Suppress default verbose output\n", + " class_weight=class_weight_dict,\n", + " callbacks=[early_stop, reduce_lr, checkpoint, ProgressCallback(EPOCHS_STAGE1)],\n", + " verbose=0\n", ")\n", "\n", - "print(\"āœ… Model training and validation completed!\")" + "# Stage 2: fine-tune last layers of MobileNetV2\n", + "print(\"\\n\" + \"=\" * 80)\n", + "print(\"šŸ”§ STAGE 2 FINE-TUNING (Unfreeze top layers)\")\n", + "print(\"=\" * 80)\n", + "\n", + "base_model.trainable = True\n", + "for layer in base_model.layers[:-40]:\n", + " layer.trainable = False\n", + "\n", + "model.compile(\n", + " optimizer=keras.optimizers.Adam(learning_rate=1e-5),\n", + " loss=keras.losses.CategoricalCrossentropy(label_smoothing=0.1),\n", + " metrics=['accuracy']\n", + ")\n", + "\n", + "EPOCHS_STAGE2 = 10\n", + "print(f\"Fine-tuning epochs: {EPOCHS_STAGE2} | lr=1e-5\")\n", + "\n", + "history_stage2 = model.fit(\n", + " train_datagen.flow(X_train, y_train_cat, batch_size=BATCH_SIZE),\n", + " epochs=EPOCHS_STAGE2,\n", + " validation_data=(X_val, y_val_cat),\n", + " class_weight=class_weight_dict,\n", + " callbacks=[early_stop, reduce_lr, checkpoint, ProgressCallback(EPOCHS_STAGE2)],\n", + " verbose=0\n", + ")\n", + "\n", + "print(\"āœ… Training + fine-tuning completed!\")\n", + "print(\"Best model saved to: best_rice_leaf_model.keras\")" ] }, { @@ -1051,7 +1107,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "0c34bf89", "metadata": {}, "outputs": [ @@ -1136,7 +1192,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "586213e5", "metadata": {}, "outputs": [ @@ -1232,7 +1288,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "a0bffc2c", "metadata": {}, "outputs": [ @@ -1347,7 +1403,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "ac7f14c3", "metadata": {}, "outputs": [ @@ -1483,7 +1539,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "id": "783a7e43", "metadata": {}, "outputs": [ @@ -1674,7 +1730,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "base", "language": "python", "name": "python3" }, @@ -1688,7 +1744,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.5" + "version": "3.12.5" } }, "nbformat": 4, diff --git a/rice leaf diseases dataset/api_server.py b/rice leaf diseases dataset/api_server.py deleted file mode 100644 index 26eea5f..0000000 --- a/rice leaf diseases dataset/api_server.py +++ /dev/null @@ -1,450 +0,0 @@ -""" -Flask API Server untuk Rice Leaf Disease Classification -Menjalankan model TensorFlow dan melayani request dari Laravel -""" - -import os -import json -import base64 -import numpy as np -from PIL import Image -from io import BytesIO -from pathlib import Path -import warnings -warnings.filterwarnings('ignore') - -# TensorFlow & Keras -try: - import tensorflow as tf - from tensorflow import keras - from tensorflow.keras.preprocessing.image import img_to_array - TENSORFLOW_AVAILABLE = True -except ImportError as e: - print(f"āš ļø Warning: TensorFlow not available: {e}") - print(" Using mock predictions for testing") - TENSORFLOW_AVAILABLE = False - keras = None - -# Flask -from flask import Flask, request, jsonify -from flask_cors import CORS - -# Inisialisasi Flask -app = Flask(__name__) -CORS(app) - -# Konfigurasi -MODEL_PATH = None # Akan diset saat startup -MODEL = None -IMG_SIZE = (224, 224) -CLASS_NAMES = ['Bacterialblight', 'Brownspot', 'Leafsmut'] - -def load_model(): - """Load model dari file yang tersedia""" - global MODEL_PATH, MODEL - - if not TENSORFLOW_AVAILABLE: - print("āš ļø TensorFlow not available - Using mock mode for testing") - MODEL_PATH = "MOCK_MODEL" - return True - - # Cari model file yang tersedia - possible_models = [ - 'rice_leaf_disease_model.keras', - 'rice_leaf_disease_model.h5', - 'rice_leaf_disease_model.json' - ] - - for model_name in possible_models: - if os.path.exists(model_name): - MODEL_PATH = model_name - print(f"āœ“ Model ditemukan: {MODEL_PATH}") - - try: - if model_name.endswith('.keras'): - MODEL = keras.models.load_model(model_name) - elif model_name.endswith('.h5'): - MODEL = keras.models.load_model(model_name) - elif model_name.endswith('.json'): - # Load model dari JSON + weights - with open(model_name, 'r') as f: - model_json = f.read() - MODEL = keras.models.model_from_json(model_json) - - # Cari weights file - weights_base = model_name.replace('.json', '') - weights_files = [ - f"{weights_base}.h5", - f"{weights_base}_weights.h5" - ] - for weights_file in weights_files: - if os.path.exists(weights_file): - MODEL.load_weights(weights_file) - print(f"āœ“ Weights dimuat: {weights_file}") - break - - print(f"āœ“ Model berhasil dimuat!") - print(f" Model input shape: {MODEL.input_shape}") - print(f" Number of layers: {len(MODEL.layers)}") - return True - except Exception as e: - print(f"āœ— Error loading model: {str(e)}") - return False - - print("āœ— Model tidak ditemukan!") - print(" Letakkan salah satu dari ini di folder yang sama dengan script ini:") - print(" - rice_leaf_disease_model.keras") - print(" - rice_leaf_disease_model.h5") - print(" - rice_leaf_disease_model.json (+ .h5 weights)") - return False - - -def preprocess_image(image_data): - """ - Preprocessing gambar dari base64 atau bytes - - Args: - image_data: base64 string atau bytes - - Returns: - Preprocessed image array atau None jika error - """ - try: - # Jika string base64, decode dulu - if isinstance(image_data, str): - image_bytes = base64.b64decode(image_data) - else: - image_bytes = image_data - - # Convert bytes ke image menggunakan PIL - image = Image.open(BytesIO(image_bytes)) - - # Convert to RGB jika diperlukan - if image.mode != 'RGB': - image = image.convert('RGB') - - # Resize ke ukuran yang diharapkan model - image = image.resize(IMG_SIZE, Image.Resampling.LANCZOS) - - # Convert ke numpy array - img_array = np.array(image, dtype='float32') - - # Normalize pixel values ke range [0, 1] - img_array = img_array / 255.0 - - # Add batch dimension - img_array = np.expand_dims(img_array, axis=0) - - return img_array - - except Exception as e: - print(f"Error preprocessing image: {str(e)}") - return None - - -def classify_image(image_data): - """ - Klasifikasi gambar menggunakan model - - Args: - image_data: base64 string atau bytes - - Returns: - Dict dengan hasil klasifikasi atau None jika error - """ - # Mock mode - jika TensorFlow tidak tersedia - if not TENSORFLOW_AVAILABLE: - import random - predictions = [random.uniform(0.1, 0.9) for _ in CLASS_NAMES] - max_pred = max(predictions) - idx = predictions.index(max_pred) - - all_predictions = {} - for i, class_name in enumerate(CLASS_NAMES): - all_predictions[class_name] = round(predictions[i], 4) - - return { - 'predicted_class': CLASS_NAMES[idx], - 'confidence': round(max_pred, 4), - 'all_predictions': all_predictions - } - - if MODEL is None: - return None - - try: - # Preprocess image - img_array = preprocess_image(image_data) - - if img_array is None: - return None - - # Prediction - predictions = MODEL.predict(img_array, verbose=0) - - # Get predicted class dan confidence - predicted_idx = np.argmax(predictions[0]) - predicted_class = CLASS_NAMES[predicted_idx] - confidence = float(predictions[0][predicted_idx]) - - # Build all predictions - all_predictions = {} - for idx, class_name in enumerate(CLASS_NAMES): - all_predictions[class_name] = float(predictions[0][idx]) - - return { - 'predicted_class': predicted_class, - 'confidence': confidence, - 'all_predictions': all_predictions - } - - except Exception as e: - print(f"Error during classification: {str(e)}") - return None - - -# ============================================================================ -# ROUTES -# ============================================================================ - -@app.route('/health', methods=['POST', 'GET']) -def health_check(): - """Check apakah API berjalan dan model tersedia""" - if not TENSORFLOW_AVAILABLE: - return jsonify({ - 'status': 'ok', - 'message': 'API running in MOCK MODE (TensorFlow not available)', - 'model_loaded': False, - 'mock_mode': True, - 'classes': CLASS_NAMES - }), 200 - - if MODEL is None: - return jsonify({ - 'status': 'error', - 'message': 'Model not loaded', - 'model_loaded': False - }), 503 - - return jsonify({ - 'status': 'ok', - 'message': 'API is running', - 'model_loaded': True, - 'model_path': MODEL_PATH, - 'classes': CLASS_NAMES, - 'input_shape': str(MODEL.input_shape) - }), 200 - - -@app.route('/classify', methods=['POST']) -def classify(): - """ - API endpoint untuk klasifikasi gambar - - Expected request: - { - "image": "base64_encoded_image_string", - "filename": "optional_filename.jpg" - } - - Response: - { - "success": true, - "predicted_class": "Bacterialblight", - "confidence": 0.95, - "all_predictions": { - "Bacterialblight": 0.95, - "Brownspot": 0.04, - "Leafsmut": 0.01 - } - } - """ - try: - data = request.get_json() - - if data is None: - return jsonify({ - 'success': False, - 'message': 'Request harus JSON' - }), 400 - - # Validasi input - if 'image' not in data: - return jsonify({ - 'success': False, - 'message': 'Field "image" (base64) diperlukan' - }), 400 - - image_data = data['image'] - filename = data.get('filename', 'unknown') - - # Klasifikasi - result = classify_image(image_data) - - if result is None: - return jsonify({ - 'success': False, - 'message': 'Gagal memproses gambar' - }), 400 - - # Log hasil - print(f"āœ“ Classification done: {filename} -> {result['predicted_class']} ({result['confidence']:.2%})") - - return jsonify({ - 'success': True, - 'predicted_class': result['predicted_class'], - 'confidence': result['confidence'], - 'all_predictions': result['all_predictions'], - 'filename': filename - }), 200 - - except Exception as e: - print(f"āœ— Error in classify endpoint: {str(e)}") - return jsonify({ - 'success': False, - 'message': f'Error: {str(e)}' - }), 500 - - -@app.route('/classify-from-url', methods=['POST']) -def classify_from_url(): - """ - Alternative endpoint untuk klasifikasi dari URL gambar - - Expected request: - { - "image_url": "http://example.com/image.jpg" - } - """ - try: - data = request.get_json() - - if data is None or 'image_url' not in data: - return jsonify({ - 'success': False, - 'message': 'Field "image_url" diperlukan' - }), 400 - - image_url = data['image_url'] - - import requests - response = requests.get(image_url, timeout=10) - - if response.status_code != 200: - return jsonify({ - 'success': False, - 'message': f'Gagal download image dari URL' - }), 400 - - # Klasifikasi - result = classify_image(response.content) - - if result is None: - return jsonify({ - 'success': False, - 'message': 'Gagal memproses gambar' - }), 400 - - return jsonify({ - 'success': True, - 'predicted_class': result['predicted_class'], - 'confidence': result['confidence'], - 'all_predictions': result['all_predictions'], - 'url': image_url - }), 200 - - except Exception as e: - return jsonify({ - 'success': False, - 'message': f'Error: {str(e)}' - }), 500 - - -@app.route('/info', methods=['GET']) -def model_info(): - """Get informasi tentang model""" - if MODEL is None: - return jsonify({ - 'status': 'error', - 'message': 'Model not loaded' - }), 503 - - return jsonify({ - 'model_loaded': True, - 'model_path': MODEL_PATH, - 'classes': CLASS_NAMES, - 'number_of_classes': len(CLASS_NAMES), - 'input_shape': str(MODEL.input_shape), - 'number_of_layers': len(MODEL.layers), - 'total_parameters': int(MODEL.count_params()) - }), 200 - - -@app.route('/', methods=['GET']) -def index(): - """Root endpoint dengan informasi API""" - return jsonify({ - 'name': 'Rice Leaf Disease Classification API', - 'version': '1.0', - 'description': 'API untuk klasifikasi penyakit daun padi menggunakan CNN', - 'endpoints': { - 'POST /classify': 'Klasifikasi gambar (base64)', - 'POST /classify-from-url': 'Klasifikasi gambar dari URL', - 'GET /health': 'Health check', - 'GET /info': 'Informasi model', - 'GET /': 'Info API ini' - }, - 'model_status': 'Loaded' if MODEL is not None else 'Not loaded', - 'classes': CLASS_NAMES - }), 200 - - -@app.errorhandler(404) -def not_found(error): - return jsonify({ - 'success': False, - 'message': 'Endpoint tidak ditemukan' - }), 404 - - -@app.errorhandler(500) -def server_error(error): - return jsonify({ - 'success': False, - 'message': 'Internal server error' - }), 500 - - -# ============================================================================ -# STARTUP -# ============================================================================ - -if __name__ == '__main__': - print("\n" + "="*60) - print("Rice Leaf Disease Classification API Server") - print("="*60 + "\n") - - # Pindah ke folder yang sama dengan script - script_dir = os.path.dirname(os.path.abspath(__file__)) - os.chdir(script_dir) - print(f"Working directory: {os.getcwd()}\n") - - # Load model - print("Loading model...") - if not load_model(): - print("\nāš ļø WARNING: Model tidak dapat dimuat!") - print(" API akan berjalan tapi endpoint /classify akan gagal.\n") - - print("\n" + "="*60) - print("Starting Flask API Server...") - print("="*60) - print("Server berjalan di http://127.0.0.1:5000/") - print("Tekan CTRL+C untuk menghentikan.\n") - - # Run Flask app - app.run( - host='127.0.0.1', - port=5000, - debug=False, # Set ke True jika development - use_reloader=False - ) diff --git a/rice leaf diseases dataset/rice_leaf_cnn_classification.ipynb b/rice leaf diseases dataset/rice_leaf_cnn_classification.ipynb index e3bcbe6..7ff5365 100644 --- a/rice leaf diseases dataset/rice_leaf_cnn_classification.ipynb +++ b/rice leaf diseases dataset/rice_leaf_cnn_classification.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 20, "id": "5ca735e6", "metadata": {}, "outputs": [ @@ -10,7 +10,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "TensorFlow Version: 2.20.0\n", + "TensorFlow Version: 2.21.0\n", "GPU Available: []\n" ] } @@ -69,7 +69,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 21, "id": "63695071", "metadata": {}, "outputs": [ @@ -89,7 +89,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Loading Bacterialblight: 100%|ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ| 1604/1604 [00:02<00:00, 569.23it/s]\n" + "Loading Bacterialblight: 100%|ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ| 1604/1604 [00:03<00:00, 455.07it/s]\n" ] }, { @@ -106,28 +106,28 @@ "text": [ "\n", "Loading Brownspot: 0%| | 0/1620 [00:00Model: \"sequential\"\n", + "
Model: \"sequential_2\"\n",
        "
\n" ], "text/plain": [ - "\u001b[1mModel: \"sequential\"\u001b[0m\n" + "\u001b[1mModel: \"sequential_2\"\u001b[0m\n" ] }, "metadata": {}, @@ -547,80 +523,80 @@ "
ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”³ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”³ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”“\n",
        "ā”ƒ Layer (type)                         ā”ƒ Output Shape                ā”ƒ         Param # ā”ƒ\n",
        "└━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩\n",
-       "│ conv2d (Conv2D)                      │ (None, 224, 224, 32)        │             896 │\n",
+       "│ conv2d_16 (Conv2D)                   │ (None, 224, 224, 32)        │             896 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ batch_normalization                  │ (None, 224, 224, 32)        │             128 │\n",
+       "│ batch_normalization_20               │ (None, 224, 224, 32)        │             128 │\n",
        "│ (BatchNormalization)                 │                             │                 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ conv2d_1 (Conv2D)                    │ (None, 224, 224, 32)        │           9,248 │\n",
+       "│ conv2d_17 (Conv2D)                   │ (None, 224, 224, 32)        │           9,248 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ batch_normalization_1                │ (None, 224, 224, 32)        │             128 │\n",
+       "│ batch_normalization_21               │ (None, 224, 224, 32)        │             128 │\n",
        "│ (BatchNormalization)                 │                             │                 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ max_pooling2d (MaxPooling2D)         │ (None, 112, 112, 32)        │               0 │\n",
+       "│ max_pooling2d_8 (MaxPooling2D)       │ (None, 112, 112, 32)        │               0 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ dropout (Dropout)                    │ (None, 112, 112, 32)        │               0 │\n",
+       "│ dropout_12 (Dropout)                 │ (None, 112, 112, 32)        │               0 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ conv2d_2 (Conv2D)                    │ (None, 112, 112, 64)        │          18,496 │\n",
+       "│ conv2d_18 (Conv2D)                   │ (None, 112, 112, 64)        │          18,496 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ batch_normalization_2                │ (None, 112, 112, 64)        │             256 │\n",
+       "│ batch_normalization_22               │ (None, 112, 112, 64)        │             256 │\n",
        "│ (BatchNormalization)                 │                             │                 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ conv2d_3 (Conv2D)                    │ (None, 112, 112, 64)        │          36,928 │\n",
+       "│ conv2d_19 (Conv2D)                   │ (None, 112, 112, 64)        │          36,928 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ batch_normalization_3                │ (None, 112, 112, 64)        │             256 │\n",
+       "│ batch_normalization_23               │ (None, 112, 112, 64)        │             256 │\n",
        "│ (BatchNormalization)                 │                             │                 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ max_pooling2d_1 (MaxPooling2D)       │ (None, 56, 56, 64)          │               0 │\n",
+       "│ max_pooling2d_9 (MaxPooling2D)       │ (None, 56, 56, 64)          │               0 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ dropout_1 (Dropout)                  │ (None, 56, 56, 64)          │               0 │\n",
+       "│ dropout_13 (Dropout)                 │ (None, 56, 56, 64)          │               0 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ conv2d_4 (Conv2D)                    │ (None, 56, 56, 128)         │          73,856 │\n",
+       "│ conv2d_20 (Conv2D)                   │ (None, 56, 56, 128)         │          73,856 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ batch_normalization_4                │ (None, 56, 56, 128)         │             512 │\n",
+       "│ batch_normalization_24               │ (None, 56, 56, 128)         │             512 │\n",
        "│ (BatchNormalization)                 │                             │                 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ conv2d_5 (Conv2D)                    │ (None, 56, 56, 128)         │         147,584 │\n",
+       "│ conv2d_21 (Conv2D)                   │ (None, 56, 56, 128)         │         147,584 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ batch_normalization_5                │ (None, 56, 56, 128)         │             512 │\n",
+       "│ batch_normalization_25               │ (None, 56, 56, 128)         │             512 │\n",
        "│ (BatchNormalization)                 │                             │                 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ max_pooling2d_2 (MaxPooling2D)       │ (None, 28, 28, 128)         │               0 │\n",
+       "│ max_pooling2d_10 (MaxPooling2D)      │ (None, 28, 28, 128)         │               0 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ dropout_2 (Dropout)                  │ (None, 28, 28, 128)         │               0 │\n",
+       "│ dropout_14 (Dropout)                 │ (None, 28, 28, 128)         │               0 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ conv2d_6 (Conv2D)                    │ (None, 28, 28, 256)         │         295,168 │\n",
+       "│ conv2d_22 (Conv2D)                   │ (None, 28, 28, 256)         │         295,168 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ batch_normalization_6                │ (None, 28, 28, 256)         │           1,024 │\n",
+       "│ batch_normalization_26               │ (None, 28, 28, 256)         │           1,024 │\n",
        "│ (BatchNormalization)                 │                             │                 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ conv2d_7 (Conv2D)                    │ (None, 28, 28, 256)         │         590,080 │\n",
+       "│ conv2d_23 (Conv2D)                   │ (None, 28, 28, 256)         │         590,080 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ batch_normalization_7                │ (None, 28, 28, 256)         │           1,024 │\n",
+       "│ batch_normalization_27               │ (None, 28, 28, 256)         │           1,024 │\n",
        "│ (BatchNormalization)                 │                             │                 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ max_pooling2d_3 (MaxPooling2D)       │ (None, 14, 14, 256)         │               0 │\n",
+       "│ max_pooling2d_11 (MaxPooling2D)      │ (None, 14, 14, 256)         │               0 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ dropout_3 (Dropout)                  │ (None, 14, 14, 256)         │               0 │\n",
+       "│ dropout_15 (Dropout)                 │ (None, 14, 14, 256)         │               0 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ global_average_pooling2d             │ (None, 256)                 │               0 │\n",
+       "│ global_average_pooling2d_2           │ (None, 256)                 │               0 │\n",
        "│ (GlobalAveragePooling2D)             │                             │                 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ dense (Dense)                        │ (None, 512)                 │         131,584 │\n",
+       "│ dense_6 (Dense)                      │ (None, 512)                 │         131,584 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ batch_normalization_8                │ (None, 512)                 │           2,048 │\n",
+       "│ batch_normalization_28               │ (None, 512)                 │           2,048 │\n",
        "│ (BatchNormalization)                 │                             │                 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ dropout_4 (Dropout)                  │ (None, 512)                 │               0 │\n",
+       "│ dropout_16 (Dropout)                 │ (None, 512)                 │               0 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ dense_1 (Dense)                      │ (None, 256)                 │         131,328 │\n",
+       "│ dense_7 (Dense)                      │ (None, 256)                 │         131,328 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ batch_normalization_9                │ (None, 256)                 │           1,024 │\n",
+       "│ batch_normalization_29               │ (None, 256)                 │           1,024 │\n",
        "│ (BatchNormalization)                 │                             │                 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ dropout_5 (Dropout)                  │ (None, 256)                 │               0 │\n",
+       "│ dropout_17 (Dropout)                 │ (None, 256)                 │               0 │\n",
        "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n",
-       "│ dense_2 (Dense)                      │ (None, 3)                   │             771 │\n",
+       "│ dense_8 (Dense)                      │ (None, 3)                   │             771 │\n",
        "ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n",
        "
\n" ], @@ -628,80 +604,80 @@ "ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”³ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”³ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”ā”“\n", "ā”ƒ\u001b[1m \u001b[0m\u001b[1mLayer (type) \u001b[0m\u001b[1m \u001b[0mā”ƒ\u001b[1m \u001b[0m\u001b[1mOutput Shape \u001b[0m\u001b[1m \u001b[0mā”ƒ\u001b[1m \u001b[0m\u001b[1m Param #\u001b[0m\u001b[1m \u001b[0mā”ƒ\n", "└━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩\n", - "│ conv2d (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m32\u001b[0m) │ \u001b[38;5;34m896\u001b[0m │\n", + "│ conv2d_16 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m32\u001b[0m) │ \u001b[38;5;34m896\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ batch_normalization │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m32\u001b[0m) │ \u001b[38;5;34m128\u001b[0m │\n", + "│ batch_normalization_20 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m32\u001b[0m) │ \u001b[38;5;34m128\u001b[0m │\n", "│ (\u001b[38;5;33mBatchNormalization\u001b[0m) │ │ │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ conv2d_1 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m32\u001b[0m) │ \u001b[38;5;34m9,248\u001b[0m │\n", + "│ conv2d_17 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m32\u001b[0m) │ \u001b[38;5;34m9,248\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ batch_normalization_1 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m32\u001b[0m) │ \u001b[38;5;34m128\u001b[0m │\n", + "│ batch_normalization_21 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m32\u001b[0m) │ \u001b[38;5;34m128\u001b[0m │\n", "│ (\u001b[38;5;33mBatchNormalization\u001b[0m) │ │ │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ max_pooling2d (\u001b[38;5;33mMaxPooling2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m32\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", + "│ max_pooling2d_8 (\u001b[38;5;33mMaxPooling2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m32\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ dropout (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m32\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", + "│ dropout_12 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m32\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ conv2d_2 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m18,496\u001b[0m │\n", + "│ conv2d_18 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m18,496\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ batch_normalization_2 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m256\u001b[0m │\n", + "│ batch_normalization_22 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m256\u001b[0m │\n", "│ (\u001b[38;5;33mBatchNormalization\u001b[0m) │ │ │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ conv2d_3 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m36,928\u001b[0m │\n", + "│ conv2d_19 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m36,928\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ batch_normalization_3 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m256\u001b[0m │\n", + "│ batch_normalization_23 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m112\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m256\u001b[0m │\n", "│ (\u001b[38;5;33mBatchNormalization\u001b[0m) │ │ │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ max_pooling2d_1 (\u001b[38;5;33mMaxPooling2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", + "│ max_pooling2d_9 (\u001b[38;5;33mMaxPooling2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ dropout_1 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", + "│ dropout_13 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ conv2d_4 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m73,856\u001b[0m │\n", + "│ conv2d_20 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m73,856\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ batch_normalization_4 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m512\u001b[0m │\n", + "│ batch_normalization_24 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m512\u001b[0m │\n", "│ (\u001b[38;5;33mBatchNormalization\u001b[0m) │ │ │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ conv2d_5 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m147,584\u001b[0m │\n", + "│ conv2d_21 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m147,584\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ batch_normalization_5 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m512\u001b[0m │\n", + "│ batch_normalization_25 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m56\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m512\u001b[0m │\n", "│ (\u001b[38;5;33mBatchNormalization\u001b[0m) │ │ │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ max_pooling2d_2 (\u001b[38;5;33mMaxPooling2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", + "│ max_pooling2d_10 (\u001b[38;5;33mMaxPooling2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ dropout_2 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", + "│ dropout_14 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ conv2d_6 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m295,168\u001b[0m │\n", + "│ conv2d_22 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m295,168\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ batch_normalization_6 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m1,024\u001b[0m │\n", + "│ batch_normalization_26 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m1,024\u001b[0m │\n", "│ (\u001b[38;5;33mBatchNormalization\u001b[0m) │ │ │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ conv2d_7 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m590,080\u001b[0m │\n", + "│ conv2d_23 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m590,080\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ batch_normalization_7 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m1,024\u001b[0m │\n", + "│ batch_normalization_27 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m28\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m1,024\u001b[0m │\n", "│ (\u001b[38;5;33mBatchNormalization\u001b[0m) │ │ │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ max_pooling2d_3 (\u001b[38;5;33mMaxPooling2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m14\u001b[0m, \u001b[38;5;34m14\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", + "│ max_pooling2d_11 (\u001b[38;5;33mMaxPooling2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m14\u001b[0m, \u001b[38;5;34m14\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ dropout_3 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m14\u001b[0m, \u001b[38;5;34m14\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", + "│ dropout_15 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m14\u001b[0m, \u001b[38;5;34m14\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ global_average_pooling2d │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", + "│ global_average_pooling2d_2 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "│ (\u001b[38;5;33mGlobalAveragePooling2D\u001b[0m) │ │ │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ dense (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m512\u001b[0m) │ \u001b[38;5;34m131,584\u001b[0m │\n", + "│ dense_6 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m512\u001b[0m) │ \u001b[38;5;34m131,584\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ batch_normalization_8 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m512\u001b[0m) │ \u001b[38;5;34m2,048\u001b[0m │\n", + "│ batch_normalization_28 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m512\u001b[0m) │ \u001b[38;5;34m2,048\u001b[0m │\n", "│ (\u001b[38;5;33mBatchNormalization\u001b[0m) │ │ │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ dropout_4 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m512\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", + "│ dropout_16 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m512\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ dense_1 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m131,328\u001b[0m │\n", + "│ dense_7 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m131,328\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ batch_normalization_9 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m1,024\u001b[0m │\n", + "│ batch_normalization_29 │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m1,024\u001b[0m │\n", "│ (\u001b[38;5;33mBatchNormalization\u001b[0m) │ │ │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ dropout_5 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", + "│ dropout_17 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n", - "│ dense_2 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m3\u001b[0m) │ \u001b[38;5;34m771\u001b[0m │\n", + "│ dense_8 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m3\u001b[0m) │ \u001b[38;5;34m771\u001b[0m │\n", "ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n" ] }, @@ -828,7 +804,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 28, "id": "240316b9", "metadata": {}, "outputs": [ @@ -836,46 +812,44 @@ "name": "stdout", "output_type": "stream", "text": [ - "Compiling model...\n", + "Building MobileNetV2 Transfer Learning model...\n", "--------------------------------------------------\n", - "Configuration:\n", - " - Optimizer: Adam (lr=0.001)\n", - " - Loss: Categorical Crossentropy\n", - " - Metrics: Accuracy\n", - "--------------------------------------------------\n", - "āœ“ Model compiled successfully!\n", - "\n" + "Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/mobilenet_v2/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_224_no_top.h5\n", + "\u001b[1m9406464/9406464\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m3s\u001b[0m 0us/step\n", + "āœ“ MobileNetV2 Model compiled successfully!\n" ] } ], "source": [ - "# Option 2: Transfer Learning with MobileNetV2 (uncomment to use)\n", - "# base_model = MobileNetV2(input_shape=(IMG_SIZE, IMG_SIZE, 3), include_top=False, weights='imagenet')\n", - "# base_model.trainable = False\n", - "# \n", - "# model = models.Sequential([\n", - "# base_model,\n", - "# layers.GlobalAveragePooling2D(),\n", - "# layers.Dense(256, activation='relu'),\n", - "# layers.Dropout(0.5),\n", - "# layers.Dense(len(classes), activation='softmax')\n", - "# ])\n", - "\n", - "# Compile the model\n", - "print(\"Compiling model...\")\n", + "# MENGGUNAKAN TRANSFER LEARNING (MOBILENETV2)\n", + "print(\"Building MobileNetV2 Transfer Learning model...\")\n", "print(\"-\" * 50)\n", + "\n", + "# 1. Ambil model yang sudah pintar (Pre-trained)\n", + "base_model = MobileNetV2(input_shape=(IMG_SIZE, IMG_SIZE, 3), \n", + " include_top=False, \n", + " weights='imagenet')\n", + "\n", + "# 2. Bekukan model dasar agar tidak berubah saat training awal\n", + "base_model.trainable = False \n", + "\n", + "# 3. Rakit arsitektur model baru\n", + "model = models.Sequential([\n", + " base_model,\n", + " layers.GlobalAveragePooling2D(),\n", + " layers.Dense(128, activation='relu'),\n", + " layers.Dropout(0.3), # Mencegah overfitting (penting untuk TA!)\n", + " layers.Dense(len(classes), activation='softmax')\n", + "])\n", + "\n", + "# 4. Compile model dengan Learning Rate yang lebih kecil (0.0001) agar stabil\n", "model.compile(\n", - " optimizer=keras.optimizers.Adam(learning_rate=0.001),\n", + " optimizer=keras.optimizers.Adam(learning_rate=0.0001), \n", " loss='categorical_crossentropy',\n", " metrics=['accuracy']\n", ")\n", "\n", - "print(\"Configuration:\")\n", - "print(\" - Optimizer: Adam (lr=0.001)\")\n", - "print(\" - Loss: Categorical Crossentropy\")\n", - "print(\" - Metrics: Accuracy\")\n", - "print(\"-\" * 50)\n", - "print(\"āœ“ Model compiled successfully!\\n\")" + "print(\"āœ“ MobileNetV2 Model compiled successfully!\")" ] }, { @@ -888,7 +862,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 27, "id": "26699eff", "metadata": {}, "outputs": [ @@ -907,60 +881,51 @@ "\n", "šŸ“Š Training Progress:\n", "--------------------------------------------------------------------------------\n", - "[ā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 1/50 | Loss: 0.9152 | Val Loss: 2.1880 | Acc: 0.6974 | Val Acc: 0.3115 | ETA: 15860s\n", - "[ā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 2/50 | Loss: 0.5917 | Val Loss: 5.0228 | Acc: 0.7959 | Val Acc: 0.3457 | ETA: 14999s\n", - "[ā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 3/50 | Loss: 0.4868 | Val Loss: 3.4162 | Acc: 0.8270 | Val Acc: 0.5164 | ETA: 14541s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 4/50 | Loss: 0.3732 | Val Loss: 1.2514 | Acc: 0.8630 | Val Acc: 0.6159 | ETA: 14109s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 5/50 | Loss: 0.3450 | Val Loss: 1.0146 | Acc: 0.8661 | Val Acc: 0.7454 | ETA: 13739s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 6/50 | Loss: 0.2968 | Val Loss: 0.5368 | Acc: 0.8877 | Val Acc: 0.7923 | ETA: 13377s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 7/50 | Loss: 0.2895 | Val Loss: 0.7549 | Acc: 0.8990 | Val Acc: 0.7767 | ETA: 13036s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 8/50 | Loss: 0.2722 | Val Loss: 0.4136 | Acc: 0.9030 | Val Acc: 0.8876 | ETA: 12708s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 9/50 | Loss: 0.2799 | Val Loss: 1.5129 | Acc: 0.9015 | Val Acc: 0.6743 | ETA: 12383s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 10/50 | Loss: 0.2388 | Val Loss: 0.7123 | Acc: 0.9152 | Val Acc: 0.7653 | ETA: 12061s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 11/50 | Loss: 0.1875 | Val Loss: 0.1276 | Acc: 0.9317 | Val Acc: 0.9545 | ETA: 11745s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 12/50 | Loss: 0.2415 | Val Loss: 0.3129 | Acc: 0.9146 | Val Acc: 0.8834 | ETA: 11439s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 13/50 | Loss: 0.1797 | Val Loss: 0.2248 | Acc: 0.9347 | Val Acc: 0.9175 | ETA: 11128s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 14/50 | Loss: 0.1990 | Val Loss: 0.0882 | Acc: 0.9295 | Val Acc: 0.9758 | ETA: 10823s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 15/50 | Loss: 0.1830 | Val Loss: 0.3407 | Acc: 0.9359 | Val Acc: 0.8933 | ETA: 10523s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 16/50 | Loss: 0.1374 | Val Loss: 0.0735 | Acc: 0.9524 | Val Acc: 0.9673 | ETA: 10227s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 17/50 | Loss: 0.1476 | Val Loss: 0.3952 | Acc: 0.9491 | Val Acc: 0.8905 | ETA: 9939s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 18/50 | Loss: 0.1439 | Val Loss: 0.1214 | Acc: 0.9484 | Val Acc: 0.9659 | ETA: 9689s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 19/50 | Loss: 0.1787 | Val Loss: 7.9704 | Acc: 0.9417 | Val Acc: 0.4523 | ETA: 9383s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 20/50 | Loss: 0.1424 | Val Loss: 0.1060 | Acc: 0.9518 | Val Acc: 0.9701 | ETA: 9076s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 21/50 | Loss: 0.1164 | Val Loss: 1.1432 | Acc: 0.9585 | Val Acc: 0.7468 | ETA: 8772s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 22/50 | Loss: 0.1030 | Val Loss: 0.0563 | Acc: 0.9634 | Val Acc: 0.9844 | ETA: 8473s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 23/50 | Loss: 0.0679 | Val Loss: 0.0226 | Acc: 0.9771 | Val Acc: 0.9957 | ETA: 8181s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 24/50 | Loss: 0.0753 | Val Loss: 0.0315 | Acc: 0.9744 | Val Acc: 0.9915 | ETA: 7888s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 25/50 | Loss: 0.0921 | Val Loss: 0.1533 | Acc: 0.9671 | Val Acc: 0.9772 | ETA: 7606s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 26/50 | Loss: 0.0928 | Val Loss: 0.1205 | Acc: 0.9725 | Val Acc: 0.9673 | ETA: 7318s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 27/50 | Loss: 0.0654 | Val Loss: 0.0055 | Acc: 0.9777 | Val Acc: 1.0000 | ETA: 7022s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 28/50 | Loss: 0.0633 | Val Loss: 0.0072 | Acc: 0.9780 | Val Acc: 0.9986 | ETA: 6719s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 29/50 | Loss: 0.0886 | Val Loss: 0.0197 | Acc: 0.9728 | Val Acc: 0.9943 | ETA: 6414s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 30/50 | Loss: 0.0609 | Val Loss: 0.0272 | Acc: 0.9777 | Val Acc: 0.9915 | ETA: 6111s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 31/50 | Loss: 0.0593 | Val Loss: 0.0490 | Acc: 0.9783 | Val Acc: 0.9730 | ETA: 5806s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 32/50 | Loss: 0.0648 | Val Loss: 0.0576 | Acc: 0.9808 | Val Acc: 0.9716 | ETA: 5498s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 33/50 | Loss: 0.0487 | Val Loss: 0.0035 | Acc: 0.9823 | Val Acc: 1.0000 | ETA: 5190s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 34/50 | Loss: 0.0389 | Val Loss: 0.0049 | Acc: 0.9872 | Val Acc: 0.9986 | ETA: 4883s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 35/50 | Loss: 0.0398 | Val Loss: 0.0015 | Acc: 0.9887 | Val Acc: 1.0000 | ETA: 4575s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 36/50 | Loss: 0.0331 | Val Loss: 0.0051 | Acc: 0.9893 | Val Acc: 0.9986 | ETA: 4269s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 37/50 | Loss: 0.0446 | Val Loss: 0.0020 | Acc: 0.9832 | Val Acc: 1.0000 | ETA: 3962s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 38/50 | Loss: 0.0367 | Val Loss: 0.0035 | Acc: 0.9863 | Val Acc: 1.0000 | ETA: 3657s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 39/50 | Loss: 0.0369 | Val Loss: 0.0012 | Acc: 0.9875 | Val Acc: 1.0000 | ETA: 3351s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 40/50 | Loss: 0.0287 | Val Loss: 0.0213 | Acc: 0.9912 | Val Acc: 0.9943 | ETA: 3045s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 41/50 | Loss: 0.0434 | Val Loss: 0.0022 | Acc: 0.9851 | Val Acc: 1.0000 | ETA: 2739s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 42/50 | Loss: 0.0456 | Val Loss: 0.1725 | Acc: 0.9841 | Val Acc: 0.9459 | ETA: 2433s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 43/50 | Loss: 0.0413 | Val Loss: 0.0035 | Acc: 0.9863 | Val Acc: 1.0000 | ETA: 2128s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 44/50 | Loss: 0.0283 | Val Loss: 0.0036 | Acc: 0.9908 | Val Acc: 1.0000 | ETA: 1826s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 45/50 | Loss: 0.0326 | Val Loss: 0.0004 | Acc: 0.9893 | Val Acc: 1.0000 | ETA: 1521s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘] Epoch 46/50 | Loss: 0.0203 | Val Loss: 0.0003 | Acc: 0.9921 | Val Acc: 1.0000 | ETA: 1216s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘] Epoch 47/50 | Loss: 0.0204 | Val Loss: 0.0010 | Acc: 0.9942 | Val Acc: 1.0000 | ETA: 912s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘] Epoch 48/50 | Loss: 0.0355 | Val Loss: 0.0006 | Acc: 0.9863 | Val Acc: 1.0000 | ETA: 607s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘] Epoch 49/50 | Loss: 0.0400 | Val Loss: 0.0006 | Acc: 0.9887 | Val Acc: 1.0000 | ETA: 303s\n", - "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ] Epoch 50/50 | Loss: 0.0168 | Val Loss: 0.0004 | Acc: 0.9939 | Val Acc: 1.0000 | ETA: 0s\n", - "--------------------------------------------------------------------------------\n", - "āœ“ Training completed in 15182s\n", - "\n", - "āœ… Model training and validation completed!\n" + "[ā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 1/50 | Loss: 0.8581 | Val Loss: 2.1125 | Acc: 0.7004 | Val Acc: 0.3457 | ETA: 15230s\n", + "[ā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 2/50 | Loss: 0.5727 | Val Loss: 4.5162 | Acc: 0.7984 | Val Acc: 0.4538 | ETA: 14684s\n", + "[ā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 3/50 | Loss: 0.4536 | Val Loss: 4.7104 | Acc: 0.8331 | Val Acc: 0.2489 | ETA: 14318s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 4/50 | Loss: 0.3599 | Val Loss: 1.9790 | Acc: 0.8618 | Val Acc: 0.5818 | ETA: 13955s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 5/50 | Loss: 0.3339 | Val Loss: 1.0550 | Acc: 0.8746 | Val Acc: 0.6828 | ETA: 13671s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 6/50 | Loss: 0.3125 | Val Loss: 0.3040 | Acc: 0.8841 | Val Acc: 0.8890 | ETA: 13361s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 7/50 | Loss: 0.2461 | Val Loss: 0.4891 | Acc: 0.9097 | Val Acc: 0.8478 | ETA: 13062s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 8/50 | Loss: 0.2662 | Val Loss: 0.2847 | Acc: 0.9024 | Val Acc: 0.8962 | ETA: 12766s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 9/50 | Loss: 0.1980 | Val Loss: 0.3768 | Acc: 0.9311 | Val Acc: 0.8791 | ETA: 12465s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 10/50 | Loss: 0.2186 | Val Loss: 0.5156 | Acc: 0.9185 | Val Acc: 0.8080 | ETA: 12159s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 11/50 | Loss: 0.2260 | Val Loss: 0.4154 | Acc: 0.9234 | Val Acc: 0.8720 | ETA: 11857s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 12/50 | Loss: 0.1918 | Val Loss: 1.4041 | Acc: 0.9335 | Val Acc: 0.7013 | ETA: 11566s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 13/50 | Loss: 0.1812 | Val Loss: 0.9136 | Acc: 0.9320 | Val Acc: 0.7169 | ETA: 11274s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 14/50 | Loss: 0.1381 | Val Loss: 0.0307 | Acc: 0.9515 | Val Acc: 0.9972 | ETA: 10974s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 15/50 | Loss: 0.1227 | Val Loss: 0.0818 | Acc: 0.9561 | Val Acc: 0.9716 | ETA: 10688s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 16/50 | Loss: 0.1217 | Val Loss: 0.0901 | Acc: 0.9542 | Val Acc: 0.9744 | ETA: 10397s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 17/50 | Loss: 0.1140 | Val Loss: 0.1562 | Acc: 0.9588 | Val Acc: 0.9331 | ETA: 10112s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 18/50 | Loss: 0.0963 | Val Loss: 0.1386 | Acc: 0.9664 | Val Acc: 0.9531 | ETA: 9808s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 19/50 | Loss: 0.1015 | Val Loss: 0.0694 | Acc: 0.9655 | Val Acc: 0.9659 | ETA: 9503s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 20/50 | Loss: 0.0768 | Val Loss: 0.0260 | Acc: 0.9722 | Val Acc: 0.9929 | ETA: 9198s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 21/50 | Loss: 0.0736 | Val Loss: 0.0410 | Acc: 0.9747 | Val Acc: 0.9829 | ETA: 8893s\n", + "[ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘] Epoch 22/50 | Loss: 0.0580 | Val Loss: 0.0110 | Acc: 0.9805 | Val Acc: 0.9986 | ETA: 8584s\n" + ] + }, + { + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mKeyboardInterrupt\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[27]\u001b[39m\u001b[32m, line 65\u001b[39m\n\u001b[32m 62\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mLoss Function: Categorical Crossentropy\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 63\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33m=\u001b[39m\u001b[33m\"\u001b[39m * \u001b[32m80\u001b[39m)\n\u001b[32m---> \u001b[39m\u001b[32m65\u001b[39m history = \u001b[43mmodel\u001b[49m\u001b[43m.\u001b[49m\u001b[43mfit\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 66\u001b[39m \u001b[43m \u001b[49m\u001b[43mtrain_datagen\u001b[49m\u001b[43m.\u001b[49m\u001b[43mflow\u001b[49m\u001b[43m(\u001b[49m\u001b[43mX_train\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43my_train_cat\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mbatch_size\u001b[49m\u001b[43m=\u001b[49m\u001b[43mBATCH_SIZE\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 67\u001b[39m \u001b[43m \u001b[49m\u001b[43mepochs\u001b[49m\u001b[43m=\u001b[49m\u001b[43mEPOCHS\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 68\u001b[39m \u001b[43m \u001b[49m\u001b[43mbatch_size\u001b[49m\u001b[43m=\u001b[49m\u001b[43mBATCH_SIZE\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 69\u001b[39m \u001b[43m \u001b[49m\u001b[43mvalidation_data\u001b[49m\u001b[43m=\u001b[49m\u001b[43m(\u001b[49m\u001b[43mX_val\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43my_val_cat\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 70\u001b[39m \u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[43m=\u001b[49m\u001b[43m[\u001b[49m\u001b[43mearly_stop\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mreduce_lr\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mProgressCallback\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 71\u001b[39m \u001b[43m \u001b[49m\u001b[43mverbose\u001b[49m\u001b[43m=\u001b[49m\u001b[32;43m0\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# Suppress default verbose output\u001b[39;49;00m\n\u001b[32m 72\u001b[39m \u001b[43m)\u001b[49m\n\u001b[32m 74\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33māœ… Model training and validation completed!\u001b[39m\u001b[33m\"\u001b[39m)\n", + "\u001b[36mFile \u001b[39m\u001b[32mD:\\PROJECT TA\\.venv\\Lib\\site-packages\\keras\\src\\utils\\traceback_utils.py:117\u001b[39m, in \u001b[36mfilter_traceback..error_handler\u001b[39m\u001b[34m(*args, **kwargs)\u001b[39m\n\u001b[32m 115\u001b[39m filtered_tb = \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 116\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m117\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 118\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m 119\u001b[39m filtered_tb = _process_traceback_frames(e.__traceback__)\n", + "\u001b[36mFile \u001b[39m\u001b[32mD:\\PROJECT TA\\.venv\\Lib\\site-packages\\keras\\src\\backend\\tensorflow\\trainer.py:399\u001b[39m, in \u001b[36mTensorFlowTrainer.fit\u001b[39m\u001b[34m(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, validation_batch_size, validation_freq)\u001b[39m\n\u001b[32m 397\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m begin_step, end_step, iterator \u001b[38;5;129;01min\u001b[39;00m epoch_iterator:\n\u001b[32m 398\u001b[39m callbacks.on_train_batch_begin(begin_step)\n\u001b[32m--> \u001b[39m\u001b[32m399\u001b[39m logs = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mtrain_function\u001b[49m\u001b[43m(\u001b[49m\u001b[43miterator\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 400\u001b[39m callbacks.on_train_batch_end(end_step, logs)\n\u001b[32m 401\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m.stop_training:\n", + "\u001b[36mFile \u001b[39m\u001b[32mD:\\PROJECT TA\\.venv\\Lib\\site-packages\\keras\\src\\backend\\tensorflow\\trainer.py:241\u001b[39m, in \u001b[36mTensorFlowTrainer._make_function..function\u001b[39m\u001b[34m(iterator)\u001b[39m\n\u001b[32m 237\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mfunction\u001b[39m(iterator):\n\u001b[32m 238\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(\n\u001b[32m 239\u001b[39m iterator, (tf.data.Iterator, tf.distribute.DistributedIterator)\n\u001b[32m 240\u001b[39m ):\n\u001b[32m--> \u001b[39m\u001b[32m241\u001b[39m opt_outputs = \u001b[43mmulti_step_on_iterator\u001b[49m\u001b[43m(\u001b[49m\u001b[43miterator\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 242\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m opt_outputs.has_value():\n\u001b[32m 243\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mStopIteration\u001b[39;00m\n", + "\u001b[36mFile \u001b[39m\u001b[32mD:\\PROJECT TA\\.venv\\Lib\\site-packages\\tensorflow\\python\\util\\traceback_utils.py:164\u001b[39m, in \u001b[36mfilter_traceback..error_handler\u001b[39m\u001b[34m(*args, **kwargs)\u001b[39m\n\u001b[32m 162\u001b[39m filtered_tb = \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 163\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m164\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 165\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m 166\u001b[39m filtered_tb = _process_traceback_frames(e.__traceback__)\n", + "\u001b[36mFile \u001b[39m\u001b[32mD:\\PROJECT TA\\.venv\\Lib\\site-packages\\tensorflow\\python\\eager\\polymorphic_function\\polymorphic_function.py:833\u001b[39m, in \u001b[36mFunction.__call__\u001b[39m\u001b[34m(self, *args, **kwds)\u001b[39m\n\u001b[32m 830\u001b[39m compiler = \u001b[33m\"\u001b[39m\u001b[33mxla\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._jit_compile \u001b[38;5;28;01melse\u001b[39;00m \u001b[33m\"\u001b[39m\u001b[33mnonXla\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 832\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m OptionalXlaContext(\u001b[38;5;28mself\u001b[39m._jit_compile):\n\u001b[32m--> \u001b[39m\u001b[32m833\u001b[39m result = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_call\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwds\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 835\u001b[39m new_tracing_count = \u001b[38;5;28mself\u001b[39m.experimental_get_tracing_count()\n\u001b[32m 836\u001b[39m without_tracing = (tracing_count == new_tracing_count)\n", + "\u001b[36mFile \u001b[39m\u001b[32mD:\\PROJECT TA\\.venv\\Lib\\site-packages\\tensorflow\\python\\eager\\polymorphic_function\\polymorphic_function.py:878\u001b[39m, in \u001b[36mFunction._call\u001b[39m\u001b[34m(self, *args, **kwds)\u001b[39m\n\u001b[32m 875\u001b[39m \u001b[38;5;28mself\u001b[39m._lock.release()\n\u001b[32m 876\u001b[39m \u001b[38;5;66;03m# In this case we have not created variables on the first call. So we can\u001b[39;00m\n\u001b[32m 877\u001b[39m \u001b[38;5;66;03m# run the first trace but we should fail if variables are created.\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m878\u001b[39m results = \u001b[43mtracing_compilation\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcall_function\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 879\u001b[39m \u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwds\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_variable_creation_config\u001b[49m\n\u001b[32m 880\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 881\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._created_variables:\n\u001b[32m 882\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mCreating variables on a non-first call to a function\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 883\u001b[39m \u001b[33m\"\u001b[39m\u001b[33m decorated with tf.function.\u001b[39m\u001b[33m\"\u001b[39m)\n", + "\u001b[36mFile \u001b[39m\u001b[32mD:\\PROJECT TA\\.venv\\Lib\\site-packages\\tensorflow\\python\\eager\\polymorphic_function\\tracing_compilation.py:139\u001b[39m, in \u001b[36mcall_function\u001b[39m\u001b[34m(args, kwargs, tracing_options)\u001b[39m\n\u001b[32m 137\u001b[39m bound_args = function.function_type.bind(*args, **kwargs)\n\u001b[32m 138\u001b[39m flat_inputs = function.function_type.unpack_inputs(bound_args)\n\u001b[32m--> \u001b[39m\u001b[32m139\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunction\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_call_flat\u001b[49m\u001b[43m(\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# pylint: disable=protected-access\u001b[39;49;00m\n\u001b[32m 140\u001b[39m \u001b[43m \u001b[49m\u001b[43mflat_inputs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcaptured_inputs\u001b[49m\u001b[43m=\u001b[49m\u001b[43mfunction\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcaptured_inputs\u001b[49m\n\u001b[32m 141\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32mD:\\PROJECT TA\\.venv\\Lib\\site-packages\\tensorflow\\python\\eager\\polymorphic_function\\concrete_function.py:1322\u001b[39m, in \u001b[36mConcreteFunction._call_flat\u001b[39m\u001b[34m(self, tensor_inputs, captured_inputs)\u001b[39m\n\u001b[32m 1318\u001b[39m possible_gradient_type = gradients_util.PossibleTapeGradientTypes(args)\n\u001b[32m 1319\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m (possible_gradient_type == gradients_util.POSSIBLE_GRADIENT_TYPES_NONE\n\u001b[32m 1320\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m executing_eagerly):\n\u001b[32m 1321\u001b[39m \u001b[38;5;66;03m# No tape is watching; skip to running the function.\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1322\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_inference_function\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcall_preflattened\u001b[49m\u001b[43m(\u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1323\u001b[39m forward_backward = \u001b[38;5;28mself\u001b[39m._select_forward_and_backward_functions(\n\u001b[32m 1324\u001b[39m args,\n\u001b[32m 1325\u001b[39m possible_gradient_type,\n\u001b[32m 1326\u001b[39m executing_eagerly)\n\u001b[32m 1327\u001b[39m forward_function, args_with_tangents = forward_backward.forward()\n", + "\u001b[36mFile \u001b[39m\u001b[32mD:\\PROJECT TA\\.venv\\Lib\\site-packages\\tensorflow\\python\\eager\\polymorphic_function\\atomic_function.py:216\u001b[39m, in \u001b[36mAtomicFunction.call_preflattened\u001b[39m\u001b[34m(self, args)\u001b[39m\n\u001b[32m 214\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mcall_preflattened\u001b[39m(\u001b[38;5;28mself\u001b[39m, args: Sequence[core.Tensor]) -> Any:\n\u001b[32m 215\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Calls with flattened tensor inputs and returns the structured output.\"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m216\u001b[39m flat_outputs = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mcall_flat\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 217\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.function_type.pack_output(flat_outputs)\n", + "\u001b[36mFile \u001b[39m\u001b[32mD:\\PROJECT TA\\.venv\\Lib\\site-packages\\tensorflow\\python\\eager\\polymorphic_function\\atomic_function.py:251\u001b[39m, in \u001b[36mAtomicFunction.call_flat\u001b[39m\u001b[34m(self, *args)\u001b[39m\n\u001b[32m 249\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m record.stop_recording():\n\u001b[32m 250\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._bound_context.executing_eagerly():\n\u001b[32m--> \u001b[39m\u001b[32m251\u001b[39m outputs = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_bound_context\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcall_function\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 252\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mname\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 253\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 254\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mlen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mfunction_type\u001b[49m\u001b[43m.\u001b[49m\u001b[43mflat_outputs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 255\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 256\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 257\u001b[39m outputs = make_call_op_in_graph(\n\u001b[32m 258\u001b[39m \u001b[38;5;28mself\u001b[39m,\n\u001b[32m 259\u001b[39m \u001b[38;5;28mlist\u001b[39m(args),\n\u001b[32m 260\u001b[39m \u001b[38;5;28mself\u001b[39m._bound_context.function_call_options.as_attrs(),\n\u001b[32m 261\u001b[39m )\n", + "\u001b[36mFile \u001b[39m\u001b[32mD:\\PROJECT TA\\.venv\\Lib\\site-packages\\tensorflow\\python\\eager\\context.py:1688\u001b[39m, in \u001b[36mContext.call_function\u001b[39m\u001b[34m(self, name, tensor_inputs, num_outputs)\u001b[39m\n\u001b[32m 1686\u001b[39m cancellation_context = cancellation.context()\n\u001b[32m 1687\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m cancellation_context \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m1688\u001b[39m outputs = \u001b[43mexecute\u001b[49m\u001b[43m.\u001b[49m\u001b[43mexecute\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 1689\u001b[39m \u001b[43m \u001b[49m\u001b[43mname\u001b[49m\u001b[43m.\u001b[49m\u001b[43mdecode\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mutf-8\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1690\u001b[39m \u001b[43m \u001b[49m\u001b[43mnum_outputs\u001b[49m\u001b[43m=\u001b[49m\u001b[43mnum_outputs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1691\u001b[39m \u001b[43m \u001b[49m\u001b[43minputs\u001b[49m\u001b[43m=\u001b[49m\u001b[43mtensor_inputs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1692\u001b[39m \u001b[43m \u001b[49m\u001b[43mattrs\u001b[49m\u001b[43m=\u001b[49m\u001b[43mattrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1693\u001b[39m \u001b[43m \u001b[49m\u001b[43mctx\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 1694\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1695\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 1696\u001b[39m outputs = execute.execute_with_cancellation(\n\u001b[32m 1697\u001b[39m name.decode(\u001b[33m\"\u001b[39m\u001b[33mutf-8\u001b[39m\u001b[33m\"\u001b[39m),\n\u001b[32m 1698\u001b[39m num_outputs=num_outputs,\n\u001b[32m (...)\u001b[39m\u001b[32m 1702\u001b[39m cancellation_manager=cancellation_context,\n\u001b[32m 1703\u001b[39m )\n", + "\u001b[36mFile \u001b[39m\u001b[32mD:\\PROJECT TA\\.venv\\Lib\\site-packages\\tensorflow\\python\\eager\\execute.py:53\u001b[39m, in \u001b[36mquick_execute\u001b[39m\u001b[34m(op_name, num_outputs, inputs, attrs, ctx, name)\u001b[39m\n\u001b[32m 51\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m 52\u001b[39m ctx.ensure_initialized()\n\u001b[32m---> \u001b[39m\u001b[32m53\u001b[39m tensors = \u001b[43mpywrap_tfe\u001b[49m\u001b[43m.\u001b[49m\u001b[43mTFE_Py_Execute\u001b[49m\u001b[43m(\u001b[49m\u001b[43mctx\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_handle\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdevice_name\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mop_name\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 54\u001b[39m \u001b[43m \u001b[49m\u001b[43minputs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mattrs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnum_outputs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 55\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m core._NotOkStatusException \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m 56\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m name \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "\u001b[31mKeyboardInterrupt\u001b[39m: " ] } ], @@ -1051,48 +1016,10 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "0c34bf89", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "================================================================================\n", - "šŸ“ˆ EVALUATING MODEL ON TEST SET\n", - "================================================================================\n", - "\n", - "šŸ“Š Computing predictions...\n", - "āœ“ Predictions computed\n", - "\n", - "ā±ļø Computing metrics...\n", - "================================================================================\n", - "Test Loss: 0.0002\n", - "Test Accuracy: 1.0000 (100.00%)\n", - "================================================================================\n", - "\n", - "šŸ“‹ Generating detailed classification report...\n", - "--------------------------------------------------------------------------------\n", - " precision recall f1-score support\n", - "\n", - "Bacterialblight 1.00 1.00 1.00 241\n", - " Brownspot 1.00 1.00 1.00 243\n", - " Leafsmut 1.00 1.00 1.00 219\n", - "\n", - " accuracy 1.00 703\n", - " macro avg 1.00 1.00 1.00 703\n", - " weighted avg 1.00 1.00 1.00 703\n", - "\n", - "--------------------------------------------------------------------------------\n", - "\n", - "āœ“ Overall Accuracy: 1.0000\n", - "================================================================================\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "# Evaluate on test set\n", "print(\"\\n\" + \"=\" * 80)\n", @@ -1136,65 +1063,10 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "586213e5", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "šŸ”¢ Generating confusion matrix...\n", - "--------------------------------------------------------------------------------\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Processing: 0%| | 0/2 [00:00" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Processing: 100%|ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ| 2/2 [00:00<00:00, 4.20it/s]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "šŸ“Š Confusion Matrix Details:\n", - "--------------------------------------------------------------------------------\n", - " Bacterialblight : 241/ 241 correct (100.0%)\n", - " Brownspot : 243/ 243 correct (100.0%)\n", - " Leafsmut : 219/ 219 correct (100.0%)\n", - "--------------------------------------------------------------------------------\n", - "āœ“ Confusion matrix completed!\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "outputs": [], "source": [ "# Plot confusion matrix\n", "print(\"šŸ”¢ Generating confusion matrix...\")\n", @@ -1660,9 +1532,9 @@ ], "metadata": { "kernelspec": { - "display_name": "base", + "display_name": "Python (TA-Project)", "language": "python", - "name": "python3" + "name": "venv_ta" }, "language_info": { "codemirror_mode": { @@ -1674,7 +1546,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.5" + "version": "3.11.5" } }, "nbformat": 4, diff --git a/setup.bat b/setup.bat index 46e77ca..7554e3f 100644 --- a/setup.bat +++ b/setup.bat @@ -44,15 +44,14 @@ echo ======================================== echo. echo Langkah selanjutnya: echo. -echo 1. Buka Terminal 1 dan jalankan Python API: -echo cd "rice leaf diseases dataset" -echo python api_server.py -echo. -echo 2. Buka Terminal 2 dan jalankan Laravel: +echo 1. Buka Terminal 1 dan jalankan Laravel: echo cd "web_TA" echo php artisan serve echo. -echo 3. Test API: +echo 2. Test API: echo python test_api.py echo. +echo 3. Pastikan Python environment memiliki dependency inferensi: +echo pip install -r "rice leaf diseases dataset\requirements_api.txt" +echo. pause diff --git a/setup.sh b/setup.sh index 220bd66..5165466 100644 --- a/setup.sh +++ b/setup.sh @@ -25,9 +25,9 @@ fi # Check model files echo "" echo "Cek file model..." -if [ -f "rice_leaf_diseases dataset/rice_leaf_disease_model.keras" ]; then +if [ -f "rice leaf diseases dataset/rice_leaf_disease_model.keras" ]; then echo "[OK] Model keras ditemukan" -elif [ -f "rice_leaf_diseases dataset/rice_leaf_disease_model.h5" ]; then +elif [ -f "rice leaf diseases dataset/rice_leaf_disease_model.h5" ]; then echo "[OK] Model h5 ditemukan" else echo "[WARNING] File model tidak ditemukan" @@ -41,14 +41,13 @@ echo "========================================" echo "" echo "Langkah selanjutnya:" echo "" -echo "1. Buka Terminal 1 dan jalankan Python API:" -echo " cd \"rice leaf diseases dataset\"" -echo " python3 api_server.py" -echo "" -echo "2. Buka Terminal 2 dan jalankan Laravel:" +echo "1. Buka Terminal 1 dan jalankan Laravel:" echo " cd web_TA" echo " php artisan serve" echo "" -echo "3. Test API:" +echo "2. Test API:" echo " python3 test_api.py" echo "" +echo "3. Pastikan dependency inferensi Python sudah terpasang:" +echo " pip3 install -r \"rice leaf diseases dataset/requirements_api.txt\"" +echo "" diff --git a/test_api.py b/test_api.py index b6ff00b..fa91bbb 100644 --- a/test_api.py +++ b/test_api.py @@ -3,59 +3,60 @@ Simple test script untuk menguji API Classification """ import requests -import base64 import json from pathlib import Path # Konfigurasi LARAVEL_API_BASE = "http://127.0.0.1:8000/api/classification" -PYTHON_API_BASE = "http://127.0.0.1:5000" -def test_python_api_health(): - """Test health check Python API""" +def test_laravel_api_health(): + """Test health check API klasifikasi di Laravel""" print("\n" + "="*60) - print("TEST 1: Python API Health Check") + print("TEST 1: Laravel Classification Health Check") print("="*60) try: - response = requests.get(f"{PYTHON_API_BASE}/health", timeout=5) + response = requests.get(f"{LARAVEL_API_BASE}/health", timeout=15) if response.status_code == 200: data = response.json() - print("āœ… Python API berhasil dihubungi!") - print(f" Status: {data['status']}") - print(f" Model Loaded: {data['model_loaded']}") - print(f" Classes: {', '.join(data['classes'])}") + print("āœ… Laravel API berhasil dihubungi!") + print(f" Success: {data.get('success')}") + print(f" Message: {data.get('message')}") + model_info = data.get('model_info', {}) + print(f" Model Loaded: {model_info.get('model_loaded')}") + print(f" Classes: {', '.join(model_info.get('classes', []))}") return True else: print(f"āŒ API responded with status {response.status_code}") + print(f" Response: {response.text}") return False except requests.exceptions.ConnectionError: - print("āŒ Tidak dapat menghubungi Python API") - print(f" Pastikan server berjalan di {PYTHON_API_BASE}") + print("āŒ Tidak dapat menghubungi Laravel API") + print(" Pastikan server berjalan di http://127.0.0.1:8000") return False except Exception as e: print(f"āŒ Error: {str(e)}") return False -def test_laravel_api_connection(): - """Test connection via Laravel API""" +def test_laravel_api_info(): + """Test endpoint info model via Laravel API""" print("\n" + "="*60) - print("TEST 2: Laravel API Connection Test") + print("TEST 2: Laravel Model Info") print("="*60) try: - response = requests.get(f"{LARAVEL_API_BASE}/test", timeout=5) + response = requests.get(f"{LARAVEL_API_BASE}/info", timeout=15) if response.status_code == 200: data = response.json() - print("āœ… Laravel API berhasil dihubungi!") + print("āœ… Endpoint info berhasil diakses!") print(f" Success: {data['success']}") print(f" Message: {data['message']}") - if 'model_info' in data: - print(f" Model Info: {json.dumps(data['model_info'], indent=2)}") + if 'data' in data: + print(f" Model Info: {json.dumps(data['data'], indent=2)}") return True else: print(f"āŒ API responded with status {response.status_code}") @@ -167,11 +168,9 @@ def main(): print("# Rice Leaf Disease Classification API - Test Suite") print("#" * 60) - # Test Python API - python_ok = test_python_api_health() - - # Test Laravel API - laravel_ok = test_laravel_api_connection() + # Test Laravel API health dan info + health_ok = test_laravel_api_health() + info_ok = test_laravel_api_info() # Test dengan gambar contoh jika ada test_image_paths = [ @@ -202,8 +201,8 @@ def main(): print("\n" + "="*60) print("TEST SUMMARY") print("="*60) - print(f"Python API: {'āœ… OK' if python_ok else 'āŒ FAILED'}") - print(f"Laravel API: {'āœ… OK' if laravel_ok else 'āŒ FAILED'}") + print(f"Laravel Health: {'āœ… OK' if health_ok else 'āŒ FAILED'}") + print(f"Laravel Info: {'āœ… OK' if info_ok else 'āŒ FAILED'}") print("\nUntuk hasil lengkap, sediakan file gambar test.\n") diff --git a/web_TA/.env.example b/web_TA/.env.example index c0660ea..6de80cf 100644 --- a/web_TA/.env.example +++ b/web_TA/.env.example @@ -62,4 +62,8 @@ AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET= AWS_USE_PATH_STYLE_ENDPOINT=false +PYTHON_EXECUTABLE=python +PYTHON_CLASSIFIER_SCRIPT= +RICE_MODEL_DIR= + VITE_APP_NAME="${APP_NAME}" diff --git a/web_TA/app/Http/Controllers/ClassificationController.php b/web_TA/app/Http/Controllers/ClassificationController.php index 46addd9..8cda12e 100644 --- a/web_TA/app/Http/Controllers/ClassificationController.php +++ b/web_TA/app/Http/Controllers/ClassificationController.php @@ -3,16 +3,20 @@ namespace App\Http\Controllers; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Facades\Log; use App\Models\Classification; +use App\Services\PythonClassificationService; class ClassificationController extends Controller { - private $pythonApiUrl = 'http://127.0.0.1:5000'; // URL Flask API + public function __construct( + private readonly PythonClassificationService $classificationService + ) { + } /** - * Menerima gambar dan mengirim ke Python API untuk klasifikasi + * Menerima gambar dan mengklasifikasi melalui service internal Laravel * POST /api/classify */ public function classify(Request $request) @@ -30,36 +34,44 @@ public function classify(Request $request) $imageContent = file_get_contents($file->getRealPath()); $base64Image = base64_encode($imageContent); - // Kirim request ke Flask API - $response = Http::timeout(30)->post($this->pythonApiUrl . '/classify', [ + // Jalankan klasifikasi melalui service lokal (tanpa Flask API) + $result = $this->classificationService->classifyFromBase64([ 'image' => $base64Image, 'filename' => $file->getClientOriginalName(), ]); - // Cek apakah request berhasil - if ($response->failed()) { + if (!($result['success'] ?? false)) { return response()->json([ 'success' => false, 'message' => 'Gagal menghubungi model classification', - 'error' => $response->body() + 'error' => $result['message'] ?? 'Unknown error', ], 500); } - $result = $response->json(); - // Tambahkan informasi detail tentang penyakit $diseaseInfo = $this->getDiseaseInfo($result['predicted_class']); - // Save to database - Classification::create([ - 'filename' => $file->getClientOriginalName(), - 'predicted_class' => $result['predicted_class'], - 'confidence' => $result['confidence'], - 'all_predictions' => $result['all_predictions'], - 'disease_name' => $diseaseInfo['name'], - 'severity' => $diseaseInfo['severity'], - 'notes' => 'Classification without storage', - ]); + $savedToDatabase = true; + $persistenceWarning = null; + + try { + Classification::create([ + 'filename' => $file->getClientOriginalName(), + 'predicted_class' => $result['predicted_class'], + 'confidence' => $result['confidence'], + 'all_predictions' => $result['all_predictions'], + 'disease_name' => $diseaseInfo['name'], + 'severity' => $diseaseInfo['severity'], + 'notes' => 'Classification without storage', + ]); + } catch (\Throwable $dbException) { + $savedToDatabase = false; + $persistenceWarning = 'Klasifikasi berhasil, tetapi gagal simpan ke database.'; + Log::warning('Classification result not persisted', [ + 'filename' => $file->getClientOriginalName(), + 'error' => $dbException->getMessage(), + ]); + } return response()->json([ 'success' => true, @@ -70,6 +82,8 @@ public function classify(Request $request) 'confidence_value' => $result['confidence'], 'all_predictions' => $result['all_predictions'], 'disease_info' => $diseaseInfo, + 'saved_to_database' => $savedToDatabase, + 'persistence_warning' => $persistenceWarning, 'timestamp' => now(), ] ], 200); @@ -89,7 +103,7 @@ public function classify(Request $request) } /** - * Upload gambar dan simpan ke database, kemudian klasifikasi + * Upload gambar dan simpan ke database, kemudian klasifikasi * POST /api/classify-and-save */ public function classifyAndSave(Request $request) @@ -109,36 +123,47 @@ public function classifyAndSave(Request $request) $imageContent = file_get_contents($file->getRealPath()); $base64Image = base64_encode($imageContent); - // Kirim request ke Flask API - $response = Http::timeout(30)->post($this->pythonApiUrl . '/classify', [ + // Jalankan klasifikasi melalui service lokal (tanpa Flask API) + $result = $this->classificationService->classifyFromBase64([ 'image' => $base64Image, 'filename' => $file->getClientOriginalName(), ]); - if ($response->failed()) { + if (!($result['success'] ?? false)) { // Hapus file yang sudah disimpan jika klasifikasi gagal Storage::disk('public')->delete($storagePath); return response()->json([ 'success' => false, 'message' => 'Gagal menghubungi model classification', + 'error' => $result['message'] ?? 'Unknown error', ], 500); } - - $result = $response->json(); $diseaseInfo = $this->getDiseaseInfo($result['predicted_class']); - // Save to database - Classification::create([ - 'image_path' => $storagePath, - 'filename' => $file->getClientOriginalName(), - 'predicted_class' => $result['predicted_class'], - 'confidence' => $result['confidence'], - 'all_predictions' => $result['all_predictions'], - 'disease_name' => $diseaseInfo['name'], - 'severity' => $diseaseInfo['severity'], - 'notes' => $request->input('notes'), - ]); + $savedToDatabase = true; + $persistenceWarning = null; + + try { + Classification::create([ + 'image_path' => $storagePath, + 'filename' => $file->getClientOriginalName(), + 'predicted_class' => $result['predicted_class'], + 'confidence' => $result['confidence'], + 'all_predictions' => $result['all_predictions'], + 'disease_name' => $diseaseInfo['name'], + 'severity' => $diseaseInfo['severity'], + 'notes' => $request->input('notes'), + ]); + } catch (\Throwable $dbException) { + $savedToDatabase = false; + $persistenceWarning = 'Gambar berhasil diklasifikasi dan disimpan file, tetapi gagal simpan riwayat ke database.'; + Log::warning('Classification file stored but DB persist failed', [ + 'filename' => $file->getClientOriginalName(), + 'path' => $storagePath, + 'error' => $dbException->getMessage(), + ]); + } return response()->json([ 'success' => true, @@ -150,6 +175,8 @@ public function classifyAndSave(Request $request) 'confidence_value' => $result['confidence'], 'all_predictions' => $result['all_predictions'], 'disease_info' => $diseaseInfo, + 'saved_to_database' => $savedToDatabase, + 'persistence_warning' => $persistenceWarning, 'notes' => $request->input('notes'), 'timestamp' => now(), ] @@ -170,34 +197,143 @@ public function classifyAndSave(Request $request) } /** - * Test koneksi ke Python API + * Klasifikasi gambar dari URL + * POST /api/classification/classify-from-url + */ + public function classifyFromUrl(Request $request) + { + try { + $request->validate([ + 'image_url' => 'required|url|max:2048', + 'notes' => 'nullable|string|max:500', + 'save' => 'nullable|boolean', + ]); + + $imageUrl = $request->input('image_url'); + $result = $this->classificationService->classifyFromUrl([ + 'image_url' => $imageUrl, + ]); + + if (!($result['success'] ?? false)) { + return response()->json([ + 'success' => false, + 'message' => $result['message'] ?? 'Gagal memproses gambar dari URL', + ], 400); + } + + $diseaseInfo = $this->getDiseaseInfo($result['predicted_class']); + + if ($request->boolean('save')) { + $urlPath = parse_url($imageUrl, PHP_URL_PATH); + $filename = is_string($urlPath) && $urlPath !== '' + ? basename($urlPath) + : 'from_url_image'; + + Classification::create([ + 'filename' => $filename, + 'predicted_class' => $result['predicted_class'], + 'confidence' => $result['confidence'], + 'all_predictions' => $result['all_predictions'], + 'disease_name' => $diseaseInfo['name'], + 'severity' => $diseaseInfo['severity'], + 'notes' => $request->input('notes', 'Classification from URL'), + ]); + } + + return response()->json([ + 'success' => true, + 'message' => 'Klasifikasi dari URL berhasil', + 'data' => [ + 'url' => $imageUrl, + 'predicted_class' => $result['predicted_class'], + 'confidence' => round($result['confidence'] * 100, 2) . '%', + 'confidence_value' => $result['confidence'], + 'all_predictions' => $result['all_predictions'], + 'disease_info' => $diseaseInfo, + 'timestamp' => now(), + ] + ], 200); + } catch (\Illuminate\Validation\ValidationException $e) { + return response()->json([ + 'success' => false, + 'message' => 'Validasi gagal', + 'errors' => $e->errors() + ], 422); + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => 'Terjadi kesalahan: ' . $e->getMessage(), + ], 500); + } + } + + /** + * Health check klasifikasi service + * GET /api/classification/health + */ + public function health() + { + try { + $result = $this->classificationService->health(); + + if ($result['status'] !== 'ok') { + return response()->json([ + 'success' => false, + 'message' => $result['message'] ?? 'Model tidak siap', + 'model_info' => $result, + ], 503); + } + + return response()->json([ + 'success' => true, + 'message' => 'Koneksi ke model classification berhasil', + 'model_info' => $result, + ], 200); + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => 'Tidak dapat melakukan health check model: ' . $e->getMessage(), + ], 500); + } + } + + /** + * Informasi model klasifikasi + * GET /api/classification/info + */ + public function info() + { + try { + $result = $this->classificationService->info(); + + if (!($result['model_loaded'] ?? false)) { + return response()->json([ + 'success' => false, + 'message' => $result['message'] ?? 'Model tidak tersedia', + 'data' => $result, + ], 503); + } + + return response()->json([ + 'success' => true, + 'message' => 'Informasi model berhasil diambil', + 'data' => $result, + ], 200); + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => 'Gagal mengambil informasi model: ' . $e->getMessage(), + ], 500); + } + } + + /** + * Backward-compatible endpoint test (alias untuk health) * GET /api/classification/test */ public function testConnection() { - try { - $response = Http::timeout(10)->post($this->pythonApiUrl . '/health', []); - - if ($response->successful()) { - return response()->json([ - 'success' => true, - 'message' => 'Koneksi ke model API berhasil', - 'model_info' => $response->json() - ], 200); - } - - return response()->json([ - 'success' => false, - 'message' => 'Model API tidak merespons dengan benar' - ], 500); - - } catch (\Exception $e) { - return response()->json([ - 'success' => false, - 'message' => 'Tidak dapat menghubungi model API: ' . $e->getMessage(), - 'hint' => 'Pastikan server Python API sudah berjalan di ' . $this->pythonApiUrl - ], 500); - } + return $this->health(); } /** diff --git a/web_TA/app/Services/PythonClassificationService.php b/web_TA/app/Services/PythonClassificationService.php new file mode 100644 index 0000000..906cbb9 --- /dev/null +++ b/web_TA/app/Services/PythonClassificationService.php @@ -0,0 +1,130 @@ +pythonExecutable = env('PYTHON_EXECUTABLE', 'python'); + $this->scriptPath = env('PYTHON_CLASSIFIER_SCRIPT', base_path('scripts/rice_inference.py')); + $this->modelDirectory = env('RICE_MODEL_DIR', base_path('../rice leaf diseases dataset')); + } + + public function classifyFromBase64(array $payload): array + { + return $this->runAction('classify', $payload, 120); + } + + public function classifyFromUrl(array $payload): array + { + return $this->runAction('classify-from-url', $payload, 120); + } + + public function health(): array + { + return $this->runAction('health'); + } + + public function info(): array + { + return $this->runAction('info'); + } + + private function runAction(string $action, array $payload = [], int $timeout = 60): array + { + if (!is_file($this->scriptPath)) { + throw new RuntimeException("Script classifier tidak ditemukan di {$this->scriptPath}"); + } + + $command = [ + $this->pythonExecutable, + $this->scriptPath, + $action, + '--model-dir', + $this->modelDirectory, + ]; + + $process = new Process($command, base_path(), $this->buildProcessEnvironment()); + $process->setTimeout($timeout); + $process->setInput(json_encode($payload, JSON_UNESCAPED_SLASHES)); + $process->run(); + + if (!$process->isSuccessful()) { + $stderr = trim($process->getErrorOutput()); + $stdout = trim($process->getOutput()); + throw new RuntimeException($stderr !== '' ? $stderr : ($stdout !== '' ? $stdout : 'Gagal menjalankan proses inferensi Python')); + } + + $output = trim($process->getOutput()); + if ($output === '') { + throw new RuntimeException('Proses inferensi Python tidak mengembalikan output.'); + } + + $decoded = json_decode($output, true); + if (!is_array($decoded)) { + throw new RuntimeException('Output inferensi Python bukan JSON yang valid.'); + } + + return $decoded; + } + + private function buildProcessEnvironment(): array + { + $environment = array_merge($_SERVER, $_ENV); + + // Prevent Python from using conflicting host-level overrides. + unset($environment['PYTHONHOME'], $environment['PYTHONPATH']); + + $pythonDir = dirname($this->pythonExecutable); + $currentPath = getenv('PATH') ?: ($environment['PATH'] ?? ''); + $environment['PATH'] = $pythonDir . PATH_SEPARATOR . $currentPath; + + if (!isset($environment['SystemRoot']) || $environment['SystemRoot'] === '') { + $environment['SystemRoot'] = getenv('SystemRoot') ?: 'C:\\Windows'; + } + + if (!isset($environment['WINDIR']) || $environment['WINDIR'] === '') { + $environment['WINDIR'] = getenv('WINDIR') ?: 'C:\\Windows'; + } + + $fallbackUserProfile = getenv('USERPROFILE') ?: ('C:\\Users\\' . (getenv('USERNAME') ?: 'Public')); + + if (!isset($environment['USERPROFILE']) || $environment['USERPROFILE'] === '') { + $environment['USERPROFILE'] = $fallbackUserProfile; + } + + if (!isset($environment['HOMEDRIVE']) || $environment['HOMEDRIVE'] === '') { + $environment['HOMEDRIVE'] = getenv('HOMEDRIVE') ?: substr($fallbackUserProfile, 0, 2); + } + + if (!isset($environment['HOMEPATH']) || $environment['HOMEPATH'] === '') { + $environment['HOMEPATH'] = getenv('HOMEPATH') ?: substr($fallbackUserProfile, 2); + } + + if (!isset($environment['APPDATA']) || $environment['APPDATA'] === '') { + $environment['APPDATA'] = getenv('APPDATA') ?: ($fallbackUserProfile . '\\AppData\\Roaming'); + } + + if (!isset($environment['LOCALAPPDATA']) || $environment['LOCALAPPDATA'] === '') { + $environment['LOCALAPPDATA'] = getenv('LOCALAPPDATA') ?: ($fallbackUserProfile . '\\AppData\\Local'); + } + + if (!isset($environment['TEMP']) || $environment['TEMP'] === '') { + $environment['TEMP'] = getenv('TEMP') ?: ($fallbackUserProfile . '\\AppData\\Local\\Temp'); + } + + if (!isset($environment['TMP']) || $environment['TMP'] === '') { + $environment['TMP'] = getenv('TMP') ?: $environment['TEMP']; + } + + return $environment; + } +} diff --git a/web_TA/routes/api.php b/web_TA/routes/api.php index 0664ce1..59d2bd3 100644 --- a/web_TA/routes/api.php +++ b/web_TA/routes/api.php @@ -15,11 +15,16 @@ // Classification endpoints Route::prefix('classification')->group(function () { - // Test koneksi ke Python API + // Health check klasifikasi (alias test untuk backward compatibility) + Route::get('/health', [ClassificationController::class, 'health']); + Route::get('/info', [ClassificationController::class, 'info']); Route::get('/test', [ClassificationController::class, 'testConnection']); // Klasifikasi gambar (hanya analisis) Route::post('/classify', [ClassificationController::class, 'classify']); + + // Klasifikasi gambar dari URL + Route::post('/classify-from-url', [ClassificationController::class, 'classifyFromUrl']); // Klasifikasi dan simpan gambar Route::post('/classify-and-save', [ClassificationController::class, 'classifyAndSave']); diff --git a/web_TA/scripts/rice_inference.py b/web_TA/scripts/rice_inference.py new file mode 100644 index 0000000..e950007 --- /dev/null +++ b/web_TA/scripts/rice_inference.py @@ -0,0 +1,293 @@ +""" +Python CLI untuk inferensi model rice leaf disease. +Dipanggil langsung dari Laravel, tanpa Flask API. +""" + +import argparse +import base64 +import json +import os +import sys +from io import BytesIO + +import numpy as np +import requests +from PIL import Image + + +CLASS_NAMES = ["Bacterialblight", "Brownspot", "Leafsmut"] +IMG_SIZE = (224, 224) + +try: + from tensorflow import keras + TENSORFLOW_AVAILABLE = True + TENSORFLOW_IMPORT_ERROR = None +except Exception: + keras = None + TENSORFLOW_AVAILABLE = False + TENSORFLOW_IMPORT_ERROR = str(sys.exc_info()[1]) + +MODEL = None +MODEL_PATH = None + + +def _read_json_input() -> dict: + raw = sys.stdin.read().strip() + if not raw: + return {} + try: + data = json.loads(raw) + return data if isinstance(data, dict) else {} + except json.JSONDecodeError: + return {} + + +def _emit(data: dict, exit_code: int = 0) -> None: + print(json.dumps(data, ensure_ascii=True)) + raise SystemExit(exit_code) + + +def _find_model_file(model_dir: str) -> str | None: + candidates = [ + "rice_leaf_disease_model.keras", + "rice_leaf_disease_model.h5", + "rice_leaf_disease_model.json", + ] + + for file_name in candidates: + full_path = os.path.join(model_dir, file_name) + if os.path.isfile(full_path): + return full_path + return None + + +def _load_model(model_dir: str): + global MODEL, MODEL_PATH + + if MODEL is not None: + return MODEL + + if not TENSORFLOW_AVAILABLE: + raise RuntimeError("TensorFlow tidak tersedia pada environment Python ini.") + + model_file = _find_model_file(model_dir) + if model_file is None: + raise RuntimeError( + "Model tidak ditemukan. Pastikan salah satu file ini ada: " + "rice_leaf_disease_model.keras, rice_leaf_disease_model.h5, rice_leaf_disease_model.json" + ) + + MODEL_PATH = model_file + + if model_file.endswith(".keras") or model_file.endswith(".h5"): + MODEL = keras.models.load_model(model_file) + return MODEL + + with open(model_file, "r", encoding="utf-8") as f: + model_json = f.read() + + MODEL = keras.models.model_from_json(model_json) + + weights_base = model_file.replace(".json", "") + weight_candidates = [ + f"{weights_base}.h5", + f"{weights_base}_weights.h5", + ] + + for weights_file in weight_candidates: + if os.path.isfile(weights_file): + MODEL.load_weights(weights_file) + return MODEL + + raise RuntimeError("Model JSON ditemukan, tetapi file weights tidak ditemukan.") + + +def _preprocess_image(image_bytes: bytes): + image = Image.open(BytesIO(image_bytes)) + if image.mode != "RGB": + image = image.convert("RGB") + + try: + resample_filter = Image.Resampling.LANCZOS + except AttributeError: + resample_filter = Image.LANCZOS + + image = image.resize(IMG_SIZE, resample_filter) + + img_array = np.array(image, dtype="float32") / 255.0 + img_array = np.expand_dims(img_array, axis=0) + return img_array + + +def _predict(image_bytes: bytes, model_dir: str) -> dict: + model = _load_model(model_dir) + + img_array = _preprocess_image(image_bytes) + predictions = model.predict(img_array, verbose=0) + + predicted_idx = int(np.argmax(predictions[0])) + predicted_class = CLASS_NAMES[predicted_idx] + confidence = float(predictions[0][predicted_idx]) + + all_predictions = { + class_name: float(predictions[0][idx]) + for idx, class_name in enumerate(CLASS_NAMES) + } + + return { + "success": True, + "predicted_class": predicted_class, + "confidence": confidence, + "all_predictions": all_predictions, + "model_path": MODEL_PATH, + } + + +def action_classify(model_dir: str) -> None: + payload = _read_json_input() + image_base64 = payload.get("image") + + if not image_base64 or not isinstance(image_base64, str): + _emit({"success": False, "message": "Field 'image' (base64) diperlukan"}, 0) + + try: + image_bytes = base64.b64decode(image_base64) + result = _predict(image_bytes, model_dir) + _emit(result, 0) + except Exception as e: + _emit({"success": False, "message": str(e)}, 0) + + +def action_classify_from_url(model_dir: str) -> None: + payload = _read_json_input() + image_url = payload.get("image_url") + + if not image_url or not isinstance(image_url, str): + _emit({"success": False, "message": "Field 'image_url' diperlukan"}, 0) + + try: + response = requests.get(image_url, timeout=10) + if response.status_code != 200: + _emit({"success": False, "message": "Gagal download image dari URL"}, 0) + + result = _predict(response.content, model_dir) + result["url"] = image_url + _emit(result, 0) + except Exception as e: + _emit({"success": False, "message": str(e)}, 0) + + +def action_health(model_dir: str) -> None: + if not TENSORFLOW_AVAILABLE: + message = "TensorFlow tidak tersedia" + if TENSORFLOW_IMPORT_ERROR: + message = f"TensorFlow tidak tersedia: {TENSORFLOW_IMPORT_ERROR}" + + _emit( + { + "status": "error", + "message": message, + "model_loaded": False, + "python_executable": sys.executable, + "classes": CLASS_NAMES, + }, + 0, + ) + + try: + model = _load_model(model_dir) + _emit( + { + "status": "ok", + "message": "Model siap digunakan", + "model_loaded": True, + "model_path": MODEL_PATH, + "python_executable": sys.executable, + "classes": CLASS_NAMES, + "input_shape": str(model.input_shape), + }, + 0, + ) + except Exception as e: + _emit( + { + "status": "error", + "message": str(e), + "model_loaded": False, + "python_executable": sys.executable, + "classes": CLASS_NAMES, + }, + 0, + ) + + +def action_info(model_dir: str) -> None: + if not TENSORFLOW_AVAILABLE: + message = "TensorFlow tidak tersedia" + if TENSORFLOW_IMPORT_ERROR: + message = f"TensorFlow tidak tersedia: {TENSORFLOW_IMPORT_ERROR}" + + _emit( + { + "model_loaded": False, + "message": message, + "python_executable": sys.executable, + "classes": CLASS_NAMES, + "number_of_classes": len(CLASS_NAMES), + }, + 0, + ) + + try: + model = _load_model(model_dir) + _emit( + { + "model_loaded": True, + "model_path": MODEL_PATH, + "python_executable": sys.executable, + "classes": CLASS_NAMES, + "number_of_classes": len(CLASS_NAMES), + "input_shape": str(model.input_shape), + "number_of_layers": len(model.layers), + "total_parameters": int(model.count_params()), + }, + 0, + ) + except Exception as e: + _emit( + { + "model_loaded": False, + "message": str(e), + "python_executable": sys.executable, + "classes": CLASS_NAMES, + "number_of_classes": len(CLASS_NAMES), + }, + 0, + ) + + +def main() -> None: + parser = argparse.ArgumentParser(description="Rice leaf disease inference CLI") + parser.add_argument( + "action", + choices=["classify", "classify-from-url", "health", "info"], + ) + parser.add_argument( + "--model-dir", + required=True, + help="Direktori tempat file model berada", + ) + args = parser.parse_args() + + if args.action == "classify": + action_classify(args.model_dir) + if args.action == "classify-from-url": + action_classify_from_url(args.model_dir) + if args.action == "health": + action_health(args.model_dir) + if args.action == "info": + action_info(args.model_dir) + + +if __name__ == "__main__": + main() diff --git a/web_TA/~/.keras/keras.json b/web_TA/~/.keras/keras.json new file mode 100644 index 0000000..bc2cae3 --- /dev/null +++ b/web_TA/~/.keras/keras.json @@ -0,0 +1,6 @@ +{ + "floatx": "float32", + "epsilon": 1e-07, + "backend": "tensorflow", + "image_data_format": "channels_last" +} \ No newline at end of file