เทคนิค Data Augmentation,Batch Normalization และ Dropout ด้วยการทำ Regularization แบบสมัยใหม่

ในการเพิ่มประสิทธิภาพ Machine Learning Model มีวิธีการหลัก 2 อย่าง ที่ต้องให้ความสำคัญ คือ
- 1). การลด Generalization Error ด้วยการทำ Regularization
- 2). การลด Cost Value ด้วยการทำ Optimization หรือการหาค่าที่เหมาะสมที่สุด
ดังนั้นจึงกล่าวอีกอย่างหนึ่งว่า Regularization คือ วิธีการใดก็ตามที่ใช้เพื่อแก้ปัญหา Under Fitting หรือ Over Fitting ของ Machine Learning Model ก็ได้
เราจะทดลองแก้ปัญหา Over Fitting ของ Classification Model ที่มีการ Train ด้วย Fashion-MNIST Dataset โดยยกตัวอย่างเทคนิคที่เป็นที่นิยมในปัจจุบัน 2 เทคนิค ได้แก่ การทำ Data Augmentation และ Batch Normalization ซึ่งในที่สุดแล้วจะทำให้สามารถเพิ่มประสิทธิภาพของ Model ได้มากน้อยเท่าไหร่
Fashion-MNIST Dataset

Fashion-MNIST
- เป็น Dataset ที่เป็นภาพเสื้อผ้า กระเป๋า และรองเท้า ขนาด 28x28 Pixel แบบ Grayscale แบ่งเป็นข้อมูล Train 60,000 ภาพ และข้อมูล Test อีก 10,000 ภาพ รวมทั้งหมด 10 ประเภท โดยมีการกำหนด Label ตั้งแต่ 0–9 ดังนี้
0: T-shirt/top
1: Trouser
2: Pullover
3: Dress
4: Coat
5: Sandal
6: Shirt
7: Sneaker
8: Bag
9: Ankle boot
เราจะต้องมีการ Load Dataset แล้วขยายมิติของ Dataset ทำ Scaling ข้อมูล ระหว่าง 0–1 เข้ารหัสผลเฉลยแบบ One-hot Encoding และ Split Dataset สำหรับการ Train และ Validation ดังต่อไปนี้
config = tf.compat.v1.ConfigProto()
config.gpu_options.allow_growth = True
sess = tf.compat.v1.Session(config=config)
print( 'Tensorflow Version:', tf.__version__)
print("GPU Available::", tf.config.list_physical_devices('GPU'))
Import Library และกำหนดค่า Parameter ที่จำเป็น
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Activation, Conv2D, MaxPool2D, Flatten, Dropout, BatchNormalization
from tensorflow.keras.optimizers import RMSprop,Adam
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from sklearn.model_selection import train_test_split
from matplotlib import pyplot
from tensorflow.keras.datasets import fashion_mnist
import plotly.graph_objs as go
import cv2
IMG_ROWS = 28
IMG_COLS = 28
NUM_CLASSES = 10
TEST_SIZE = 0.2
RANDOM_STATE = 99
NO_EPOCHS = 10
BATCH_SIZE = 128
Load Dataset
(train_data, y), (test_data, y_test) = fashion_mnist.load_data()
print("Fashion MNIST train - rows:",train_data.shape[0]," columns:", train_data.shape[1], " rows:", train_data.shape[2])
print("Fashion MNIST test - rows:",test_data.shape[0]," columns:", test_data.shape[1], " rows:", train_data.shape[2])

for i in range(9):
pyplot.subplot(330 + 1 + i)
pyplot.imshow(train_data[i], cmap=pyplot.get_cmap('spring'))
pyplot.show()

ขยายมิติของ Dataset
print(train_data.shape, test_data.shape)
train_data = train_data.reshape((train_data.shape[0], 28, 28, 1))
test_data = test_data.reshape((test_data.shape[0], 28, 28, 1))
print(train_data.shape, test_data.shape)

ทำ Scaling
train_data = train_data / 255.0
test_data = test_data / 255.0
เข้ารหัสผลเฉลยแบบ One-hot Encoding
print(y.shape, y_test.shape)
print(y[:10])

y = to_categorical(y)
y_test = to_categorical(y_test)
print(y.shape, y_test.shape)
y[:10]

แบ่งข้อมูลสำหรับ Train และ Validate โดยการสุ่มในสัดส่วน 80:20
X_train, X_val, y_train, y_val = train_test_split(train_data, y, test_size=TEST_SIZE, random_state=RANDOM_STATE)
X_train.shape, X_val.shape, y_train.shape, y_val.shape

Baseline Model
- นิยาม Model, Compile Model และ Train Model โดยยังไม่ใช้เทคนิค Regularization ดังต่อไปนี้
นิยาม Model
model = Sequential()
#1. CNN LAYER
model.add(Conv2D(filters = 32, kernel_size = (3,3), padding = 'Same', input_shape=(28, 28, 1)))
model.add(Activation("relu"))
#2. CNN LAYER
model.add(Conv2D(filters = 32, kernel_size = (3,3), padding = 'Same'))
model.add(Activation("relu"))
model.add(MaxPool2D(pool_size=(2, 2)))
#3. CNN LAYER
model.add(Conv2D(filters = 64, kernel_size = (3,3), padding = 'Same'))
model.add(Activation("relu"))
#4. CNN LAYER
model.add(Conv2D(filters = 64, kernel_size = (3,3), padding = 'Same'))
model.add(Activation("relu"))
model.add(MaxPool2D(pool_size=(2, 2)))
#FULLY CONNECTED LAYER
model.add(Flatten())
model.add(Dense(256))
model.add(Activation("relu"))
#OUTPUT LAYER
model.add(Dense(10, activation='softmax'))
Compile Model
optimizer = Adam()
model.compile(optimizer = optimizer, loss = "categorical_crossentropy", metrics=["accuracy"])
model.summary()

Train Model
train_model = model.fit(X_train, y_train,
batch_size=BATCH_SIZE,
epochs=NO_EPOCHS,
verbose=1,
validation_data=(X_val, y_val))

Evaluation
from plotlt import tools
import plotlydef create_trace(x,y,ylabel,color):
trace = go.Scatter(
x = x,y = y,
name=ylabel,
marker=dict(color=color),
mode = "markers+lines",
text=x
)
return trace
def plot_accuracy_and_loss(train_model):
hist = train_model.history
acc = hist['accuracy']
val_acc = hist['val_accuracy']
loss = hist['loss']
val_loss = hist['val_loss']
epochs = list(range(1,len(acc)+1))
trace_ta = create_trace(epochs,acc,"Training accuracy", "Green")
trace_va = create_trace(epochs,val_acc,"Validation accuracy", "Red")
trace_tl = create_trace(epochs,loss,"Training loss", "Blue")
trace_vl = create_trace(epochs,val_loss,"Validation loss", "Magenta")
fig = tools.make_subplots(rows=1,cols=2, subplot_titles=('Training and validation accuracy',
'Training and validation loss'))
fig.append_trace(trace_ta,1,1)
fig.append_trace(trace_va,1,1)
fig.append_trace(trace_tl,1,2)
fig.append_trace(trace_vl,1,2)
fig['layout']['xaxis'].update(title = 'Epoch')
fig['layout']['xaxis2'].update(title = 'Epoch')
fig['layout']['yaxis'].update(title = 'Accuracy', range=[0,1])
fig['layout']['yaxis2'].update(title = 'Loss', range=[0,1])
plotly.offline.iplot(fig, filename='accuracy-loss')
Plot Accuracy and Loss
plot_accuracy_and_loss(train_model)

score = model.evaluate(test_data, y_test,verbose=0)
print("Test Loss:",score[0])
print("Test Accuracy:",score[1])

จากกราฟ Loss ด้านบน พบว่า Model มีปัญหา Over Fitting ตั้งแต่รอบที่ 7 โดยเมื่อวัดประสิทธิภาพการ Predict ด้วย Test Dataset ได้ค่า “Accuracy 92.23%”
Image Augmentation
ปัญหา Over Fitting ของ Model สามารถแก้ได้ด้วยการเพิ่มจำนวน Data ในการ Train แต่ด้วย Dataset ของเรามีจำกัด ดังนั้นในบางกรณีเราสามารถสังเคราะห์ Data ขึ้นมาเองได้ โดยเฉพาะ Data แบบ Image ที่สามารถใช้เทคนิคอย่างเช่นการหมุนภาพ การเลื่อนภาพ การกลับภาพ เป็นต้น ซึ่งนอกจากเป็นการขยายจำนวน Data แล้ว Image Augmentation ยังช่วยเพิ่มความหลากของภาพใน Dataset
ยกตัวอย่างการทำ Image Augmentation ในแบบต่างๆ ได้แก่
- Vertical Shift
- Horizontal Shift
- Shear
- Zoom
- Vertical Flip
- Horizontal Flip
- Rotate
- Fill mode
8.1 Constant Values
8.2 Nearest Neighbor
8.3 Reflect Values
อ่านไฟล์ภาพ
- โดยไฟล์ที่ใช้จะเป็นภาพรถ
car = cv2.imread('car.jpg')
car.shape
แปลงระบบสีจาก BGR ซึ่งเป็นค่า Defult ของ OpenCV Library เป็น RGB
car = cv2.cvtColor(car, cv2.COLOR_RGB2BGR)
Plot ภาพ
plt.figure(dpi=100)
plt.imshow(car)

ขยายมิติของภาพจาก 3 มิติเป็น 4 มิติ เพื่อเตรียมนำเข้า Function ทำ Image Augmentation
print(car.shape)
car = car.reshape(1,car.shape[0],car.shape[1],car.shape[2])
print(car.shape)

ทดลองทำ Vertical Shift ด้วยการเลื่อนภาพขึ้นลงแบบสุ่มไม่เกิน 20%
datagen = ImageDataGenerator(height_shift_range=0.2)
aug_iter = datagen.flow(car, batch_size=1)
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(15,15))
for i in range(3):
image = next(aug_iter)[0].astype('uint8')
ax[i].imshow(image)
ax[i].axis('off')

ทดลองทำ Horizontal Shift ด้วยการเลื่อนภาพซ้ายขวาแบบสุ่มไม่เกิน 20%
datagen = ImageDataGenerator(width_shift_range=0.2)
aug_iter = datagen.flow(cat, batch_size=1)
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(15,15))
for i in range(3):
image = next(aug_iter)[0].astype('uint8')
ax[i].imshow(image)
ax[i].axis('off')

ทดลองบิดภาพ (Shear) แบบสุ่มไม่เกิน 20 องศา
datagen = ImageDataGenerator(shear_range=20)
aug_iter = datagen.flow(cat, batch_size=1)
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(15,15))
for i in range(3):
image = next(aug_iter)[0].astype('uint8')
ax[i].imshow(image)
ax[i].axis('off')

ทดลองขยายภาพ (Zoom) แบบสุ่มไม่เกิน 30 องศา
datagen = ImageDataGenerator(zoom_range=0.3)
aug_iter = datagen.flow(cat, batch_size=1)
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(15,15))
for i in range(3):
image = next(aug_iter)[0].astype('uint8')
ax[i].imshow(image)
ax[i].axis('off')

ทดลองกลับภาพแนวตั้ง (Vertical Flip) แบบสุ่ม
datagen = ImageDataGenerator(vertical_flip=True)
aug_iter = datagen.flow(cat, batch_size=1)
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(15,15))
for i in range(3):
image = next(aug_iter)[0].astype('uint8')
ax[i].imshow(image)
ax[i].axis('off')
fig.savefig('cat.jpeg', dpi=300)

ทดลองกลับภาพแนวนอน (Horizontal Flip) แบบสุ่ม
datagen = ImageDataGenerator(horizontal_flip=True)
aug_iter = datagen.flow(cat, batch_size=1)
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(15,15))
for i in range(3):
image = next(aug_iter)[0].astype('uint8')
ax[i].imshow(image)
ax[i].axis('off')

ทดลองหมุนภาพ (Rotate) ไม่เกิน 30 องศา แบบสุ่ม
datagen = ImageDataGenerator(rotation_range=30)
aug_iter = datagen.flow(cat, batch_size=1)
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(15,15))
for i in range(3):
image = next(aug_iter)[0].astype('uint8')
ax[i].imshow(image)
ax[i].axis('off')

Fill mode
โดย Default เมื่อมีการเลื่อนภาพ บิดภาพ หมุนภาพ จะเกิดพื้นที่ว่างที่มุม ซึ่งจะมีการเติมภาพให้เต็มโดยใช้เทคนิคแบบ Nearest neighbor ซึ่งเป็นการดึงสีบริเวณใหล้าเคียงมาระบายให้เต็ม แต่เราก็ยังสามารถกำหนดวิธีการ Fill ภาพด้วยเทคนิคอื่นได้จาก Parameter fill_mode
เติมสีดำ (Constant values)
datagen = ImageDataGenerator(rotation_range=30, fill_mode = 'constant')
aug_iter = datagen.flow(cat, batch_size=1)
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(15,15))
for i in range(3):
image = next(aug_iter)[0].astype('uint8')
ax[i].imshow(image)
ax[i].axis('off')

เติมสีข้างเคียง (Nearest neighbor)
datagen = ImageDataGenerator(rotation_range=30, fill_mode = 'nearest')
aug_iter = datagen.flow(cat, batch_size=1)
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(15,15))
for i in range(3):
image = next(aug_iter)[0].astype('uint8')
ax[i].imshow(image)
ax[i].axis('off')

เติมสีจากภาพที่มีการกลับด้าน (Reflect values)
datagen = ImageDataGenerator(rotation_range=50, fill_mode = 'reflect')
aug_iter = datagen.flow(cat, batch_size=1)
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(15,15))
for i in range(3):
image = next(aug_iter)[0].astype('uint8')
ax[i].imshow(image)
ax[i].axis('off')

เติมสีจากภาพแบบต่อกัน (Wrap values)
datagen = ImageDataGenerator(rotation_range=30, fill_mode = 'wrap')
aug_iter = datagen.flow(cat, batch_size=1)
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(15,15))
for i in range(3):
image = next(aug_iter)[0].astype('uint8')
ax[i].imshow(image)
ax[i].axis('off')

เราจะนำเทคนิคเหล่านี้บางเทคนิคมาแก้ปัญหา Over Fitting ตามขั้นตอนดังนี้
- นิยามวิธีการทำ Image Augmentation
datagen = ImageDataGenerator(
rotation_range=0.5, # Randomly rotate images in the range
zoom_range = 0.2, # Randomly zoom image
width_shift_range=0.1, # Randomly shift images horizontally
height_shift_range=0.1, # Randomly shift images vertically
)
datagen.fit(X_train)
- นิยาม Model
model = Sequential()
#1. CNN LAYER
model.add(Conv2D(filters = 32, kernel_size = (3,3), padding = 'Same', input_shape=(28, 28, 1)))
model.add(Activation("relu"))
#2. CNN LAYER
model.add(Conv2D(filters = 32, kernel_size = (3,3), padding = 'Same'))
model.add(Activation("relu"))
model.add(MaxPool2D(pool_size=(2, 2)))
#3. CNN LAYER
model.add(Conv2D(filters = 64, kernel_size = (3,3), padding = 'Same'))
model.add(Activation("relu"))
#4. CNN LAYER
model.add(Conv2D(filters = 64, kernel_size = (3,3), padding = 'Same'))
model.add(Activation("relu"))
model.add(MaxPool2D(pool_size=(2, 2)))
#FULLY CONNECTED LAYER
model.add(Flatten())
model.add(Dense(256))
model.add(Activation("relu"))
#OUTPUT LAYER
model.add(Dense(10, activation='softmax'))
- Compile Model
optimizer = Adam()
model.compile(optimizer = optimizer, loss = "categorical_crossentropy", metrics=["accuracy"])
model.summary()

- Train Model
NO_EPOCHS = 30history = model.fit_generator(datagen.flow(X_train, y_train, batch_size=BATCH_SIZE),
shuffle=True,
epochs=NO_EPOCHS, validation_data = (X_val, y_val),
verbose = 1, steps_per_epoch=X_train.shape[0] // BATCH_SIZE)
- Plot กราฟ
plot_accuracy_and_loss(history)

- วัดค่า Accuracy จาก Test Dataset
score = model.evaluate(test_data, y_test,verbose=0)
print("Test Loss:",score[0])
print("Test Accuracy:",score[1])

จากกราฟ Loss ด้านบน เมื่อมีการ Train ทั้งหมด 30 Epoch พบว่า Validation Loss ไม่พุ่งขึ้นเหมือนในการ Train ครั้งแรกแบบไม่ใช้เทคนิค Image Augmentation แต่อย่างไรก็ตามเมื่อวัดประสิทธิภาพการ Predict ด้วย Test Dataset ได้ค่า Accuracy 92.55% อย่างไรก็ตาม ใน Epoch ท้ายๆ Validation Loss ของเราก็ยังมีแนวโน้มที่จะยกขึ้นจนเกิดปัญหา Over Fitting
Batch Normalization
- เป็นเทคนิคในการทำ Scaling Data หรือเรียกอีกอย่างหนึ่งว่าการทำ Normalization เพื่อปรับค่าข้อมูลให้อยู่ในขอบเขตที่กำหนด ก่อนส่งออกจาก Node ใน Neural Network Layer เพื่อเป็น Input ใน Layer ถัดไป ซึ่งเดิมเราจะทำ Normalization ในขั้นตอน Feature Engineering เช่น ด้วยการเแปลงค่าสีของภาพแบบ Grayscale จาก 0–255 เป็น 0–1 โดยนำค่าสีเดิมหาร
ในการทำ Data Normalization เราสามารถเลือกวิธีการได้หลายวิธี เช่น การทำ Min-Max Normalization หรือ การทำ Standardization เป็นต้น
Min-Max Normalization = 𝑥′ = [𝑥–min(𝑥)]/[max(𝑥) — max(𝑥)]
Standardization = 𝑥′ = (𝑥–𝑥¯)/𝜎2
โดยที่ 𝑥¯ คือ Mean และ 𝜎2 คือ Varianceด้วย 255 เป็นต้น
- Batch Normalization ใช้วิธีการแบบ Standardization ซึ่งจะมีการกำหนดค่า Mean และ Variance โดยการเรียนรู้จาก Batch ขนาดเล็ก ที่หยิบมาสอน Model ด้วย Layer พิเศษใน Neural Network เอง
เราจะทดลองใช้ Batch Normalization ร่วมกับเทคนิค Image Augmentation เพื่อแก้ปัญหา Over Fitting ตามขั้นตอนดังต่อไปนี้
นิยามวิธีการทำ Image Augmentation
datagen = ImageDataGenerator(
rotation_range=0.5, # Randomly rotate images in the range
zoom_range = 0.2, # Randomly zoom image
width_shift_range=0.1, # Randomly shift images horizontally
height_shift_range=0.1, # Randomly shift images vertically
)datagen.fit(X_train)
นิยาม Model
model = Sequential()#1. LAYER
model.add(Conv2D(filters = 32, kernel_size = (3,3), padding = 'Same', input_shape=(28, 28, 1)))
model.add(BatchNormalization())
model.add(Activation("relu"))#2. LAYER
model.add(Conv2D(filters = 32, kernel_size = (3,3), padding = 'Same'))
model.add(BatchNormalization())
model.add(Activation("relu"))model.add(MaxPool2D(pool_size=(2, 2)))#3. LAYER
model.add(Conv2D(filters = 64, kernel_size = (3,3), padding = 'Same'))
model.add(BatchNormalization())
model.add(Activation("relu"))#4. LAYER
model.add(Conv2D(filters = 64, kernel_size = (3,3), padding = 'Same'))
model.add(BatchNormalization())
model.add(Activation("relu"))model.add(MaxPool2D(pool_size=(2, 2)))#FULLY CONNECTED LAYER
model.add(Flatten())
model.add(Dense(256))
model.add(BatchNormalization())
model.add(Activation("relu"))#OUTPUT LAYER
model.add(Dense(10, activation='softmax'))
- Batch Normalization เหมาะสำหรับการวางไว้หลัง Activate Function ที่มีรูปร่างแบบ s-shapes เช่น Hyperbolic Tangent และ Sigmoid Activation Function

- หรือวางไว้หน้า Activate Function ที่ให้ผลลัพธ์แบบ Non-Gaussian Distributions เช่น Rectified Linear (ReLU) Activation Function

ทดลองกำหนด Learning Rate เท่ากับ 0.01 (ค่า Default คือ 0.001) แล้ว Compile Model
optimizer = Adam()
model.compile(optimizer = optimizer, loss = "categorical_crossentropy", metrics=["accuracy"])model.summary()

Train Model
NO_EPOCHS = 50
history = model.fit_generator(datagen.flow(X_train, y_train, batch_size=BATCH_SIZE),
shuffle=True,
epochs=NO_EPOCHS, validation_data = (X_val, y_val),
verbose = 1, steps_per_epoch=X_train.shape[0] // BATCH_SIZE)
Plot กราฟ
plot_accuracy_and_loss(history)

วัดค่า Accuracy จาก Test Dataset
score = model.evaluate(test_data, y_test,verbose=0)
print("Test Loss:",score[0])
print("Test Accuracy:",score[1])

จากกราฟ Loss จะพบว่าทั้งค่า Training Loss และ Validation Loss มีแนวโน้มที่จะลดลงได้ Model มีประสิทธิภาพในการเรียนรู้ที่ดี
Dropout
- เป็นเทคนิคในการทำ Regularization ที่เรียบง่าย แต่มีประสิทธิภาพอย่างมาก โดยเมื่อมีการใช้งาน Dropout ภายใน Layer ที่กำหนดแล้ว Node ใน Layer นั้นจะถูกสุ่มเพื่อปิดการทำงานชั่วคราวในแต่ละรอบของการทำ Forward Propagation และ Back-propagation ตามอัตราที่กำหนด ในขณะที่มีการ Train Model ทำให้ไม่มีการ Update Weight ใดๆ ที่ถูกเชื่อมต่อกับ Neuron Node ที่กำลังถูกปิด
ในกรณีที่ไม่มีการใช้ Dropout
- เปรียบได้กับการที่บริษัทมีนโยบายไม่อนุญาตให้พนักงานคนใดเลยลาหยุดงาน ทำให้แต่ละคนต้องรับผิดชอบงานของตัวเองจนมีความเชี่ยวชาญเฉพาะด้าน สามารถแก้ปัญหาแบบเดิมๆ ที่เคยเรียนรู้มาแล้วเป็นอย่างดี แต่เมื่อเจอปัญหาใหม่ๆ พวกเขาจะไม่สามารถประยุกต์ใช้ความรู้และทักษะความชำนาญเดิมมาแก้ปัญหาได้อย่างมีประสิทธิภาพ
ในกรณีที่มีการใช้ Dropout
- เปรียบได้กับการที่บริษัทมีนโยบายอนุญาตให้พนักงานสามารถลาหยุดงาน ด้วยการสุ่มในอัตราที่กำหนด เช่น วันละ 1 คน โดยคนที่ยังคงปฏิบัติงานต้องสลับกันมารับผิดชอบในหน้าที่ของพนักงานที่ได้หยุดพัก จนทำให้ทุกคนสามารถทำงานต่างๆ ทดแทนกันได้อย่างดี โดยไม่มีพนักงานคนใดมีอิทธิพลเหนือคนอื่น เมื่อเจอปัญหาใหม่ๆ พวกเขาจะสามารถประยุกต์ใช้ความรู้และทักษะความชำนาญมาแก้ปัญหาได้อย่างมีประสิทธิภาพ
เราจะทดลองใช้เทคนิค Dropout ร่วมกับ Batch Normalization และ Image Augmentation เพื่อแก้ปัญหา Overfitting ตามขั้นตอนดังต่อไปนี้
นิยามวิธีการทำ Image Augmentation
datagen = ImageDataGenerator(
rotation_range=0.05, #Randomly rotate images in the range
zoom_range = 0.2, # Randomly zoom image
width_shift_range=0.1, #Randomly shift images horizontally
height_shift_range=0.1, #Randomly shift images vertically
shear_range=0.05 #Randomly shear images
)
datagen.fit(X_train)
นิยาม Model
model = Sequential()
#1. CNN LAYER
model.add(Conv2D(filters = 32, kernel_size = (3,3), padding = 'Same', input_shape=(28, 28, 1)))
model.add(BatchNormalization())
model.add(Activation("relu"))
model.add(Dropout(0.3))
#2. CNN LAYER
model.add(Conv2D(filters = 32, kernel_size = (3,3), padding = 'Same'))
model.add(BatchNormalization())
model.add(Activation("relu"))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Dropout(0.3))
#3. CNN LAYER
model.add(Conv2D(filters = 64, kernel_size = (3,3), padding = 'Same'))
model.add(BatchNormalization())
model.add(Activation("relu"))
model.add(Dropout(0.3))
#4. CNN LAYER
model.add(Conv2D(filters = 64, kernel_size = (3,3), padding = 'Same'))
model.add(BatchNormalization())
model.add(Activation("relu"))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Dropout(0.3))
#FULLY CONNECTED LAYER
model.add(Flatten())
model.add(Dense(256))
model.add(BatchNormalization())
model.add(Activation("relu"))
model.add(Dropout(0.30))
#OUTPUT LAYER
model.add(Dense(10, activation='softmax'))
Compile Model
optimizer = Adam()
model.compile(optimizer = optimizer, loss = "categorical_crossentropy", metrics=["accuracy"])model.summary()


Train Model
NO_EPOCHS = 500
history = model.fit_generator(datagen.flow(X_train, y_train, batch_size=BATCH_SIZE),
shuffle=True,
epochs=NO_EPOCHS, validation_data = (X_val, y_val),
verbose = 1, steps_per_epoch=X_train.shape[0] // BATCH_SIZE)


Plot กราฟ
plot_accuracy_and_loss(history)

วัดค่า Accuracy จาก Test Dataset
score = model.evaluate(test_data, y_test,verbose=0)
print("Test Loss:",score[0])
print("Test Accuracy:",score[1])

จากกราฟ Loss ด้านบน เมื่อมีการ Train ทั้งหมด 200 Epoch โดยใช้เทคนิค Augmentation, Batch Normalization และ Dropout พบว่า Validation Loss ไม่พุ่งขึ้นจนเกิดปัญหา Overfitting เหมือนกับในการ Train แบบไม่ใช้ Dropout เมื่อวัดประสิทธิภาพการ Predict ด้วย Test Dataset ได้ค่า Accuracy 93.65%
THANKYOU FOR WACTHING👍👍