อย่างที่เราทราบกันดีว่า I/O หรือระบบ Input/Output เป็นอะไรที่ช้าที่สุด ของระบบคอมพิวเตอร์ การที่จะ Optimize ให้คอมพิวเตอร์ทำงานได้ประสิทธิภาพมากที่สุด ต้องใช้ความรู้ความเข้าใจ บริหารจัดการทรัพยากรส่วนต่าง ๆ เช่น CPU, GPU, Memory, Storage, Network ให้ทำงาน Utilize มากที่สุด ลด Bottleneck ที่ต้องรอข้อมูลระหว่างกัน

แต่ในการเทรน Machine Learning ที่เราวิธีที่เราทำกันอยู่ Training Loop จะเริ่มต้นจาก อ่านข้อมูล, สับไพ่ข้อมูล, Split, Data Augmentation, Feed Forward, Loss Function, Backpropagation, Optimizer Update Weight แล้วเริ่มต้น Loop ใหม่ เป็นอย่างนี้ซ้ำ ๆ ไปเรื่อย ๆ ตามลำดับ โดยไม่ได้คำนึงถึงประเด็นด้านบน แล้วเราจะแก้ไขอย่างไร

Data Echoing คืออะไร

มี Paper ฉบับหนึ่ง ชื่อ Faster Neural Network Training with Data Echoing โดย Dami Choi, Alexandre Passos, Christopher J. Shallue, George E. Dahl เสนอแนวคิดง่าย ๆ ที่จะมาช่วยแก้ไขปัญหานี้ คือ Data Echoing

แนวคิดของ Data Echoing คือเอา Data เดิมที่อยู่ในระบบอยู่แล้ว มาป้อนให้กับโมเดลใหม่ ระหว่างที่รอ Data ใหม่ ถึงแม้ว่าจะเป็น Data ที่โมเดลเพิ่งจะเห็นไป คุณภาพอาจจะไม่ดีเหมือน Data ใหม่ แต่ก็ดีกว่าปล่อยให้ โมเดล / GPU อยู่ว่าง ๆ และอย่างไรก็ตาม Epoch หน้าโมเดลก็ต้องเจอข้อมูลเหล่านี้ใหม่อยู่ดี

Data Augmentation Pets Dataset
Data Augmentation Pets Dataset

ส่วนปัญหาที่ว่า Data เก่า ก็ให้ใช้ Data Augmentation เข้ามาช่วย ให้เหมือนกับว่าเป็น Data ใหม่อีก 1 Batch ซึ่งแนวคิดใน Paper มีอีกหลายวิธี ทั้ง Echoing ก่อน/หลัง Data Augmentation, Echoing ทีละ Batch หรือทีละ Example, วิธีการ Shuffle สับไพ่ข้อมูล, จำนวนที่ Echoing โดยเราสามารถประยุกต์ใช้ Data Echoing ได้ในงานที่หลากหลาย ไม่ว่าจะเป็น Image Classification, Object Detection และ Language Modeling จะอธิบายต่อไป

ใน ep นี้ เราจะทดลองเปรียบเทียบกันแบบง่าย ๆ ระหว่าง Batch Size ขนาดต่าง ๆ เป็น Baseline และ ลดขนาด Batch Size ลงมา แต่เราจะเพิ่ม Data Echoing เข้าไปเสริมก่อน Data Augmentation แล้วดูว่าโมเดลของเราจะ Converge เร็วขึ้นจริงหรือไม่

เรามาเริ่มกันเลยดีกว่า

Open In Colab

อย่างที่เราทราบกันดีว่า I/O หรือระบบ Input/Output เป็นอะไรที่ช้าที่สุด ของระบบคอมพิวเตอร์ การที่จะ Optimize ให้คอมพิวเตอร์ทำงานได้ประสิทธิภาพมากที่สุด ต้องใช้ความรู้ความเข้าใจ บริหารจัดการทรัพยากรส่วนต่าง ๆ เช่น CPU, GPU, Memory, Storage ให้ทำงาน Utilize มากที่สุด ลด Bottleneck ที่ต้องรอข้อมูลระหว่างกัน

แต่ในการเทรน Machine Learning ที่เรากันอยู่ Training Loop จะเป็น อ่านข้อมูล, สับไพ่ข้อมูล, Split, Data Augmentation, Feed Forward, Loss Function, Backpropagation, Optimizer Update Weight แล้วเริ่มต้น Loop ใหม่ เป็นอย่างนี้ซ้ำ ๆ ไปเรื่อย ๆ ตามลำดับ โดยไม่ได้คำนึงประเด็นด้านบน

Data Echoing

มี Paper ฉบับหนึ่ง ชื่อ Faster Neural Network Training with Data Echoing โดย Dami Choi, Alexandre Passos, Christopher J. Shallue, George E. Dahl เสนอแนวคิดง่าย ๆ ที่จะมาช่วยแก้ไขปัญหานี้ คือ Data Echoing แนวคิดคือเอา Data เดิมที่อยู่ในระบบอยู่แล้ว มาป้อนให้กับโมเดลใหม่ ระหว่างที่รอ Data ใหม่ ถึงแม้ว่าจะเป็น Data ที่โมเดลเพิ่งจะเห็นไป คุณภาพอาจจะไม่ดีเหมือน Data ใหม่ แต่ก็ดีกว่าปล่อยให้ โมเดล / GPU อยู่ว่าง ๆ

ส่วนปัญหาที่ว่า Data เก่า ก็ให้ใช้ Data Augmentation เข้ามาช่วย ให้เหมือนกับว่าเป็น Data ใหม่อีก 1 Batch ซึ่งแนวคิดใน Paper มีอีกหลายวิธี จะอธิบายต่อไป

เราจะทดลองเปรียบเทียบกันแบบง่าย ๆ ระหว่าง Batch Size ขนาดต่าง ๆ เป็น Baseline และ ลดขนาด Batch Size ลงมา แต่เราจะเพิ่ม Data Echoing เข้าไปเสริม แล้วดูว่าโมเดลของเราจะ Converge เร็วขึ้นจริงหรือไม่

0. Magic Commands

In [0]:
%reload_ext autoreload
%autoreload 2
%matplotlib inline

เช็ค GPU ด้วย คำสั่ง nvidia-smi

In [2]:
!nvidia-smi
Fri May 15 08:04:09 2020       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.82       Driver Version: 418.67       CUDA Version: 10.1     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Tesla P100-PCIE...  Off  | 00000000:00:04.0 Off |                    0 |
| N/A   37C    P0    27W / 250W |      0MiB / 16280MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

1. Install & Import Library

In [3]:
!pip install fastai2 -q
     |████████████████████████████████| 194kB 5.0MB/s 
In [0]:
from fastai2.basics import *
from fastai2.vision.all import *
from fastai2.callback.all import *

2. Echoing Transform

ด้วยการออกแบบ DataBlock API ที่แสนยืดหยุ่นของ fastai2 ทำให้เราสามารถสร้าง Transform ไปแทรกใน Data Pipeline ก่อนที่จะ Feed ให้กับโมเดลได้อย่างเป็นระเบียบ และ Clean โดยเราจะสร้าง class EchoingTransform ที่ subclass มาจาก ItemTransform เพื่อเราจะไปใส่ไว้ใน batch_transform ก่อน Data Augmentation

In [0]:
class EchoingTransform(ItemTransform):
    order = 2
    split_idx = 0
    def __init__(self, e): self.e = e
    def encodes(self, x):
        img, lbl = x
        img = img.repeat(self.e, 1, 1, 1)
        lbl = lbl.repeat(self.e)
        # print(img.shape)
        # print(lbl.shape)
        return img, lbl

order = 2 เพราะต้องการให้ทำก่อน Data Augmentation และ split_idx = 0 คือ ให้ทำเฉพาะ Training Set

3. Dataset

In [6]:
path = untar_data(URLs.PETS)
fnames = get_image_files(path/"images")
pat = r"([^/]+)_\d+.*$"

4. DataBlock

กำหนดขนาดรูป

In [0]:
size = 224

สร้างฟังก์ชันสำหรับสร้าง DataLoaders

In [0]:
def get_dls(bs, e):
    item_tfms = RandomResizedCrop(300, min_scale=0.75, ratio=(1., 1.))
    batch_tfms = [EchoingTransform(e), *aug_transforms(size=size), Normalize.from_stats(*imagenet_stats)]
    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)
    # pets.summary(path/"images")
    dls = pets.dataloaders(path/"images", bs=bs)
    return dls
In [9]:
Resize.order, EchoingTransform.order, IntToFloatTensor.order, Normalize.order
Out[9]:
(1, 2, 10, 99)

ลองดูข้อมูลใน Batch ด้วย show_batch ด้วย Batch Size 4 และ Echoing 4 ครั้ง

In [10]:
bs = 4
e = 4

dls = get_dls(bs, e)
dls.show_batch(max_n=16, figsize=(11, 12))

5. เปรียบเทียบ Echoing กับ Batch Size ต่าง ๆ

5.1 No Echoing, bs=128

In [0]:
bs = 128
e = 1
In [12]:
learn = cnn_learner(get_dls(bs, e), resnet34, metrics=error_rate, cbs=[ShowGraphCallback]).to_fp16()
learn.fit_one_cycle(4)
Downloading: "https://download.pytorch.org/models/resnet34-333f7ec4.pth" to /root/.cache/torch/checkpoints/resnet34-333f7ec4.pth
epochtrain_lossvalid_losserror_ratetime
02.8302560.4364330.13599500:55
11.1915290.2685030.08863300:55
20.6517110.2440960.08186700:54
30.4438750.2418810.07983800:53
In [0]:
# learn.summary()

5.2 No Echoing, bs=64

In [0]:
bs = 64
e = 1
In [14]:
learn = cnn_learner(get_dls(bs, e), resnet34, metrics=error_rate, cbs=[ShowGraphCallback]).to_fp16()
learn.fit_one_cycle(4)
epochtrain_lossvalid_losserror_ratetime
01.9718810.3490610.11772700:55
10.7011640.2720490.08254400:54
20.4015220.2499070.06968900:54
30.2911640.2429510.06901200:54

5.3 No Echoing, bs=32

In [0]:
bs = 32
e = 1
In [16]:
learn = cnn_learner(get_dls(bs, e), resnet34, metrics=error_rate, cbs=[ShowGraphCallback]).to_fp16()
learn.fit_one_cycle(4)
epochtrain_lossvalid_losserror_ratetime
01.1796280.3575910.11231401:00
10.4641020.2625480.08998601:00
20.3424430.2456680.08119101:01
30.2986870.2315450.07036501:01

5.4 Echoing 2, bs=64

In [0]:
bs = 64
e = 2
In [18]:
learn = cnn_learner(get_dls(bs, e), resnet34, metrics=error_rate, cbs=[ShowGraphCallback]).to_fp16()
learn.fit_one_cycle(4)
epochtrain_lossvalid_losserror_ratetime
01.7699900.3309700.10148900:57
10.6199980.2397660.07916100:57
20.3375940.2112920.06630600:57
30.2479050.2085330.06833600:58

5.5 Echoing 4, bs=32

In [0]:
bs = 32
e = 4
In [20]:
learn = cnn_learner(get_dls(bs, e), resnet34, metrics=error_rate, cbs=[ShowGraphCallback]).to_fp16()
learn.fit_one_cycle(4)
epochtrain_lossvalid_losserror_ratetime
00.9925950.2778520.09404601:09
10.4510040.2212090.07442501:09
20.2769610.2007640.06157001:09
30.1900320.1918400.05954001:09

6. สรุป

  • การ Echoing เหมือนกับเราใส่ข้อมูลเดิมเข้าไปให้โมเดลอีก e-1 รอบ ดังนั้น 1 Epoch ไม่ใช่การที่โมเดลเห็นข้อมูลครบทั้ง Dataset 1 รอบแล้ว กลายเป็น e รอบแทน
  • ในเคส Echoing = 2 เวลาที่เพิ่มขึ้นมานิดหน่อย ต่อ 1 Epoch น้อยกว่าเวลาที่ใช้ในการเทรนปกติ 2 Epoch เพราะ GPU ทำงานเร็วกว่ามาก
  • จากสถิติ Validation Loss จะเห็นว่า Echoing ช่วยให้โมเดล เทรนได้เร็วขึ้น ใช้เวลาน้อยลง Converge เร็วขึ้น
  • นี่เป็นเพียงการทดลองง่าย ๆ ใน Paper ยังมีแนวคิดอีกหลายเรื่อง จะอธิบายต่อไป
In [0]:
 

แชร์ให้เพื่อน:

Keng Surapong on FacebookKeng Surapong on GithubKeng Surapong on Linkedin
Keng Surapong
Project Manager at Bua Labs
The ultimate test of your knowledge is your capacity to convey it to another.

Published by Keng Surapong

The ultimate test of your knowledge is your capacity to convey it to another.

Enable Notifications    Ok No thanks