- 計劃將所有的經典模型使用pytorch在搞一遍
- 接上一篇NIN現實mnist手寫識別
- 使用經典模型GoogLeNet模型實現相同的功能
- GoogLeNet論文原文地址
GoogLeNet簡介
GoogLeNet是2014年提出的一種全新的深度學習結構,在這之前的AlexNet、VGG、NIN等結構都通過增大網絡深度(層數)來獲得更好的訓練效果,但層數的增加會帶來很多負作用,比如overfit、梯度消失、梯度爆炸等。inception從另一種角度來提升訓練結果:在相同的計算量下能提取到更多的特徵,從而提升性能
- GoogLeNet 由Inception基礎塊組成
- Inception塊相當於一個有4條子線路的小網絡,它通過不同的filter卷積層和最大池化層來並行抽取信息,並使用1X1卷積層減少通道數從而降低模型複雜度.
- 可以自定義的超參數是每個層的輸出通道數沒我們以此來控制模型的複雜度.
- inception 經歷了V1、V2、V3、V4 多個版本發展,不斷調整完善
- inception V1 Block structure:
- 前面也介紹過1X1卷積核的作用:
- 爲了減少維度,消除計算瓶頸
- 增加深度而不會降低性能
- 還用於修正線性激活函數,
- 還可以減少很多的可訓練參數
GoogLeNet模型
- 完整模型結構
- GoogLeNet採用了可以重複堆疊使用的Inception Block 該做法,與前面介紹的VGG類似
- GoogLeNet採用GAP代替全連接層,與前面介紹的NIN模塊的想法也類似
- 汲取前面模型的優點
- GoogLeNet網絡結構細節表圖:
- 輸入原始圖片尺寸224X224X3
- 上表中“#3x3 reduce”,“#5x5 reduce”表示在3x3,5x5卷積操作之前使用了1x1卷積的數量
- 其他的都可以看懂,就不解釋了
- 下面還是老節目,上代碼:
import sys
import torch
import torch.nn as nn
import torch.optim as optim
import time
import torchvision
import torchvision.transforms as transforms
from torchviz import make_dot
import matplotlib.pyplot as plt
import torch.nn.functional as F
device = torch.device(“cuda” if torch.cuda.is_available() else “cpu”)
class GlobalAvgPool2d(nn.Module):
#全局平均池化層可通過將池化窗口形狀設置成輸入的高和寬實現
def init(self):
super(GlobalAvgPool2d, self).init()
def forward(self, x):
return F.avg_pool2d(x, kernel_size=x.size()[2:])
class FlattenLayer(torch.nn.Module):
def init(self):
super(FlattenLayer, self).init()
def forward(self, x): # x shape: (batch, *, *, …)
return x.view(x.shape[0], -1)
#計算準確率
def evaluate_accuracy(data_iter,net,device = torch.device(“cpu”)):
#創建 正確率 和 總個數
acc_sum ,n = torch.tensor([0],dtype=torch.float32,device=device),0
for X,y in data_iter:
# 適配 設備
X,y = X.to(device),y.to(device)
# 設置 驗證模式
net.eval()
with torch.no_grad(): #隔離開 不要計算在計算圖內
y = y.long()#在這裏將y轉成long確實是不必要的。但是在計算交叉熵時,Pytorch強制要求y是long
acc_sum += torch.sum((torch.argmax(net(X),dim=1) == y)) # 累計預測正確的個數
n += y.shape[0] # 累計總的標籤個數
return acc_sum.item() / n
#下載數據 組裝好訓練數據 測試數據
def load_data_fashion_mnist(batch_size,resize = None,root = “./dataset/input/FashionMNIST2065”):
trans = []
if resize:
# 做數據增強 處理 將圖片轉化爲 規定大小 數據內容不會丟失 等比例 處理
trans.append(torchvision.transforms.Resize(size=resize))
#將 圖片 類型 轉化爲Tensor類型
trans.append(torchvision.transforms.ToTensor())
#將圖片 增強方式 添加到Compose 類中處理
transform = torchvision.transforms.Compose(trans)
#讀取訓練數據
mnist_train = torchvision.datasets.FashionMNIST(root=root,train=True,download=False,transform = transform)
#讀取 測試數據
mnist_test = torchvision.datasets.FashionMNIST(root = root,train=False,download=False,transform = transform)
#數據加載器 在訓練 測試階段 使用多線程按批採樣數據 默認不使用多線程 num_worker 表示設置的線程數量
train_iter = torch.utils.data.DataLoader(mnist_train,batch_size = batch_size,shuffle = True,num_workers = 2)
test_iter = torch.utils.data.DataLoader(mnist_test,batch_size = batch_size,shuffle = False,num_workers = 2)
return train_iter,test_iter
batch_size = 16
#如出現“out of memory”的報錯信息,可減小batch_size或resize
train_iter,test_iter = load_data_fashion_mnist(batch_size,224)
def train_fit(net,train_iter,test_iter,batch_size,optimizer,device,num_epochs):
#將讀取的數據 拷貝到 指定的GPU上
net = net.to(device)
print("tainning 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)
# 優化函數 梯度置爲零
# 1、因爲梯度可以累加
# 2、每批採樣的梯度不同,只需記錄本次樣本的梯度
optimizer.zero_grad()
# 反向求導
l.backward()
# 更新權重參數
optimizer.step()
train_l_sum += l.cpu().item()
#train_acc_sum += (torch.argmax(y_hat,dim = 1) == y).cpu().item()
#將張量元素值累計
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()
n += y.shape[0]
batch_count += 1
test_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))
#創建Inception block
class Inception(nn.Module):
def init(self,in_c,c1,c2,c3,c4):
super(Inception,self).init()
self.p1_1 = nn.Conv2d(in_c,c1,kernel_size=1)
self.p2_1 = nn.Conv2d(in_c,c1,kernel_size=1)
self.p2_2 = nn.Conv2d(c2[0],c2[1],kernel_size=3,padding=1)
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)
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(F.relu(self.p4_1(x))))
return torch.cat((p1,p2,p3,p4),dim=1)
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)
)
b2 = nn.Sequential(
nn.Conv2d(64,64,kernel_size=1),
nn.Conv2d(64,192,kernel_size=3,padding=1),
nn.MaxPool2d(kernel_size=3,stride=2,padding=1)
)
b3 = nn.Sequential(
Inception(192,64,(96,128),(16,32),32),
Inception(256,128,(128,192),(32,96),64),
nn.MaxPool2d(kernel_size=3,stride=2,padding=1)
)
b4 = nn.Sequential(
Inception(192,64,(96,128),(16,32),32),
Inception(512,160,(112,224),(24,64),64),
Inception(512,128,(128,256),(24,64),64),
Inception(512,112,(144,288),(32,64),64),
Inception(528,256,(160,320),(32,128),128),
nn.MaxPool2d(kernel_size=3,stride=2,padding=1)
)
b5 = nn.Sequential(
Inception(832,256,(160,320),(32,128),128),
Inception(832,384,(192,384),(48,128),128),
GlobalAvgPool2d()
)
net = nn.Sequential(b1,b2,b3,b4,b5,
FlattenLayer(),
nn.Linear(1024,10)
)
print(“net:\n”,net)
X = torch.rand(1,1,96,96)
for blk in net.children():
X = blk(X)
print(“out shape”,X.shape)
lr,num_epochs = 0.001,5
optimizer = torch.optim.Adam(net.parameters(),lr= lr)
train_fit(net,train_iter,test_iter,batch_size,optimizer,device=device,num_epochs = num_epochs)
上訓練圖: