2024-03-30 · CNN, Architecture
Xin chào các bạn!
Trong bài post này mình sẽ giới thiệu về kiến trúc VGG, một trong những kiến trúc lâu đời nhất của mạng tích chập. Thật tình cờ, vào thời điểm viết bài blog này cũng là tròn 10 năm ngày ra mắt của mô hình này. Vậy, VGG có gì đặc biệt và có ảnh hưởng đến cách xây dựng các mô hình CNNs sau này như thế nào, chúng ta cùng khám phá nhé.
Vào thời điểm 10 năm trước, có 2 mô hình CNNs rất nổi trước khi VGG được ra mắt đó là AlexNet (2012) được published trong paper "ImageNet Classification with Deep Convolutional Neural Networks" và LeNet của YanLeCun. Mô hình LeNet của YanLeCun là mô hình đầu tiên áp dụng thuật toán convolution vào mạng neural networks. Còn về AlexNet, mô hình này cực kì nổi tiếng mà hầu như ai trong lĩnh vực AI này cũng biết, nó đặc biệt ở chỗ dùng hàm ReLU activation function, sử dụng Dropout và Local Response Normalisation, và training với nhiều GPUs.
Tuy nhiên, nếu nhìn qua về kiến trúc của cả 2 mạng này, ta sẽ thấy nó khá là nông với chỉ từ 5 đến 6 layers. Một điều nữa là cách sử dụng convolutional layers ở các mạng này là dùng kernel size và stride có kích cỡ lớn ở những layers đầu để thu nhỏ receptive field.
Như có thể thấy ở trên, mạng AlexNet có nhiều convolutional layers hơn so với LeNet tuy nhiên, AlexNet chỉ dừng lại ở 5 convolution layers và 3 fully connected layers.
Mạng VGG khác với các mô hình đi trước ở 2 điểm chính đó là sử dụng kernel size = 3 xuyên suốt toàn bộ kiến trúc và độ sâu của mô hình được tăng lên 16-19 layers thay vì chỉ vài layers như AlexNet và LeNet trước đó.
Trong paper của mình, tác giả VGG đề xuất 6 options cho VGG đó là A, A-LRN, B, C, D, và E. Với từng options sẽ có thông số cụ thể như sau:
| ConvNet Configuration | |||||
|---|---|---|---|---|---|
| A | A-LRN | B | C | D | E |
| 11 weight layers | 11 weight layers | 13 weight layers | 16 weight layers | 16 weight layers | 19 weight layers |
| input (224x224 RGB image) | |||||
| Conv3-64 | Conv3-64 LRN |
Conv3-64 Conv3-64 |
Conv3-64 Conv3-64 |
Conv3-64 Conv3-64 |
Conv3-64 Conv3-64 |
| MaxPool | |||||
| Conv3-128 | Conv3-128 | Conv3-128 Conv3-128 |
Conv3-128 Conv3-128 |
Conv3-128 Conv3-128 |
Conv3-128 Conv3-128 |
| MaxPool | |||||
| Conv3-256 Conv3-256 |
Conv3-256 Conv3-256 |
Conv3-256 Conv3-256 |
Conv3-256 Conv3-256 Conv1-256 |
Conv3-256 Conv3-256 Conv3-256 |
Conv3-256 Conv3-256 Conv3-256 Conv3-256 |
| MaxPool | |||||
| Conv3-512 Conv3-512 |
Conv3-512 Conv3-512 |
Conv3-512 Conv3-512 |
Conv3-512 Conv3-512 Conv1-512 |
Conv3-512 Conv3-512 Conv3-512 |
Conv3-512 Conv3-512 Conv3-512 Conv3-512 |
| MaxPool | |||||
| Conv3-512 Conv3-512 |
Conv3-512 Conv3-512 |
Conv3-512 Conv3-512 |
Conv3-512 Conv3-512 Conv1-512 |
Conv3-512 Conv3-512 Conv3-512 |
Conv3-512 Conv3-512 Conv3-512 Conv3-512 |
| MaxPool | |||||
| FC-4096 | |||||
| FC-4096 | |||||
| FC-1000 | |||||
| Softmax | |||||
So sánh giữa 2 kiến trúc như trên hình thì có thể thấy được rằng kiến trúc VGG sâu hơn hẳn so với AlexNet.
Với kiến trúc sử dụng full conv 3x3 và 16 layers, tác giả Karen Simonyan & Andrew Zisserman đã chứng minh được độ hiệu quả với việc giảnh giải nhất của cuộc thi ILSVRC 2014 cho task classification và giải nhi cho task localisation.
Giờ mình sẽ build kiến trúc này với Pytorch để chúng ta có thể nắm rõ hơn kiến trúc này nhé.
import torch
from torch import nn
torch.set_grad_enabled(False)
# Configuration for each option
cfg = {
'A': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
'A-LRN': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
'B': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
'C': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],
'D': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],
'E': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'],
}
class VGG(nn.Module):
def __init__(self, vgg_name, num_classes):
super().__init__()
if vgg_name not in cfg:
raise "Architecture not included, please try again"
self.layer_names = cfg[vgg_name]
layers = [nn.Conv2d(3, self.layer_names[0], kernel_size=3, padding = 1)]
if "LRN" in vgg_name:
layers += [nn.LocalResponseNorm(5)]
layers += [nn.ReLU()]
last_num_layer = self.layer_names[0]
for ith, layer_name in enumerate(self.layer_names[1:]):
if isinstance(layer_name, int):
if vgg_name == 'C' and ith >= 6 and self.layer_names[ith+2] == 'M':
layers += [nn.Conv2d(last_num_layer, layer_name, kernel_size=1, stride=1, padding = 0)]
else:
layers += [nn.Conv2d(last_num_layer, layer_name, kernel_size=3, stride=1, padding = 1)]
layers += [nn.ReLU()]
last_num_layer = layer_name
else:
layers.append(nn.MaxPool2d(2, 2))
self.layers = nn.Sequential(*layers)
self.global_avg_pool = nn.AdaptiveAvgPool2d(1)
self.fc = nn.Linear(last_num_layer, num_classes)
def forward(self, x):
x = self.layers(x)
x = self.global_avg_pool(x)
x = x.view(x.shape[0], -1)
return self.fc(x)
if __name__ == "__main__":
modelA = VGG("A", 10)
modelALRN = VGG("A-LRN", 10)
modelB = VGG('B', 10)
modelC = VGG("C", 10)
modelD = VGG("D", 10)
modelE = VGG('E', 10)
dummy = torch.randn((1, 3, 224, 224))
print(f"Model A output shape: {modelA(dummy).shape}")
print(f"Model A-LRN output shape: {modelALRN(dummy).shape}")
print(f"Model B output shape: {modelB(dummy).shape}")
print(f"Model C output shape: {modelC(dummy).shape}")
print(f"Model D output shape: {modelD(dummy).shape}")
print(f"Model E output shape: {modelE(dummy).shape}")
Output của mô hình
> Model A output shape: torch.Size([1, 10])
> Model A-LRN output shape: torch.Size([1, 10])
> Model B output shape: torch.Size([1, 10])
> Model C output shape: torch.Size([1, 10])
> Model D output shape: torch.Size([1, 10])
> Model E output shape: torch.Size([1, 10])
Trong các kiến trúc của AlexNet hay LeNet, tại sao họ không làm kiến trúc sâu hơn? Lý do chính bởi vì họ dùng kernel size và stride lớn nên đến khoảng layer 5 và 6 thì số lượng features đã rất nhỏ nên họ đã không thể cho thêm nhiều layers để extract features hơn nữa.
Tại sao tác giả của VGG dùng 3x3 mà không phải là 5x5, 2x2, 4x4? Lý do chính họ sử dụng kernel size 3x3 là vì đây là kernel size nhỏ nhất có thể bắt được toàn bộ vùng xung quanh của một pixels (trái/phải, trên/dưới, và chính giữa). Hơn nữa việc dùng 2 kernel 3x3 tương tự với việc dùng conv 5x5 với ít số lượng parameters hơn, nhưng khối lượng tính toán sẽ nhiều hơn (tác giả không nhắc đến điều này trong paper).
Mô hình VGG đã làm thay đổi cách các AI researchers/scientists thiết kế các kiến trúc CNN, với việc mô hình hiện đại ngày nay đa số dùng kernel size 3x3, và có độ sâu lớn hơn rất nhiều so với các mô hình ra mắt trước VGG. Vì vậy, ta có thể nói VGG là một pioneer model.
Cảm ơn bạn đã đọc đến cuối bài, hãy ủng hộ mình bằng cách tiếp tục theo dõi và feedback nếu thấy chỗ nào chưa hợp lý nhé.
1. Very Deep Convolutional Networks for Large-Scale Image
2. The Convolutional Neural Network - Theory and Implementation of LeNet-5 and AlexNet
Cited as:
Le Hoang Viet. "Giải thích và code paper Very Deep Convolutional Networks for Large-Scale Image Recognition". Le Hoang Viet's Homepage, March 2024. https://mikyx-1.github.io/blog/vgg.html
Or the BibTeX entry:
@misc{le2024vgg,
title = {Giải thích và code paper Very Deep Convolutional Networks for Large-Scale Image Recognition},
author = {Le, Hoang Viet},
journal = {Le Hoang Viet's Homepage},
year = {2024},
month = {March},
url = {https://mikyx-1.github.io/blog/vgg.html}
}
← Back to the blog index. Send feedback by email.