จาก ep ที่แล้ว AI จำแนกรูปภาพ หมา แมว 37 สายพันธุ์ เราได้ใช้ fastai version 1 ในการทำ Image Classification ได้ผลลัพธ์แม่นยำ 94% โดยใช้เวลาเทรนเพียงแค่ไม่เกิน 5 นาที กับ Code หลัก ๆ เพียงแค่ 3 บรรทัด เวลาผ่านไปหลายเดือน ขณะนี้ fastai ออกเวอร์ชันใหม่ เป็น fastai2 มี API ที่เปลี่ยนไปเล็กน้อย เน้นความยืดหยุ่นมากขึ้น ช่วยให้เราเทรนโมเดล และข้อมูลที่มีความซับซ้อนได้อย่างสะดวกยิ่งขึ้น
Dataset

ในเคสนี้ เราจะใช้ข้อมูลจาก Oxford-IIIT Pet Dataset by O. M. Parkhi et al., 2012 เหมือนเดิม ซึ่งเป็นชุดข้อมูลรูปภาพหมา 25 พันธุ์ และรูปแมว 12 พันธุ์ รวมเป็น 37 หมวดหมู่
Model Architecture

เราจะใช้ resnet34 และ resnet50 ซึ่งเป็นโมเดลแบบ Convolutional Neural Network (CNN, ConvNet) ที่ถูกเทรนกับ ImageNet Dataset มาเรียบร้อยแล้ว โมเดลในตระกูล ResNet เป็นโมเดลที่ได้รับความนิยม และมีประสิทธิภาพสูง
เรามาเริ่มกันเลยดีกว่า
จาก ep ที่แล้ว AI จำแนกรูปภาพ หมา แมว 37 สายพันธุ์ เราได้ใช้ fastai version 1 ในการทำ Image Classification ได้ผลลัพธ์แม่นยำ 94% โดยใช้เวลาเทรนเพียงแค่ไม่เกิน 5 นาที กับ Code หลัก ๆ เพียงแค่ 3 บรรทัด เวลาผ่านไปหลายเดือน ขณะนี้ fastai ออกเวอร์ชันใหม่ เป็น fastai2 มี API ที่เปลี่ยนไปเล็กน้อย เน้นความยืดหยุ่นมากขึ้น ช่วยให้เราเทรนโมเดล และข้อมูลที่มีความซับซ้อนได้อย่างสะดวกยิ่งขึ้น
เช็ค GPU ด้วย คำสั่ง nvidia-smi
!nvidia-smi
0. Magic Commands¶
ให้ใส่ไว้บนสุดทุก Notebook เป็นการสั่งให้ Notebook ก่อนรัน ให้รีโหลด Library ภายนอกที่เรา import ไว้ใหม่โดยอัตโนมัติ
และให้พล็อตกราฟ matplotlib ใน Output ของ cell แบบ code ได้เลย
%reload_ext autoreload
%autoreload 2
%matplotlib inline
1. Install & Import Library¶
ติดตั้ง fastai2 หรือ fastai version 2 และ Import Library ที่เราจะใช้
การ import * หมายความว่า import ทุกอย่างที่อยู่ใน package ทำให้เราไม่ได้ต้องมา import ทีละ class การ import แบบนี้ เหมาะสำหรับการทดลองอะไรใหม่ ๆ เพราะเราไม่ต้องย้อนมาแก้ import ทุกครั้งเมื่อต้องการใช้ class ใหม่ ๆ แต่ไม่แนะนำสำหรับใช้งานจริงบน Production
# !pip install -Uqq fastbook
# import fastbook
# fastbook.setup_book()
Import Package ทั้งหมดที่เกี่ยวข้อง
from fastbook import *
เราจะกำหนด Random Seed จะได้ผลลัพธ์ที่เหมือนกันทุกครั้ง จะได้สะดวกในการเปรียบเทียบ
np.random.seed(5555)
2. ข้อมูล¶
ในเคสนี้ เราจะใช้ข้อมูลจาก Oxford-IIIT Pet Dataset by O. M. Parkhi et al., 2012 ซึ่งเป็นชุดข้อมูลรูปภาพหมา 25 พันธุ์ และรูปแมว 12 พันธุ์ รวมเป็น 37 หมวดหมู่
อ้างอิงจากใน paper ในปี 2012 โมเดลที่ดีที่สุด สามารถทำนายพันธุ์สัตว์ ได้ถูกต้อง 59.21% โดยโมเดลนั้น ออกแบบมาเฉพาะทาง สำหรับแยกรูปสัตว์ หัวสัตว์ ตัวสัตว์ โดยเฉพาะ (สมัยนันยังไม่ใช่ Deep Learning)
เรามาดูกันว่าเราจะใช้ Deep Learning ทำนายพันธุ์หมาแมวจากรูป ได้ถูกต้องแม่นยำกว่าหรือไม่
URL ของ Dataset ที่เราจะ Download มาใช้
URLs.PETS
untar_data จะดาวน์โหลดไฟล์มาเก็บไว้ แตกไฟล์ แล้ว return path ที่เก็บไฟล์เหล่านั้นไว้
path = untar_data(URLs.PETS)
path
List ดูว่ามีโฟลเดอร์อะไรบ้าง
path.ls()
เข้าไป List ดูไฟล์ในโฟลเดอร์ annotations
(path/"annotations").ls()
เข้าไป List ดูไฟล์ในโฟลเดอร์ images ชื่อพันธุ์จะอยู่ในชื่อไฟล์รูปภาพ
(path/"images").ls()
3. เตรียมข้อมูล¶
ก่อนนำข้อมูลใด ๆ มาเทรน Model เราต้องมีการเตรียมข้อมูล กำหนด Data Pipeline ให้อยู่ในรูปแบบที่เหมาะสมเสียก่อน ใน fastai2 นี้ มีการเปลี่ยนแปลง Data Pipeline จาก fastai version 1 หลายอย่าง จะอธิบายต่อไป
นำรายชื่อไฟล์รูปภาพทั้งหมด มาใส่ไว้ใน ตัวแปร fnames
fnames = get_image_files(path/"images")
fnames
กำหนด Pattern ของ Regex สำหรับสกัดข้อมูล ชื่อสายพันธุ์ จากชื่อไฟล์รูปภาพ
pat = r"([^/]+)_\d+.*$"
re.findall(pat, str(fnames[0]))
กำหนด Data Augmentation สำหรับ การ Transformation ระดับ Item เช่น การย่อรูปภาพ การสุ่ม Crop บางส่วนของรูป การปรับสัดส่วนกว้างยาวของรูป ให้เป็นรูปขนาดเท่า ๆ กัน เพื่อส่งไป batch_tfms โดย Transformation เหล่านี้จะถูกประมวลผลบน CPU ทีละรูป
item_tfms = RandomResizedCrop(460, min_scale=0.8, ratio=(1., 1.))
กำหนด Data Augmentation สำหรับ การ Transformation ระดับ Batch เช่น การ Normalize และ Data Augmentation ทั้ง Batch โดย Transformation เหล่านี้จะถูกประมวลผลบน GPU เป็น Batch
batch_tfms = [*aug_transforms(size=224, max_warp=0.15), Normalize.from_stats(*imagenet_stats)]
?? ด้านล่างคือ help ให้ Uncomment ทีละบรรทัด แล้ว Run Cell
# aug_transforms??
# RandomResizedCrop??
กำหนด ขนาด Batch Size ที่จะ Feed ให้โมเดล
bs = 64
3.1 ImageDataLoaders¶
สร้าง ImageDataLoaders จากชื่อไฟล์ และ RegEx
ใน fastai version 1 ที่เคยใช้ ImageDataBunch.from_name_re จะกลายเป็น ImageDataLoaders.from_name_re ใน fastai2
dls = ImageDataLoaders.from_name_re(path, fnames, pat, bs=bs,
batch_tfms=batch_tfms, item_tfms=item_tfms)
# ImageDataLoaders.from_name_re??
show_batch แสดงข้อมูลรูปภาพ พร้อม Label จำนวน 1 Batch
dls.show_batch(max_n=9, figsize=(8, 9))
3.2 DataBlock¶
อีกวิธีหนึ่ง คือ เราสามารถ สร้าง Data Pipeline ด้วย DataBlock API เวอร์ชันใหม่ ที่มีความยืดหยุ่นมากกว่า ImageDataLoaders
pets = DataBlock(blocks=(ImageBlock, CategoryBlock),
get_items=get_image_files,
splitter=RandomSplitter(),
get_y=RegexLabeller(pat=r"/([^/]+)_\d+.*"),
item_tfms=item_tfms,
batch_tfms=batch_tfms)
ข้างบนเราจะใช้ RandomSplitter ในการ Split ข้อมูลใน Dataset ออกเป็น Traning Set และ Validation Set แบบ Random โดยระบุให้ Split valid_pct 80/20
splitter = RandomSplitter(valid_pct=0.2, seed=5555)
splitter(fnames)
ได้ List แรก จำนวน 5912 Item ระบุ Index ของ fnames ที่จะอยู่ใน Training Set ส่วน List ที่สอง จำนวน 1478 Item ระบุ Index ของ fnames ที่จะอยู่ใน Validation Set
กำหนด path ของรูปภาพ
path_im = path/"images"
สร้าง DataLoaders จาก DataBlock ด้านบน ให้ไปอ่านไฟล์รูปภาพจาก path ที่กำหนด ด้วย Batch Size = bs
dls = pets.dataloaders(path_im, bs=bs)
แสดงข้อมูล Batch ต่อไป แต่จำกัดไว้ที่ 9 Item
เราสามารถรัน Cell นี้ได้หลายครั้ง
dls.show_batch(max_n=9, figsize=(8, 9))
Class ทั้งหมด จะถูกเก็บอยู่ใน List ชื่อ .vocab ให้เราสามารถเข้าถึงได้ตาม Index
dls.vocab[2]
dls.vocab
ในการแปลงชื่อ Class กลับเป็นเลข Index เราสามารถใช้ Dictionary ชื่อ .vocab.o2i
dls.vocab.o2i.get("Birman")
dls.vocab.o2i
4. สร้างโมเดล¶
สร้าง CNN Learner ด้วยฟังก์ชัน cnn_learner ระบุ DataLoaders ของข้อมูลที่จะใช้เทรน, Model Architecture, ต้องการใช้ Pretrained Model ในการทำ Transfer Learning หรือไม่, metrics การวัดผลในเคสนี้เราจะใช้ Error Rate, และ เทรนแบบ Mixed Precision ด้วย .to_fp16()
learn = cnn_learner(dls, resnet34, pretrained=True, metrics=error_rate).to_fp16()
โดย default cnn_learner จะลบ layer สุดท้ายของ model ที่ดาวน์โหลดมาทิ้งไป และแทนที่ด้วย Dense Layer ใหม่ เป็น Head ที่มี 37 Output ตรงกับ .vocab ของเรา model จะถูก Save ไว้ที่ $HOME/.cache/torch/checkpoints/ เมื่อรันอีกครั้งจะไม่ได้ต้องดาวน์โหลดใหม่
5. เริ่มต้นเทรนโมเดล¶
ลอง fit ด้วย ค่า default จำนวน 4 epoch
- 1 epoch คือ ใช้ป้อนข้อมูล หมด Dataset 1 รอบ
- fit_one_cycle คือ การเทรนแบบพิเศษ คือมีการ Schedule Learning Rate เพิ่มลด ตามแต่ละช่วงของการเทรน
learn.fit_one_cycle(4)
สำเร็จแล้ว¶
ด้วยพลังของ GPU เพียงแค่เวลาไม่ถึง 4 นาที เราเทรน Model ได้ Error Rate 0.07 หรือ Accuracy ความแม่นยำประมาณ 93%
เปรียบเทียบกับ paper เมื่อปี 2012 ที่ได้ 59.21% หรือเปรียบเทียบกับมนุษย์ คงเป็นเรื่องยาก ถ้าให้เวลาเรา 4 นาที ดูรูปหมาแมว แล้วให้ทายว่าเป็นพันธุ์อะไร 1 ใน 37 สายพันธุ์ แต่ Model Deep Learning ของเรา นี้สามารถทายได้ถูกต้องถึง 93% โดยใช้เวลาในการเรียนรู้เพียงแค่ 4 นาทีเท่านั้น
เรามา Save Model ที่เราเพิ่งเทรนไปเก็บไว้ก่อน
learn.save("01i-stage1")
6. ดูสถิติของโมเดล¶
เรามาดูกราฟ Training Loss และ Validation Loss ต่อจำนวน Batch ข้อมูลที่เราป้อนให้โมเดล
learn.recorder.plot_loss()
กราฟค่า lr Learning Rate และ mom Momentum ตามจำนวน Batch / Iteration ของการเทรน จะเห็นได้ว่า Learning Rate และ Momentum ไม่คงที มีขึ้นมีลง ตั้งแต่ประมาณ 0.0003-0.001, 0.95-0.85 เป็นความพิเศษของ fit_one_cycle
learn.recorder.plot_sched()
7. ดูผลลัพธ์¶
ถ้าเราดูแค่ Metrics Error Rate อย่างเดียว ว่ากี่เปอร์เซ็นต์ เราอาจจะไม่เห็นภาพว่า Model ทำงานได้ผลลัพธ์อย่างไร เราควรดูข้อมูลจริง รูปจริง Label จริง ด้วย ว่าโมเดล Predict อะไรออกมา
learn.show_results()