{ "cells": [ { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# import libraries\n", "import tensorflow as tf\n", "from tensorflow.keras import layers, models\n", "from matplotlib import pyplot as plt\n", "from tensorflow.keras.preprocessing.image import ImageDataGenerator" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Found 1674 images belonging to 8 classes.\n", "Found 157 images belonging to 8 classes.\n", "Found 79 images belonging to 8 classes.\n" ] } ], "source": [ "TRAIN_DIR = 'dataset/train'\n", "TEST_DIR = 'dataset/test'\n", "VAL_DIR = 'dataset/val'\n", "# Load dataset\n", "datagen = ImageDataGenerator(rescale=1./255)\n", "# Load data dari direktori menggunakan flow_from_directory\n", "train_generator = datagen.flow_from_directory(\n", " TRAIN_DIR,\n", " target_size=(224, 224), # Sesuaikan dengan ukuran gambar input model\n", " batch_size=32,\n", " class_mode='categorical'\n", ")\n", "\n", "val_generator = datagen.flow_from_directory(\n", " VAL_DIR,\n", " target_size=(224, 224),\n", " batch_size=32,\n", " class_mode='categorical'\n", ")\n", "\n", "test_generator = datagen.flow_from_directory(\n", " TEST_DIR,\n", " target_size=(224, 224),\n", " batch_size=32,\n", " class_mode='categorical',\n", " shuffle=False # Untuk testing, tidak perlu shuffle\n", ")" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'Daun Jambu Biji': 0,\n", " 'Daun Kemangi': 1,\n", " 'Daun Kunyit': 2,\n", " 'Daun Mint': 3,\n", " 'Daun Pepaya': 4,\n", " 'Daun Sirih': 5,\n", " 'Daun Sirsak': 6,\n", " 'Lidah Buaya': 7}" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "train_generator.class_indices" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "model = models.Sequential()\n", "\n", "model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(224, 224, 3)))\n", "model.add(layers.MaxPooling2D((2, 2)))\n", "model.add(layers.Conv2D(64, (3, 3), activation='relu'))\n", "model.add(layers.MaxPooling2D((2, 2)))\n", "model.add(layers.Conv2D(128, (3, 3), activation='relu'))\n", "model.add(layers.MaxPooling2D((2, 2)))\n", "model.add(layers.Conv2D(128, (3, 3), activation='relu'))\n", "model.add(layers.MaxPooling2D((2, 2)))\n", "\n", "model.add(layers.Flatten())\n", "model.add(layers.Dense(512, activation='relu'))\n", "model.add(layers.Dense(8, activation='softmax'))\n", "\n", "model.compile(optimizer='adam',\n", " loss='categorical_crossentropy',\n", " metrics=['accuracy'])\n", " " ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Model: \"sequential_4\"\n",
"
\n"
],
"text/plain": [
"\u001b[1mModel: \"sequential_4\"\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n", "┃ Layer (type) ┃ Output Shape ┃ Param # ┃\n", "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n", "│ conv2d_13 (Conv2D) │ (None, 222, 222, 32) │ 896 │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ max_pooling2d_13 (MaxPooling2D) │ (None, 111, 111, 32) │ 0 │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ conv2d_14 (Conv2D) │ (None, 109, 109, 64) │ 18,496 │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ max_pooling2d_14 (MaxPooling2D) │ (None, 54, 54, 64) │ 0 │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ conv2d_15 (Conv2D) │ (None, 52, 52, 128) │ 73,856 │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ max_pooling2d_15 (MaxPooling2D) │ (None, 26, 26, 128) │ 0 │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ conv2d_16 (Conv2D) │ (None, 24, 24, 128) │ 147,584 │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ max_pooling2d_16 (MaxPooling2D) │ (None, 12, 12, 128) │ 0 │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ flatten_4 (Flatten) │ (None, 18432) │ 0 │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ dense_8 (Dense) │ (None, 512) │ 9,437,696 │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ dense_9 (Dense) │ (None, 8) │ 4,104 │\n", "└─────────────────────────────────┴────────────────────────┴───────────────┘\n", "\n" ], "text/plain": [ "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\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_13 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m222\u001b[0m, \u001b[38;5;34m222\u001b[0m, \u001b[38;5;34m32\u001b[0m) │ \u001b[38;5;34m896\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ max_pooling2d_13 (\u001b[38;5;33mMaxPooling2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m111\u001b[0m, \u001b[38;5;34m111\u001b[0m, \u001b[38;5;34m32\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ conv2d_14 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m109\u001b[0m, \u001b[38;5;34m109\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m18,496\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ max_pooling2d_14 (\u001b[38;5;33mMaxPooling2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m54\u001b[0m, \u001b[38;5;34m54\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ conv2d_15 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m52\u001b[0m, \u001b[38;5;34m52\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m73,856\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ max_pooling2d_15 (\u001b[38;5;33mMaxPooling2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m26\u001b[0m, \u001b[38;5;34m26\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ conv2d_16 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m24\u001b[0m, \u001b[38;5;34m24\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m147,584\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ max_pooling2d_16 (\u001b[38;5;33mMaxPooling2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m12\u001b[0m, \u001b[38;5;34m12\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ flatten_4 (\u001b[38;5;33mFlatten\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m18432\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ dense_8 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m512\u001b[0m) │ \u001b[38;5;34m9,437,696\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ dense_9 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m8\u001b[0m) │ \u001b[38;5;34m4,104\u001b[0m │\n", "└─────────────────────────────────┴────────────────────────┴───────────────┘\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
Total params: 9,682,632 (36.94 MB)\n", "\n" ], "text/plain": [ "\u001b[1m Total params: \u001b[0m\u001b[38;5;34m9,682,632\u001b[0m (36.94 MB)\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
Trainable params: 9,682,632 (36.94 MB)\n", "\n" ], "text/plain": [ "\u001b[1m Trainable params: \u001b[0m\u001b[38;5;34m9,682,632\u001b[0m (36.94 MB)\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
Non-trainable params: 0 (0.00 B)\n", "\n" ], "text/plain": [ "\u001b[1m Non-trainable params: \u001b[0m\u001b[38;5;34m0\u001b[0m (0.00 B)\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "model.summary()" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch 1/20\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2024-10-14 11:16:05.231584: I tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.cc:117] Plugin optimizer for device_type GPU is enabled.\n", "/Users/edoaurahman/development/anaconda/anaconda3/envs/tensorflow/lib/python3.10/site-packages/keras/src/trainers/data_adapters/py_dataset_adapter.py:122: UserWarning: Your `PyDataset` class should call `super().__init__(**kwargs)` in its constructor. `**kwargs` can include `workers`, `use_multiprocessing`, `max_queue_size`. Do not pass these arguments to `fit()`, as they will be ignored.\n", " self._warn_if_super_not_called()\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m26s\u001b[0m 353ms/step - accuracy: 0.2478 - loss: 2.1296 - val_accuracy: 0.5987 - val_loss: 1.1171\n", "Epoch 2/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m18s\u001b[0m 339ms/step - accuracy: 0.6152 - loss: 1.0357 - val_accuracy: 0.6688 - val_loss: 0.8430\n", "Epoch 3/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m18s\u001b[0m 322ms/step - accuracy: 0.7471 - loss: 0.7063 - val_accuracy: 0.7898 - val_loss: 0.6230\n", "Epoch 4/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m20s\u001b[0m 370ms/step - accuracy: 0.8481 - loss: 0.4345 - val_accuracy: 0.8408 - val_loss: 0.5627\n", "Epoch 5/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m26s\u001b[0m 471ms/step - accuracy: 0.9096 - loss: 0.2562 - val_accuracy: 0.8408 - val_loss: 0.5344\n", "Epoch 6/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m20s\u001b[0m 363ms/step - accuracy: 0.9161 - loss: 0.2274 - val_accuracy: 0.8408 - val_loss: 0.8011\n", "Epoch 7/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m20s\u001b[0m 367ms/step - accuracy: 0.9671 - loss: 0.0961 - val_accuracy: 0.8408 - val_loss: 0.6227\n", "Epoch 8/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m22s\u001b[0m 403ms/step - accuracy: 0.9832 - loss: 0.0657 - val_accuracy: 0.7898 - val_loss: 0.9990\n", "Epoch 9/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m23s\u001b[0m 420ms/step - accuracy: 0.9750 - loss: 0.0758 - val_accuracy: 0.8344 - val_loss: 0.8001\n", "Epoch 10/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m20s\u001b[0m 378ms/step - accuracy: 0.9909 - loss: 0.0312 - val_accuracy: 0.8344 - val_loss: 1.0499\n", "Epoch 11/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m19s\u001b[0m 359ms/step - accuracy: 0.9803 - loss: 0.0627 - val_accuracy: 0.8599 - val_loss: 0.8847\n", "Epoch 12/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m20s\u001b[0m 369ms/step - accuracy: 0.9984 - loss: 0.0089 - val_accuracy: 0.8280 - val_loss: 1.0634\n", "Epoch 13/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m20s\u001b[0m 377ms/step - accuracy: 0.9980 - loss: 0.0106 - val_accuracy: 0.8217 - val_loss: 1.2077\n", "Epoch 14/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m18s\u001b[0m 333ms/step - accuracy: 0.9768 - loss: 0.0614 - val_accuracy: 0.8535 - val_loss: 0.8965\n", "Epoch 15/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m19s\u001b[0m 345ms/step - accuracy: 0.9867 - loss: 0.0368 - val_accuracy: 0.7962 - val_loss: 1.3721\n", "Epoch 16/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m22s\u001b[0m 408ms/step - accuracy: 0.9825 - loss: 0.0534 - val_accuracy: 0.8153 - val_loss: 1.1506\n", "Epoch 17/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m20s\u001b[0m 372ms/step - accuracy: 0.9965 - loss: 0.0116 - val_accuracy: 0.8471 - val_loss: 1.2062\n", "Epoch 18/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m20s\u001b[0m 376ms/step - accuracy: 1.0000 - loss: 0.0027 - val_accuracy: 0.8408 - val_loss: 1.2559\n", "Epoch 19/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m22s\u001b[0m 415ms/step - accuracy: 1.0000 - loss: 2.3890e-04 - val_accuracy: 0.8535 - val_loss: 1.3033\n", "Epoch 20/20\n", "\u001b[1m53/53\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m22s\u001b[0m 411ms/step - accuracy: 1.0000 - loss: 1.3011e-04 - val_accuracy: 0.8471 - val_loss: 1.2932\n" ] } ], "source": [ "# Melatih model dengan data train, validasi dilakukan dengan data validation\n", "history = model.fit(\n", " train_generator,\n", " epochs=10, # Sesuaikan jumlah epoch\n", " validation_data=val_generator\n", ")" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "WARNING:absl:You are saving your model as an HDF5 file via `model.save()` or `keras.saving.save_model(model)`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save('my_model.keras')` or `keras.saving.save_model(model, 'my_model.keras')`. \n" ] } ], "source": [ "# save model\n", "model.save('model.h5')" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "# save history\n", "import pickle\n", "with open('history.pkl', 'wb') as file_pi:\n", " pickle.dump(history.history, file_pi)" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2024-10-14 11:41:05.053908: I metal_plugin/src/device/metal_device.cc:1154] Metal device set to: Apple M1\n", "2024-10-14 11:41:05.053947: I metal_plugin/src/device/metal_device.cc:296] systemMemory: 8.00 GB\n", "2024-10-14 11:41:05.053957: I metal_plugin/src/device/metal_device.cc:313] maxCacheSize: 2.67 GB\n", "2024-10-14 11:41:05.054262: I tensorflow/core/common_runtime/pluggable_device/pluggable_device_factory.cc:305] Could not identify NUMA node of platform GPU ID 0, defaulting to 0. Your kernel may not have been built with NUMA support.\n", "2024-10-14 11:41:05.054278: I tensorflow/core/common_runtime/pluggable_device/pluggable_device_factory.cc:271] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 0 MB memory) -> physical PluggableDevice (device: 0, name: METAL, pci bus id: