K-Fold cross validation, đánh giá model hiệu quả hơn khi có ít dữ liệu

Hello anh em Mì AI, hôm nay chúng ta sẽ cùng tìm hiểu về K-Fold cross validation, một tuyệt chiêu khá hay khi chúng ta không có nhiều dữ liệu cho các bài toàn Machine Learning và Deep Learning nhé.

K-Fold CV là một phương pháp để đánh giá model một cách chính xác khi chúng ta train model nhưng có quá ít dữ liệu.

Phần 1 – Vấn đề đánh giá “sai” model khi train với ít dữ liệu

Chắc hẳn anh em đã quen thuộc với cách chia dữ liệu train, valdiation và test đúng không? Cụ thể như hình sau:

data split
Nguồn: Tại đây

Bây giờ ta tạm bỏ qua Test set sang một bên bởi vì đó là tập chúng ta sẽ sử dụng để kiểm tra model sau khi train xong để xem model sẽ handle dữ liệu như nào trong thực tế. Chúng ta xét train và val set thôi nha!

Thông thường các bạn sẽ thấy chúng ta hay chia train/val theo tỷ lệ 80/20 đúng không nhỉ? 80% dữ liệu (sau khi đã bỏ phần test) sẽ là dữ liệu để train model và 20% còn lại sẽ làm dữ liệu để kiểm tra model trong quá trình train.

Việc chia này hoàn toàn okie nếu như bạn có lượng dữ liệu đủ lớn. Tuy nhiên khi bạn có ít dữ liệu thì việc chia như này sẽ dẫn đến model của bạn hoạt động cực kém. Lý do? Là vì có thể một số điểm dữ liệu có ích cho qúa trình train đã bị bạn ném vào để làm validation, test và model không có cơ hội học điểm dữ liệu đó. Thậm chí, đôi khi do ít dữ liệu nên có một vài class chỉ có trong validation, test mà không có trong train (do việc chia train, val là hoàn toàn ngẫu nhiên) dẫn đến một kết quả tồi tệ khi validation và test. Và nếu chúng ta dựa ngay vào kết quả đó để đánh giá rằng model không tốt thì thật là oan uổng cho nó giống như một học sinh không được học Tiếng Anh mà phải đi thi TOEFL vậy =))

Và đó là lúc chúng ta cần đến K-Fold Cross Validation!

Phần 2 – Vậy K-Fold Cross Validation là gì?

K-Fold CV sẽ giúp chúng ta đánh giá một model đầy đủ và chính xác hơn khi chúng ta có một tập dữ liệu không lớn. Để sau đó chúng ta đưa ra quyết định model đó có phù hợp với dữ liệu, bài toán hiện tại hay không để mà đưa ra next action.

Bắt đầu nhé!

k-fold cross validation
Nguồn: Tại đây

Như hình bên train, các bạn sẽ thấy:

  • Phần dữ liệu Test data sẽ đc để riêng và dành cho bước đánh giá cuối cùng nhằm kiểm tra “phản ứng” của model khi gặp các dữ liệu unseen hoàn toàn.
  • Phần dữ liệu Training thì sẽ được chia ngẫu nhiên thành K phần (K là một số nguyên, hay chọn là 5 hoặc 10). Sau đó train model K lần, mỗi lần train sẽ chọn 1 phần làm dữ liệu validation và K-1 phần còn lại làm dữ liệu training. Kết quả đánh giá model cuối cùng sẽ là trung bình cộng kết quả đánh giá của K lần train. Đó chính là lý do vì sao ta đánh giá khách quan và chính xác hơn.

Sau khi đánh giá xong model và nếu cảm thấy kết quả (ví dụ accuracy trung bình) chấp nhận được thì ta có thể thực hiện một trong 2 cách sau để tạo ra model cuối cùng (để mang đi dùng predict):

  • Cách một: Trong quá trình train các fold, ta lưu lại model tốt nhất và mang model đó di dùng luôn. Cách này sẽ có ưu điểm là không cần train lại nhưng lại có nhược điểm là model sẽ không nhìn được all data và có thể không làm việc tốt với các dữ liệu trong thực tế.
  • Cách hai: train model 1 lần nữa với toàn bộ dữ liệu (không chia train, val nữa) và sau đó save lại và mang đi predict với test set để xem kết quả như nào 😀

Chú ý: còn có một cách khác mà theo mình thấy là nó mở rộng từ K-Fold CV và hay hơn nhiều là Stratified K-Fold CV. Với phương pháp này thì nó sẽ chỉ shuffle dữ liệu một lần đầu tiên trước khi bắt đầu chia fold và nó sẽ cố gắng chia sao cho tỷ lệ các class trong các fold là tương đồng nhau.

Phần 3 – Thực hành với Keras

Rồi, nói dông dài lý thuyết rồi, bây giờ chúng ta sẽ cùng thực hành một bài nhỏ nhỏ làm với một model DL bằng Keras nha!

Để đơn giản ta sẽ sử dụng luôn bộ dữ liệu CIFAR10 bởi vì bộ dữ liệu này dã được tích hợp sẵn vào Keras (vì nó quá nổi tiếng kaka) nên công tác load dữ liệu đơn giản hơn.

Các thư viện càn thiết phải import

from tensorflow.keras.datasets import cifar10 from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Flatten, Conv2D, MaxPooling2D from sklearn.model_selection import KFold import numpy as np
Code language: JavaScript (javascript)

Sau đó mình viết một hàm để load dữ liệu, chuẩn hoá dữ liệu và ghép dữ liệu theo đúng mục đích.

def load_data(): # Load dữ liệu CIFAR đã được tích hợp sẵn trong Keras (X_train, y_train), (X_test, y_test) = cifar10.load_data() # Chuẩn hoá dữ liệu X_train = X_train.astype('float32') X_test = X_test.astype('float32') X_test = X_test / 255 X_train = X_train / 255 # Do CIFAR đã chia sẵn train và test nên ta nối lại để chia K-Fold X = np.concatenate((X_train, X_test), axis=0) y = np.concatenate((y_train, y_test), axis=0) return X, y
Code language: PHP (php)

Sau khi xong bước này ta sẽ có dữ liệu X và y, ta tiến hành xây dựng kiến trúc model và sau đó là chia Fold mà cụ thể ở đây là 10 Folds.

def get_model(): model = Sequential() model.add(Conv2D(64, kernel_size=(3, 3), activation='relu', input_shape=(32, 32, 3))) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Conv2D(128, kernel_size=(3, 3), activation='relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Flatten()) model.add(Dense(256, activation='relu')) model.add(Dense(128, activation='relu')) model.add(Dense(no_classes, activation='softmax')) # Compile model model.compile(loss="sparse_categorical_crossentropy", optimizer="Adam", metrics=['accuracy']) return model
Code language: PHP (php)

Chia Fold thì ta dùng luôn thư viện KFold của Sklearn (bạn đọc cũng có thể thử đổi sang Stratified K-Fold để thực hành).

# Định nghĩa K-Fold CV kfold = KFold(n_splits=num_folds, shuffle=True) # K-fold Cross Validation model evaluation fold_idx = 1 for train_ids, val_ids in kfold.split(X, y): model = get_model() print("Bắt đầu train Fold ", fold_idx) # Train model model.fit(X[train_ids], y[train_ids], batch_size=batch_size, epochs=no_epochs, verbose=1) # Test và in kết quả scores = model.evaluate(X[val_ids], y[val_ids], verbose=0) print("Đã train xong Fold ", fold_idx) # Thêm thông tin accuracy và loss vào list accuracy_list.append(scores[1] * 100) loss_list.append(scores[0]) # Sang Fold tiếp theo fold_idx = fold_idx + 1
Code language: PHP (php)

Đại khái là chúng ta dùng KFold để lấy ra train index set và val index set tại mỗi fold, sau đó trích các phần tử theo index set đó để đưa vào train, val cho phù hợp. Kết quả accuracy và loss sẽ được lưu lại vào list để hiển thị và tính trung bình cộng.

Model sẽ được train lần lượt từng fold, mỗi fold 25 epochs:

k-fold cross validation

Và sau khi train xong toàn bộ 10 folds, ta sẽ in kết quả tổng thể:

# In kết quả tổng thể print('* Chi tiết các fold') for i in range(0, len(accuracy_list)): print(f'> Fold {i+1} - Loss: {loss_list[i]} - Accuracy: {accuracy_list[i]}%') print('* Đánh giá tổng thể các folds:') print(f'> Accuracy: {np.mean(accuracy_list)} (Độ lệch +- {np.std(accuracy_list)})') print(f'> Loss: {np.mean(loss_list)}')
Code language: PHP (php)
k-fold cross validation

Okie, như vậy thay vì đánh giá model một cách phiến diện với một train set và một val set, chúng ta đã có thể đánh giá model một các chính xác và hiệu quả hơn với K-Fold CV. Các bạn có thể tự thực hành với Stratified K-Fold (mình hay xài món này :D).

Hẹn gặp lại các bạn! Mình xin tặng bạn nào đọc đến đây link github thay lời cảm ơn nha: Tại đây.

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

#MìAI

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

Tài liệu tham khảo: https://medium.com/the-owl/k-fold-cross-validation-in-keras-3ec4a3a00538

Related Post

Leave a Reply

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