φυβλαςのβλογ
phyblasのブログ



pytorch เบื้องต้น บทที่ ๑๑: การใช้ข้อมูลรูปภาพ
เขียนเมื่อ 2018/09/13 00:10
แก้ไขล่าสุด 2021/09/28 16:42
>> ต่อจาก บทที่ ๑๐



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

pytorch ได้เตรียมคำสั่งสำหรับอ่านและจัดการกับข้อมูลรูปภาพเอาไว้อย่างสะดวกดี อยู่ภายในมอดูล torchvision

นอกจากนี้ยังได้มีข้อมูลตัวอย่างที่นิยมใช้ เช่น MNIST หรือ CIFAR เตรียมไว้ด้วย



มอดูลต่างๆที่ต้องใช้

เพื่อที่จะใช้งานรูปภาพ นอกจากเรียกใช้ torch ตามปกติแล้วยังต้องใช้อีกหลายอย่าง ตามนี้
import torch
from torch.utils.data import DataLoader as Dalo
import torchvision.datasets as ds
import torchvision.transforms as tf

DataLoader นั้นเหมือนกับในบทที่แล้ว ต้องใช้เพื่อสร้างเจเนอเรเตอร์สำหรับมินิแบตช์ดึงข้อมูลออกมาทีละกลุ่ม

torchvision.datasets คือมอดูลที่มีคำสั่งสำหรับโหลดไฟล์รูปภาพที่เตรียมไว้ หรือโหลดข้อมูลตัวอย่างอย่าง MNIST, cifar, ฯลฯ

torchvision.transforms คือมอดูลที่มีคำสั่งต่างๆสำหรับแปลงไฟล์รูปภาพให้เป็นรูปแบบต่างๆที่เหมาะแก่การใช้

ต่อจากนี้จะย่อเหลือเป็น Dalo, ds, tf แบบนี้เพื่อให้เรียกได้สั้นๆ



การอ่านรูปภาพที่เตรียมไว้

ก่อนอื่นหากต้องการใช้ไฟล์รูปภาพที่ตัวเองเตรียมไว้เอง ให้เตรียมไฟล์ภาพโดยแยกข้อมูลแต่ละประเภทไว้คนละโฟลเดอร์

เพื่อเป็นตัวอย่าง ลองโหลดข้อมูลชุดนี้ไปใช้ได้ https://phyblas.hinaboshi.com/triamhai/ruprang-raisi-25x25x1000x5.rar

ข้อมูลนี้สร้างขึ้นด้วยฟังก์ชันที่เขียนถึงไปในนี้ https://phyblas.hinaboshi.com/20180811

หากสนใจสามารถลองสร้างขึ้นมาใช้เองได้

หากโหลดไปแล้วแตกไฟล์จะเห็นว่าภายในโฟลเดอร์ประกอบไปด้วย ๕ โฟลเดอร์แยกกัน

ให้ใช้ ds.ImageFolder(root=ที่อยู่ไฟล์,transform=ตัวเลือกแปลงไฟล์)

ที่อยู่ไฟล์ในที่นี้หมายถึงโฟลเดอร์ใหญ่ที่เก็บโฟลเดอร์ย่อยที่บรรจุไฟล์รูปแต่ละกลุ่มไว้

ตัวเลือกแปลงไฟล์คือออบเจ็กต์ในมอดูล torchvision.transforms (tf) ซึ่งกำหนดตัวเลือกว่าจะต้องการอ่านไฟล์ในลักษณะไหน

โดยทั่วไปเพื่อจะใช้งานในโครงข่ายประสาทจะต้องการข้อมูลในรูปเทนเซอร์ ดังนั้นจึงต้องใช้ตัวเลือก tf.ToTensor() เสมอ

การโหลดข้อมูลรูปภาพอาจเขียนได้ดังนี้
folder = 'ruprang-raisi-25x25x1000x5'
data = ds.ImageFolder(root=folder,transform=tf.ToTensor())
minibatch = Dalo(data,batch_size=16,shuffle=1) # สุ่มข้อมูลออกมาทีละ 16 ตัว
for Xb,zb in minibatch: break
print(Xb.shape,zb) # ได้ torch.Size([16, 3, 25, 25]) tensor([2, 3, 0, 3, 4, 1, 4, 4, 1, 4, 4, 1, 2, 2, 0, 1])

ตรง for Xb,zb in minibatch: break นี้มีไว้เพื่อให้ทำการอ่านข้อมูลออกมาเอาชุดแรกมาดูเพื่อเป็นตัวอย่างให้ดูเฉยๆ เพราะปกติ DataLoader จะทำงานเมื่อเจอ for เท่านั้น

ในที่นี้ข้อมูลตัวแรกที่ได้คือ Xb ก็คือข้อมูลรูปภาพเป็นเทนเซอร์ขนาด 16,3,25,25 มา คือ (จำนวนข้อมูล,จำนวนสี,ความสูง,ความกว้าง)

ส่วนตัวหลังคือ zb คือคำตอบ จะเป็นเลข 0,1,2,... ต่างกันตามโฟลเดอร์ของรูป

จำนวนข้อมูลแต่ชะชุดจะเท่ากับจำนวน batch_size ที่ตั้งไว้

สี ในที่นี้ข้อมูลถูกอ่านออกมาเป็น ๓ สี แม้ว่าจะเป็นภาพขาวดำก็ตาม ดังนั้นต้องทำการยุบให้เหลือสีเดียวโดยใช้เมธอด .mean() เพียงแต่ว่าเวลาที่จะป้อนเข้าโครงข่ายประสาทคอนโวลูชันยังจะต้องเหลือมิติไว้ ดังนั้นให้ใส่คีย์เวิร์ด keepdim=True เพื่อรักษามิตินั้นไว้ด้วย
print(Xb.mean(1)) # ได้ torch.Size([16, 25, 25])
print(Xb.mean(1,keepdim=1)) # ได้ torch.Size([16, 1, 25, 25])

นอกจากนี้ยังมีอีกวิธีหนึ่งคือใช้ตัวเลือกปรับแต่งรูป tf.Grayscale() จะกล่าวถึงตอนหลัง

ลองสั่งวาดภาพตัวอย่างดูได้
import numpy as np
import matplotlib.pyplot as plt
plt.axes([0,0,1,1]).imshow(np.hstack(Xb.mean(1)),cmap='gray')
plt.axis('off')
plt.show()



ส่วน zb ก็คือเลขแสดงประเภทของข้อมูล ในที่นี้มี ๕ โฟลเดอร์ เลขกลุ่มจึงอยู่ในช่วง 0 ถึง 4



การใช้ข้อมูล MNIST

MNIST คือชุดข้อมูลตัวเลขที่เขียนด้วยลายมือ

ใน sklearn ก็มีคำสั่งโหลดข้อมูลนี้เช่นกัน รายละเอียดอ่านได้ใน https://phyblas.hinaboshi.com/20170920

สำหรับใน pytorch ข้อมูล MNIST สามารถดึงมาใช้ได้โดยใช้ df.MNIST()

ตัวเลือกมีดังนี้

root = ที่อยู่ที่จะเก็บไฟล์
train = จะเอาชุดข้อมูลฝึกหรือข้อมูลทดสอบ
transform = ตัวเลือกแปลงไฟล์
download = ถ้ายังไม่มีข้อมูลอยู่จะโหลดใหม่หรือไม่

โดยค่าตั้งต้นแล้ว download อยู่ที่ False ดังนั้นถ้าเราจะใช้คำสั่งนี้ครั้งแรกยังไม่ได้โหลดข้อมูลเตรียมไว้ต้องตั้ง download=True ไว้ด้วย

ไฟล์จะถูกโหลดไปอยู่ที่โฟลเดอร์ที่เรากำหนดใน root

ข้อมูลมี ๒ ชุดคือชุดฝึก (train) กับชุดทดสอบ (test) ถ้าให้ train=1 จะได้ชุดข้อมูลฝึก ถ้าให้ train=0 จะได้ชุดข้อมูลทดสอบ

ลองดึงข้อมูลฝึกออกมาแสดง
folder_mnist = '~/pytorchdata/mnist'
data = ds.MNIST(folder_mnist,transform=tf.ToTensor(),download=1,train=1)
minibatch = Dalo(data,batch_size=16,shuffle=1)
for Xb,zb in minibatch: break
print(Xb.shape,zb) # ได้ torch.Size([16, 1, 28, 28]) tensor([2, 1, 4, 4, 1, 0, 3, 0, 4, 4, 2, 1, 8, 5, 5, 2])
plt.axes([0,0,1,1]).imshow(np.hstack(Xb[:,0]),cmap='gray')
plt.axis('off')
plt.show()



นอกจากนี้ยังมีชุดข้อมูล FashionMNIST ซึ่งเป็นข้อมูลภาพเสื้อผ่านรูปแบบต่างๆ ๑๐ แบบ เทียบกับข้อมูลตัวเลขแล้วยากขึ้นมาหน่อย

ลองโหลดมาใช้ดูได้เช่นกัน
folder_fashionmnist = '~/pytorchdata/fashionmnist'
data = ds.FashionMNIST(folder_fashionmnist,transform=tf.ToTensor(),download=1,train=1)





การใช้ข้อมูล CIFAR-10

CIFAR-10 คือข้อมูลรูปภาพ 10 ชนิด ประกอบไปด้วย เครื่องบิน, รถยนต์, นก, แมว, กวาง, หมา, กบ, ม้า, เรือ, รถบรรทุก

สำหรับใน pytorch ข้อมูล CIFAR-10 สามารถดึงมาใช้ได้โดยใช้ df.CIFAR10() ตัวเลือกต่างๆเหมือน MNIST

นอกจากนี้ยังมี CIFAR-100 ซึ่งคล้ายกันแต่มีข้อมูลถึง 100 กลุ่ม เรียกโดยใช้ df.CIFAR100()

ลองโหลดมาแสดงดู
folder_cifar = '~/pytorchdata/cifar'
data = ds.CIFAR10(folder_cifar,transform=tf.ToTensor(),download=1,train=1)
minibatch = Dalo(data,batch_size=16,shuffle=1)
for Xb,zb in minibatch: break
plt.axes([0,0,1,1]).imshow(np.hstack(Xb.numpy().transpose(0,2,3,1)))
plt.axis('off')
plt.show()



เนื่องจากไฟล์ที่โหลดมาปกติจะเอามิติของสีขึ้นก่อน แต่สำหรับ matplotlib เวลาจะแสดงรูปต้องการอาเรย์ที่มีมิติของสีอยู่ท้ายสุด ดังนั้นจึงต้องสลับแกนด้วย .transpose() ก่อนจึงจะแสดงผลได้



ตัวเลือกปรับแต่งภาพ

บางทีภาพที่เตรียมไว้อาจไม่ได้อยู่ในรูปแบบที่ต้องการนำมาใช้ทันที ใน torchvision.transform ได้เตรียมตัวแปลงต่างๆไว้มากมายใช้ได้สะดวก

เวลาที่ต้องการจะใส่ตัวเลือกปรับแต่งมากกว่าหนึ่งตัวให้สร้างออบเจ็กต์ tf.Compose() เก็บตัวเลือกปรับแต่งทั้งหมดไว้แล้วนำตัวนี้มาใช้

ตัวอย่างเช่น หากต้องการแปลงภาพให้เป็นขาวดำ ใช้ tf.Grayscale()
folder_cifar = '~/pytorchdata/cifar'
tran = tf.Compose([
    tf.Grayscale(),
    tf.ToTensor()])
data = ds.CIFAR10(folder_cifar,transform=tran)
minibatch = Dalo(data,batch_size=16)
for Xb,zb in minibatch: break
plt.axes([0,0,1,1]).imshow(np.hstack(Xb[:,0]),cmap='gray')
plt.axis('off')
plt.show()




tf.Resize ใช้เปลี่ยนขนาดภาพให้เป็นขนาดที่ต้องการ ขนาดรูปกำหนดเป็น (สูง,กว้าง) หรือใช้เลขตัวเดียวจะเป็นจตุรัส
tran = tf.Compose([
    tf.Resize((40,30)),
    tf.ToTensor()])

data = ds.CIFAR10(folder_cifar,transform=tran,download=1,train=1)
minibatch = Dalo(data,batch_size=16,shuffle=1)
for Xb,zb in minibatch: break
plt.axes([0,0,1,1]).imshow(np.hstack(Xb.numpy().transpose(0,2,3,1)))
plt.axis('off')
plt.show()



tf.Pad ใช้เติมขอบ
tran = tf.Compose([
    tf.Pad(10,fill=(100,150,20),padding_mode='constant'),
    tf.ToTensor()])



tf.CenterCrop จะตัดรูปเอาส่วนตรงกลางตามขนาดที่กำหนด
tran = tf.Compose([
    tf.CenterCrop((20,30)),
    tf.ToTensor()])



tf.Normalize ปรับค่าของเทนเซอร์โดยลบค่าเฉลี่ยและหารค่าส่วนเบี่ยงเบนมาตรฐานตามที่กำหนด
input[channel] = (input[channel] - mean[channel]) / std[channel]

เช่น รูปภาพโดยทั่วไปจะมีค่าความเข้มของแต่ละสีอยู่ระหว่าง 0 ถึง 1 แต่ถ้าหากต้องการแปลงให้อยู่ระหว่าง -1 ถึง 1 ก็อาจใส่แบบนี้
tran = tf.Compose([
    tf.ToTensor(),
    tf.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])

tf.Lambda แปลงโดยใช้ฟังก์ชันอะไรบางอย่างที่กำหนดเอง
เช่น ลองสุ่มสร้างคลื่นรบกวนให้ภาพ
tran = tf.Compose([
    tf.ToTensor(),
    tf.Lambda(lambda x: x*(0.4+0.6*torch.rand(x.shape)))])



ตัวแปลงที่ใช้จะทำตามลำดับที่ใส่ไป ดังนั้นหากตัวแปลงไหนที่ใช้จัดการกับเทนเซอร์ เช่น tf.Normalize ต้องไว้หลัง tf.ToTensor() ถ้าเป็นตัวแปลงสำหรับจัดการรูปภาพก่อนแปลงให้ไว้ก่อน



ตัวเลือกแปลงภาพแบบสุ่ม

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

tf.RandomRotation หมุนภาพแบบสุ่ม
tran = tf.Compose([
    tf.RandomRotation((-45,45)),
    tf.ToTensor(),
])

data = ds.CIFAR10(folder_cifar,transform=tran)
minibatch = Dalo(data,batch_size=16)
rup = []
for i in range(4):
    for Xb,zb in minibatch: break
    rup.append(np.hstack(Xb.numpy().transpose(0,2,3,1)))
rup = np.vstack(rup)
plt.axes([0,0,1,1]).imshow(rup)
plt.axis('off')
plt.show()



tf.RandomResizedCrop ตัดเอาส่วนของภาพแบบสุ่มมาขยายเป็นขนาดที่กำหนด
tran = tf.Compose([
    tf.RandomResizedCrop(size=48,scale=(0.25,1),ratio=(0.5,2)),
    tf.ToTensor(),
])



tf.RandomVerticalFlip และ tf.RandomHorizontalFlip พลิกบนล่าง และซ้ายขวา ด้วยความน่าจะเป็นตามที่กำหนด
tran = tf.Compose([
    tf.RandomVerticalFlip(0.25),
    tf.RandomHorizontalFlip(0.5),
    tf.ToTensor(),
])



tf.RandomGrayscale สุ่มเปลี่ยนเป็นขาวดำ
tran = tf.Compose([
    tf.RandomGrayscale(0.25),
    tf.ToTensor(),
])



tf.ColorJitter ปรับ brightness, contrast, saturation, hue แบบสุ่ม
tran = tf.Compose([
    tf.ColorJitter(brightness=1,contrast=1,saturation=1,hue=0.5),
    tf.ToTensor(),
])



tf.RandomApply สุ่มทำคำสั่งแปลงบางอย่างด้วยความน่าจะเป็นตามที่กำหนด
tran = tf.Compose([
    tf.RandomApply([tf.Resize((24,24)),tf.Pad(4)],p=0.5),
    tf.RandomApply([tf.Resize((48,48)),tf.CenterCrop((32,32))],p=0.5),
    tf.ToTensor(),
])





ใช้วิธีต่างๆนี้ทำให้สามารถดึงภาพมาใช้และปรับแต่งสร้างความหลากหลายได้สะดวก



ในบทต่อไปจะนำรูปมาใช้ในการเรียนรู้ของโครงข่ายประสาทแบบคอนโวลูชัน



>> อ่านต่อ บทที่ ๑๒


-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> ปัญญาประดิษฐ์ >> โครงข่ายประสาทเทียม
-- คอมพิวเตอร์ >> เขียนโปรแกรม >> python >> pytorch

ไม่อนุญาตให้นำเนื้อหาของบทความไปลงที่อื่นโดยไม่ได้ขออนุญาตโดยเด็ดขาด หากต้องการนำบางส่วนไปลงสามารถทำได้โดยต้องไม่ใช่การก๊อปแปะแต่ให้เปลี่ยนคำพูดเป็นของตัวเอง หรือไม่ก็เขียนในลักษณะการยกข้อความอ้างอิง และไม่ว่ากรณีไหนก็ตาม ต้องให้เครดิตพร้อมใส่ลิงก์ของทุกบทความที่มีการใช้เนื้อหาเสมอ

目次

日本による名言集
モジュール
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
機械学習
-- ニューラル
     ネットワーク
maya
javascript
確率論
日本での日記
中国での日記
-- 北京での日記
-- 香港での日記
-- 澳門での日記
台灣での日記
北欧での日記
他の国での日記
qiita
その他の記事

記事の類別



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  記事を検索

  おすすめの記事

การสร้างแบบจำลองสามมิติเป็นไฟล์ .obj วิธีการอย่างง่ายที่ไม่ว่าใครก็ลองทำได้ทันที
รวมรายชื่อนักร้องเพลงกวางตุ้ง
ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
หลักการเขียนทับศัพท์ภาษาจีนกวางตุ้ง
การใช้ unix shell เบื้องต้น ใน linux และ mac
หลักการเขียนทับศัพท์ภาษาจีนกลาง
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
บันทึกการเที่ยวสวีเดน 1-12 พ.ค. 2014
แนะนำองค์การวิจัยและพัฒนาการสำรวจอวกาศญี่ปุ่น (JAXA)
เล่าประสบการณ์ค่ายอบรมวิชาการทางดาราศาสตร์โดยโซวเคนได 10 - 16 พ.ย. 2013
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
บันทึกการเที่ยวญี่ปุ่นครั้งแรกในชีวิต - ทุกอย่างเริ่มต้นที่สนามบินนานาชาติคันไซ
หลักการเขียนทับศัพท์ภาษาญี่ปุ่น
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ
ทำไมถึงอยากมาเรียนต่อนอก
เหตุผลอะไรที่ต้องใช้ภาษาวิบัติ?

ไทย

日本語

中文