“Flow from directory” – Thuốc đặc trị hết RAM, tràn bộ nhớ khi train model

Chào tuần mới anh em Mì AI, hôm nay chúng ta sẽ cùng nhau tìm hiểu món Flow from directory, một chiêu thức đặc trị bệnh hết RAM, tràn bộ nhớ khi train model nhé.

Thời gian vừa qua mình gặp một số câu hỏi của các bạn trên Group trao đổi, chia sẻ: https://facebook.com/groups/miaigroup về việc bị báo hết RAM khi train (trên PC hoặc Colab) các bài toán về hình ảnh. Do đó, để trả lời chung cho các bạn thì mình viết bài này luôn.

Cùng bắt đầu nào?

Phần 1 – Vì sao khi train model bị báo thiếu bộ nhớ (Out of Memory)?

Bộ nhớ ở đây là mình nói chung cho cả bộ nhớ RAM khi train bằng CPU và bộ nhớ VRAM của GPU (cái này có thể mình nhớ không chuẩn, các bạn cứ hình dung là bộ nhớ của GPU là okie).

Khi train model chúng ta sẽ hay gặp việc báo tràn bộ nhớ ở bước load dữ liệu từ ổ cứng vào biến X, y để chuẩn bị train.

Lý do? Bạn cứ hình dung như này, một tấm ảnh của bạn chụp bây giờ cũng tầm 2MB rồi (đấy là còn ít đó). Trong khi train Deep Learning thì lại cần nhiều, thật nhiều dữ liệu (tính bằng đơn vị nghìn ảnh) và điều gì đến sẽ đến, đó là…. hết bộ nhớ.

Nguồn: Tại đây

Ví dụ để các bạn dễ hiểu, ta có 10.000 ảnh , mỗi ảnh 2MB thì khi load đủ số ảnh này vào bộ nhớ sẽ là 10.000 x 2 = 20.000 MB = 19.5GB. Vâng, mười chín Gigabyte và với lượng bộ nhớ như này thì PC học tập của các bạn và kể cả Colab cũng tèo ngay lập tức.

Đó là lý do, bây giờ chúng ta cùng tìm giải pháp nhé!

Phần 2 – Cách giải quyết vấn đề bằng Flow from directory

Suy nghĩ hoàn toàn theo logic thông thường thì chúng ta có thể giải quyết vấn đề trên bằng cách chia nhỏ dữ liệu ra và load từ từ vào là xong. Đúng! Chuẩn là như thế, trong Keras có món DataGenerator và model.fit_generator để chúng ta tự viết các hàm load dữ liệu và feed cho model một cách tuần tự.

Tuy nhiên hôm nay để đơn giản mình xin chỉ focus vào món Flow from directory còn các bạn có thể search và triển khai các biện pháp custom hơn, can thiệp sâu hơn vào món feed dữ liệu qua DataGenerator nhé.

Cụ thể hơn trong bài này mình sẽ focus vào việc dùng flow from directory để load dữ liệu từ các folder một cách tuần tự. Rồi , cùng làm nào!

Bước 1. Chuẩn bị dữ liệu

Để làm việc với Deep Learning thì phải có dữ liệu là chắc rồi. Giả sử chúng ta train model phân loại Chó và Mèo đi. Chúng ta sẽ sử dụng DogCat data của Kaggle luôn (tải về tại đây: https://www.kaggle.com/c/dogs-vs-cats/data) .

Dữ liệu của chúng ta sẽ được chia ra thành 3 folder: train, valid (validation) và test. Chi tiết về vì sao phải chia như này các bạn xem tại clip này.

flow from directory

Sau khi tải dữ liệu về chúng ta sẽ có 2 folder train và test. Trong folder train chứa cả Dog (các file bắt đầu bằng dog.*) và Cat (các file bắt đầu bằng Cat.*). Giờ nhiệm vụ của chúng ta là phỉa chia thành các folder có cấu trúc như sau (valid được lấy ngẫu nhiên tầm 20% từ file tổng số file trong thư mục Train) :

flow from directory

Chúng ta sẽ sử dụng đoạn code sau (đây có thể chưa là cách tối ưu nhé):

from os import listdir
import os
import random

data_folder = "data/train"
train_folder = "data/train"
valid_folder = "data/valid"

try:
    # Tạo thư mục con cho train
    os.mkdir(train_folder + "/dog")
    os.mkdir(train_folder + "/cat")

    # Tạo thư mục valid và các thư mục con
    os.mkdir(valid_folder)
    os.mkdir(valid_folder + "/dog")
    os.mkdir(valid_folder + "/cat")
except:
    pass


for file in listdir(data_folder):
    if os.path.isfile(data_folder + "/" + file):
        print("Process file ", file)
        if random.random()>0.8:
            print("Move to valid ", file)

            # Cho vào valid
            dest_dir = file[0:3]
            os.rename(data_folder + "/" + file, valid_folder + "/" + dest_dir + "/" + file)
        else:
            print("Move to train ", file)
            dest_dir = file[0:3]
            os.rename(data_folder + "/" + file, train_folder + "/" + dest_dir + "/" + file)Code language: PHP (php)

Sau khi chạy xong đoạn code thì 25,000 tấm ảnh chó mèo đã được chia ra ngon lành thành 20K ảnh (10K chó trong thư mục dog, 10K mèo trong thư mục cat) cho train và phần còn lại 5K cho valid.

flow from directory

Bạn nào còn thắc mắc về cách chia dữ liệu thì post lên Group trao đổi, chia sẻ: https://facebook.com/groups/miaigroup để cùng thảo luận nhé.

Bước 2. Viết code load dữ liệu để train model

Nếu như trước đây khi load dữ liệu ta load toàn bộ dữ liệu vào X, y sau đó dùng train_test_split để chia ra train, valid và test chẳng hạn. Thì bây giờ chúng ta sẽ sử dụng 3 bộ ImageGenerator của Keras để load dữ liệu.

Với bộ dữ liệu train ta load như sau:

# Data generators
train_datagen = ImageDataGenerator(
      rescale=1./255,
      rotation_range=40,
      width_shift_range=0.2,
      height_shift_range=0.2,
      shear_range=0.2,
      zoom_range=0.2,
      horizontal_flip=True,
      fill_mode='nearest')
# Data flow
train_generator = train_datagen.flow_from_directory(
    directory="data/train/",
    target_size=(224, 224),
    color_mode="rgb",
    batch_size=32,
    label_mode="categorical",
    shuffle=True,
    seed=42
)Code language: PHP (php)

Các bạn chú ý các thành phần sau:

Phần 1 là cái món train_datagen. Nó chính là cái ImageDataGenerator dùng để Augment data mình đã nói trong bài này. Các bạn đọc lại nếu chưa đọc nhé.

Phần 2 là cái train_generator. Nó dùng để load dữ liệu trong thư mục train ra. Các tham số gồm:

  • directory: thư mục chứa dữ liệu train
  • target_size: kích thước ảnh sau khi resize, cái này các bạn chỉnh cho phù hợp với model. Ở đây mình để 224×224 vì mình train VGG
  • color_mode: có 2 giá trị chính là “grayscale” – ảnh đầu ra là ảnh xám và “rgb” – ảnh đầu ra là ảnh màu RGB
  • batch_size: cái này quen thuộc rồi, mỗi lần lấy bao nhiêu ảnh để feed vào model.
  • label_mode: cái này nếu nhiều lớp thì cứ để là “categorical”. Các nhãn sẽ được encode thành các categorical vector. Nếu để binary thì các nhãn sẽ là các số thực 0 và 1.
  • shuffle: xáo trộn dữ liệu trước khi load.
  • seed: Là một số ngẫu nhiên tuỳ chọn.

Tương tự cho valid nhé:

valid_generator = valid_datagen.flow_from_directory(
    directory="data/valid/",
    target_size=(224, 224),
    color_mode="rgb",
    batch_size=32,
    class_mode="categorical",
    shuffle=True,
    seed=42
)Code language: PHP (php)

Chú ý là với valid và test có thể không cần augment và chỉ cần dùng bộ Generator đơn giản như sau:

valid_datagen = ImageDataGenerator(rescale=1./255)

Riêng với ông test thì lại khác một chút, chúng ta sử dụng đoạn lệnh:

test_generator = test_datagen.flow_from_directory(
    directory="data/test/",
    target_size=(224, 224),
    color_mode="rgb",
    batch_size=1,
    class_mode=None,
    shuffle=False,
    seed=42
)Code language: PHP (php)

Các bạn để ý sẽ thấy khác mấy chỗ:

  • batch_size = 1: Chúng ta làm như này để test từng ảnh một.
  • shuffle= False: Chúng ta không xáo trộn dữ liệu để còn tiện map giữa giá trị y thực và y dự đoán xem model ta chuẩn hay không?

Okie, vậy đủ 3 bộ data generator rồi, chúng ta train model thôi:

n_train_steps = train_generator.n//train_generator.batch_size
n_valid_steps = valid_generator.n//valid_generator.batch_size
model.fit_generator(generator=train_generator,
                    steps_per_epoch=n_train_steps,
                    validation_data=valid_generator,
                    validation_steps=n_valid_steps,
                    epochs=100
)Code language: JavaScript (javascript)
Bước 3. Predict thử xem model dư lào?

Train xong thì ta thử predict cái nào:

n_test_steps = test_generator.n
test_generator.reset()
y_pred = model.predict_generator(test_generator,
steps=n_test_steps,
verbose=1)

Vậy là chúng at sẽ có biến y_pred chứa kết quả predict của cả tập test rồi. Nhiệm vụ tiếp theo là các bạn so sánh với ground truth hoặc trích xuất ra label thực là xong rồi đó.

y_pred = np.argmax(y_pred,axis=1)
labels = (train_generator.class_indices)
labels = dict((v,k) for k,v in labels.items())
y_class = [labels[k] for k in y_pred]

Y_class sẽ là một list các nhãn theo đúng thứ tự của tập test nhé.

Okie như vậy mình đã guide các bạn cách sử dụn flow from directory để tránh được tràn bộ nhớ khi load dữ liệu và train model. Hẹn gặp lại các bạn trong các bài tiếp theo nha!

Chúc các bạn thành công!

Hãy join cùng cộng đồng Mì AI nhé!

Fanpage: http://facebook.com/miaiblog
Group trao đổi, chia sẻ: https://www.facebook.com/groups/miaigroup
Website: https://miai.vn
Youtube: http://bit.ly/miaiyoutube

Related Post

4 Replies to ““Flow from directory” – Thuốc đặc trị hết RAM, tràn bộ nhớ khi train model”

  1. Hay quá !
    Cho mình hỏi liệu Flow from directory có thể áp dụng cho code phân loại ảnh Mát Mẽ được ko ???
    ( https://www.miai.vn/2019/12/16/xay-dung-model-phan-biet-va-tranh-xa-anh-mat-me/?fbclid=IwAR15Zo94DVu0iHO-1MuovKwirCr8z9E8M3rnHIu_KiKF6aRRP1Ex8uHX15M )

    Chuyển vào đọc thư mục thì trước khi Train ta chuyển ảnh qua numpy thế nào ?
    Biến data = train_generator đúng ko ?
    còn label giữ nguyên ???
    # Change to numpy array
    label = onehot_encoded
    data = np.array(data)

    Mong được hồi âm … Thank !!!

Leave a Reply

Your email address will not be published. Required fields are marked *