{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":5048,"databundleVersionId":868335,"sourceType":"competition"}],"dockerImageVersionId":31236,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install ultralytics tensorflow scikit-image","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-19T19:50:10.633596Z","iopub.execute_input":"2026-01-19T19:50:10.633892Z","iopub.status.idle":"2026-01-19T19:50:16.702674Z","shell.execute_reply.started":"2026-01-19T19:50:10.633869Z","shell.execute_reply":"2026-01-19T19:50:16.701815Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd \nimport tensorflow as tf\nfrom sklearn.metrics import accuracy_score, classification_report, confusion_matrix\nfrom sklearn.model_selection import train_test_split\nimport os \nimport glob\nimport seaborn as sns\nfrom tensorflow.keras import Model\nfrom sklearn.decomposition import PCA\nfrom sklearn.feature_selection import SelectKBest, mutual_info_classif\nfrom skimage.feature import hog, local_binary_pattern\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint, ReduceLROnPlateau\nfrom tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Flatten, BatchNormalization, Dropout, GlobalAveragePooling2D\nfrom tensorflow.keras.applications.resnet50 import ResNet50\nfrom sklearn.model_selection import GroupShuffleSplit\nfrom tensorflow.keras.applications.vgg16 import VGG16\nfrom sklearn.preprocessing import StandardScaler\nfrom ultralytics import YOLO\nimport matplotlib.pyplot as plt\nimport cv2\nimport joblib","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T19:50:32.203504Z","iopub.execute_input":"2026-01-19T19:50:32.204052Z","iopub.status.idle":"2026-01-19T19:50:54.398347Z","shell.execute_reply.started":"2026-01-19T19:50:32.204018Z","shell.execute_reply":"2026-01-19T19:50:54.397724Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMG_SIZE = (192, 144)\nHOG_PIXELS_PER_CELL = (8, 8)\nHOG_ORIENTATIONS = 12\nHOG_CELLS_PER_BLOCK = (2, 2)\nCOLOR_BINS = (24, 24)\nSEED = 42\nCSV_PATH = '/kaggle/input/state-farm-distracted-driver-detection/driver_imgs_list.csv'\nBATCH_SIZE = 32\nEPOCHS = 50","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T19:50:54.405349Z","iopub.execute_input":"2026-01-19T19:50:54.405888Z","iopub.status.idle":"2026-01-19T19:50:54.419003Z","shell.execute_reply.started":"2026-01-19T19:50:54.405842Z","shell.execute_reply":"2026-01-19T19:50:54.418265Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Retrieve Data","metadata":{}},{"cell_type":"code","source":"dataset = \"/kaggle/input/state-farm-distracted-driver-detection/imgs\"\n\ntrain_dataset = dataset + \"/train/\"\ntest_dataset = dataset + \"/test/\"\n\ntrain_classes = sorted(os.listdir(train_dataset))\ntest_classes = os.listdir(test_dataset)\n\nprint(f\"Training classes: {train_classes}\")\nprint(f\"Number of classes: {len(train_classes)}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T19:50:54.420610Z","iopub.execute_input":"2026-01-19T19:50:54.420808Z","iopub.status.idle":"2026-01-19T19:50:55.311506Z","shell.execute_reply.started":"2026-01-19T19:50:54.420787Z","shell.execute_reply":"2026-01-19T19:50:55.310700Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_vector_from_image(image_path):\n    img = cv2.imread(image_path)\n    if img is None: return None\n    img = cv2.resize(img, IMG_SIZE, interpolation=cv2.INTER_AREA)\n    \n    img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)\n    hist_h = cv2.calcHist([img_hsv], [0], None, [COLOR_BINS[0]], [0, 180])\n    hist_s = cv2.calcHist([img_hsv], [1], None, [COLOR_BINS[1]], [0, 256])\n    color_feat = np.concatenate([hist_h.flatten(), hist_s.flatten()])\n    \n    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n    \n    hog_feat = hog(img_gray, \n                   orientations=HOG_ORIENTATIONS,\n                   pixels_per_cell=HOG_PIXELS_PER_CELL,\n                   cells_per_block=HOG_CELLS_PER_BLOCK,\n                   block_norm='L2-Hys', \n                   transform_sqrt=True,\n                   feature_vector=True)\n\n    lbp = local_binary_pattern(img_gray, P=8, R=1, method=\"uniform\")\n    lbp_hist = np.histogram(lbp.ravel(), bins=10, range=(0, 10), density=True)[0]\n    \n    # concat\n    all_features = [hog_feat, color_feat, lbp_hist]\n    shapes = [f.shape for f in all_features]\n    \n    for f in all_features:\n        if np.any(np.isnan(f)) or np.any(np.isinf(f)):\n            return None\n    \n    final_features = np.concatenate(all_features)\n    \n    if len(final_features) == 0 or np.any(np.isnan(final_features)):\n        return None\n        \n    return final_features","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T19:50:55.312526Z","iopub.execute_input":"2026-01-19T19:50:55.313061Z","iopub.status.idle":"2026-01-19T19:50:55.321114Z","shell.execute_reply.started":"2026-01-19T19:50:55.313034Z","shell.execute_reply":"2026-01-19T19:50:55.320543Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Feature Extractor","metadata":{}},{"cell_type":"code","source":"X, y, groups = [], [], []\n\ndriver_df = pd.read_csv(CSV_PATH)\ndriver_dict = dict(zip(driver_df['img'], driver_df['subject']))\n\nfor idx, class_name in enumerate(train_classes):\n    class_path = os.path.join(train_dataset, class_name)\n    img_files = glob.glob(os.path.join(class_path, '*.jpg'))\n\n    print(f\"Class {class_name} has {len(img_files)} images\")\n\n    for img_path in img_files:\n        features = get_vector_from_image(img_path)\n        \n        if features is not None:\n            X.append(features)\n            y.append(idx)\n\n            fname = os.path.basename(img_path)\n            groups.append(driver_dict.get(fname, 'unknown'))\n\nX = np.array(X, dtype=np.float32)\ny = np.array(y)\ngroups = np.array(groups)\n    \nprint(f\"\\nDONE! Final Shape: {X.shape}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T19:50:55.322315Z","iopub.execute_input":"2026-01-19T19:50:55.322972Z","iopub.status.idle":"2026-01-19T20:00:48.391254Z","shell.execute_reply.started":"2026-01-19T19:50:55.322935Z","shell.execute_reply":"2026-01-19T20:00:48.390427Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import joblib\n\nCACHE_PATH = '/kaggle/working/features.joblib'\n\nif len(X) > 0:\n    joblib.dump((X, y, groups), CACHE_PATH)\n    print(f\"Features saved to {CACHE_PATH}\")\n    print(f\"Saved shapes: X={X.shape}, y={y.shape}, groups={groups.shape}\")\nelse:\n    print(\"Error: X is empty.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T20:00:48.392753Z","iopub.execute_input":"2026-01-19T20:00:48.393009Z","iopub.status.idle":"2026-01-19T20:00:49.596491Z","shell.execute_reply.started":"2026-01-19T20:00:48.392982Z","shell.execute_reply":"2026-01-19T20:00:49.595687Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"CACHE_PATH = '/kaggle/working/features.joblib'\n\nif os.path.exists(CACHE_PATH):\n    X, y, groups = joblib.load(CACHE_PATH)\n    print(f\"Loaded features from {CACHE_PATH}\")\n    print(f\"Loaded shapes: X={X.shape}, y={y.shape}, groups={groups.shape}\")\nelse:\n    print(f\"File {CACHE_PATH} not found. You must run feature extraction first.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T20:00:49.597485Z","iopub.execute_input":"2026-01-19T20:00:49.597871Z","iopub.status.idle":"2026-01-19T20:00:50.084325Z","shell.execute_reply.started":"2026-01-19T20:00:49.597844Z","shell.execute_reply":"2026-01-19T20:00:50.083691Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### **Coba kalo pake approach awal**","metadata":{}},{"cell_type":"code","source":"print(f\"\\nOriginal feature dimension: {X.shape[1]}\")\n\nprint('Selecting best features...')\nfeature_selector = SelectKBest(mutual_info_classif, k=min(1500, X.shape[1]))\nX_selected = feature_selector.fit_transform(X, y)\nprint(f\"Selected feature dimension: {X_selected.shape[1]}\")\n\nprint('Scaling features...')\nscaler = StandardScaler()\nX_scaled = scaler.fit_transform(X_selected)\n\nprint('Applying PCA...')\nif X_selected.shape[1] > 1000:\n    pca = PCA(n_components=0.98, random_state=SEED)\n    X_final = pca.fit_transform(X_scaled)\n    print(f\"Final dimension after PCA: {X_final.shape[1]}\")\nelse:\n    X_final = X_scaled","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T20:00:50.085722Z","iopub.execute_input":"2026-01-19T20:00:50.086002Z","iopub.status.idle":"2026-01-19T20:30:39.366705Z","shell.execute_reply.started":"2026-01-19T20:00:50.085979Z","shell.execute_reply":"2026-01-19T20:30:39.365967Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"gss = GroupShuffleSplit(n_splits=1, test_size=0.2, random_state=SEED)\ntrain_idx, val_idx = next(gss.split(X_final, y, groups))\n\nX_train, X_val = X_final[train_idx], X_final[val_idx]\ny_train, y_val = y[train_idx], y[val_idx]\n\nprint(f\"\\nTrain set: {X_train.shape}, Val set: {X_val.shape}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T20:30:39.367741Z","iopub.execute_input":"2026-01-19T20:30:39.367995Z","iopub.status.idle":"2026-01-19T20:30:39.419622Z","shell.execute_reply.started":"2026-01-19T20:30:39.367964Z","shell.execute_reply":"2026-01-19T20:30:39.418471Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Model Dense**","metadata":{}},{"cell_type":"code","source":"mlp_model = Sequential([\n    Dense(512, activation='relu', input_shape=(X_final.shape[1],)),\n    BatchNormalization(),\n    Dropout(0.5),\n    Dense(256, activation='relu'),\n    BatchNormalization(),\n    Dropout(0.4),\n    Dense(128, activation='relu'),\n    BatchNormalization(),\n    Dropout(0.3),\n    Dense(10, activation='softmax')\n])\n\nmlp_model.compile(optimizer=Adam(learning_rate=0.001), \n                  loss='sparse_categorical_crossentropy', \n                  metrics=['accuracy'])\nmlp_model.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T20:30:39.421680Z","iopub.execute_input":"2026-01-19T20:30:39.421943Z","iopub.status.idle":"2026-01-19T20:30:41.099071Z","shell.execute_reply.started":"2026-01-19T20:30:39.421912Z","shell.execute_reply":"2026-01-19T20:30:41.098354Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"early_stop = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True)\ncheckpoint = ModelCheckpoint('/kaggle/working/mlp_best.h5', save_best_only=True, monitor='val_accuracy')\n\nhistory_mlp = mlp_model.fit(\n    X_train, y_train,\n    validation_data=(X_val, y_val),\n    epochs=EPOCHS,\n    batch_size=BATCH_SIZE,\n    callbacks=[early_stop, checkpoint],\n    verbose=1\n)\n\n# Evaluate MLP\nval_loss, val_acc = mlp_model.evaluate(X_val, y_val)\nprint(f\"\\nMLP Validation Accuracy: {val_acc:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T20:30:41.100046Z","iopub.execute_input":"2026-01-19T20:30:41.100548Z","iopub.status.idle":"2026-01-19T20:31:07.839567Z","shell.execute_reply.started":"2026-01-19T20:30:41.100522Z","shell.execute_reply":"2026-01-19T20:31:07.838991Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- MLP Prediction & Classification Report ---\ny_pred_mlp = mlp_model.predict(X_val, verbose=0)\ny_pred_classes_mlp = np.argmax(y_pred_mlp, axis=1)\n\nprint(\"\\n--- MLP Classification Report ---\")\nprint(classification_report(y_val, y_pred_classes_mlp, target_names=train_classes))\n\n# --- MLP Confusion Matrix ---\ncm_mlp = confusion_matrix(y_val, y_pred_classes_mlp)\nplt.figure(figsize=(12, 10))\nsns.heatmap(cm_mlp, annot=True, fmt='d', cmap='Blues', xticklabels=train_classes, yticklabels=train_classes)\nplt.title('MLP - Confusion Matrix', fontsize=16, fontweight='bold')\nplt.ylabel('True Label')\nplt.xlabel('Predicted Label')\nplt.tight_layout()\nplt.savefig('/kaggle/working/mlp_confusion_matrix.png', dpi=300)\nplt.show()\n\n# --- MLP Training History Plots ---\nplt.figure(figsize=(14, 5))\n\n# Accuracy Plot\nplt.subplot(1, 2, 1)\nplt.plot(history_mlp.history['accuracy'], label='Train Accuracy', linewidth=2)\nplt.plot(history_mlp.history['val_accuracy'], label='Val Accuracy', linewidth=2)\nplt.title('MLP - Accuracy', fontsize=14, fontweight='bold')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.legend()\nplt.grid(True, alpha=0.3)\n\n# Loss Plot\nplt.subplot(1, 2, 2)\nplt.plot(history_mlp.history['loss'], label='Train Loss', linewidth=2)\nplt.plot(history_mlp.history['val_loss'], label='Val Loss', linewidth=2)\nplt.title('MLP - Loss', fontsize=14, fontweight='bold')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.legend()\nplt.grid(True, alpha=0.3)\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T20:31:07.840413Z","iopub.execute_input":"2026-01-19T20:31:07.840625Z","iopub.status.idle":"2026-01-19T20:31:10.167842Z","shell.execute_reply.started":"2026-01-19T20:31:07.840604Z","shell.execute_reply":"2026-01-19T20:31:10.167258Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Data Generator","metadata":{}},{"cell_type":"code","source":"train_datagen = ImageDataGenerator(\n    rescale=1./255,\n    rotation_range=20,\n    width_shift_range=0.2,\n    height_shift_range=0.2,\n    shear_range=0.2,\n    zoom_range=0.2,\n    horizontal_flip=True,\n    fill_mode='nearest'\n)\n\nval_datagen = ImageDataGenerator(rescale=1./255)\n\ntrain_generator = train_datagen.flow_from_directory(\n    train_dataset,\n    target_size=IMG_SIZE,\n    batch_size=BATCH_SIZE,\n    class_mode='categorical',\n    shuffle=True,\n    seed=SEED\n)\n\nvalidation_split = 0.2\ntotal_samples = train_generator.samples\nval_samples = int(total_samples * validation_split)\n\n\nval_generator = val_datagen.flow_from_directory(\n    train_dataset,\n    target_size=IMG_SIZE,\n    batch_size=BATCH_SIZE,\n    class_mode='categorical',\n    shuffle=False\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.231339Z","iopub.status.idle":"2026-01-19T09:24:29.231697Z","shell.execute_reply.started":"2026-01-19T09:24:29.231514Z","shell.execute_reply":"2026-01-19T09:24:29.231539Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Model 1: CNN**","metadata":{}},{"cell_type":"code","source":"cnn_model = tf.keras.Sequential([\n    Conv2D(16, (3,3), activation='relu', input_shape=(144, 192, 3)),\n    MaxPooling2D(2, 2),\n    Conv2D(32, (3, 3), activation='relu'),\n    MaxPooling2D(2, 2),\n    Conv2D(64, (3, 3), activation='relu'),\n    MaxPooling2D(2, 2),\n    Flatten(),\n    Dense(512, activation='relu'),\n    Dropout(0.5),\n    Dense(1024, activation='relu'),\n    BatchNormalization(),\n    Dropout(0.5),\n    Dense(10, activation='softmax')\n])\n\ncnn_model.compile(optimizer=Adam(learning_rate=0.0001), \n                  loss='categorical_crossentropy', \n                  metrics=['accuracy'])\n\ncnn_model.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.232669Z","iopub.status.idle":"2026-01-19T09:24:29.232903Z","shell.execute_reply.started":"2026-01-19T09:24:29.232791Z","shell.execute_reply":"2026-01-19T09:24:29.232806Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history_cnn = cnn_model.fit(\n    train_generator,\n    steps_per_epoch=train_generator.samples // BATCH_SIZE,\n    validation_data=val_generator,\n    validation_steps=val_generator.samples // BATCH_SIZE,\n    epochs=20,\n    callbacks=[EarlyStopping(patience=5, restore_best_weights=True)],\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.233974Z","iopub.status.idle":"2026-01-19T09:24:29.234207Z","shell.execute_reply.started":"2026-01-19T09:24:29.234096Z","shell.execute_reply":"2026-01-19T09:24:29.234112Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_generator.reset()\ny_pred = cnn_model.predict(val_generator, steps=len(val_generator), verbose=1)\ny_pred_classes = np.argmax(y_pred, axis=1)\ny_true = val_generator.classes\n\nprint(classification_report(y_true, y_pred_classes, target_names=train_classes))\n\ncm = confusion_matrix(y_true, y_pred_classes)\nplt.figure(figsize=(12, 10))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=train_classes, yticklabels=train_classes)\nplt.title('CNN - Confusion Matrix', fontsize=16, fontweight='bold')\nplt.ylabel('True Label')\nplt.xlabel('Predicted Label')\nplt.tight_layout()\nplt.savefig('/kaggle/working/cnn_confusion_matrix.png', dpi=300)\nplt.show()\n\n# Plot Training History\nplt.figure(figsize=(14, 5))\nplt.subplot(1, 2, 1)\nplt.plot(history_cnn.history['accuracy'], label='Train Accuracy', linewidth=2)\nplt.plot(history_cnn.history['val_accuracy'], label='Val Accuracy', linewidth=2)\nplt.title('CNN - Accuracy', fontsize=14, fontweight='bold')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.legend()\nplt.grid(True, alpha=0.3)\n\nplt.subplot(1, 2, 2)\nplt.plot(history_cnn.history['loss'], label='Train Loss', linewidth=2)\nplt.plot(history_cnn.history['val_loss'], label='Val Loss', linewidth=2)\nplt.title('CNN - Loss', fontsize=14, fontweight='bold')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.legend()\nplt.grid(True, alpha=0.3)\n\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.235279Z","iopub.status.idle":"2026-01-19T09:24:29.235746Z","shell.execute_reply.started":"2026-01-19T09:24:29.235570Z","shell.execute_reply":"2026-01-19T09:24:29.235602Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Model 2: VGG16**","metadata":{}},{"cell_type":"code","source":"pretrained_model = VGG16(weights='imagenet', include_top=False, input_shape=(192, 144, 3))\npretrained_model.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.236735Z","iopub.status.idle":"2026-01-19T09:24:29.236994Z","shell.execute_reply.started":"2026-01-19T09:24:29.236875Z","shell.execute_reply":"2026-01-19T09:24:29.236892Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for layer in pretrained_model.layers[:-5]:\n    layer.trainable = False","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.237956Z","iopub.status.idle":"2026-01-19T09:24:29.238208Z","shell.execute_reply.started":"2026-01-19T09:24:29.238096Z","shell.execute_reply":"2026-01-19T09:24:29.238113Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"last_layer = pretrained_model.get_layer('block4_pool')\nlast_output = last_layer.output\n\nx = Flatten()(last_output)\nx = Dropout(0.2)(x)\nx = Dense(128, activation = 'relu')(x)\nx = BatchNormalization()(x)\nx = Dense(256, activation = 'relu')(x)\nx = BatchNormalization()(x)\nx = Dense(10, activation = 'softmax')(x)\n\n\nvgg_model = Model(pretrained_model.input, x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.239395Z","iopub.status.idle":"2026-01-19T09:24:29.239654Z","shell.execute_reply.started":"2026-01-19T09:24:29.239530Z","shell.execute_reply":"2026-01-19T09:24:29.239546Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"vgg_model.compile(optimizer=Adam(learning_rate=0.0001), loss='categorical_crossentropy', metrics=['acc'])\nvgg_model.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.240643Z","iopub.status.idle":"2026-01-19T09:24:29.240971Z","shell.execute_reply.started":"2026-01-19T09:24:29.240809Z","shell.execute_reply":"2026-01-19T09:24:29.240833Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history_vgg = vgg_model.fit(\n    train_generator,\n    steps_per_epoch=train_generator.samples // BATCH_SIZE,\n    validation_data=val_generator,\n    validation_steps=val_generator.samples // BATCH_SIZE,\n    epochs=20,\n    callbacks=[EarlyStopping(patience=5, restore_best_weights=True)],\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.242472Z","iopub.status.idle":"2026-01-19T09:24:29.242813Z","shell.execute_reply.started":"2026-01-19T09:24:29.242648Z","shell.execute_reply":"2026-01-19T09:24:29.242670Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_generator.reset()\ny_pred = vgg_model.predict(val_generator, steps=len(val_generator), verbose=1)\ny_pred_classes = np.argmax(y_pred, axis=1)\ny_true = val_generator.classes\n\nprint(classification_report(y_true, y_pred_classes, target_names=train_classes))\n\ncm = confusion_matrix(y_true, y_pred_classes)\nplt.figure(figsize=(12, 10))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=train_classes, yticklabels=train_classes)\nplt.title('VGG16 - Confusion Matrix', fontsize=16, fontweight='bold')\nplt.ylabel('True Label')\nplt.xlabel('Predicted Label')\nplt.tight_layout()\nplt.savefig('/kaggle/working/vgg_confusion_matrix.png', dpi=300)\nplt.show()\n\n# Plot Training History\nplt.figure(figsize=(14, 5))\nplt.subplot(1, 2, 1)\nplt.plot(history_vgg.history['acc'], label='Train Accuracy', linewidth=2)\nplt.plot(history_vgg.history['val_acc'], label='Val Accuracy', linewidth=2)\nplt.title('VGG - Accuracy', fontsize=14, fontweight='bold')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.legend()\nplt.grid(True, alpha=0.3)\n\nplt.subplot(1, 2, 2)\nplt.plot(history_vgg.history['loss'], label='Train Loss', linewidth=2)\nplt.plot(history_vgg.history['val_loss'], label='Val Loss', linewidth=2)\nplt.title('VGG - Loss', fontsize=14, fontweight='bold')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.legend()\nplt.grid(True, alpha=0.3)\n\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.243863Z","iopub.status.idle":"2026-01-19T09:24:29.244206Z","shell.execute_reply.started":"2026-01-19T09:24:29.244006Z","shell.execute_reply":"2026-01-19T09:24:29.244032Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Model 3: ResNet50**","metadata":{}},{"cell_type":"code","source":"pretrained_model = ResNet50(weights='imagenet', include_top=False, input_shape=(192, 144, 3))\npretrained_model.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.244986Z","iopub.status.idle":"2026-01-19T09:24:29.245213Z","shell.execute_reply.started":"2026-01-19T09:24:29.245102Z","shell.execute_reply":"2026-01-19T09:24:29.245118Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for layer in pretrained_model.layers[:-3]:\n    layer.trainable = False","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.246366Z","iopub.status.idle":"2026-01-19T09:24:29.246700Z","shell.execute_reply.started":"2026-01-19T09:24:29.246532Z","shell.execute_reply":"2026-01-19T09:24:29.246556Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"last_layer = pretrained_model.get_layer('conv5_block2_out')\nlast_output = last_layer.output\n\nx = Flatten()(last_output)\nx = Dropout(0.2)(x)\nx = Dense(128, activation = 'relu')(x)\nx = BatchNormalization()(x)\nx = Dense(256, activation = 'relu')(x)\nx = BatchNormalization()(x)\nx = Dense(10, activation = 'softmax')(x)\n\n\nresnet50_model = Model(pretrained_model.input, x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.247856Z","iopub.status.idle":"2026-01-19T09:24:29.248205Z","shell.execute_reply.started":"2026-01-19T09:24:29.248023Z","shell.execute_reply":"2026-01-19T09:24:29.248050Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"resnet50_model.compile(optimizer = Adam(), loss = 'categorical_crossentropy', metrics = ['acc'])\nresnet50_model.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.249346Z","iopub.status.idle":"2026-01-19T09:24:29.249693Z","shell.execute_reply.started":"2026-01-19T09:24:29.249515Z","shell.execute_reply":"2026-01-19T09:24:29.249540Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history_resnet = resnet50_model.fit(\n    train_generator,\n    steps_per_epoch=train_generator.samples // BATCH_SIZE,\n    validation_data=val_generator,\n    validation_steps=val_generator.samples // BATCH_SIZE,\n    epochs=20,\n    callbacks=[EarlyStopping(patience=5, restore_best_weights=True)],\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.250926Z","iopub.status.idle":"2026-01-19T09:24:29.251262Z","shell.execute_reply.started":"2026-01-19T09:24:29.251072Z","shell.execute_reply":"2026-01-19T09:24:29.251100Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_generator.reset()\ny_pred = resnet50_model.predict(val_generator, steps=len(val_generator), verbose=1)\ny_pred_classes = np.argmax(y_pred, axis=1)\ny_true = val_generator.classes\n\nprint(classification_report(y_true, y_pred_classes, target_names=train_classes))\n\ncm = confusion_matrix(y_true, y_pred_classes)\nplt.figure(figsize=(12, 10))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=train_classes, yticklabels=train_classes)\nplt.title('ResNet50 - Confusion Matrix', fontsize=16, fontweight='bold')\nplt.ylabel('True Label')\nplt.xlabel('Predicted Label')\nplt.tight_layout()\nplt.savefig('/kaggle/working/resnet_confusion_matrix.png', dpi=300)\nplt.show()\n\n# Plot Training History\nplt.figure(figsize=(14, 5))\nplt.subplot(1, 2, 1)\nplt.plot(history_resnet.history['acc'], label='Train Accuracy', linewidth=2)\nplt.plot(history_resnet.history['val_acc'], label='Val Accuracy', linewidth=2)\nplt.title('ResNet - Accuracy', fontsize=14, fontweight='bold')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.legend()\nplt.grid(True, alpha=0.3)\n\nplt.subplot(1, 2, 2)\nplt.plot(history_resnet.history['loss'], label='Train Loss', linewidth=2)\nplt.plot(history_resnet.history['val_loss'], label='Val Loss', linewidth=2)\nplt.title('ResNet - Loss', fontsize=14, fontweight='bold')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.legend()\nplt.grid(True, alpha=0.3)\n\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.252062Z","iopub.status.idle":"2026-01-19T09:24:29.252388Z","shell.execute_reply.started":"2026-01-19T09:24:29.252261Z","shell.execute_reply":"2026-01-19T09:24:29.252282Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Model 4: YOLOv8**","metadata":{}},{"cell_type":"code","source":"yolo_model = YOLO('yolov8n-cls.pt')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.253363Z","iopub.status.idle":"2026-01-19T09:24:29.253754Z","shell.execute_reply.started":"2026-01-19T09:24:29.253627Z","shell.execute_reply":"2026-01-19T09:24:29.253646Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"try:\n    results = yolo_model.train(\n        data=train_dataset,\n        epochs=20,\n        imgsz=192,\n        batch=BATCH_SIZE,\n        patience=5\n    )\nexcept Exception as e:\n    print(f\"YOLO training note: {e}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.255341Z","iopub.status.idle":"2026-01-19T09:24:29.255684Z","shell.execute_reply.started":"2026-01-19T09:24:29.255508Z","shell.execute_reply":"2026-01-19T09:24:29.255537Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom sklearn.metrics import classification_report, confusion_matrix\nimport glob\n\n\ntry:\n    results_dir = yolo_model.trainer.save_dir\n    \n    results_path = os.path.join(results_dir, 'results.csv')\n    df_res = pd.read_csv(results_path)\n    df_res.columns = df_res.columns.str.strip() \n\n    plt.figure(figsize=(14, 5))\n\n\n    plt.subplot(1, 2, 1)\n   \n    acc_col = 'metrics/accuracy_top1' if 'metrics/accuracy_top1' in df_res.columns else 'train/top1_acc'\n    \n    plt.plot(df_res['epoch'], df_res[acc_col], label='Top-1 Accuracy', linewidth=2)\n    plt.title('YOLOv8 - Accuracy', fontsize=14, fontweight='bold')\n    plt.xlabel('Epoch')\n    plt.ylabel('Accuracy')\n    plt.legend()\n    plt.grid(True, alpha=0.3)\n\n    # Plot Loss\n    plt.subplot(1, 2, 2)\n    plt.plot(df_res['epoch'], df_res['train/loss'], label='Train Loss', linewidth=2)\n    if 'val/loss' in df_res.columns:\n        plt.plot(df_res['epoch'], df_res['val/loss'], label='Val Loss', linewidth=2)\n        \n    plt.title('YOLOv8 - Loss', fontsize=14, fontweight='bold')\n    plt.xlabel('Epoch')\n    plt.ylabel('Loss')\n    plt.legend()\n    plt.grid(True, alpha=0.3)\n\n    plt.tight_layout()\n    plt.show()\n\nexcept Exception as e:\n    print(f\"Gagal plot history (mungkin training belum selesai atau path salah): {e}\")\n\n# ==========================================\n# 2. CONFUSION MATRIX & REPORT\n# ==========================================\n\nif 'train_classes' not in locals():\n    train_classes = sorted(os.listdir(train_dataset))\n\ny_true = []\ny_pred_classes = []\n\nfor idx, class_name in enumerate(train_classes):\n    class_path = os.path.join(train_dataset, class_name)\n    \n    results = yolo_model.predict(class_path, verbose=False, stream=True)\n    \n    for r in results:\n        y_true.append(idx)\n        y_pred_classes.append(r.probs.top1)\n\nprint(\"\\n--- YOLO Classification Report ---\")\nprint(classification_report(y_true, y_pred_classes, target_names=train_classes))\n\n# Plot Confusion Matrix\ncm = confusion_matrix(y_true, y_pred_classes)\nplt.figure(figsize=(12, 10))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=train_classes, yticklabels=train_classes)\nplt.title('YOLOv8 - Confusion Matrix', fontsize=16, fontweight='bold')\nplt.ylabel('True Label')\nplt.xlabel('Predicted Label')\nplt.tight_layout()\nplt.savefig('/kaggle/working/yolo_confusion_matrix.png', dpi=300)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.256399Z","iopub.status.idle":"2026-01-19T09:24:29.256624Z","shell.execute_reply.started":"2026-01-19T09:24:29.256513Z","shell.execute_reply":"2026-01-19T09:24:29.256528Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\n=== SAVING MODELS ===\")\ncnn_model.save('/kaggle/working/cnn_model.h5')\nvgg_model.save('/kaggle/working/vgg_model.h5')\nresnet50_model.save('/kaggle/working/resnet50_model.h5')\nprint(\"All models saved successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.257538Z","iopub.status.idle":"2026-01-19T09:24:29.257856Z","shell.execute_reply.started":"2026-01-19T09:24:29.257673Z","shell.execute_reply":"2026-01-19T09:24:29.257698Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### **Kalo langsung masukin featuresnya**","metadata":{}},{"cell_type":"code","source":"dataset = \"/kaggle/input/state-farm-distracted-driver-detection/imgs\"\ntrain_dataset = dataset + \"/train/\"\ntest_dataset = dataset + \"/test/\"\n\ntrain_classes = sorted(os.listdir(train_dataset))\nprint(f\"Training classes: {train_classes}\")\nprint(f\"Number of classes: {len(train_classes)}\")\n\n\nfor class_name in train_classes:\n    class_path = os.path.join(train_dataset, class_name)\n    num_images = len(glob.glob(os.path.join(class_path, '*.jpg')))\n    print(f\"Class {class_name}: {num_images} images\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.258746Z","iopub.status.idle":"2026-01-19T09:24:29.259093Z","shell.execute_reply.started":"2026-01-19T09:24:29.258913Z","shell.execute_reply":"2026-01-19T09:24:29.258940Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_datagen = ImageDataGenerator(\n    rescale=1./255,\n    rotation_range=20,\n    width_shift_range=0.2,\n    height_shift_range=0.2,\n    shear_range=0.15,\n    zoom_range=0.2,\n    horizontal_flip=True,\n    brightness_range=[0.8, 1.2],\n    fill_mode='nearest',\n    validation_split=0.2  \n)\n\n\ntest_datagen = ImageDataGenerator(rescale=1./255)\n\n# Create generators\ntrain_generator = train_datagen.flow_from_directory(\n    train_dataset,\n    target_size=IMG_SIZE,\n    batch_size=BATCH_SIZE,\n    class_mode='categorical',\n    shuffle=True,\n    seed=SEED,\n    subset='training'\n)\n\nvalidation_generator = train_datagen.flow_from_directory(\n    train_dataset,\n    target_size=IMG_SIZE,\n    batch_size=BATCH_SIZE,\n    class_mode='categorical',\n    shuffle=False,\n    seed=SEED,\n    subset='validation'\n)\n\ntest_generator = test_datagen.flow_from_directory(\n    test_dataset,\n    target_size=IMG_SIZE,\n    batch_size=1,\n    class_mode=None,\n    shuffle=False\n)\n\nprint(f\"\\nTraining samples: {train_generator.samples}\")\nprint(f\"Validation samples: {validation_generator.samples}\")\nprint(f\"Test samples: {test_generator.samples}\")\nprint(f\"Class indices: {train_generator.class_indices}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.260216Z","iopub.status.idle":"2026-01-19T09:24:29.260586Z","shell.execute_reply.started":"2026-01-19T09:24:29.260402Z","shell.execute_reply":"2026-01-19T09:24:29.260427Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_callbacks(model_name):\n    return [\n        EarlyStopping(\n            monitor='val_loss',\n            patience=10,\n            restore_best_weights=True,\n            verbose=1\n        ),\n        ModelCheckpoint(\n            f'/kaggle/working/{model_name}_best.h5',\n            save_best_only=True,\n            monitor='val_accuracy',\n            mode='max',\n            verbose=1\n        ),\n        ReduceLROnPlateau(\n            monitor='val_loss',\n            factor=0.5,\n            patience=5,\n            min_lr=1e-7,\n            verbose=1\n        )\n    ]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.261802Z","iopub.status.idle":"2026-01-19T09:24:29.262167Z","shell.execute_reply.started":"2026-01-19T09:24:29.261971Z","shell.execute_reply":"2026-01-19T09:24:29.261997Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**1. Model CNN**","metadata":{}},{"cell_type":"code","source":"cnn_model = tf.keras.Sequential([\n    Conv2D(32, (3,3), activation='relu', padding='same', input_shape=(144, 192, 3)),\n    BatchNormalization(),\n    Conv2D(32, (3,3), activation='relu', padding='same'),\n    BatchNormalization(),\n    MaxPooling2D(2, 2),\n    Dropout(0.25),\n    \n    Conv2D(64, (3, 3), activation='relu', padding='same'),\n    BatchNormalization(),\n    Conv2D(64, (3, 3), activation='relu', padding='same'),\n    BatchNormalization(),\n    MaxPooling2D(2, 2),\n    Dropout(0.25),\n    \n    Conv2D(128, (3, 3), activation='relu', padding='same'),\n    BatchNormalization(),\n    Conv2D(128, (3, 3), activation='relu', padding='same'),\n    BatchNormalization(),\n    MaxPooling2D(2, 2),\n    Dropout(0.25),\n    \n    Flatten(),\n    Dense(512, activation='relu'),\n    BatchNormalization(),\n    Dropout(0.5),\n    Dense(256, activation='relu'),\n    BatchNormalization(),\n    Dropout(0.5),\n    Dense(10, activation='softmax')\n], name='SimpleCNN')\n\ncnn_model.compile(\n    optimizer=Adam(learning_rate=0.001),\n    loss='categorical_crossentropy',\n    metrics=['accuracy']\n)\n\ncnn_model.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.265247Z","iopub.status.idle":"2026-01-19T09:24:29.265616Z","shell.execute_reply.started":"2026-01-19T09:24:29.265417Z","shell.execute_reply":"2026-01-19T09:24:29.265447Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history_cnn = cnn_model.fit(\n    train_generator,\n    steps_per_epoch=train_generator.samples // BATCH_SIZE,\n    validation_data=validation_generator,\n    validation_steps=validation_generator.samples // BATCH_SIZE,\n    epochs=EPOCHS,\n    callbacks=get_callbacks('cnn'),\n    verbose=1\n)\n\n# Evaluate\ncnn_val_loss, cnn_val_acc = cnn_model.evaluate(validation_generator)\nprint(f\"\\nCNN Validation Accuracy: {cnn_val_acc:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.266539Z","iopub.status.idle":"2026-01-19T09:24:29.266825Z","shell.execute_reply.started":"2026-01-19T09:24:29.266687Z","shell.execute_reply":"2026-01-19T09:24:29.266712Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**2. Model VGG**","metadata":{}},{"cell_type":"code","source":"base_vgg = VGG16(weights='imagenet', include_top=False, input_shape=(144, 192, 3))\n\n\nfor layer in base_vgg.layers[:-4]:\n    layer.trainable = False\n\n\nx = base_vgg.output\nx = GlobalAveragePooling2D()(x)\nx = Dense(512, activation='relu')(x)\nx = BatchNormalization()(x)\nx = Dropout(0.5)(x)\nx = Dense(256, activation='relu')(x)\nx = BatchNormalization()(x)\nx = Dropout(0.5)(x)\nx = Dense(10, activation='softmax')(x)\n\nvgg_model = Model(inputs=base_vgg.input, outputs=x, name='VGG16_Custom')\n\nvgg_model.compile(\n    optimizer=Adam(learning_rate=0.0001),\n    loss='categorical_crossentropy',\n    metrics=['accuracy']\n)\n\nvgg_model.summary()\n\n\nhistory_vgg = vgg_model.fit(\n    train_generator,\n    steps_per_epoch=train_generator.samples // BATCH_SIZE,\n    validation_data=validation_generator,\n    validation_steps=validation_generator.samples // BATCH_SIZE,\n    epochs=EPOCHS,\n    callbacks=get_callbacks('vgg16'),\n    verbose=1\n)\n\n\nvgg_val_loss, vgg_val_acc = vgg_model.evaluate(validation_generator)\nprint(f\"\\nVGG16 Validation Accuracy: {vgg_val_acc:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.268192Z","iopub.status.idle":"2026-01-19T09:24:29.268561Z","shell.execute_reply.started":"2026-01-19T09:24:29.268379Z","shell.execute_reply":"2026-01-19T09:24:29.268405Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**3. Model ResNet50**","metadata":{}},{"cell_type":"code","source":"base_resnet = ResNet50(weights='imagenet', include_top=False, input_shape=(144, 192, 3))\n\n# Freeze most layers\nfor layer in base_resnet.layers[:-15]:\n    layer.trainable = False\n\n# Add custom layers\nx = base_resnet.output\nx = GlobalAveragePooling2D()(x)\nx = Dense(512, activation='relu')(x)\nx = BatchNormalization()(x)\nx = Dropout(0.5)(x)\nx = Dense(256, activation='relu')(x)\nx = BatchNormalization()(x)\nx = Dropout(0.5)(x)\nx = Dense(10, activation='softmax')(x)\n\nresnet_model = Model(inputs=base_resnet.input, outputs=x, name='ResNet50_Custom')\n\nresnet_model.compile(\n    optimizer=Adam(learning_rate=0.0001),\n    loss='categorical_crossentropy',\n    metrics=['accuracy']\n)\n\nresnet_model.summary()\n\n# Train ResNet50\nhistory_resnet = resnet_model.fit(\n    train_generator,\n    steps_per_epoch=train_generator.samples // BATCH_SIZE,\n    validation_data=validation_generator,\n    validation_steps=validation_generator.samples // BATCH_SIZE,\n    epochs=EPOCHS,\n    callbacks=get_callbacks('resnet50'),\n    verbose=1\n)\n\n# Evaluate\nresnet_val_loss, resnet_val_acc = resnet_model.evaluate(validation_generator)\nprint(f\"\\nResNet50 Validation Accuracy: {resnet_val_acc:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.269295Z","iopub.status.idle":"2026-01-19T09:24:29.269591Z","shell.execute_reply.started":"2026-01-19T09:24:29.269452Z","shell.execute_reply":"2026-01-19T09:24:29.269480Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Model 4: YOLO**","metadata":{}},{"cell_type":"code","source":"try:\n    yolo_model = YOLO('yolov8n-cls.pt')\n    \n    # Note: YOLO requires specific dataset structure\n    # Train YOLO if data is properly structured\n    yolo_results = yolo_model.train(\n        data=train_dataset,\n        epochs=30,\n        imgsz=192,\n        batch=BATCH_SIZE,\n        patience=10,\n        project='/kaggle/working/yolo_training'\n    )\n    print(\"YOLO training completed!\")\n    \nexcept Exception as e:\n    print(f\"YOLO training skipped: {e}\")\n    print(\"YOLO requires data in ImageNet-style folder structure.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.270883Z","iopub.status.idle":"2026-01-19T09:24:29.271123Z","shell.execute_reply.started":"2026-01-19T09:24:29.271005Z","shell.execute_reply":"2026-01-19T09:24:29.271026Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cnn_model.save('/kaggle/working/cnn_model_final.h5')\nvgg_model.save('/kaggle/working/vgg_model_final.h5')\nresnet_model.save('/kaggle/working/resnet_model_final.h5')\nyolo_model.save('/kaggle/working/yolo_model_final.h5')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.272836Z","iopub.status.idle":"2026-01-19T09:24:29.273150Z","shell.execute_reply.started":"2026-01-19T09:24:29.273021Z","shell.execute_reply":"2026-01-19T09:24:29.273040Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_training_history(history, model_name):\n    \"\"\"Plot training and validation metrics\"\"\"\n    fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n    \n    # Accuracy plot\n    axes[0].plot(history.history['accuracy'], label='Train Accuracy', linewidth=2)\n    axes[0].plot(history.history['val_accuracy'], label='Val Accuracy', linewidth=2)\n    axes[0].set_title(f'{model_name} - Accuracy', fontsize=14, fontweight='bold')\n    axes[0].set_xlabel('Epoch', fontsize=12)\n    axes[0].set_ylabel('Accuracy', fontsize=12)\n    axes[0].legend(fontsize=10)\n    axes[0].grid(True, alpha=0.3)\n    \n    # Loss plot\n    axes[1].plot(history.history['loss'], label='Train Loss', linewidth=2)\n    axes[1].plot(history.history['val_loss'], label='Val Loss', linewidth=2)\n    axes[1].set_title(f'{model_name} - Loss', fontsize=14, fontweight='bold')\n    axes[1].set_xlabel('Epoch', fontsize=12)\n    axes[1].set_ylabel('Loss', fontsize=12)\n    axes[1].legend(fontsize=10)\n    axes[1].grid(True, alpha=0.3)\n    \n    plt.tight_layout()\n    plt.savefig(f'/kaggle/working/{model_name.lower().replace(\" \", \"_\")}_history.png', dpi=300, bbox_inches='tight')\n    plt.show()\n\n# Plot all histories\nplot_training_history(history_cnn, 'Simple CNN')\nplot_training_history(history_vgg, 'VGG16')\nplot_training_history(history_resnet, 'ResNet50')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.274678Z","iopub.status.idle":"2026-01-19T09:24:29.275034Z","shell.execute_reply.started":"2026-01-19T09:24:29.274903Z","shell.execute_reply":"2026-01-19T09:24:29.274921Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom sklearn.metrics import classification_report, confusion_matrix\nimport glob\n\n\ntry:\n    results_dir = yolo_model.trainer.save_dir\n    \n    results_path = os.path.join(results_dir, 'results.csv')\n    df_res = pd.read_csv(results_path)\n    df_res.columns = df_res.columns.str.strip() \n\n    plt.figure(figsize=(14, 5))\n\n\n    plt.subplot(1, 2, 1)\n   =\n    acc_col = 'metrics/accuracy_top1' if 'metrics/accuracy_top1' in df_res.columns else 'train/top1_acc'\n    \n    plt.plot(df_res['epoch'], df_res[acc_col], label='Top-1 Accuracy', linewidth=2)\n    plt.title('YOLOv8 - Accuracy', fontsize=14, fontweight='bold')\n    plt.xlabel('Epoch')\n    plt.ylabel('Accuracy')\n    plt.legend()\n    plt.grid(True, alpha=0.3)\n\n    # Plot Loss\n    plt.subplot(1, 2, 2)\n    plt.plot(df_res['epoch'], df_res['train/loss'], label='Train Loss', linewidth=2)\n    if 'val/loss' in df_res.columns:\n        plt.plot(df_res['epoch'], df_res['val/loss'], label='Val Loss', linewidth=2)\n        \n    plt.title('YOLOv8 - Loss', fontsize=14, fontweight='bold')\n    plt.xlabel('Epoch')\n    plt.ylabel('Loss')\n    plt.legend()\n    plt.grid(True, alpha=0.3)\n\n    plt.tight_layout()\n    plt.show()\n\nexcept Exception as e:\n    print(f\"Gagal plot history (mungkin training belum selesai atau path salah): {e}\")\n\n# ==========================================\n# 2. CONFUSION MATRIX & REPORT\n# ==========================================\n\nif 'train_classes' not in locals():\n    train_classes = sorted(os.listdir(train_dataset))\n\ny_true = []\ny_pred_classes = []\n\nfor idx, class_name in enumerate(train_classes):\n    class_path = os.path.join(train_dataset, class_name)\n    \n    results = yolo_model.predict(class_path, verbose=False, stream=True)\n    \n    for r in results:\n        y_true.append(idx)\n        y_pred_classes.append(r.probs.top1)\n\nprint(\"\\n--- YOLO Classification Report ---\")\nprint(classification_report(y_true, y_pred_classes, target_names=train_classes))\n\n# Plot Confusion Matrix\ncm = confusion_matrix(y_true, y_pred_classes)\nplt.figure(figsize=(12, 10))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=train_classes, yticklabels=train_classes)\nplt.title('YOLOv8 - Confusion Matrix', fontsize=16, fontweight='bold')\nplt.ylabel('True Label')\nplt.xlabel('Predicted Label')\nplt.tight_layout()\nplt.savefig('/kaggle/working/yolo_confusion_matrix.png', dpi=300)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T09:24:29.276032Z","iopub.status.idle":"2026-01-19T09:24:29.276289Z","shell.execute_reply.started":"2026-01-19T09:24:29.276149Z","shell.execute_reply":"2026-01-19T09:24:29.276164Z"}},"outputs":[],"execution_count":null}]}