- vừa được xem lúc

Paper reading | Inception-v4, Inception-ResNet and the Impact of Residual Connections on Learning

0 0 9

Người đăng: Viblo AI

Theo Viblo Asia

Đóng góp bài báo

Trong bài báo, nhóm tác giả nghiên cứu sự kết hợp của 2 ý tưởng có thể coi là kinh điển trong lịch sử các mô hình CNN nổi tiếng là Residual connection và phiên bản mới nhất của kiến trúc Inception 😄.

Residual connection (xem hình dưới) đóng vai trò quan trọng trong việc training các mạng deep learning sâu.

image.png

Inception cũng là một kiểu mạng deep learning "rất là deep" 😄 nên là một cách rất tự nhiên khi ta kết hợp Residual connection với mô hình Inception. Bằng cách này mô hình mới sẽ có được ưu điểm của Residual connection mà vẫn giữ nguyên độ hiệu quả tính toán.

Bên cạnh đó, nhóm tác giả cũng thực hiện nghiên cứu mô hình Inception và cải thiện mô hình bằng cách làm cho nó rộng và sâu hơn 😄 Cụ thể, bài báo giới thiệu mô hình Inception-v4 là một kiến trúc đơn giản đồng nhất hơn và nhiều module Inception hơn so với Inception-v3.

Ngoài ra, các thực nghiệm và so sánh giữa các mô hình cũng được báo cáo để làm rõ sự vượt trội của mô hình Inception-v4.

Kiến trúc mô hình

Inception-v4

Trong Inception-v4, các block Inception thuần được sử dụng và các block này không có residual connection. Trong khi các mô hình Inception trước đó, mô hình được chia thành các mạng con (sub-network) để fit với bộ nhớ của phần cứng thì mô hình Inception-v4 với khả năng tối ưu hóa bộ nhớ sử dụng cho backpropagation, ta có thể train mà không cần phân chia thành các bản sao.

Kiến trúc tổng quan của Inception-v4 như sau:

image.png

Khối stem trong Inception-v4 được mô tả trong hình dưới:

image.png

Tiếp theo là khối Inception-A, Inception-B và Inception-C được thể hiện trong hình dưới:

image.png

image.png

image.png

Khối reduction-A được biểu diễn như sau:

image.png

Lưu ý rằng, khối reduction-A cũng được sử dụng trong Inception-ResNet-v1 và Inception-ResNet-v2. Về mặt kiến trúc thì giống nhưng có sự thay đổi về kích thước filter bank tại mỗi model (tại các tham số k,l,m,nk, l, m, n).

Cuối cùng ta có khối reduction-B

image.png

Inception-ResNet

Inception-Resnet-v1 và Inception-ResNet-v2 đều chúng một kiến trúc như hình dưới. Tuy nhiên bên trong các thành phần của 2 mô hình có một chút sự khác biệt.

image.png

Inception-ResNet-V1

Đối với Inception-ResNet-v1, ta sử dụng các module sau như hình dưới.

Module stem

image.png

Module Inception-ResNet-A

image.png

Module Reduction-A

image.png

Module Inception-ResNet-B

image.png

Module Reduction-B

image.png

Inception-ResNet-V2

Tương tự, đối với Inception-ResNet-v2, ta sử dụng các module sau.

Module Stem

image.png

Module Inception-ResNet-A

image.png

Module Reduction-A

image.png

Module Inception-ResNet-B

image.png

Module Reduction-B

image.png

Các tham số k,l,m,nk, l, m, n trong module Reduction-A của cả 3 mô hình có giá trị thể hiện trong bảng dưới

image.png

Coding

Vì 3 mô hình đều có một số điểm chung trong các module và kiến trúc tổng nên khi code ta có thể gộp chung chúng vào một file model và viết các class module riêng để có thể tái sử dụng. Các module trong code được follow chính xác như các hình trong paper

import torch
import torch.nn as nn class BasicConv2d(nn.Module): def __init__(self, input_channels, output_channels, **kwargs): super().__init__() self.conv = nn.Conv2d(input_channels, output_channels, bias=False, **kwargs) self.bn = nn.BatchNorm2d(output_channels) self.relu = nn.ReLU(inplace=True) def forward(self, x): x = self.conv(x) x = self.bn(x) x = self.relu(x) return x class Inception_Stem(nn.Module): #"""Figure 3. The schema for stem of the pure Inception-v4 and #Inception-ResNet-v2 networks. This is the input part of those #networks.""" def __init__(self, input_channels): super().__init__() self.conv1 = nn.Sequential( BasicConv2d(input_channels, 32, kernel_size=3), BasicConv2d(32, 32, kernel_size=3, padding=1), BasicConv2d(32, 64, kernel_size=3, padding=1) ) self.branch3x3_conv = BasicConv2d(64, 96, kernel_size=3, padding=1) self.branch3x3_pool = nn.MaxPool2d(3, stride=1, padding=1) self.branch7x7a = nn.Sequential( BasicConv2d(160, 64, kernel_size=1), BasicConv2d(64, 64, kernel_size=(7, 1), padding=(3, 0)), BasicConv2d(64, 64, kernel_size=(1, 7), padding=(0, 3)), BasicConv2d(64, 96, kernel_size=3, padding=1) ) self.branch7x7b = nn.Sequential( BasicConv2d(160, 64, kernel_size=1), BasicConv2d(64, 96, kernel_size=3, padding=1) ) self.branchpoola = nn.MaxPool2d(kernel_size=3, stride=1, padding=1) self.branchpoolb = BasicConv2d(192, 192, kernel_size=3, stride=1, padding=1) def forward(self, x): x = self.conv1(x) x = [ self.branch3x3_conv(x), self.branch3x3_pool(x) ] x = torch.cat(x, 1) x = [ self.branch7x7a(x), self.branch7x7b(x) ] x = torch.cat(x, 1) x = [ self.branchpoola(x), self.branchpoolb(x) ] x = torch.cat(x, 1) return x class InceptionA(nn.Module): #"""Figure 4. The schema for 35 × 35 grid modules of the pure #Inception-v4 network. This is the Inception-A block of Figure 9.""" def __init__(self, input_channels): super().__init__() self.branch3x3stack = nn.Sequential( BasicConv2d(input_channels, 64, kernel_size=1), BasicConv2d(64, 96, kernel_size=3, padding=1), BasicConv2d(96, 96, kernel_size=3, padding=1) ) self.branch3x3 = nn.Sequential( BasicConv2d(input_channels, 64, kernel_size=1), BasicConv2d(64, 96, kernel_size=3, padding=1) ) self.branch1x1 = BasicConv2d(input_channels, 96, kernel_size=1) self.branchpool = nn.Sequential( nn.AvgPool2d(kernel_size=3, stride=1, padding=1), BasicConv2d(input_channels, 96, kernel_size=1) ) def forward(self, x): x = [ self.branch3x3stack(x), self.branch3x3(x), self.branch1x1(x), self.branchpool(x) ] return torch.cat(x, 1) class ReductionA(nn.Module): #"""Figure 7. The schema for 35 × 35 to 17 × 17 reduction module. #Different variants of this blocks (with various number of filters) #are used in Figure 9, and 15 in each of the new Inception(-v4, - ResNet-v1, #-ResNet-v2) variants presented in this paper. The k, l, m, n numbers #represent filter bank sizes which can be looked up in Table 1. def __init__(self, input_channels, k, l, m, n): super().__init__() self.branch3x3stack = nn.Sequential( BasicConv2d(input_channels, k, kernel_size=1), BasicConv2d(k, l, kernel_size=3, padding=1), BasicConv2d(l, m, kernel_size=3, stride=2) ) self.branch3x3 = BasicConv2d(input_channels, n, kernel_size=3, stride=2) self.branchpool = nn.MaxPool2d(kernel_size=3, stride=2) self.output_channels = input_channels + n + m def forward(self, x): x = [ self.branch3x3stack(x), self.branch3x3(x), self.branchpool(x) ] return torch.cat(x, 1) class InceptionB(nn.Module): #"""Figure 5. The schema for 17 × 17 grid modules of the pure Inception-v4 network. #This is the Inception-B block of Figure 9.""" def __init__(self, input_channels): super().__init__() self.branch7x7stack = nn.Sequential( BasicConv2d(input_channels, 192, kernel_size=1), BasicConv2d(192, 192, kernel_size=(1, 7), padding=(0, 3)), BasicConv2d(192, 224, kernel_size=(7, 1), padding=(3, 0)), BasicConv2d(224, 224, kernel_size=(1, 7), padding=(0, 3)), BasicConv2d(224, 256, kernel_size=(7, 1), padding=(3, 0)) ) self.branch7x7 = nn.Sequential( BasicConv2d(input_channels, 192, kernel_size=1), BasicConv2d(192, 224, kernel_size=(1, 7), padding=(0, 3)), BasicConv2d(224, 256, kernel_size=(7, 1), padding=(3, 0)) ) self.branch1x1 = BasicConv2d(input_channels, 384, kernel_size=1) self.branchpool = nn.Sequential( nn.AvgPool2d(3, stride=1, padding=1), BasicConv2d(input_channels, 128, kernel_size=1) ) def forward(self, x): x = [ self.branch1x1(x), self.branch7x7(x), self.branch7x7stack(x), self.branchpool(x) ] return torch.cat(x, 1) class ReductionB(nn.Module): #"""Figure 8. The schema for 17 × 17 to 8 × 8 grid-reduction mod- ule. #This is the reduction module used by the pure Inception-v4 network in #Figure 9.""" def __init__(self, input_channels): super().__init__() self.branch7x7 = nn.Sequential( BasicConv2d(input_channels, 256, kernel_size=1), BasicConv2d(256, 256, kernel_size=(1, 7), padding=(0, 3)), BasicConv2d(256, 320, kernel_size=(7, 1), padding=(3, 0)), BasicConv2d(320, 320, kernel_size=3, stride=2, padding=1) ) self.branch3x3 = nn.Sequential( BasicConv2d(input_channels, 192, kernel_size=1), BasicConv2d(192, 192, kernel_size=3, stride=2, padding=1) ) self.branchpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) def forward(self, x): x = [ self.branch3x3(x), self.branch7x7(x), self.branchpool(x) ] return torch.cat(x, 1) class InceptionC(nn.Module): def __init__(self, input_channels): #"""Figure 6. The schema for 8×8 grid modules of the pure #Inceptionv4 network. This is the Inception-C block of Figure 9.""" super().__init__() self.branch3x3stack = nn.Sequential( BasicConv2d(input_channels, 384, kernel_size=1), BasicConv2d(384, 448, kernel_size=(1, 3), padding=(0, 1)), BasicConv2d(448, 512, kernel_size=(3, 1), padding=(1, 0)), ) self.branch3x3stacka = BasicConv2d(512, 256, kernel_size=(1, 3), padding=(0, 1)) self.branch3x3stackb = BasicConv2d(512, 256, kernel_size=(3, 1), padding=(1, 0)) self.branch3x3 = BasicConv2d(input_channels, 384, kernel_size=1) self.branch3x3a = BasicConv2d(384, 256, kernel_size=(3, 1), padding=(1, 0)) self.branch3x3b = BasicConv2d(384, 256, kernel_size=(1, 3), padding=(0, 1)) self.branch1x1 = BasicConv2d(input_channels, 256, kernel_size=1) self.branchpool = nn.Sequential( nn.AvgPool2d(kernel_size=3, stride=1, padding=1), BasicConv2d(input_channels, 256, kernel_size=1) ) def forward(self, x): branch3x3stack_output = self.branch3x3stack(x) branch3x3stack_output = [ self.branch3x3stacka(branch3x3stack_output), self.branch3x3stackb(branch3x3stack_output) ] branch3x3stack_output = torch.cat(branch3x3stack_output, 1) branch3x3_output = self.branch3x3(x) branch3x3_output = [ self.branch3x3a(branch3x3_output), self.branch3x3b(branch3x3_output) ] branch3x3_output = torch.cat(branch3x3_output, 1) branch1x1_output = self.branch1x1(x) branchpool = self.branchpool(x) output = [ branch1x1_output, branch3x3_output, branch3x3stack_output, branchpool ] return torch.cat(output, 1) class InceptionV4(nn.Module): def __init__(self, A, B, C, k=192, l=224, m=256, n=384, class_nums=100): super().__init__() self.stem = Inception_Stem(3) self.inception_a = self._generate_inception_module(384, 384, A, InceptionA) self.reduction_a = ReductionA(384, k, l, m, n) output_channels = self.reduction_a.output_channels self.inception_b = self._generate_inception_module(output_channels, 1024, B, InceptionB) self.reduction_b = ReductionB(1024) self.inception_c = self._generate_inception_module(1536, 1536, C, InceptionC) self.avgpool = nn.AvgPool2d(7) #"""Dropout (keep 0.8)""" self.dropout = nn.Dropout2d(1 - 0.8) self.linear = nn.Linear(1536, class_nums) def forward(self, x): x = self.stem(x) x = self.inception_a(x) x = self.reduction_a(x) x = self.inception_b(x) x = self.reduction_b(x) x = self.inception_c(x) x = self.avgpool(x) x = self.dropout(x) x = x.view(-1, 1536) x = self.linear(x) return x @staticmethod def _generate_inception_module(input_channels, output_channels, block_num, block): layers = nn.Sequential() for l in range(block_num): layers.add_module("{}_{}".format(block.__name__, l), block(input_channels)) input_channels = output_channels return layers class InceptionResNetA(nn.Module): #"""Figure 16. The schema for 35 × 35 grid (Inception-ResNet-A) #module of the Inception-ResNet-v2 network.""" def __init__(self, input_channels): super().__init__() self.branch3x3stack = nn.Sequential( BasicConv2d(input_channels, 32, kernel_size=1), BasicConv2d(32, 48, kernel_size=3, padding=1), BasicConv2d(48, 64, kernel_size=3, padding=1) ) self.branch3x3 = nn.Sequential( BasicConv2d(input_channels, 32, kernel_size=1), BasicConv2d(32, 32, kernel_size=3, padding=1) ) self.branch1x1 = BasicConv2d(input_channels, 32, kernel_size=1) self.reduction1x1 = nn.Conv2d(128, 384, kernel_size=1) self.shortcut = nn.Conv2d(input_channels, 384, kernel_size=1) self.bn = nn.BatchNorm2d(384) self.relu = nn.ReLU(inplace=True) def forward(self, x): residual = [ self.branch1x1(x), self.branch3x3(x), self.branch3x3stack(x) ] residual = torch.cat(residual, 1) residual = self.reduction1x1(residual) shortcut = self.shortcut(x) output = self.bn(shortcut + residual) output = self.relu(output) return output class InceptionResNetB(nn.Module): #"""Figure 17. The schema for 17 × 17 grid (Inception-ResNet-B) module of #the Inception-ResNet-v2 network.""" def __init__(self, input_channels): super().__init__() self.branch7x7 = nn.Sequential( BasicConv2d(input_channels, 128, kernel_size=1), BasicConv2d(128, 160, kernel_size=(1, 7), padding=(0, 3)), BasicConv2d(160, 192, kernel_size=(7, 1), padding=(3, 0)) ) self.branch1x1 = BasicConv2d(input_channels, 192, kernel_size=1) self.reduction1x1 = nn.Conv2d(384, 1154, kernel_size=1) self.shortcut = nn.Conv2d(input_channels, 1154, kernel_size=1) self.bn = nn.BatchNorm2d(1154) self.relu = nn.ReLU(inplace=True) def forward(self, x): residual = [ self.branch1x1(x), self.branch7x7(x) ] residual = torch.cat(residual, 1) #"""In general we picked some scaling factors between 0.1 and 0.3 to scale the residuals #before their being added to the accumulated layer activations (cf. Figure 20).""" residual = self.reduction1x1(residual) * 0.1 shortcut = self.shortcut(x) output = self.bn(residual + shortcut) output = self.relu(output) return output class InceptionResNetC(nn.Module): def __init__(self, input_channels): #Figure 19. The schema for 8×8 grid (Inception-ResNet-C) #module of the Inception-ResNet-v2 network.""" super().__init__() self.branch3x3 = nn.Sequential( BasicConv2d(input_channels, 192, kernel_size=1), BasicConv2d(192, 224, kernel_size=(1, 3), padding=(0, 1)), BasicConv2d(224, 256, kernel_size=(3, 1), padding=(1, 0)) ) self.branch1x1 = BasicConv2d(input_channels, 192, kernel_size=1) self.reduction1x1 = nn.Conv2d(448, 2048, kernel_size=1) self.shorcut = nn.Conv2d(input_channels, 2048, kernel_size=1) self.bn = nn.BatchNorm2d(2048) self.relu = nn.ReLU(inplace=True) def forward(self, x): residual = [ self.branch1x1(x), self.branch3x3(x) ] residual = torch.cat(residual, 1) residual = self.reduction1x1(residual) * 0.1 shorcut = self.shorcut(x) output = self.bn(shorcut + residual) output = self.relu(output) return output class InceptionResNetReductionA(nn.Module): #"""Figure 7. The schema for 35 × 35 to 17 × 17 reduction module. #Different variants of this blocks (with various number of filters) #are used in Figure 9, and 15 in each of the new Inception(-v4, - ResNet-v1, #-ResNet-v2) variants presented in this paper. The k, l, m, n numbers #represent filter bank sizes which can be looked up in Table 1. def __init__(self, input_channels, k, l, m, n): super().__init__() self.branch3x3stack = nn.Sequential( BasicConv2d(input_channels, k, kernel_size=1), BasicConv2d(k, l, kernel_size=3, padding=1), BasicConv2d(l, m, kernel_size=3, stride=2) ) self.branch3x3 = BasicConv2d(input_channels, n, kernel_size=3, stride=2) self.branchpool = nn.MaxPool2d(kernel_size=3, stride=2) self.output_channels = input_channels + n + m def forward(self, x): x = [ self.branch3x3stack(x), self.branch3x3(x), self.branchpool(x) ] return torch.cat(x, 1) class InceptionResNetReductionB(nn.Module): #"""Figure 18. The schema for 17 × 17 to 8 × 8 grid-reduction module. #Reduction-B module used by the wider Inception-ResNet-v1 network in #Figure 15.""" #I believe it was a typo(Inception-ResNet-v1 should be Inception-ResNet-v2) def __init__(self, input_channels): super().__init__() self.branchpool = nn.MaxPool2d(3, stride=2) self.branch3x3a = nn.Sequential( BasicConv2d(input_channels, 256, kernel_size=1), BasicConv2d(256, 384, kernel_size=3, stride=2) ) self.branch3x3b = nn.Sequential( BasicConv2d(input_channels, 256, kernel_size=1), BasicConv2d(256, 288, kernel_size=3, stride=2) ) self.branch3x3stack = nn.Sequential( BasicConv2d(input_channels, 256, kernel_size=1), BasicConv2d(256, 288, kernel_size=3, padding=1), BasicConv2d(288, 320, kernel_size=3, stride=2) ) def forward(self, x): x = [ self.branch3x3a(x), self.branch3x3b(x), self.branch3x3stack(x), self.branchpool(x) ] x = torch.cat(x, 1) return x class InceptionResNetV2(nn.Module): def __init__(self, A, B, C, k=256, l=256, m=384, n=384, class_nums=100): super().__init__() self.stem = Inception_Stem(3) self.inception_resnet_a = self._generate_inception_module(384, 384, A, InceptionResNetA) self.reduction_a = InceptionResNetReductionA(384, k, l, m, n) output_channels = self.reduction_a.output_channels self.inception_resnet_b = self._generate_inception_module(output_channels, 1154, B, InceptionResNetB) self.reduction_b = InceptionResNetReductionB(1154) self.inception_resnet_c = self._generate_inception_module(2146, 2048, C, InceptionResNetC) #6x6 featuresize self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) #"""Dropout (keep 0.8)""" self.dropout = nn.Dropout2d(1 - 0.8) self.linear = nn.Linear(2048, class_nums) def forward(self, x): x = self.stem(x) x = self.inception_resnet_a(x) x = self.reduction_a(x) x = self.inception_resnet_b(x) x = self.reduction_b(x) x = self.inception_resnet_c(x) x = self.avgpool(x) x = self.dropout(x) x = x.view(-1, 2048) x = self.linear(x) return x @staticmethod def _generate_inception_module(input_channels, output_channels, block_num, block): layers = nn.Sequential() for l in range(block_num): layers.add_module("{}_{}".format(block.__name__, l), block(input_channels)) input_channels = output_channels return layers def inceptionv4(): return InceptionV4(4, 7, 3) def inception_resnet_v2(): return InceptionResNetV2(5, 10, 5)

Thực nghiệm

Hình dưới mô tả Top-5 error của cả 4 model. Nhận thấy rằng các model có sử dụng residual hội tụ nhanh hơn nhưng độ chính xác có vẻ vẫn phụ thuộc chính vào kích thước model.

image.png

Tương tự, hình dưới là Top-1 error của 4 model.

image.png

Cụ thể hơn, bảng dưới là kết quả chi tiết của thực nghiệm trên 4 model.

image.png

Tài liệu tham khảo

[1] Inception-v4, Inception-ResNet and the Impact of Residual Connections on Learning

[2] Review: Inception-v4 — Evolved From GoogLeNet, Merged with ResNet Idea (Image Classification)

Bình luận

Bài viết tương tự

- vừa được xem lúc

Tấn công và phòng thủ bậc nhất cực mạnh cho các mô hình học máy

tấn công bậc nhất cực mạnh = universal first-order adversary. Update: Bleeding edge của CleverHans đã lên từ 3.1.0 đến 4.

0 0 42

- vừa được xem lúc

[Deep Learning] Key Information Extraction from document using Graph Convolution Network - Bài toán trích rút thông tin từ hóa đơn với Graph Convolution Network

Các nội dung sẽ được đề cập trong bài blog lần này. . Tổng quan về GNN, GCN. Bài toán Key Information Extraction, trích rút thông tin trong văn bản từ ảnh.

0 0 219

- vừa được xem lúc

Trích xuất thông tin bảng biểu cực đơn giản với OpenCV

Trong thời điểm nhà nước đang thúc đẩy mạnh mẽ quá trình chuyển đổi số như hiện nay, Document Understanding nói chung cũng như Table Extraction nói riêng đang trở thành một trong những lĩnh vực được quan tâm phát triển và chú trọng hàng đầu. Vậy Table Extraction là gì? Document Understanding là cái

0 0 230

- vừa được xem lúc

Con đường AI của tôi

Gần đây, khá nhiều bạn nhắn tin hỏi mình những câu hỏi đại loại như: có nên học AI, bắt đầu học AI như nào, làm sao tự học cho đúng, cho nhanh, học không bị nản, lộ trình học AI như nào... Sau nhiều lần trả lời, mình nghĩ rằng nên viết hẳn một bài để trả lời chi tiết hơn, cũng như để các bạn sau này

0 0 157

- vừa được xem lúc

[B5'] Smooth Adversarial Training

Đây là một bài trong series Báo khoa học trong vòng 5 phút. Được viết bởi Xie et. al, John Hopkins University, trong khi đang intern tại Google. Hiện vẫn là preprint do bị reject tại ICLR 2021.

0 0 45

- vừa được xem lúc

Deep Learning với Java - Tại sao không?

Muốn tìm hiểu về Machine Learning / Deep Learning nhưng với background là Java thì sẽ như thế nào và bắt đầu từ đâu? Để tìm được câu trả lời, hãy đọc bài viết này - có thể kỹ năng Java vốn có sẽ giúp bạn có những chuyến phiêu lưu thú vị. DJL là tên viết tắt của Deep Java Library - một thư viện mã ng

0 0 139