DEV Community

Cover image for การจำแนกสายพันธุ์มะม่วง โดยใช้ Visual Geometry Group 16 (VGG16) ใน Python
Sirinya Thanyajaroen
Sirinya Thanyajaroen

Posted on

การจำแนกสายพันธุ์มะม่วง โดยใช้ Visual Geometry Group 16 (VGG16) ใน Python

ถ้าเราต้องการจำแนกประเภทของรูปภาพหรือประเภทของสิ่งของ เช่น ยี่ห้อรถยนต์ สายพันธุ์มะม่วง หรือสายพันธุ์สัตว์ หนึ่งในวิธีที่ง่ายและเหมาะสมที่ใช้สำหรับการทำนายข้อมูลแบบนี้คือ การใช้ VGG16 เป็นอีกหนึ่งวิธีที่เราสามารถเลือกใช้ได้ ซึ่ง Visual Geometry Group 16 หรือ VGG16 เป็นโมเดลเครือข่ายประสาทเทียม โดยจุดเด่นของ VGG16 คือ โครงสร้างของโมเดลที่เรียบง่าย ประกอบไปด้วย Convolutional 3x3 ซ้อนกัน 16 ชั้น โดยใช้ stride 1 และ padding แบบ same

โดย Convolutional 3x3 หมายถึง การใช้ตัวกรอง (Filter) ขนาด 3x3 ทำการแปลงข้อมูลภาพ (Input) ให้เป็นข้อมูลภาพ (Output)
การซ้อนกัน 16 ชั้น หมายถึง การใช้ Convolutional 3x3 ซ้ำกัน 16 ครั้ง
Stride 1 หมายถึง การเลื่อนตัวกรอง (Filter) ทีละ 1 พิกเซล
Padding แบบ Sameหมายถึง การเติมขอบของข้อมูลภาพ (Input) ด้วยค่า 0 ก่อนการ Convolutional

บทความนี้ เราจะมาดู Visual Geometry Group 16 หรือ VGG16 ใน Python กัน เราจะใช้ Google Colab ในการรันโค้ด โดย dataset ที่เราใช้เป็นตัวอย่างคือ Mango Variety and Grading Dataset ซึ่งเป็นข้อมูลสายพันธุ์มะม่วงได้มาจาก Mango Varieties Classification and Grading

ขั้นตอนที่ 1 นำเข้า Libraries ที่ต้องใช้ทั้งหมดลงใน Google Colab
ตัวอย่างของ Libraries

import numpy as np
import cv2
import os
from tqdm import tqdm
import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPool2D
from keras.applications.vgg16 import VGG16
import tensorflow as tf
from os import listdir
from os.path import isfile, join
Enter fullscreen mode Exit fullscreen mode

ขั้นตอนที่ 2 โหลดข้อมูลชุดรูปภาพลงใน Google Colab
โดยขั้นตอนนี้เราจะทำการอัพโหลด dataset เข้า Google Drive และทำการเชื่อม Google Colab เข้ากับโฟลเดอร์ที่เก็บ dataset ใน Google Drive

from google.colab import drive
drive.mount('/content/drive')
Enter fullscreen mode Exit fullscreen mode

ตัวอย่างผลที่ได้จาก Code

Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount("/content/drive", force_remount=True).
Enter fullscreen mode Exit fullscreen mode

เราก็จะสามารถเชื่อม Google Colab กับ Google Drive ได้ หลังจากนั้นเราก็ทำการ ลิ้งค์ไปยังโฟลเดอร์Project ของเราด้วยคำสั่ง

FOLDERNAME = 'VGG16/MangoVGG16'
%cd /content/drive/My\ Drive/VGG16
Enter fullscreen mode Exit fullscreen mode

ขั้นตอนที่ 3 กำหนดขนาดของรูปภาพ
โดยในขั้นตอนนี้เราจะต้องทำการกำหนดขนาดของรูปภาพให้รูปภาพมีความกว้าง 128 พิกเซล และกำหนดคลาสของมะม่วงแต่ละชนิดโดยในที่นี้เราจะทำการกำหนดไว้ 3 คลาส เพราะต้องจำแนกสายพันธุ์มะม่วง จำนวน 3 สายพันธุ์

width = 128
num_classes = 3
Enter fullscreen mode Exit fullscreen mode

ขั้นตอนที่ 4 ทำการอ่านไฟล์รูปภาพในแต่ละโฟลเดอร์
โดยในขั้นตอนนี้เราจะทำการอ่านไฟล์รูปภาพที่ใช้ในการเทรนโมเดล และรูปภาพในการเทสโมเดล

trainpath = 'train/'
testpath = 'test/'

trainImg = [trainpath+f for f in listdir(trainpath) if not f.startswith('.')]
testImg = [testpath + f for f in listdir(testpath) if not f.startswith('.')]
Enter fullscreen mode Exit fullscreen mode

ขั้นตอนที่ 5 สร้างฟังก์ชันในการอ่านไฟล์รูปภาพ
ในขั้นตอนนี้จะทำการอ่านไฟล์รูปภาพและสีแต่ละพิกเซลออกมา

•โดยเราจะสร้างฟังก์ชันที่ชื่อว่า img2data โดยเราจะสร้างตัวแปร rawImgs เพื่อเก็บตัวแปรของแต่ละพิกเซลเอาไว้
•จะสร้าง labels เอาไว้เพื่อให้อ่าน path ว่าเป็นรูปของมะม่วงสายพันธุ์อะไร

ถ้าหากว่าอ่านออกมาแล้วเป็นมะม่วงสายพันธุ์ Fajri จะกำหนดให้ labels เป็น [1,0,0] มะม่วงสายพันธุ์ Dasheri กำหนดให้ labels เป็น [0,1,0] มะม่วงสายพันธุ์ Chaunsa กำหนดให้ labels เป็น [0,0,1] และจะใช้ฟังก์ชัน OpenCV ในการอ่านไฟล์รูปภาพออกมา และทำการปรับขนาดที่เราทำการกำหนดไว้ และให้ส่งค่ากลับไปด้วยสั่ง return ไปที่ rawImgs และ labels

def img2data(path):
  rawImgs = []
  labels = []
  c = 0
  for imagePath in path:
    for item in tqdm(os.listdir(imagePath,)):
      file = os.path.join(imagePath, item)
      c += 1
      l = imagePath.split('/')[1]

      # Ensure file exists before loading
      if not os.path.exists(file):
        print(f"Error: File not found: {file}")
        continue  # Skip this image

      try:
        img = cv2.imread(file, cv2.COLOR_BGR2RGB)
        if img is None:
          print(f"Error: Failed to read image: {file}")
          continue  # Skip this image

        # Check for valid dimensions
        if img.shape[0] == 0 or img.shape[1] == 0:
          print(f"Error: Image has invalid dimensions: {file}")
          continue  # Skip this image

        img = cv2.resize(img, (width, width))
        rawImgs.append(img)

        if l == 'Fajri':
          labels.append([1, 0, 0])
        elif l == 'Dasheri':
          labels.append([0, 1, 0])
        elif l == 'Chaunsa':
          labels.append([0, 0, 1])
      except Exception as e:
        print(f"Error processing image: {file} - {e}")

  return rawImgs, labels
Enter fullscreen mode Exit fullscreen mode

ขั้นตอนที่ 6 ทำการอ่านไฟล์รูปภาพออกมา
ขั้นตอนนี้เราจะทำตัวแปรมารับค่าของ trainImg และ testImg ขึ้นมา โดยจะเป็นค่าของ RGB สำหรับรูปภาพในการเทสและเทรนของโมเดลแต่ละโมเดล

x_train , y_train = img2data(trainImg)
x_test, y_test = img2data(testImg)
Enter fullscreen mode Exit fullscreen mode

ผลลัพธ์ที่ได้จาก Code

Image description

ขั้นตอนที่ 7 ทำแปรข้อมูลรูปภาพให้เป็น Array
ขั้นตอนนี้ต้องแปรรูปภาพเป็น Array เพื่อให้โมเดล Deep Leaning ทำงานโดยใช้หลักการคณิตศาสตร์และอัลกอริทึมที่ทำงานบนข้อมูลตัวเลข ซึ่งแต่ละพิกเซล จะมีค่าสีเป็น RGB ระหว่าง 0 ถึง 255 และเป็นการเพิ่มประสิทธิภาพการประมวลผล และการสร้างความสม่ำเสมอของข้อมูล

x_train = np.array(x_train)
y_train = np.array(y_train)
x_test = np.array(x_test)
y_test = np.array(y_test)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /=255
x_test /=255
Enter fullscreen mode Exit fullscreen mode

ขั้นตอนที่ 8 ดูผลลัพธ์ทั้งหมดของรูปภาพ
ขั้นตอนนี้เราจะทำการดูผลลัพธ์ของรูปภาพที่แปลงเป็น Array

x_train.shape,y_train.shape,x_test.shape,y_test.shape
Enter fullscreen mode Exit fullscreen mode

ผลลัพธ์ที่ได้จาก Code

Image description

560 คือรูปภาพทั้งหมดที่ใช้ในการ train และมีอัตราส่วนของรูปภาพอยู่ที่ 128 แถว และ 128 คอลัม และมิติของภาพ คือ 3 มิติ คือค่า RGB และผลเฉลยของภาพ มี 560 และมี 3 ค่า คือ มะม่วงสายพันธุ์ Fajri ที่กำหนดให้ labels เป็น [1,0,0] สายพันธุ์ Dasheri ที่กำหนดให้ labels เป็น [0,1,0] มะม่วงสายพันธุ์ Chaunsa ที่กำหนดให้ labels เป็น [0,0,1]

ขั้นตอนที่ 9 สร้างโมเดล Convolutional Neural Network (CNN) สำหรับการจัดประเภทภาพ (Image Classification) โดยใช้ Transfer Learning จาก Pre-Trained Model VGG16
จะทำการสร้างโมเดล CNN แบบสองชั้นขึ้นมา โดยใช้ VGG16 แล้วต่อด้วยโมเดล CNN ที่สร้างขึ้นใหม่ เพื่อปรับแต่งการเรียนรู้ของโมเดลให้มีประสิทธิภาพมากขึ้น (Fine – Tune) สำหรับการจัดประเภทภาพโดยเฉพาะ

base_model = VGG16(input_shape=(128, 128, 3), include_top=False, weights='imagenet')
base_model.trainable = False  # ฟรีซเลเยอร์โมเดล VGG16
num_classes = 3

width = 128
model = Sequential([
    keras.layers.Conv2D(32,(3,3), activation='relu', input_shape = (width,width, 3)),
    keras.layers.MaxPooling2D(pool_size=(2,2)),
    keras.layers.Conv2D(64,(3,3), activation='relu'),
    keras.layers.MaxPooling2D(pool_size=(2,2)),
    keras.layers.Dense(128),
    keras.layers.Flatten(),
    keras.layers.Dense(num_classes, activation='softmax')
])
Enter fullscreen mode Exit fullscreen mode

ขั้นตอนที่ 10 ดูรูปร่างของข้อมูลที่ได้นำมาทดสอบ
ทำการแสดงผลลัพธ์เพื่อวิเคราะห์โครงสร้างและประสิทธิภาพของโมเดล Deep Learning

model.summary()
Enter fullscreen mode Exit fullscreen mode

ผลลัพธ์ที่ได้จาก Code

Image description

โมเดลนี้มีทั้งหมด 6 ชั้น ประกอบด้วย Convolution layer 2 ชั้น , MaxPooling layer 2 ชั้น, Flatten layer 1 ชั้นและ Dense layer 1 ชั้น โดยมพารามิเตอร์ทั้งหมด 373,315 ตัว โมเดลมีพารามิเตอร์ที่ trainable 352,641 ตัว และโมเดลที่ไม่มีพารามิเตอร์ 0 ตัว

ขั้นตอนที่ 11 Compile Model Deep Learning เตรียมโมเดลให้พร้อมสำหรับการ Train
ทำการกำหนด epochs เอาไว้ที่ 20 ครั้ง และ batch size อยู่ที่ 32 รอบ คือจำนวนในการเทรน 1 ครั้ง จะมีจำนวนอยู่ที่ 32 รอบ

model.compile(optimizer=keras.optimizers.RMSprop(learning_rate=0.0001),loss='categorical_crossentropy',metrics=['accuracy'])
batch_size = 32
epochs = 20
Enter fullscreen mode Exit fullscreen mode

ขั้นตอนที่ 12 การ Train Model
ทำการ Train Model จำนวน 20 รอบ ตามจำนวน epochs

history = model.fit(x_train, y_train ,batch_size=batch_size, epochs=epochs ,validation_data=(x_test, y_test))
Enter fullscreen mode Exit fullscreen mode

ผลลัพธ์ที่ได้จาก Code

Image description

ขั้นตอนที่ 13 การพล๊อตกราฟ
ขั้นตอนนี้เราจะทำการพล๊อตกราฟขึ้นมา เพื่อดูค่าความแม่นยำของโมเดลที่เราได้นำมา
ทดลอง และเราได้ใช้ไลบรารี่ Matplotlib ขึ้นมา เพื่อสามารถพล๊อตกราฟได้

import matplotlib.pyplot as plt
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'validation'], loc='upper left')
plt.show()
# "Loss"
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'validation'], loc='upper left')
plt.show()

acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
avg_acc = np.mean(acc) * 100
avg_val_acc = np.mean(val_acc) * 100


print("Average training accuracy:", avg_acc, "%")
print("Average validation accuracy:", avg_val_acc, "%")

Enter fullscreen mode Exit fullscreen mode

ผลลัพธ์ที่ได้จาก Code

จากกราฟที่ได้มาสรุปได้ว่า ในแนวแกน X มีจำนวน epoch 20 epoch และแนวแกน Y คือความแม่นยำของโมเดล โดยที่จะเริ่มต้นที่ร้อยละ 0.4 และสูงสุดที่ร้อยละ 0.97

Image description

จากกราฟที่ได้มาสรุปได้ว่า ในแนวแกน X มีจำนวน epoch 20 epoch และแนวแกน Y คือค่าความคลาดเคลื่อนของโมเดล โดยความคลาดเคลื่อนจะเริ่มต้นที่ร้อยละ 1.0 และต่ำสุดที่ร้อยละ 0.07
Image description

ในการทดสอบนี้ทั้งหมด ค่าความแม่นยำของการเทรนโมเดลเฉลี่ยอยู่ที่ 87.17 % และความแม่นยำของการทดสอบโมเดลเฉลี่ยอยู่ที่ 87.80 %

ขั้นตอนที่ 14 ทำนายผลของสายพันธุ์มะม่วง
ทำการทำนายสายพันธุ์มะม่วง โดยใช้ Model Deep Learning ที่เรา Train เอาไว้ในการทดสอบ

testpath = 'test/'
testImg = [testpath+f for f in listdir(testpath) if listdir(join(testpath, f))]
rimg = []
for imagePath in (testImg):
    for item in (os.listdir(imagePath)):
        file = os.path.join(imagePath, item)
        if item.split('.')[0] != "":

          img = cv2.imread(file , cv2.COLOR_BGR2RGB)
          ori = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
          img = cv2.resize(img ,(width,width))
          rimg = np.array(img)
          rimg = rimg.astype('float32')
          rimg /= 255
          rimg = np.reshape(rimg ,(1,128,128,3))
          predict = model.predict(rimg)
          label = ['Fajri','Dasheri','Chaunsa']
          result = label[np.argmax(predict)]
          print(predict * 100)
          print('real:'+str(item))
          print('predict:'+str(result))
          plt.imshow(ori)
          plt.show()

Enter fullscreen mode Exit fullscreen mode

ผลลัพธ์ที่ได้จาก Code
จากผลลัพธ์ในการทดสอบความแม่นยำในการตรวจสายพันธุ์มะม่วง ภาพแรกพบว่ามีความเป็นได้ว่าเป็นมะม่วงสายพันธุ์ Dasheri อยู่ที่ 99 % ซึ่งเป็นคำตอบที่ถูกต้อง

Image description

จากผลลัพธ์ในการทดสอบความแม่นยำในการตรวจสายพันธุ์มะม่วง ภาพที่สองพบว่ามีความเป็นได้ว่าเป็นมะม่วงสายพันธุ์ Chaunsa อยู่ที่ 99 % ซึ่งเป็นคำตอบที่ถูกต้อง

Image description

จากผลลัพธ์ในการทดสอบความแม่นยำในการตรวจสายพันธุ์มะม่วง ภาพที่สามพบว่ามีความเป็นได้ว่าเป็นมะม่วงสายพันธุ์ Fajri อยู่ที่ 94 % ซึ่งเป็นคำตอบที่ถูกต้อง

Image description

สรุป
จากการทดลองเราจะเห็นได้ว่า Visual Geometry Group 16 (VGG16) ใน Python ที่ใช้ในการจำแนกสายพันธุ์มะม่วง มีความแม่นยำอยู่ที่ 91 % ถึงแม้ว่าจะมีความแม่นยำที่สูงแต่ก็ยังมีโอกาสที่จะผิดพลาดได้ และโมเดลนี้สามารถนำไปใช้ในการตรวจสอบเพิ่มเติมเกี่ยวกับมะม่วงได้ เช่น การตรวจสอบคุณภาพของมะม่วง วิเคราะห์ความสุก-ดิบของมะม่วงได้

Referenceshttps
https://www.kaggle.com/datasets/riyaelizashaju/skin-disease-image-dataset-balanced?fbclid=IwAR3wbTp8l5yo_5fx6HAX8Vd2-9cca3khAc8EiBGFObaALfdVid29IuB_rYE
https://keras.io/api/applications/vgg/
https://www.tensorflow.org/tutorials/images/cnn?hl=th
https://opencv.org/

Top comments (0)