Deep Learning แบบสามัญชน EP 3 Keras with Image Classification : แยกภาพหมาแมวแบบง่ายๆ

Mr.P L
mmp-li
Published in
4 min readAug 25, 2019

แยกภาพแบบง่ายๆ ไม่ต้องมีความรู้ด้าน image เลยด้วยซ้ำ

ในบทความก่อนหน้านี้ผมไม่เคยทดลองข้อมูลกับรูปภาพเลยสักครั้ง และเนื่องจากเทรน “Image/Computer Vision” กำลังมาแรงมา เด็กๆที่มา Intern ที่มหาลัยของผมส่วนมากสนใจด้าน Image กันหมดเลย ทำให้ตัวผมต้องเปลี่ยนแปลงแนวทางของบทความบ้าง

ที่จริงแล้วการจัดการรูปภาพ (Image) สามารถทำได้ง่ายๆด้วยคำสั่ง open แล้วก็จะแปลงไฟล์รูปภาพมาเป็น…. Matrix นั้นเอง

ช่วงความรู้รอบตัว

รู้หมือไร่ ? รูปภาพนั้นที่แท้จริงคือ pixel แต่ละ pixel มารวมกัน และเมื่อรวมกันเสร็จเราลอง inspect รูปภาพดูจะพบว่ามันมีโครงสร้างเป็น Matrix โดยที่มีขนาดเท่ากับขนาดของภาพและมีจำนวนชั้นอีก 3 ชั้นนั้นคือ R/G/B นั้นเองงงงงงงงง

โดยในครั้งนี้จะเป็นการทดลองแบบน้ำจิ้มเพื่อเป็นการปูทางไปยัง CNN นั้นเองโดยในปัจจุบัน Neural Network Architecture นั้นมีมากมายและซับซ้อนไม่ว่าจะเป็น RNN — LSTM — Seq2Seq — GRU โดยบทความนี้จะแค่ Neural network พื้นฐานเพื่อให้เห็นภาพและเข้าใจได้ง่ายมากที่สุดนั้นเอง

เริ่มเขียนโค้ดกัน

Dataset ที่เราจะใช้ในวันนี้คือ

ซึ่งภายในเป็นรูปหมากับแมวแยก Training / Test อย่างชัดเจนแต่วันนี้เราจะใช้แค่ Training Set เพราะ Test เอาไว้ Test และส่งเข้าแข่งขันของ Kaggle (ซึ่งจะมีบทความแนะนำในภายหลัง)

โดยโค้ดทั้งหมดอยู่ใน github แล้ว

ขั้นตอนแรกเรียก lib ที่เป็น Deep Learning Model ซึ่งเราจะใช้ Keras (ติดตั้ง Tensorflow แล้วจะได้ Keras มาด้วยเลย)

import keras
from keras.models import Sequential
from keras.layers import Dense
from keras.utils import to_categorical
from keras.layers import Dropout

และเรียก lib สำหรับ Evaluate โมเดลไว้เลย

from sklearn.metrics import classification_report 
from sklearn.metrics import f1_score ,accuracy_score

ทีนี้ผมจะสร้างฟังก์ชั่นขึ้นมาหนึ่งอันเพื่อแปลงรูป ให้เป็น Histogram

def extract_color_histogram(image, bins=(8, 8, 8)):
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
hist = cv2.calcHist([hsv], [0, 1, 2], None, bins,
[0, 180, 0, 256, 0, 256])
if imutils.is_cv2():
hist = cv2.normalize(hist)
else:
cv2.normalize(hist, hist)
return hist.flatten()

แต่ถ้าเราจำได้ว่ารูปของเรามีหลายรูปและอยู่ในโฟล์เดอร์อีกเราจะทำยังไงเพื่อจะเปิดทุกๆรูปดี ? เราก็จะต้องหาชื่อรูปทั้งหมดแล้วค่อยๆเปิดทีละรูปก่อนตามโค้ดข้างล่าง

from os import listdir
from os.path import isfile, join
mypath = ‘train/’
imagePaths = [mypath+f for f in listdir(mypath) if isfile(join(mypath, f))]

เมื่อรันแล้วเราจะได้ชื่อรูปทั้งหมดอยู่ในตัวแปร imagePaths นั้นเอง

แต่เราแค่ได้ชื่อรูป เราจะต้องเปิดทีละรูปและเรียกฟังก์ชั่น Histogram เพื่อจัดการกันรูปเรานั้นเอง

for (i, imagePath) in enumerate(imagePaths):
image = cv2.imread(imagePath)
label = imagePath.split(os.path.sep)[-1].split(“.”)[0]

pixels = extract_color_histogram(image)
rawImages.append(pixels)
labels.append(label)
if i > 0 and i % 1000 == 0:
print(“[INFO] processed {}/{}”.format(i, len(imagePaths)))
ทำไปเรื่อยๆจนกว่าจะครบ 25K รูปนั้นเอง

และขั้นต่อไปมันอาจจะแปลกๆนิดหน่อย คือในบทความที่ผ่านๆมาเราจะแบ่งข้อมูลเป็น Train/Test แต่ในทางปฏิบัติจริงๆควรจะแบ่งเป็น 3 ส่วนคือ Train/ Validation ใช้สำหรับฝึกฝนข้อมูลและปรับพารามิเตอร์และมี Test เอาไว้ทดสอบโมเดลจริงๆ

โดยตอนแรกแบ่งเป็น 80/20

X_train, X_test, y_train, y_test = train_test_split(np.array(rawImages), np.array(labels), random_state=1)

จากนั้นแบ่งจาก 80 อีกเป็น 80/20 ก็จะได้สัดส่วน 60/20/20

X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.25, random_state=1)

และจากนั้นแปลง output ของเราให้เป็น 1-hot

from sklearn import preprocessing
le = preprocessing.LabelEncoder()
le.fit(y_train)
y_train = le.transform(y_train)
y_test = le.transform(y_test)
y_val = le.transform(y_val)
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
y_val = to_categorical(y_val)

ทีนี้ก็สร้างโมเดลขึ้นมา

def classification_model():
model = Sequential()
model.add(Dense(124, activation=’relu’, input_shape=(X_train.shape[1],)))
model.add(Dropout(0.2))
model.add(Dense(124, activation=’relu’))
model.add(Dropout(0.2))
model.add(Dense(32, activation=’relu’))
model.add(Dense(y_train.shape[1], activation=’softmax’))
opt = keras.optimizers.Adam(lr=0.0001, decay=1e-6)
model.compile(optimizer=opt, loss=’categorical_crossentropy’, metrics=[‘accuracy’])
return model

จะอธิบายแบบละเอียดดังนี้

สกิลการวาดรูป D+ จริงๆ
  1. ส่วน Input : เราจะเอาโนดจำนวน X โนด ซึ่ง 1 node จะเท่ากับ 1 มิติข้อมูล ถ้าเราโชว์ค่าภายใน X_xxx จะได้ shape= (จำนวนข้อมูล,มิติของข้อมูล) โดยโนด Input จะต้องมีจำนวนเท่ากับ “มิติของข้อมูล” นั้นเอง
  2. ส่วน Hidden Layer : มีทั้งหมด 3 อัน โดยมีจำนวน 124,124 และ 32 ตามลำดับ โดยแต่ละโนดของ Hidden layer อันแรกจะรับค่ามาจากโนดของ Input โดยทุกๆโนดจะเชื่อมต่อหากัน เราเรียกการเชื่อมแบบนี้ว่า “Fully Connected Layer”

โดยทุกๆครั้งที่ข้อมูลวิ่งผ่านเส้นก็จะเกิดการคำนวณ โดยภายในโนดก็จะมีคณิตศาสตร์คอยคำนวณข้อมูลที่วิ่งเข้ามา และยังคำนวณ Activation Function โดยเราเลือกใช้ ReLu นั้นเอง (ถ้าใครอยากศึกษาเรื่องการคำนวณแต่ละโนด หรือการคำนวณ Activation Function และ Optimization เพิ่มเชิญที่ : IBM Deep Learning Course)

แต่ข้างในนั้นผมได้ใส่ Drop out เข้าไปอีก โดยถ้าเราสังเกตุดีๆจะพบว่าถ้าเราทำ Fully connected layer เท่ากับว่าทุกโนดจะเชื่อมต่อหากันทำให้เกิดการคำนวณจำนวนมาก และใช้ข้อมูลจำนวนมาก พอใช้ข้อมูลจำนวนมากก็จะเกิดสิ่งที่เรียกว่า “Over fitting” เราจึงใช้เทคนิค Drop out เพื่อให้เส้นที่แต่ละโนดเชื่อมกันอยู่ “ในบางครั้งอาจจะไม่เชื่อมกัน” ตัวอย่างเช่น

แต่ละครั้งเส้นก็จะหายไม่เหมือนกันด้วย แต่บางครั้งก็อาจจะเหมือนกันได้ ถ้าเทรนจำนวนเยอะๆ

3. ส่วน Output : เมื่อข้อมูลถูกส่งผ่านทุกโนดจนครบแล้ว ทีนี้มาถึงจุดทางออก แต่ก่อนจะถึงจุดทางออกผมก็ใส่ Soft Max function ไว้อีก

โดยปกติแล้ว output ของ neural network จะออกมาเป็นตัวเลข (ความน่าจะเป็น) ซึ่งวิธีการหาคำตอบคือเราจะดูว่าค่าความน่าจะเป็นอันไหนเยอะสุด ถ้าอันไหนเยอะสุด ก็คือคลาสนั้น แต่ความตลกคือ ถ้าเราเอาความน่าจะเป็นแต่ละคลาสมารวมกันมันจะได้มากกว่า 1 (wtf ! ความน่าจะเป็นคือ 0–1 ไม่ใช่เหรอ !) ยิ่งถ้าเรามี 100 คลาส เราก็จะต้องมานั่งหา Max เองอีก แถมค่า prob มันก็จะแปลกๆ เพราะฉนั้นทางที่ดีที่สุดคือการใส่ Soft Max function เข้าไปใน Neural network ของเรา (แต่ไม่ใช้ทุกงานที่สมควรใช้ soft max ไว้จะอธิบายเพิ่มให้บทความถัดๆไป) เมื่อใส่ soft max แล้วค่า prob ทั้งหมดเวลารวมกันจะไม่เกิน “1” นั้นเอง ทำให้เราหาคำตอบได้ง่ายมากขึ้นว่าคำตอบคือคลาสไหนกันแน่

จากนั้นก็เรียกฟังก์ชั่น แล้วเทรนโมเดลได้เลย โดยเราจะเทรนแค่ 10 รอบ โดยแต่ละรอบตัวโมเดลก็จะเอาค่าใน validation ไปทดสอบโมเดลและปรับปรุงด้วยตัว Optimizer ที่เราเลือก (Adam) นั้นเอง

model = classification_model()
model.fit(X_train, y_train, validation_data=(X_val, y_val,), epochs=10, verbose=2)

ทีนี้ทดสอบกับชุด Test ของเรา โดยใส่ค่าเข้าไปได้เลย โดยไม่ต้องแปลงอะไรเลย

value = model.predict(X_test)
y_pred =np.argmax(value,axis=1)
y_true = np.argmax(y_test,axis=1)

แต่ขาออกมาจะต้องใช้ฟังก์ชั่นช่วยนิดหน่อยคือ argmax เพราะ output ของเราเป็น prob ที่ผ่าน soft max มาแล้วก็จริง แต่เราก็ต้องหาอยู่ดีกว่าแล้วค่า prob ของคลาสไหนเยอะที่สุด

โดยเมื่อแปลงค่ามาแล้ว ก็เอาไปเข้าฟังก์ชั่นช่วย evaluate ได้เลย

print(classification_report(y_true,y_pred))
ไม่แย่ แต่ดีได้มากกว่านี้

สรุป

ในบทความนี้จะเน้นไปที่การขยายความโมเดล ในบางส่วนผมก็ต้องข้ามไปบ้าง เพราะถ้าเล่าเรื่องวิธีการคำนวณแต่ละโนด สามารถเขียนแยกเป็นอีกหนึ่งบทความได้เลย ส่วนถ้าใครสนใจจริงๆก็สามารถคลิกลิ้งที่ผมแทรกไว้ให้ได้นะครับ

แต่ในบทความถัดไปผมจะเน้นไปโมเดลยิ่งกว่านี้เพราะ CNN มันกำลังดังจริงๆ แถมมันยังได้ผลลัพธ์ที่ดีกว่าการทำ Multilayer Perceptron แบบนี้อีกเพราะเราต้องทำ Feature Engineering เองอีก แถมความแม่นยำในบทความนี้ก็แค่ 0.65 มันยังไม่ดีพอจะใช้งานได้

บทความถัดไป

Deep Learning แบบสามัญชน EP 4 Keras CNN with Image Classification : ขั้นกว่าของการจำแนกภาพ

Github : https://github.com/peeratpop/Deep_learning_easy_style

Medium : https://medium.com/@pingloaf

Linkedin : https://www.linkedin.com/in/peerat-limkonchotiwat/

--

--

Mr.P L
mmp-li
Editor for

Lifestyle of Programmer & IoT (Node-RED|Blynk) & Data Science (ML,DL,NLP) and Whatever I want to do | cat can coding too | Ph.D. -> VISTEC -> IST