1. 程式人生 > >《Computer vision》筆記-GoodLeNet(3)

《Computer vision》筆記-GoodLeNet(3)

   作者: 石文華          

編輯: 龔   賽          

前  言

GoodLeNet在2014年舉辦的ILSVRC中獲得了分類任務第一名,與同年在分類任務中獲得亞軍的VGGNet模型相比,它的深度更深,達到22層。該網路引入Inception模組,Inception的目的是設計一種具有優良區域性拓撲結構的網路,即對輸入影象並行地執行多個卷積運算或池化操作,並將所有輸出結果拼接為一個非常深的特徵圖。因為 1*1、3*3 或 5*5 等不同的卷積運算與池化操作可以獲得輸入影象的不同資訊,並行處理這些運算並結合所有結果將獲得更好的影象表徵。

01

Inception單元結構

最初開始設計的Inception 模組(Native Inception)。它使用 3 個不同大小的濾波器(1x1、3x3、5x5)對輸入執行卷積操作,此外它還會執行最大池化。最後將所有子層的輸出合併起來,作為輸出的特徵圖。

假設輸入是28*28*192的特徵圖
(1)使用64個1*1卷積核,輸出結果會是28*28*64; 
(2)使用128個3*3的卷積核,為了使最後不同卷積核得到的特徵圖合併時維度匹配,padding為same,使得輸出維度還是28*28,那麼得到的特徵圖維度是28*28*128. 
(3)使用32個5*5的卷積核,padding為same,輸出特徵圖為28*28*32. 
(4)使用maxpool,在最大池化之前,設定padding為same。步長為1,然後再進行最大池化,使得輸出特徵圖維度為28*28*192,192個通道數太大,為了避免最後輸出時,池化層佔據大多數的通道。使用32個1*1的卷積壓縮通道數,最後得到的特徵圖維度是28*28*32。將上述輸出的特徵圖疊加在一起,那麼最後得到特度特徵圖大小為28*28*(64+128+32+32),如下圖所示:

640?wx_fmt=png

上述的結構存在一個非常大的問題,就是計算成本特別的大,還是以28*28*192的輸入特徵圖為例子,重點來看5*5的過濾器,由於輸出特徵圖有192個通道,所以一個過濾器的引數為5*5*192。由於期望得到的輸出特徵圖通道為32,長寬為28,所以需要計算28*28*32次。最後乘法運算的總次數為每個輸出值所需要執行的乘法運算次數 (5×5×192)乘以輸出值個數(28×28×32),把這些數相乘結果等於 1.2 億(120422400) 。運算成本是非常高的。解決這個問題的方法就是先使用1*1卷積,壓縮通道數,比如像將輸入特徵圖的通道數壓縮為16,然後在這個壓縮之後的特徵圖上使用32個5*5的卷積核進行卷積。

640?wx_fmt=png

計算量為(1*1*192*28*28*16)+(5*5*16*28*28*32)=2408448+10035200,約等於1204萬,相比之前的1.2億下降到了原來的十分之一。 
因此,Native Inception結構使用1*1的卷積之後變為(Inception V1):

640?wx_fmt=png

利用實現降維的 Inception 模組可以構建 GoogLeNet(Inception v1),其架構如下圖所示:

640?wx_fmt=png

可以發現,網路中間有兩個分支,它們是全連線層再加上softmax層,確保了即便是隱藏單元和中間層也參與了特徵計算,也能預測圖片的分類。它在Inception 網路中,起到一種調整的效果,並且能防止網路發生過擬合。假設這兩個分支的損失值分別是aux_loss_1 ,aux_loss_2,論文中將它們的權重值設定為0.3,從而得到訓練時的總損失計算:training.total_loss = real_loss + 0.3 * aux_loss_1 + 0.3 * aux_loss_2。

02

Inception V2

將 5×5 的卷積分解為兩個 3×3 的卷積運算以提升計算速度。因為一個 5×5 的卷積在計算成本上是一個 3×3 卷積的 2.78 倍。所以疊加兩個 3×3 卷積實際上在效能上會有所提升,將 n*n 的卷積核尺寸分解為 1×n 和 n×1 兩個卷積。例如,一個 3×3 的卷積等價於首先執行一個 1×3 的卷積再執行一個 3×1 的卷積。他們還發現這種方法在成本上要比單個 3×3 的卷積降低 33%。如下圖所示:

640?wx_fmt=png

03

Inception v3 

V3整合了前面v2 中提到的所有升級,還使用了RMSProp 優化器、Factorized 7x7 卷積、輔助分類器使用了 BatchNorm;標籤平滑(新增到損失公式的一種正則化項,旨在阻止網路對某一類別過分自信,即阻止過擬合) 。

注:Inception v4和Inception -resnet可以閱讀論文:https://arxiv.org/pdf/1602.07261.pdf 

程式碼:

1)載入資料

import torchfrom torchvision import datasets,transformsimport osimport matplotlib.pyplot as pltimport time# transform = transforms.Compose是把一系列圖片操作組合起來,比如減去畫素均值等。# DataLoader讀入的資料型別是PIL.Image# 這裡對圖片不做任何處理,僅僅是把PIL.Image轉換為torch.FloatTensor,從而可以被pytorch計算transform = transforms.Compose(
    [
        transforms.Scale([224,224]),
        transforms.ToTensor(),        #transforms.Normalize(mean=[0.5,0.5,0.5],std=[0.5,0.5,0.5])
    ]
)# 訓練集train_set = datasets.CIFAR10(root='drive/pytorch/inception/', train=True, transform=transform, target_transform=None, download=True)# 測試集test_set=datasets.CIFAR10(root='drive/pytorch/inception/',train=False,download=True,transform=transform)
trainloader=torch.utils.data.DataLoader(train_set,batch_size=32,shuffle=True,num_workers=0)
testloader=torch.utils.data.DataLoader(test_set,batch_size=32,shuffle=True,num_workers=0)
classes=('plane','car','bird','cat','deer','dog','frog','horse','ship','truck')
(data,label)=train_set[64]
print(classes[label])

直接使用Pytorch的models裡面預訓練好的模型,進行遷移學習,首先先下載模型,然後凍結所有的層,僅對後面全連線層引數進行調整以及後面的AuxLogits和Mixed_7c兩個模組的引數更新策略設定為parma.requires_grad=True,允許更新引數,調整之後再進行遷移學習,程式碼如下:

from torchvision import models
inceptionv3=models.inception_v3(pretrained=True)import torchimport torch.nn as nnfor parma in inceptionv3.parameters():
    parma.requires_grad = Falseinceptionv3.fc=nn.Linear(in_features=2048, out_features=10, bias=True)
inceptionv3.AuxLogits.fc=nn.Linear(in_features=768, out_features=10, bias=True)for parma in inceptionv3.AuxLogits.parameters():
    parma.requires_grad=Truefor parma in inceptionv3.Mixed_7c.parameters():
    parma.requires_grad=True

2)訓練模型

import torch.optim as optim          #匯入torch.potim模組import timefrom torch.autograd import Variable   # 這一步還沒有顯式用到variable,但是現在寫在這裡也沒問題,後面會用到import torch.nn as nnimport torch.nn.functional as F
optimizer=torch.optim.Adam(inceptionv3.parameters(),lr=0.0001)
epoch_n=5for epoch in range(epoch_n):
  print("Epoch{}/{}".format(epoch,epoch_n-1))
  print("-"*10)
  running_loss = 0.0  #定義一個變數方便我們對loss進行輸出
  running_corrects=0
  for i, data in enumerate(trainloader, 1): # 這裡我們遇到了第一步中出現的trailoader,程式碼傳入
    inputs, labels = data   # data是從enumerate返回的data,包含資料和標籤資訊,分別賦值給inputs和labels
    #inputs=inputs.permute(0, 2, 3, 1)
    #print("hahah",len(labels))
    y_pred = inceptionv3(inputs)                # 把資料輸進網路net,這個net()在第二步的程式碼最後一行我們已經定義了
    _,pred=torch.max(y_pred.data,1)
    optimizer.zero_grad()                # 要把梯度重新歸零,因為反向傳播過程中梯度會累加上一次迴圈的梯度
    loss = cost(y_pred, labels)    # 計算損失值,criterion我們在第三步裡面定義了
    loss.backward()                      # loss進行反向傳播,下文詳解
    optimizer.step()                     # 當執行反向傳播之後,把優化器的引數進行更新,以便進行下一輪
    # print statistics                   # 這幾行程式碼不是必須的,為了打印出loss方便我們看而已,不影響訓練過程
    running_loss += loss.item()       # 從下面一行程式碼可以看出它是每迴圈0-1999共兩千次才打印一次
    running_corrects+=torch.sum(pred==labels.data)    if(i % 2 == 0):    # print every 2000 mini-batches   所以每個2000次之類先用running_loss進行累加
      print("Batch{},Train Loss:{:.4f},Train ACC:{:.4f}".format(i,running_loss/i,100*running_corrects/(32*i)))

參考文獻: 
http://baijiahao.baidu.com/s?id=1601882944953788623&wfr=spider&for=pc 
Andrew Ng 《Deep Learning》 
https://blog.csdn.net/u014114990/article/details/52583912

640?wx_fmt=gif

END

往期回顧之作者石文華



機器學習演算法工程師

                            一個用心的公眾號

640?wx_fmt=jpeg

長按,識別,加關注

進群,學習,得幫助

你的關注,我們的熱度,

我們一定給你學習最大的幫助