created LinkedIn


Setup Base Directory¶

In [ ]:
from google.colab import drive
drive.mount('/content/drive')
Mounted at /content/drive
In [ ]:
BaseDir = '/content/drive/MyDrive/Colab Notebooks/Klasifikasi-Gambar/'

Download Library¶

In [ ]:
%%capture
!pip install --upgrade tensorflow
!pip install tensorflowjs
!pip install tensorflow

Import Library¶

In [ ]:
import os
import shutil

import numpy as np
import pandas as pd
import matplotlib 
import matplotlib.pyplot as plt
import seaborn as sns
import sklearn
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix, accuracy_score

from PIL import Image
import cv2

import tensorflow as tf
import tensorflowjs as tfjs
from tensorflow import keras
from tensorflow.keras import layers, models, optimizers
from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau
from tensorflow.keras.applications import MobileNetV2, mobilenet_v2
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input

import warnings
tf.get_logger().setLevel('ERROR')
warnings.filterwarnings('ignore')
In [ ]:
print(f"{'Numpy Version':20}: {np.__version__}")
print(f"{'Pandas Version':20}: {pd.__version__}")
print(f"{'Tensorflow Version':20}: {tf.__version__}")
print(f"{'TensorflowJS Version':20}: {tfjs.__version__}")
print(f"{'Matplotlib Version':20}: {matplotlib.__version__}")
print(f"{'Scikit-Learn Version':20}: {sklearn.__version__}")
Numpy Version       : 1.26.4
Pandas Version      : 2.2.2
Tensorflow Version  : 2.15.1
TensorflowJS Version: 4.20.0
Matplotlib Version  : 3.7.5
Scikit-Learn Version: 1.2.2
In [ ]:
from google.colab import files
files.upload()

Data Loading¶

In [ ]:
# %%capture
!unzip fruits.zip

Merged Dataset¶

Menggabungkan gambar dari 3 folder yang berbeda.

In [ ]:
import os
import shutil

# Define the paths
training_dataset_path = '/content/fruits/Training'
test_dataset_path = '/content/fruits/Test'
validation_dataset_path = '/content/fruits/Validation'
dataset_path = BaseDir+'dataset/fruits'

def copy_contents(src, dst):
    for item in os.listdir(src):
        s = os.path.join(src, item)
        d = os.path.join(dst, item)
        if os.path.isdir(s):
            if not os.path.exists(d):
                os.makedirs(d)
            copy_contents(s, d)
        else:
            shutil.copy2(s, d)

# Create the new directory for the merged dataset
os.makedirs(dataset_path, exist_ok=True)

# Copy contents into the new directory
copy_contents(training_dataset_path, dataset_path)
copy_contents(validation_dataset_path, dataset_path)
copy_contents(test_dataset_path, dataset_path)

Menampilkan kelas yang terdapat pada dataset¶

In [ ]:
# Classes
DIR = BaseDir+'dataset/fruits'

classes = [i for i in os.listdir(DIR) if '.' not in i]
classes
Out[ ]:
['cucumber_1',
 'pear_3',
 'apple_red_3',
 'apple_golden_1',
 'cabbage_white_1',
 'apple_granny_smith_1',
 'apple_crimson_snow_1',
 'apple_golden_2',
 'carrot_1',
 'apple_braeburn_1',
 'apple_golden_3',
 'apple_red_delicios_1',
 'apple_pink_lady_1',
 'pear_1',
 'zucchini_dark_1',
 'apple_red_2',
 'apple_red_yellow_1',
 'eggplant_long_1',
 'apple_hit_1',
 'cucumber_3',
 'zucchini_1',
 'apple_rotten_1',
 'apple_6',
 'apple_red_1']
In [ ]:
# List ekstensi file gambar yang diizinkan
allowed_extensions = ['.png', '.jpg', '.jpeg']

# Inisialisasi list untuk label dan path
label = []
path = []

for dirname, _, filenames in os.walk(DIR):
    for filename in filenames:
        # Jika ekstensi file ada dalam allowed_extensions
        if os.path.splitext(filename)[-1].lower() in allowed_extensions:
            label.append(os.path.split(dirname)[-1])
            path.append(os.path.join(dirname, filename))

# Create df
df = pd.DataFrame(columns=['path','label'])
df['path']=path
df['label']=label
In [ ]:
df.head()
path label
0 /content/drive/MyDrive/Colab Notebooks/Klasifi... apple_6
1 /content/drive/MyDrive/Colab Notebooks/Klasifi... apple_6
2 /content/drive/MyDrive/Colab Notebooks/Klasifi... apple_6
3 /content/drive/MyDrive/Colab Notebooks/Klasifi... apple_6
4 /content/drive/MyDrive/Colab Notebooks/Klasifi... apple_6
In [ ]:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 12455 entries, 0 to 12454
Data columns (total 2 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   path    12455 non-null  object
 1   label   12455 non-null  object
dtypes: object(2)
memory usage: 194.7+ KB

Terdapat 12.455 data gambar yang siap digunakan.

✅ Kriteria > 10.000 data gambar terpenuhi

In [ ]:
# Check value counts
df['label'].value_counts()
Out[ ]:
label
apple_hit_1             936
pear_1                  650
apple_granny_smith_1    640
apple_braeburn_1        640
apple_rotten_1          638
apple_crimson_snow_1    636
apple_red_2             636
apple_golden_3          632
apple_6                 630
apple_pink_lady_1       625
apple_red_1             618
apple_red_yellow_1      616
apple_golden_2          616
apple_golden_1          616
apple_red_delicios_1    600
apple_red_3             562
cucumber_3              325
eggplant_long_1         320
zucchini_1              320
zucchini_dark_1         320
pear_3                  287
carrot_1                201
cucumber_1              200
cabbage_white_1         191
Name: count, dtype: int64
In [ ]:
# df['label'].unique()

Cek Resolution¶

Pada langkah ini, hanya akan menampilkan 5 resolusi gambar yang berbeda. Jika menampilkan semuanya notebook dapat mengalami not responding.

In [ ]:
def print_images_resolution(directory):
    unique_sizes = set()
    total_images = 0

    for subdir in os.listdir(directory):
        subdir_path = os.path.join(directory, subdir)
        image_files = os.listdir(subdir_path)
        num_images = len(image_files)
        print(f"{subdir}: {num_images}")
        total_images += num_images

        for img_file in image_files:
            if len(unique_sizes) >= 5:
                break
            img_path = os.path.join(subdir_path, img_file)
            with Image.open(img_path) as img:
                unique_sizes.add(img.size)

        for size in list(unique_sizes)[:5]:  # Menampilkan 5 resolusi unik
            print(f"- {size}")
        print("---------------")
        unique_sizes.clear()

    print(f"\nTotal: {total_images}")
In [ ]:
print_images_resolution(DIR)
cucumber_1: 200
- (319, 800)
- (313, 803)
- (337, 800)
- (329, 795)
- (359, 791)
---------------
pear_3: 287
- (623, 747)
- (608, 742)
- (595, 745)
- (607, 739)
- (603, 744)
---------------
apple_red_3: 562
- (571, 563)
- (601, 565)
- (544, 546)
- (539, 557)
- (572, 564)
---------------
apple_golden_1: 616
- (360, 320)
- (363, 322)
- (361, 367)
- (353, 368)
- (360, 368)
---------------
cabbage_white_1: 191
- (736, 705)
- (781, 768)
- (740, 718)
- (766, 726)
- (727, 705)
---------------
apple_granny_smith_1: 640
- (474, 461)
- (445, 462)
- (478, 462)
- (508, 459)
- (469, 462)
---------------
apple_crimson_snow_1: 636
- (585, 569)
- (647, 577)
- (611, 552)
- (572, 532)
- (568, 570)
---------------
apple_golden_2: 616
- (418, 396)
- (425, 395)
- (418, 398)
- (406, 443)
- (411, 440)
---------------
carrot_1: 201
- (205, 633)
- (193, 632)
- (184, 652)
- (182, 630)
- (178, 645)
---------------
apple_braeburn_1: 640
- (474, 393)
- (474, 457)
- (467, 412)
- (471, 458)
- (474, 458)
---------------
apple_golden_3: 632
- (438, 383)
- (404, 433)
- (417, 378)
- (415, 436)
- (398, 433)
---------------
apple_red_delicios_1: 600
- (605, 553)
- (538, 582)
- (577, 539)
- (602, 555)
- (557, 591)
---------------
apple_pink_lady_1: 625
- (430, 446)
- (465, 422)
- (473, 420)
- (469, 420)
- (417, 445)
---------------
pear_1: 650
- (540, 435)
- (415, 522)
- (537, 434)
- (415, 525)
- (551, 433)
---------------
zucchini_dark_1: 320
- (257, 924)
- (312, 890)
- (282, 852)
- (218, 836)
- (235, 849)
---------------
apple_red_2: 636
- (445, 453)
- (458, 450)
- (434, 410)
- (447, 451)
- (443, 406)
---------------
apple_red_yellow_1: 616
- (327, 310)
- (322, 340)
- (353, 340)
- (335, 306)
- (352, 339)
---------------
eggplant_long_1: 320
- (197, 703)
- (177, 699)
- (123, 739)
- (155, 722)
- (105, 741)
---------------
apple_hit_1: 936
- (780, 907)
- (820, 898)
- (890, 815)
- (891, 793)
- (848, 801)
---------------
cucumber_3: 325
- (265, 917)
- (242, 949)
- (368, 971)
- (286, 922)
- (243, 928)
---------------
zucchini_1: 320
- (276, 1033)
- (288, 1029)
- (283, 1010)
- (289, 1009)
- (281, 1033)
---------------
apple_rotten_1: 638
- (335, 317)
- (339, 342)
- (353, 344)
- (350, 317)
- (342, 340)
---------------
apple_6: 630
- (322, 253)
- (314, 262)
- (314, 320)
- (310, 320)
- (313, 320)
---------------
apple_red_1: 618
- (424, 470)
- (458, 412)
- (421, 446)
- (460, 410)
- (446, 414)
---------------

Total: 12455

Show Images Sample¶

In [ ]:
No description has been provided for this image

Terlihat dari gambar yang ditampilkan memiliki resolusi/ ukuran yang berbeda.

Split Dataset¶

In [ ]:
# Train Test Split 
train_df, test_df = train_test_split(df, test_size=0.2, shuffle=True, random_state=42)
In [ ]:
# Check shape of df
print(train_df.shape)
print(test_df.shape)
(9964, 2)
(2491, 2)

Data Preprocessing¶

In [ ]:
train_generator = ImageDataGenerator(
    preprocessing_function=preprocess_input
)

test_generator = ImageDataGenerator(
    preprocessing_function=preprocess_input
)
In [ ]:
train_images = train_generator.flow_from_dataframe(
    dataframe=train_df, 
    x_col='path', 
    y_col='label', 
    target_size=(224, 224), 
    color_mode='rgb',
#     color_mode='grayscale',
    class_mode='categorical', 
    batch_size=32, 
    shuffle=False, 
    seed=42
)

test_images = test_generator.flow_from_dataframe(
    dataframe=test_df, 
    x_col='path', 
    y_col='label', 
    target_size=(224, 224), 
    color_mode='rgb', 
#     color_mode='grayscale',
    class_mode='categorical', 
    batch_size=32, 
    shuffle=False 
)
Found 9964 validated image filenames belonging to 24 classes.
Found 2491 validated image filenames belonging to 24 classes.

Plotting images after pre-processing¶

In [ ]:
No description has been provided for this image

Modelling¶

Sequential¶

In [ ]:
model = tf.keras.Sequential([
    tf.keras.layers.Conv2D(32, (3, 3), input_shape=(224, 224, 3)),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.LeakyReLU(),
    tf.keras.layers.MaxPooling2D((2, 2)),
    
    tf.keras.layers.Conv2D(64, (3, 3)),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.LeakyReLU(),
    tf.keras.layers.MaxPooling2D((2, 2)),
    
    tf.keras.layers.Conv2D(128, (3, 3)),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.LeakyReLU(),
    tf.keras.layers.MaxPooling2D((2, 2)),
    
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(128),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.LeakyReLU(),
    tf.keras.layers.Dropout(0.5),
    
    tf.keras.layers.Dense(24, activation='softmax')
])

model.summary()
Model: "sequential_1"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d_1 (Conv2D)           (None, 222, 222, 32)      896       
                                                                 
 batch_normalization (Batch  (None, 222, 222, 32)      128       
 Normalization)                                                  
                                                                 
 leaky_re_lu (LeakyReLU)     (None, 222, 222, 32)      0         
                                                                 
 max_pooling2d_1 (MaxPoolin  (None, 111, 111, 32)      0         
 g2D)                                                            
                                                                 
 conv2d_2 (Conv2D)           (None, 109, 109, 64)      18496     
                                                                 
 batch_normalization_1 (Bat  (None, 109, 109, 64)      256       
 chNormalization)                                                
                                                                 
 leaky_re_lu_1 (LeakyReLU)   (None, 109, 109, 64)      0         
                                                                 
 max_pooling2d_2 (MaxPoolin  (None, 54, 54, 64)        0         
 g2D)                                                            
                                                                 
 conv2d_3 (Conv2D)           (None, 52, 52, 128)       73856     
                                                                 
 batch_normalization_2 (Bat  (None, 52, 52, 128)       512       
 chNormalization)                                                
                                                                 
 leaky_re_lu_2 (LeakyReLU)   (None, 52, 52, 128)       0         
                                                                 
 max_pooling2d_3 (MaxPoolin  (None, 26, 26, 128)       0         
 g2D)                                                            
                                                                 
 flatten_1 (Flatten)         (None, 86528)             0         
                                                                 
 dense_3 (Dense)             (None, 128)               11075712  
                                                                 
 batch_normalization_3 (Bat  (None, 128)               512       
 chNormalization)                                                
                                                                 
 leaky_re_lu_3 (LeakyReLU)   (None, 128)               0         
                                                                 
 dropout (Dropout)           (None, 128)               0         
                                                                 
 dense_4 (Dense)             (None, 24)                3096      
                                                                 
=================================================================
Total params: 11173464 (42.62 MB)
Trainable params: 11172760 (42.62 MB)
Non-trainable params: 704 (2.75 KB)
_________________________________________________________________
In [ ]:
# Compile the model
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# Define callbacks
early_stopping = EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True)
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.2, patience=2, min_lr=0.001)

# Train the model
history = model.fit(train_images,
                    validation_data=test_images,
                    epochs=10,
                    callbacks=[early_stopping, reduce_lr])
Epoch 1/10
312/312 [==============================] - 44s 122ms/step - loss: 0.2747 - accuracy: 0.9386 - val_loss: 0.6819 - val_accuracy: 0.7896 - lr: 0.0010
Epoch 2/10
312/312 [==============================] - 31s 101ms/step - loss: 0.0167 - accuracy: 0.9993 - val_loss: 0.0036 - val_accuracy: 1.0000 - lr: 0.0010
Epoch 3/10
312/312 [==============================] - 32s 102ms/step - loss: 0.0319 - accuracy: 0.9940 - val_loss: 0.0105 - val_accuracy: 0.9972 - lr: 0.0010
Epoch 4/10
312/312 [==============================] - 32s 102ms/step - loss: 0.0063 - accuracy: 0.9997 - val_loss: 4.3834e-04 - val_accuracy: 1.0000 - lr: 0.0010
Epoch 5/10
312/312 [==============================] - 32s 104ms/step - loss: 0.0037 - accuracy: 0.9997 - val_loss: 0.0040 - val_accuracy: 0.9980 - lr: 0.0010
Epoch 6/10
312/312 [==============================] - 32s 101ms/step - loss: 0.0031 - accuracy: 0.9999 - val_loss: 2.8158e-04 - val_accuracy: 1.0000 - lr: 0.0010
Epoch 7/10
312/312 [==============================] - 32s 102ms/step - loss: 0.0014 - accuracy: 1.0000 - val_loss: 6.3109e-05 - val_accuracy: 1.0000 - lr: 0.0010
Epoch 8/10
312/312 [==============================] - 36s 114ms/step - loss: 0.0010 - accuracy: 1.0000 - val_loss: 2.8823e-05 - val_accuracy: 1.0000 - lr: 0.0010
Epoch 9/10
312/312 [==============================] - 33s 104ms/step - loss: 6.2821e-04 - accuracy: 1.0000 - val_loss: 1.8058e-05 - val_accuracy: 1.0000 - lr: 0.0010
Epoch 10/10
312/312 [==============================] - 33s 105ms/step - loss: 6.6366e-04 - accuracy: 1.0000 - val_loss: 2.2270e-05 - val_accuracy: 1.0000 - lr: 0.0010

Plot Accuracy and Validation¶

In [ ]:
# Plotting Akurasi
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Train Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')

# Plotting Loss
plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Train Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend(loc='upper right')

plt.show()
No description has been provided for this image

Test Accuracy¶

In [ ]:
results = model.evaluate(test_images, verbose=0)
print("Test Loss: {:.5f}".format(results[0]))
print("Test Accuracy: {:.2f}%".format(results[1] * 100))
Test Loss: 0.00002
Test Accuracy: 100.00%

MobileNetV2¶

In [ ]:
# Load pretrained MobileNetV2 model without the top layers
pretrained_model = tf.keras.applications.MobileNetV2(
    input_shape=(224, 224, 3),
    include_top=False,
    weights='imagenet'
)
pretrained_model.trainable = False

# Create a new Sequential model
model2 = models.Sequential()

# Add the pretrained model
model2.add(pretrained_model)

# Add Conv2D layer
model2.add(layers.Conv2D(32, (3, 3), activation='relu'))

# Add Pooling layer
model2.add(layers.MaxPooling2D((2, 2)))

# Flatten the output from the pretrained model
model2.add(layers.Flatten())

# Add Dense layers
model2.add(layers.Dense(128, activation='relu'))
model2.add(layers.Dense(128, activation='relu'))

# Add the output layer
model2.add(layers.Dense(24, activation='softmax'))

# Display the model summary
model2.summary()
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
9406464/9406464 [==============================] - 0s 0us/step
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 mobilenetv2_1.00_224 (Func  (None, 7, 7, 1280)        2257984   
 tional)                                                         
                                                                 
 conv2d (Conv2D)             (None, 5, 5, 32)          368672    
                                                                 
 max_pooling2d (MaxPooling2  (None, 2, 2, 32)          0         
 D)                                                              
                                                                 
 flatten (Flatten)           (None, 128)               0         
                                                                 
 dense (Dense)               (None, 128)               16512     
                                                                 
 dense_1 (Dense)             (None, 128)               16512     
                                                                 
 dense_2 (Dense)             (None, 24)                3096      
                                                                 
=================================================================
Total params: 2662776 (10.16 MB)
Trainable params: 404792 (1.54 MB)
Non-trainable params: 2257984 (8.61 MB)
_________________________________________________________________
In [ ]:
model2.compile(optimizer='adam', loss='categorical_crossentropy',metrics=['accuracy'])

# Define callbacks
early_stopping = EarlyStopping(monitor='val_loss', patience=4, restore_best_weights=True)
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.2, patience=2, min_lr=0.0001)

history = model2.fit(train_images,
                     validation_data=test_images,
                     epochs=5,
                     callbacks=[early_stopping, reduce_lr]
                   )
Epoch 1/5
312/312 [==============================] - 38s 110ms/step - loss: 0.0302 - accuracy: 0.9909 - val_loss: 1.3443e-04 - val_accuracy: 1.0000 - lr: 0.0010
Epoch 2/5
312/312 [==============================] - 33s 104ms/step - loss: 4.8600e-05 - accuracy: 1.0000 - val_loss: 4.0772e-05 - val_accuracy: 1.0000 - lr: 0.0010
Epoch 3/5
312/312 [==============================] - 32s 103ms/step - loss: 1.9244e-05 - accuracy: 1.0000 - val_loss: 2.1851e-05 - val_accuracy: 1.0000 - lr: 0.0010
Epoch 4/5
312/312 [==============================] - 33s 104ms/step - loss: 9.1450e-06 - accuracy: 1.0000 - val_loss: 1.0300e-05 - val_accuracy: 1.0000 - lr: 0.0010
Epoch 5/5
312/312 [==============================] - 32s 102ms/step - loss: 4.1812e-06 - accuracy: 1.0000 - val_loss: 5.3353e-06 - val_accuracy: 1.0000 - lr: 0.0010

Plot Accuracy and Validation¶

In [ ]:
# Plotting Akurasi
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Train Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')

# Plotting Loss
plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Train Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend(loc='upper right')

plt.show()
No description has been provided for this image

Save Model¶

Saved Model¶

In [ ]:
os.makedirs(BaseDir+'saved_model', exist_ok=True)

tf.saved_model.save(model, 'saved_model')

TFLITE¶

In [ ]:
# Convert the model to TF-Lite format
converter = tf.lite.TFLiteConverter.from_keras_model(model)
tflite_model = converter.convert()

# Save the TF-Lite model to a specific folder
os.makedirs(BaseDir+'tf_lite', exist_ok=True)
folder_path = BaseDir+'tf_lite'
file_path = f'{folder_path}/model.tflite'

with open(file_path, 'wb') as f:
    f.write(tflite_model)
In [ ]:
def recreate_labels():
    labels = [folder for folder in os.listdir(DIR) if not folder.startswith('.')]

    with open(BaseDir+'tf_lite/labels.txt', 'w') as file:
        for label in labels:
            file.write(label)
            file.write('\n')

recreate_labels()

TFJS¶

In [ ]:
import tensorflowjs as tfjs

os.makedirs(BaseDir+'tfjs_model', exist_ok=True)
tfjs.converters.save_keras_model(model, BaseDir+'tfjs_model')

Inference¶

In [ ]:
In [ ]:
image_path = BaseDir+'dataset/fruits/cucumber_1/r0_49.jpg'
input_data = preprocess_image(image_path)

interpreter.set_tensor(input_details[0]['index'], input_data)
interpreter.invoke()
output_data = interpreter.get_tensor(output_details[0]['index'])

predicted_class_idx = np.argmax(output_data, axis=1)[0]
predicted_class = labels[predicted_class_idx]
print('Predicted Class:', predicted_class)
Predicted Class: cucumber_1
In [ ]:
image_path = BaseDir+'dataset/fruits/pear_1/r1_292.jpg'
input_data = preprocess_image(image_path)

interpreter.set_tensor(input_details[0]['index'], input_data)
interpreter.invoke()
output_data = interpreter.get_tensor(output_details[0]['index'])

predicted_class_idx = np.argmax(output_data, axis=1)[0]
predicted_class = labels[predicted_class_idx]
print('Predicted Class:', predicted_class)
Predicted Class: pear_1

Conclusion¶

  • Berdasarkan percobaan, dihasilkan untuk:

    • Model 1
      • Model : Sequential
      • Akurasi : 100 %
      • Validasi akurasi : 100 %
    • Model 2
      • Model : Sequential dengan pretrained MobileNetV2
      • Akurasi : 100 %
      • Validasi akurasi : 100 %

    Sehingga model tersebut dapat digunakan untuk memprediksi gambar buah (fruits) dimasa mendatang.

  • Hasil model mampu memprediksi gambar buah (fruits) yang diinputkan melalui inference.