(pytorch-深度学习)包含并行连结的网络(GoogLeNet)
包含并行連結(jié)的網(wǎng)絡(luò)(GoogLeNet)
在2014年的ImageNet圖像識(shí)別挑戰(zhàn)賽中,一個(gè)名叫GoogLeNet的網(wǎng)絡(luò)結(jié)構(gòu)大放異彩。它雖然在名字上向LeNet致敬,但在網(wǎng)絡(luò)結(jié)構(gòu)上已經(jīng)很難看到LeNet的影子。GoogLeNet吸收了NiN中網(wǎng)絡(luò)串聯(lián)網(wǎng)絡(luò)的思想,并在此基礎(chǔ)上做了很大改進(jìn)。在隨后的幾年里,研究人員對(duì)GoogLeNet進(jìn)行了數(shù)次改進(jìn)。
Inception塊
GoogLeNet中的基礎(chǔ)卷積塊叫作Inception塊,得名于同名電影《盜夢(mèng)空間》(Inception)。
Inception塊里有4條并行的線路。
- 前3條線路使用窗口大小分別是1×11\times 11×1、3×33\times 33×3和5×55\times 55×5的卷積層來抽取不同空間尺寸下的信息,其中中間2個(gè)線路會(huì)對(duì)輸入先做1×11\times 11×1卷積來減少輸入通道數(shù),以降低模型復(fù)雜度。
- 第四條線路則使用3×33\times 33×3最大池化層,后接1×11\times 11×1卷積層來改變通道數(shù)。
- 4條線路都使用了合適的填充來使輸入與輸出的高和寬一致。
- 最后將每條線路的輸出在通道維上連結(jié),并輸入接下來的層中去。
Inception塊中可以自定義的超參數(shù)是每個(gè)層的輸出通道數(shù),以此來控制模型復(fù)雜度
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為每條線路里的層的輸出通道數(shù)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) # 在通道維上連結(jié)輸出GoogLeNet模型
GoogLeNet跟VGG一樣,在主體卷積部分中使用5個(gè)模塊(block),每個(gè)模塊之間使用步幅為2的3×33\times 33×3最大池化層來減小輸出高寬。
這里我們假設(shè)輸入為96*96的圖片,來推算每一個(gè)模塊的輸入輸出尺寸
(1) 第一模塊使用一個(gè)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個(gè)卷積層:首先是64通道的1×11\times 11×1卷積層,然后是輸出通道為192的3×33\times 33×3卷積層。它對(duì)應(yīng)著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) 第三模塊串聯(lián)2個(gè)完整的Inception塊。
- 第一個(gè)Inception塊的輸出通道數(shù)為64+128+32+32=25664+128+32+32=25664+128+32+32=256,其中4條線路的輸出通道數(shù)比例為64:128:32:32=2:4:1:164:128:32:32=2:4:1:164:128:32:32=2:4:1:1。其中第二、第三條線路先分別將輸入通道數(shù)減小至96/192=1/296/192=1/296/192=1/2和16/192=1/1216/192=1/1216/192=1/12后,再接上第二層卷積層。
- 第二個(gè)Inception塊輸出通道數(shù)增至128+192+96+64=480128+192+96+64=480128+192+96+64=480,每條線路的輸出通道數(shù)之比為128:192:96:64=4:6:3:2128:192:96:64 = 4:6:3:2128:192:96:64=4:6:3:2。其中第二、第三條線路先分別將輸入通道數(shù)減小至128/256=1/2128/256=1/2128/256=1/2和32/256=1/832/256=1/832/256=1/8。
(4) 第四模塊更加復(fù)雜。
- 它串聯(lián)了5個(gè)Inception塊,其輸出通道數(shù)分別是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。
- 這些線路的通道數(shù)分配和第三模塊中的類似,首先含3×33\times 33×3卷積層的第二條線路輸出最多通道,其次是僅含1×11\times 11×1卷積層的第一條線路,之后是含5×55\times 55×5卷積層的第三條線路和含3×33\times 33×3最大池化層的第四條線路。其中第二、第三條線路都會(huì)先按比例減小通道數(shù)。這些比例在各個(gè)Inception塊中都略有不同。
(5) 第五模塊有輸出通道數(shù)為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的兩個(gè)Inception塊。
- 其中每條線路的通道數(shù)的分配思路和第三、第四模塊中的一致,只是在具體數(shù)值上有所不同。
- 需要注意的是,第五模塊的后面緊跟輸出層,該模塊同NiN一樣使用全局平均池化層來將每個(gè)通道的高和寬變成1。最后我們將輸出變成二維數(shù)組后接上一個(gè)輸出個(gè)數(shù)為標(biāo)簽類別數(shù)的全連接層。
GoogLeNet模型的計(jì)算復(fù)雜,而且不如VGG那樣便于修改通道數(shù)。本節(jié)里我們將輸入的高和寬從224降到96來簡(jiǎn)化計(jì)算。下面演示各個(gè)模塊之間的輸出的形狀變化。
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])獲取數(shù)據(jù)訓(xùn)練模型
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表示不用額外的進(jìn)程來加速讀取數(shù)據(jù)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 # 如出現(xiàn)“out of memory”的報(bào)錯(cuò)信息,可減小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)總結(jié)
以上是生活随笔為你收集整理的(pytorch-深度学习)包含并行连结的网络(GoogLeNet)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pytorch命令式和符号式混合编程
- 下一篇: 梳理百年深度学习发展史-七月在线机器学习