Practice Image Classification by Pytorch (ภาษาไทย) แบบเข้าใจง่าย

PUSH TECH
4 min readDec 12, 2020

--

ถ้าพูดถึงการประมวลผลด้วยภาพด้วย AI สิ่งแรกๆ ที่ต้องนึกถึงเลยก็คือ Image classification

Image classification คือตัวที่ทำให้แมชชีนแมชชีนหนึ่ง มองเห็นภาพแล้วสามารถตอบได้ว่า ภาพนั้นเกี่ยวกับอะไร ภาพนั้นคืออะไร ซึ่งบทความนี้ก็จะมาพูดถึงการสอนแมชชีนให้เรียนรู้กันว่ามันทำอย่างไรด้วย Pytorch

OVERVIEW

ก่อนอื่นมาดูภาพรวมของการทำ Image Classifier ก่อนกันดีกว่า

0. Installation ในส่วนนี้จะพูดคร่าว ๆ ไม่ลงลึกส่วนใครลงpytorch ไว้เรียบร้อยแล้วก็ข้ามอันนี้ไปได้เลย
1. Data ซึ่งในขั้นตอนนี้จะรวมไปถึงการดาวโหลดและ Preprocessing data โดยในขั้นตอนนี้เราจะใช้ตัวช่วยคือ torchvision ซึ่งทาง pytorch ได้ทำการจัดการทุกอย่างให้ใช้งานง่ายๆ หน้าที่ของเราก็แค่เรียกใช้ตามที่ต้องการเท่านั้น
2. Define Convolutional and Fully connected Neural Network ในส่วนของการกำหนดนั้น จริงๆจะมีหลายตัวแปรให้เราได้เล่นกันมาก แต่หลักๆ ที่เราจะต้องสนใจนั่นก็คือ ขนาดรูปภาพตั้งต้นและจำนวน class ที่ออกมานั่นเอง
3. Define loss function ในส่วนนี้ถ้าพูดกันแบบบ้านๆคือ การหาสมการหาค่าError ของ NN ของเรานั่นเอง
4. Train มาสักทีขั้นตอนที่ทุกคนอยากรู้ว่ามันทำอย่างไร
5. Test พอเรียนรู้ ก็ต้องมีบททดสอบเป็นเรื่องธรรมดา

Installation
ก่อนอื่นก่อนที่ทุกคนจะทำเรียนต่อในบทนี้คือ ทุกคนต้องลง Python Pytorch
ซึ่งส่วนตัวผมแนะนำให้ลง Anaconda Navigator ซึ่งจะทำให้ทุกอย่างง่ายขึ้น
เมื่อมี conda แล้วการลง pytorch ก็ง่ายขึ้น สามารถเช็คได้ที่นี่

จากนั้นก็ก๊อปทุกอย่างลงไปวางใน Anaconda prompt แค่นี้ก็เสร็จเรียบร้อย

DATA

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

CIFAR10

ในส่วนของขั้นตอนของการจัดการData จะมี 3 ส่วนหลักๆ
1. transform เป็นส่วนที่เราจะกำหนดการะปรับเปลี่ยน เปลี่ยนแปลงรูปภาพให้เป็นไปตามที่กำหนด เช่น resize, rotate แต่ส่วนที่สำคัญคือ ToTensor และ Normalize
2. dataset หรือ trainset, testset ในตัวอย่างถ้าอธิบายง่ายๆ ในส่วนนี้คือการ เอาdata มาจัด transform ตามขั้นตอนที่ 1
3. dataloader คือการเปลี่ยน dataset ที่เรากำหนดในขั้นตอนที่ 2 มาเปลี่ยนรูปแบบการจัดเรียงให้พร้อมต่อการนำไปใช้ใน Neural Network ซึ่งจะมีตัวแปรเข้ามาเกี่ยวข้องคือ batch size
Batch size คือจำนวนรูปภาพที่เราจะใส่เข้าไปให้แมชชีนเรียนรู้ใน 1 รอบการเรียนรู้

import torch
import torchvision
import torchvision.transforms as transforms
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
shuffle=True, num_workers=2)
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
shuffle=False, num_workers=2)
classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

Define Neural Network

ขั้นตอนนี้เราสามารถออกแบบ NNได้เองเลย โดย format ของ Conv คือ nn.Conv2d(Input, Output, Kernel size, stride) เพิ่มเติม สามารถออกแบบได้อย่างตามใจฉันได้เลย แต่ต้องคงคอนเซปไว้อย่างนึงคือ output ของlayer ก่อนหน้าจะต้องเท่ากับ Input ของ layer ถัดไปสังเกตุจากตัวอย่างคือ conv1(3,6,5) conv2(6,16,5) สังเกตว่า 6 จะต้องเท่ากับ 6

ก่อนที่จะไปเข้า Fully connected layer เราต้องทำการดึงรูปภาพที่เป็นสี่เหลี่ยมให้กลายเป็นเส้นยาวๆ เส้นเดียว โดยการใช้ .view(-1, multiple all size exclude batch)

ส่วนถัดมาคือ Fully connected layer คือ ก็ยังคงคอนเซปเดิมคือ Output ของlayer ก่อนหน้า เท่ากับ Input ถัด

import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16 * 5 * 5, 150)
self.fc2 = nn.Linear(150, 50)
self.fc3 = nn.Linear(50, 10)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = x.view(-1, 16 * 5 * 5)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
net = Net()

Define a Loss function and optimizer

ในส่วนนี้ผมจะใช้ Cross-Entropy Loss และ Stochastic gradient descent
โดย lr ในตัวอย่างคือ learning rate ถ้ามีค่ามากจะทำให้ค่า loss ลดลงอย่างรวดเร็วแต่ผลสุดท้ายอาจจะไม่สามารถหาค่า loss ที่ต่ำเท่าที่ควรจะเป็นได้

import torch.optim as optimcriterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

Train

ในการ trainning จุดประสงค์หลักคือเพื่อหา loss ที่ต่ำที่สุดแล้วหยัดซึ้งหลักการคือมันต้องทำงานวนไปเรื่อยๆ อัพเดด weights ไปเรื่อยๆจนถึงจุดที่ loss แทบจะไม่เปลี่ยนแปลงแล้วหยุดหรือเรียนว่า Converged ซึ่งเราจะใช้ for loop ที่จะคำณวนวนไปเรื่อย ๆ ตามตัวอย่างด้านล่าง ได้ใช้จำนวน loop เท่ากับ 10 กล่างคือเราจะทำการวนซ้ำๆ ทั้ง dataset นี้ทั้งหมด 10 รอบด้วยกัน ส่วน loop ข้างในจะเป็นการวน data ไปจนครบทั้ง dataset

for epoch in range(10):  # loop over the dataset multiple times    running_loss = 0.0 
for i, data in enumerate(trainloader, 0):
# data is a list of [inputs, labels] from torchvision
inputs, labels = data
# zero the parameter gradients
optimizer.zero_grad()
# forward + backward + optimize
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# print result each step
running_loss += loss.item()
if i % 2000 == 1999: # print every 2000 mini-batches
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0

out

[1,  2000] loss: 2.218
[1, 4000] loss: 1.860
[1, 6000] loss: 1.661
[1, 8000] loss: 1.575
[1, 10000] loss: 1.494
[1, 12000] loss: 1.470
[2, 2000] loss: 1.394
. . .

เมื่อ train เสร็จแล้วเราก็ต้อง save model เพื่อที่จะเอาไปเรียกใช้ในการ test หรือการใช้จริงก็ว่ากันไป

PATH = './cifar.pth'
torch.save(net.state_dict(), PATH)

Test

ในที่สุดเราก็เดินทางมาถึง ขั้นตอนสุดท้ายกันแล้ว . . .

ใน่ส่วนแรกขอเขียน function show image ก่อนนะ

จากนั้นทำการโหลด โมเดลที่เราได้เซฟกันในขั้นตอนการเทรนนิ่ง

net = Net()
net.load_state_dict(torch.load(PATH))

จากนั้นทำการตรวจสอบความแม่นยำของโมเดล

dataiter = iter(testloader)
images, labels = dataiter.next()
correct = 0
total = 0
with torch.no_grad():
for data in testloader:
images, labels = data
outputs = net(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('Accuracy of the network on the 10000 test images: %d %%' % (
100 * correct / total))

หากอยากจะหาความแม่นยำของแต่ละคราสของโมเดล ให้ใช้โค้ดด้านล่าง

num_class =class_correct = list(0. for i in range(num_class))
class_total = list(0. for i in range(num_class))
with torch.no_grad():
for data in testloader:
images, labels = data
outputs = net(images)
_, predicted = torch.max(outputs, 1)
c = (predicted == labels).squeeze()
for i in range(4):
label = labels[i]
class_correct[label] += c[i].item()
class_total[label] += 1
for i in range(10):
print('Accuracy of %5s : %2d %%' % (
classes[i], 100 * class_correct[i] / class_total[i]))

อันนี้เพิ่มเติมเผื่อใครอยากจะดูภาพจากการ training และ testing

import matplotlib.pyplot as plt
import numpy as np
def imshow(img):
img = img / 2 + 0.5 # unnormalize
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
dataiter = iter(testloader)
images, labels = dataiter.next()
# print images
imshow(torchvision.utils.make_grid(images))

การปริ้น labels และ prediction results(ต่อจากโคดด้านบน)

# print GROUND TRUTH
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))
# print prediction results
outputs = net(images)
_, predicted = torch.max(outputs, 1)
print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
for j in range(4)))

เป็นยังไงกันบ้าง กับการทำ Image classifier ในบทความนี้อาจจะไม่ได้อธิบายยถึงตัวทฤษฏีกันลึกเท่าไรสำหรับใครที่เข้ามาแล้วยังไม่มีตัวพื้นฐานทางทฤษฏีสามารถ ฝึกตามโค้ดกันไปก่อนนะ ลองเปลี่ยนตัวแปร แล้วดูผลว่าดีขึ้นหรือแย่ลงอย่างไรบ้าง

ก่อนไปขอฝากเพจไว้นิดนึง https://www.facebook.com/pushtechh
https://www.youtube.com/channel/UCLrjRLorMApEfgknAq4IkgA/

อ้างอิง

https://pytorch.org/
https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html?fbclid=IwAR19NA14U5qbPQ0NPqBWR_eAXcjfm5An9jmpib3FwX7nK0X5nQSNSle-c9w

--

--

No responses yet