การจำแนกสายพันธุ์มะม่วง โดยใช้ Visual Geometry Group 16 (VGG16) ใน Python
Sirinya Thanyajaroen
Posted on April 16, 2024
ถ้าเราต้องการจำแนกประเภทของรูปภาพหรือประเภทของสิ่งของ เช่น ยี่ห้อรถยนต์ สายพันธุ์มะม่วง หรือสายพันธุ์สัตว์ หนึ่งในวิธีที่ง่ายและเหมาะสมที่ใช้สำหรับการทำนายข้อมูลแบบนี้คือ การใช้ 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
ขั้นตอนที่ 2 โหลดข้อมูลชุดรูปภาพลงใน Google Colab
โดยขั้นตอนนี้เราจะทำการอัพโหลด dataset เข้า Google Drive และทำการเชื่อม Google Colab เข้ากับโฟลเดอร์ที่เก็บ dataset ใน Google Drive
from google.colab import drive
drive.mount('/content/drive')
ตัวอย่างผลที่ได้จาก Code
Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount("/content/drive", force_remount=True).
เราก็จะสามารถเชื่อม Google Colab กับ Google Drive ได้ หลังจากนั้นเราก็ทำการ ลิ้งค์ไปยังโฟลเดอร์Project ของเราด้วยคำสั่ง
FOLDERNAME = 'VGG16/MangoVGG16'
%cd /content/drive/My\ Drive/VGG16
ขั้นตอนที่ 3 กำหนดขนาดของรูปภาพ
โดยในขั้นตอนนี้เราจะต้องทำการกำหนดขนาดของรูปภาพให้รูปภาพมีความกว้าง 128 พิกเซล และกำหนดคลาสของมะม่วงแต่ละชนิดโดยในที่นี้เราจะทำการกำหนดไว้ 3 คลาส เพราะต้องจำแนกสายพันธุ์มะม่วง จำนวน 3 สายพันธุ์
width = 128
num_classes = 3
ขั้นตอนที่ 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('.')]
ขั้นตอนที่ 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
ขั้นตอนที่ 6 ทำการอ่านไฟล์รูปภาพออกมา
ขั้นตอนนี้เราจะทำตัวแปรมารับค่าของ trainImg และ testImg ขึ้นมา โดยจะเป็นค่าของ RGB สำหรับรูปภาพในการเทสและเทรนของโมเดลแต่ละโมเดล
x_train , y_train = img2data(trainImg)
x_test, y_test = img2data(testImg)
ผลลัพธ์ที่ได้จาก Code
ขั้นตอนที่ 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
ขั้นตอนที่ 8 ดูผลลัพธ์ทั้งหมดของรูปภาพ
ขั้นตอนนี้เราจะทำการดูผลลัพธ์ของรูปภาพที่แปลงเป็น Array
x_train.shape,y_train.shape,x_test.shape,y_test.shape
ผลลัพธ์ที่ได้จาก Code
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')
])
ขั้นตอนที่ 10 ดูรูปร่างของข้อมูลที่ได้นำมาทดสอบ
ทำการแสดงผลลัพธ์เพื่อวิเคราะห์โครงสร้างและประสิทธิภาพของโมเดล Deep Learning
model.summary()
ผลลัพธ์ที่ได้จาก Code
โมเดลนี้มีทั้งหมด 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
ขั้นตอนที่ 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))
ผลลัพธ์ที่ได้จาก Code
ขั้นตอนที่ 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, "%")
ผลลัพธ์ที่ได้จาก Code
จากกราฟที่ได้มาสรุปได้ว่า ในแนวแกน X มีจำนวน epoch 20 epoch และแนวแกน Y คือความแม่นยำของโมเดล โดยที่จะเริ่มต้นที่ร้อยละ 0.4 และสูงสุดที่ร้อยละ 0.97
จากกราฟที่ได้มาสรุปได้ว่า ในแนวแกน X มีจำนวน epoch 20 epoch และแนวแกน Y คือค่าความคลาดเคลื่อนของโมเดล โดยความคลาดเคลื่อนจะเริ่มต้นที่ร้อยละ 1.0 และต่ำสุดที่ร้อยละ 0.07
ในการทดสอบนี้ทั้งหมด ค่าความแม่นยำของการเทรนโมเดลเฉลี่ยอยู่ที่ 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()
ผลลัพธ์ที่ได้จาก Code
จากผลลัพธ์ในการทดสอบความแม่นยำในการตรวจสายพันธุ์มะม่วง ภาพแรกพบว่ามีความเป็นได้ว่าเป็นมะม่วงสายพันธุ์ Dasheri อยู่ที่ 99 % ซึ่งเป็นคำตอบที่ถูกต้อง
จากผลลัพธ์ในการทดสอบความแม่นยำในการตรวจสายพันธุ์มะม่วง ภาพที่สองพบว่ามีความเป็นได้ว่าเป็นมะม่วงสายพันธุ์ Chaunsa อยู่ที่ 99 % ซึ่งเป็นคำตอบที่ถูกต้อง
จากผลลัพธ์ในการทดสอบความแม่นยำในการตรวจสายพันธุ์มะม่วง ภาพที่สามพบว่ามีความเป็นได้ว่าเป็นมะม่วงสายพันธุ์ Fajri อยู่ที่ 94 % ซึ่งเป็นคำตอบที่ถูกต้อง
สรุป
จากการทดลองเราจะเห็นได้ว่า 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/
Posted on April 16, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.