(pytorch-深度学习)包含并行连结的网络(GoogLeNet)
包含并行連結的網絡(GoogLeNet)
在2014年的ImageNet圖像識別挑戰賽中,一個名叫GoogLeNet的網絡結構大放異彩。它雖然在名字上向LeNet致敬,但在網絡結構上已經很難看到LeNet的影子。GoogLeNet吸收了NiN中網絡串聯網絡的思想,并在此基礎上做了很大改進。在隨后的幾年里,研究人員對GoogLeNet進行了數次改進。
Inception塊
GoogLeNet中的基礎卷積塊叫作Inception塊,得名于同名電影《盜夢空間》(Inception)。
Inception塊里有4條并行的線路。
- 前3條線路使用窗口大小分別是1×11\times 11×1、3×33\times 33×3和5×55\times 55×5的卷積層來抽取不同空間尺寸下的信息,其中中間2個線路會對輸入先做1×11\times 11×1卷積來減少輸入通道數,以降低模型復雜度。
- 第四條線路則使用3×33\times 33×3最大池化層,后接1×11\times 11×1卷積層來改變通道數。
- 4條線路都使用了合適的填充來使輸入與輸出的高和寬一致。
- 最后將每條線路的輸出在通道維上連結,并輸入接下來的層中去。
Inception塊中可以自定義的超參數是每個層的輸出通道數,以此來控制模型復雜度
import time import torch from torch import nn, optim import torch.nn.functional as Fdevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')class Inception(nn.Module):# c1 - c4為每條線路里的層的輸出通道數def __init__(self, in_c, c1, c2, c3, c4):super(Inception, self).__init__()# 線路1,單1 x 1卷積層self.p1_1 = nn.Conv2d(in_c, c1, kernel_size=1)# 線路2,1 x 1卷積層后接3 x 3卷積層self.p2_1 = nn.Conv2d(in_c, c2[0], kernel_size=1)self.p2_2 = nn.Conv2d(c2[0], c2[1], kernel_size=3, padding=1)# 線路3,1 x 1卷積層后接5 x 5卷積層self.p3_1 = nn.Conv2d(in_c, c3[0], kernel_size=1)self.p3_2 = nn.Conv2d(c3[0], c3[1], kernel_size=5, padding=2)# 線路4,3 x 3最大池化層后接1 x 1卷積層self.p4_1 = nn.MaxPool2d(kernel_size=3, stride=1, padding=1)self.p4_2 = nn.Conv2d(in_c, c4, kernel_size=1)def forward(self, x):p1 = F.relu(self.p1_1(x))p2 = F.relu(self.p2_2(F.relu(self.p2_1(x))))p3 = F.relu(self.p3_2(F.relu(self.p3_1(x))))p4 = F.relu(self.p4_2(self.p4_1(x)))return torch.cat((p1, p2, p3, p4), dim=1) # 在通道維上連結輸出GoogLeNet模型
GoogLeNet跟VGG一樣,在主體卷積部分中使用5個模塊(block),每個模塊之間使用步幅為2的3×33\times 33×3最大池化層來減小輸出高寬。
這里我們假設輸入為96*96的圖片,來推算每一個模塊的輸入輸出尺寸
(1) 第一模塊使用一個64通道的7×77\times 77×7卷積層。
b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),nn.ReLU(),nn.MaxPool2d(kernel_size=3, stride=2, padding=1))input=1?96?96卷積1:size=?(96+2+3?2?7)/2?=48∣∣output1=64?48?48池化2:size=?(48+2+1?2?3)/2?=24∣∣output2=64?24?24output=64?24?24input = 1*96*96 \\ 卷積1:size= \lfloor \left(96+2+3*2-7\right)/2 \rfloor=48||output1 = 64*48*48\\ 池化2:size= \lfloor \left(48+2+1*2-3\right)/2 \rfloor=24||output2=64*24*24\\ output = 64*24*24 input=1?96?96卷積1:size=?(96+2+3?2?7)/2?=48∣∣output1=64?48?48池化2:size=?(48+2+1?2?3)/2?=24∣∣output2=64?24?24output=64?24?24
(2) 第二模塊使用2個卷積層:首先是64通道的1×11\times 11×1卷積層,然后是輸出通道為192的3×33\times 33×3卷積層。它對應著Inception塊中的第二條線路。
input=64?24?24卷積1:size=?(24+1+0?2?1)/1?=24∣∣output1=64?24?24卷積2:size=?(24+1+1?2?3)/1?=24∣∣output1=192?24?24池化3:size=?(24+2+1?2?3)/2?=12∣∣output2=192?12?12output=192?12?12input = 64*24*24 \\ 卷積1:size= \lfloor \left(24+1+0*2-1\right)/1 \rfloor=24||output1 = 64*24*24\\ 卷積2:size= \lfloor \left(24+1+1*2-3\right)/1 \rfloor=24||output1 = 192*24*24\\ 池化3:size= \lfloor \left(24+2+1*2-3\right)/2 \rfloor=12||output2=192*12*12\\ output = 192*12*12 input=64?24?24卷積1:size=?(24+1+0?2?1)/1?=24∣∣output1=64?24?24卷積2:size=?(24+1+1?2?3)/1?=24∣∣output1=192?24?24池化3:size=?(24+2+1?2?3)/2?=12∣∣output2=192?12?12output=192?12?12
(3) 第三模塊串聯2個完整的Inception塊。
- 第一個Inception塊的輸出通道數為64+128+32+32=25664+128+32+32=25664+128+32+32=256,其中4條線路的輸出通道數比例為64:128:32:32=2:4:1:164:128:32:32=2:4:1:164:128:32:32=2:4:1:1。其中第二、第三條線路先分別將輸入通道數減小至96/192=1/296/192=1/296/192=1/2和16/192=1/1216/192=1/1216/192=1/12后,再接上第二層卷積層。
- 第二個Inception塊輸出通道數增至128+192+96+64=480128+192+96+64=480128+192+96+64=480,每條線路的輸出通道數之比為128:192:96:64=4:6:3:2128:192:96:64 = 4:6:3:2128:192:96:64=4:6:3:2。其中第二、第三條線路先分別將輸入通道數減小至128/256=1/2128/256=1/2128/256=1/2和32/256=1/832/256=1/832/256=1/8。
(4) 第四模塊更加復雜。
- 它串聯了5個Inception塊,其輸出通道數分別是192+208+48+64=512192+208+48+64=512192+208+48+64=512、160+224+64+64=512160+224+64+64=512160+224+64+64=512、128+256+64+64=512128+256+64+64=512128+256+64+64=512、112+288+64+64=528112+288+64+64=528112+288+64+64=528和256+320+128+128=832256+320+128+128=832256+320+128+128=832。
- 這些線路的通道數分配和第三模塊中的類似,首先含3×33\times 33×3卷積層的第二條線路輸出最多通道,其次是僅含1×11\times 11×1卷積層的第一條線路,之后是含5×55\times 55×5卷積層的第三條線路和含3×33\times 33×3最大池化層的第四條線路。其中第二、第三條線路都會先按比例減小通道數。這些比例在各個Inception塊中都略有不同。
(5) 第五模塊有輸出通道數為256+320+128+128=832256+320+128+128=832256+320+128+128=832和384+384+128+128=1024384+384+128+128=1024384+384+128+128=1024的兩個Inception塊。
- 其中每條線路的通道數的分配思路和第三、第四模塊中的一致,只是在具體數值上有所不同。
- 需要注意的是,第五模塊的后面緊跟輸出層,該模塊同NiN一樣使用全局平均池化層來將每個通道的高和寬變成1。最后我們將輸出變成二維數組后接上一個輸出個數為標簽類別數的全連接層。
GoogLeNet模型的計算復雜,而且不如VGG那樣便于修改通道數。本節里我們將輸入的高和寬從224降到96來簡化計算。下面演示各個模塊之間的輸出的形狀變化。
net = nn.Sequential(b1, b2, b3, b4, b5, d2l.FlattenLayer(), nn.Linear(1024, 10)) X = torch.rand(1, 1, 96, 96) for blk in net.children(): X = blk(X)print('output shape: ', X.shape)輸出:
output shape: torch.Size([1, 64, 24, 24]) output shape: torch.Size([1, 192, 12, 12]) output shape: torch.Size([1, 480, 6, 6]) output shape: torch.Size([1, 832, 3, 3]) output shape: torch.Size([1, 1024, 1, 1]) output shape: torch.Size([1, 1024]) output shape: torch.Size([1, 10])獲取數據訓練模型
def load_data_fashion_mnist(batch_size, resize=None, root='~/Datasets/FashionMNIST'):"""Download the fashion mnist dataset and then load into memory."""trans = []if resize:trans.append(torchvision.transforms.Resize(size=resize))trans.append(torchvision.transforms.ToTensor())transform = torchvision.transforms.Compose(trans)mnist_train = torchvision.datasets.FashionMNIST(root=root, train=True, download=True, transform=transform)mnist_test = torchvision.datasets.FashionMNIST(root=root, train=False, download=True, transform=transform)if sys.platform.startswith('win'):num_workers = 0 # 0表示不用額外的進程來加速讀取數據else:num_workers = 4train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)return train_iter, test_iterdef train(net, train_iter, test_iter, batch_size, optimizer, device, num_epochs):net = net.to(device)print("training on ", device)loss = torch.nn.CrossEntropyLoss()for epoch in range(num_epochs):train_l_sum, train_acc_sum, n, batch_count, start = 0.0, 0.0, 0, 0, time.time()for X, y in train_iter:X = X.to(device)y = y.to(device)y_hat = net(X)l = loss(y_hat, y)optimizer.zero_grad()l.backward()optimizer.step()train_l_sum += l.cpu().item()train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()n += y.shape[0]batch_count += 1test_acc = evaluate_accuracy(test_iter, net)print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f, time %.1f sec'% (epoch + 1, train_l_sum / batch_count, train_acc_sum / n, test_acc, time.time() - start)) batch_size = 128 # 如出現“out of memory”的報錯信息,可減小batch_size或resize train_iter, test_iter = load_data_fashion_mnist(batch_size, resize=96)lr, num_epochs = 0.001, 5 optimizer = torch.optim.Adam(net.parameters(), lr=lr) train(net, train_iter, test_iter, batch_size, optimizer, device, num_epochs)總結
以上是生活随笔為你收集整理的(pytorch-深度学习)包含并行连结的网络(GoogLeNet)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pytorch命令式和符号式混合编程
- 下一篇: 代价敏感多标签主动学习的代码开发跟踪