[Tutorial] Tính số lượng parameters trong Convolutional Neural Network

deep-learning
convolution

#1

1. Giới thiệu về CNN

Convolutional Neural Network (CNN) là một mô hình kiến trúc deep learning nổi tiếng. Hầu hết các bài toán liên quan đến nhận dạng và phân loại ảnh hiện nay đều sử dụng phương pháp này trong việc xây dựng model từ cơ bản cho đến phức tạp. Các kiến trúc như Alex, LeNet, Inception, VGG… đều có sử dụng CNN là thành phần trong mô hình của chúng. Bài viết này chúng ta sẽ đi sâu vào việc tính số lượng các tham số trong kiến trúc CNN cơ bản. Vấn đề này sẽ được minh hoạ thông qua việc tạo một mô hình CNN bằng thư viện Keras cho tập MNIST.

2. Phép tích chập với filter (kernel)

Trước khi vào vấn đề nêu trên, ta cần nhắc lại một chút về phương pháp biến đổi một ảnh bằng cách nhân tích chập nó với một filter (hay còn gọi là kernel) nào đó trong Computer Vision (CV). Đây là ví dụ về một ảnh được áp dụng filter để phát hiện ra các cạnh dọc của hình (Vertical Edge Detection).

Đầu vào là một ảnh 6 x 6, áp dụng một filter 3 x 3 trượt với stride = 1, ta có kết quả đầu ra là một ảnh 4 x 4. Nhìn bằng mắt thường, ta thấy ảnh kết quả nổi bật màu trắng ở giữa ( giá trị pixel > 0 ) là khu vực chứa cạnh của hình gốc, còn khu vực không chứa cạnh thì giá tri pixel = 0. Đây là cách làm trong CV để trích xuất features của ảnh. Một ảnh có thể áp dụng nhiều filters để tìm ra nhiều features khác nhau (cạnh ngang, đường chéo …).

3. Phép tích chập trong CNN

Convolutional Neural Network là một kiến trúc lấy ý tưởng từ việc áp dụng các filters để tìm ra các features cho ảnh đầu vào, sau đó các sử dụng các features đó cho việc phân loại ảnh. Trong kiến trúc này có nhiều thành phần, nhưng core của nó là một thành phần được gọi là lớp Convolution (CONV). Lớp CONV này sẽ chứa các filters cho kiến trúc CNN. Tuy nhiên, thay vì phải tự đưa ra các filters thủ công như CV, các filters sẽ được tìm ra một cách tự động thông qua quá trình trainning. Có nghĩa là các giá trị trong mỗi một filter lúc này (chính là các weights) ban đầu sẽ được khởi tạo ngẫu nhiên, sau quá trình học mà chúng sẽ được cập nhật lại, và kết quả cố định cuối cùng được ổn định khi kết thúc training. Khi xây dựng CNN, chúng ta chỉ cần chỉ ra là ở mỗi lớp CONV cần số lượng bao nhiêu filter, size mỗi filter, giá trị stride, giá trị padding…

Đây là một ví dụ ta chọn các tham số cho lớp CONV đặt sau ảnh input. Trong đó số lượng filters chúng ta muốn là 32. Kích thước mỗi filters là 3 x 3 (9 weights). Ở đây không thấy nói rõ stride = bao nhiêu nhưng quan sát ảnh đầu vào và các ảnh đầu ra sau khi filter có kích thước = nhau (28 x 28) thì suy ra là stride = 1 và padding = “SAME”.

Minh hoạ cách tính CONV cho hình RGB (h x w x 3).

4. Minh hoạ cách tính số lượng parameters trong CNN

Okay, tiếp theo ta xây dựng một model đơn giản sử dụng thư viện keras trên tập mnist để minh hoạ. Mã nguồn như sau.

from keras import layers
from keras import models

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))
model.summary()

Model nhận đầu vào là các ảnh (28 x 28 x 1), đầu ra là lớp fc softmax với nodes = 10. Ở giữa là 1 kiến trúc CNN đơn giản với thứ tự các lớp như sau: CONV (relu) => MAXPOOL => CONV (relu) => MAXPOOL => CONV(relu) => FC(relu) => FC(softmax).

Hàm model.summary cho ta bảng tóm tắt kiến trúc model.

Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 26, 26, 32)        320       
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 13, 13, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 11, 11, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 5, 5, 64)          0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 3, 3, 64)          36928     
_________________________________________________________________
flatten_1 (Flatten)          (None, 576)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 64)                36928     
_________________________________________________________________
dense_2 (Dense)              (None, 10)                650       
=================================================================
Total params: 93,322
Trainable params: 93,322
Non-trainable params: 0

Bảng này có 3 cột , cột thứ nhất là tên các layers theo thứ tự từ trên xuống dưới là từ input => output, cột thứ hai là shape cho mỗi output của từng layers, cột thứ ba là số lượng param (hay weights) cho từng layers. Hàng cuối cùng là tổng hợp số lượng params cho cả model.

Lớp đầu tiên là một CONV. Input là ảnh đầu vào có size là (None, 28, 28, 1). Tham số thứ nhất là None thể hiện số lượng ảnh trainning. Ta tạm thời không cần quan tâm giá trị này, xem như đang làm việc với một ảnh duy nhất (28 x 28 x 1), và cả những output sau đó khi gặp None thì ta hiểu tương tự. Output là của lớp này sẽ có size là (None, 26, 26, 32). Bây giờ ta tìm hiểu tại sao ở lớp này có output size = như trên và số lượng params (weights) = 320 được tính như thế nào.

Xét mã nguồn của lớp CONV đầu tiên là:

model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))

Thông số Conv2D(32, (3, 3) có nghĩa là ta muốn tạo 32 filter với kích thước mỗi filter là 3 x 3. Vì mỗi filter mang 3 x 3 weights (w1 … w8) nên tổng số weights sẽ là 32 x 3 x 3 = 288. Đến đây, ta sẽ thắc mắc tại sao model summary ra ở lớp này là 320. Câu trả lời là vì với mỗi filters chúng ta chưa cộng w0 (bias weight) cho nó. Vì vậy, kết quả chính xác phải là 32 x 3 x 3 + 32 x 1 = 320.

Thêm nữa, xét mỗi filter (3 x 3) trượt trên hình gốc (28 x 28 x 1) ko có padding = “SAME” thì output là một hình (26 x 26 x 1). Vì vậy 32 filter sẽ cho ra output có size là (26 x 26 x 32).

Xét lớp thứ hai là Max Pooling. Lớp này chỉ đơn giản là giảm kích thước ma trận input theo một cửa sổ kích thước (2 x 2). Vì vậy lớp này ko có params (weights) nào cả. Và từ input = (26 x 26 x 32), output của lớp này sẽ là (13 x 13 x 32).

Lớp thứ ba là một CONV khác. CONV này có thông số Conv2D(64, (3, 3) có nghĩa là ta muốn tạo 64 filter với kích thước mỗi filter là 3 x 3. Input của lớp này là lớp Max Pooling trước đó (13 x 13 x 32). Cách tính số weights như sau. Với mỗi filter ta sẽ có 3 x 3 weights (chưa tính bias) nhưng vì lớp này có 32 ma trận đầu vào nên số weights mỗi filter sẽ là 3 x 3 x 32 + 1 (bias weight) = 289. Và 64 filter sẽ có số weights = 289 x 64 = 18496.

Thêm nữa, xét mỗi filter (3 x 3 x 32) trượt trên hình gốc (13 x 13 x 32) ko có padding = “SAME” thì output là một hình (11 x 11 x 1). Vì vậy 64 filter sẽ cho ra output có size là (11 x 11 x 64).

Cách tính của lớp MaxPooling (5x5) và lớp CONV tiếp theo (Conv2D(64, (3, 3)) tương tự như trên. Số lượng weights sẽ là (3 x 3 x 64 + 1) x 64 = 36928. Outputs có size là (3 x 3 x 64).

Lớp tiếp theo sẽ flatten (3 x 3 x 64) thành layer có 576 nodes, lớp này kết nối với FC sau nó có 64 nodes. Cách tính số lượng weights cho lớp FC này là 576 x 64 + 64 = 36928. Tương tự lớp FC cuối cùng cho ra 10 nodes có số lượng weights là 64 x 10 + 10 = 650.

Kết luận, tổng số lượng weights sẽ là 99322. Số lượng weights càng nhiều thì độ phức tạp của model càng lớn và dễ dẫn đến overfit, hoặ quá ít thì không đủ mạnh với những dữ liệu phức tạp. Vì vậy ta cần cân nhắc kỹ việc lựa chọn model sao cho phù hợp với dữ liệu huấn luyện để cho ra một model có độ chính xác cao.

Tham khảo:

[1] Deep Learning with Python - Francois Chollet

[2] Deep Learning withTensorflow - Giancarlo_Zaccone,_Md._Rezaul_Karim,_Ahmed_Mensha

[3]https://lovesnowbest.site/2018/02/24/Intro-to-Convolutional-Neural-Networks/

[4]http://machinelearninguru.com/computer_vision/basics/convolution/convolution_layer.html


Xây dựng mạng CNN với tensorflow và keras
#2

Great tutorial, bro

Mình có 1 câu hỏi như sau: Giờ mình cần train với large dataset, chạy trên Google Cloud (hay local đi). Mình muốn split large dataset này để chạy với TF + Keras như bạn thì mình có thể làm như nào?

Mình đang nghĩ tới 2 cách đều là split data thành n lần Cách 1/ Split large DS thành n lần, mỗi phần chỉ bao gồm số class / n. Cách 2/ Split large DS thành n lần, mỗi phần bao gồm đủ các class số lượng input là total_input / n

Mình muốn hỏi 2 sự khác biệt của hai cách trên :smile:

Và muốn hỏi thêm là làm sao checkpoint Keras để chạy tiếp, or just model.save() -> model.load() -> model.fit() ?

Thank you