卷積神經網路——第一部分:認識并搭建卷積神經網路
- 序言
- 網路實作
- 模型構建
- 類和函式的意義及性質
- nn.Conv2d
- F.max_pool2d
- F.relu
- F.log_softmax
- nn.Linear
- 模型總結
- 總結
序言
? ?本文基于北郵資料科學基礎課程的期中作業和相關課件知識,以Pytorch環境為基礎,實作兩層卷積神經網路,并針對各個引數進行推導和演算,本文目標:
- 鞏固知識 ,掌味訓器學習的相關基礎知識,主要包含課件上卷積層、激活層等內容;
- 強化流程,掌握常規機器學習流程,并對模型中的部分引數進行修改,了解引數的意義;
- 薪火相傳,幫助后來的學弟學妹們能更好地理解課程的知識,更好上手(但部分代碼會隱去,不會給你們抄代碼的機會)
? ?整個模型的實作包括:構建網路、訓練模型和模型評估三個部分,分別對應了網路結構、學習策略和評價指標三個部分的對應知識,本部分,將主要從網路的研究和實作開始,詳細闡述引數以及函式設定的意義,
? ?由于筆者確實是太忙太忙了,所以只能想到什么就先更新什么了~慢慢更新也是慢慢學習的程序(萬一以后真的去了資料科學相關的實驗室呢)
——2020年11月19日于北郵教三333
網路實作
模型構建
? ?現在先構建我們所需要的兩層卷積神經網路模型了,那么在構建之前,我們需要思考卷積神經網路一共有幾個函式,這里我們以課件上給出的模型為例,進行注意地研究其中函式和引數對應的意義,
class ConvNet(nn.Module):
# 課件上的代碼
def __init__(self):
super().__init__()
self.conv1 = torch.nn.Conv2d(1, 10, 5)
self.conv2 = torch.nn.Conv2d(10, 20, 3)
self.fc1 = torch.nn.Linear(20*10*10, 500)
self.fc2 = torch.nn.Linear(500, 10)
def forward(self, x):
in_size = x.size(0)
# 卷積層 -> Relu -> 最大池化
out = self.conv1(x)
out = F.relu(out)
out = F.max_pool2d(out, 2, 2)
# 卷積層 -> Relu
out = self.conv2(out)
out = F.relu(out)
# 多行變一行 -> 全連接層 -> relu -> 全連接層 -> sigmoid
out = out.view(in_size, -1)
# 全連接層 -> Relu
out = self.fc1(out)
out = F.relu(out)
out = self.fc2(out)
out = F.log_softmax(out, dim = 1)
return out
類和函式的意義及性質
nn.Conv2d
? ?Conv2d類完整地引數如下所示:
nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
? ?其對應有較多的引數,在本文中我們重點研究前三個引數,這些是限制我們的主要因素,我們會在下一個函式中重點討論引數修改的意義,而單獨看這里的代碼,幾個引數都屬于是根據需求自定義的部分,
| in_channels | 輸入通道數,也就是當前層的深度 |
|---|---|
| out_channels | 輸出通道數,也就是輸出的深度 |
| kernel_size | 卷積核尺寸 |
? ?Conv2d是一個針對2d的卷積神經網路生成的類,其作用是生成一個輸入通道與輸出通道對應的卷積神經網路,并通過卷積核(默認步長為1)對影像進行再輸出,其作用機理如下:

? ?從圖中來看,我們可以發現,每當影像進行了一次卷積之后,它的輸出都發生了對應的改變,如圖所示,其輸入為5x5,在尺寸為3x3的卷積核和1的步長作用下,最后得到的輸出影像特征的尺寸不再是5,而是5-3+1=3,所以經過了卷積層之后,其實他的輸出尺寸都會發生相應的變化,
F.max_pool2d
? ?F.max_pool2d類完整地引數如下所示:
F.max_pool2d_with_indices(input, kernel_size, stride)
| input | 輸入 |
|---|---|
| kernel_size | 最大池化的視窗大小 |
| stride | 步長 |
? ?F.max_pool2d是用于對特征矩陣進行池化濾波的一個類,除此之外,nn當中的MaxPool2d類也可以起到同樣的效果,其最大池化的原理示例如下:

? ?我們可以看到最大池化后會和卷積后一樣,使得特征矩陣大小發生變化,這里需要記住的是,其發生變化的大小和視窗大小有關,
F.relu
? ?F.relu就是一個非線性激勵函式,在這里選擇的就是relu作為非線性激活函式,它有很多好處和劣勢,但是用的比較廣,可以參考這篇文章進一步了解它的特性:relu的作用
? ?比較常有的問題還包括:為什么relu要在conv1之后?這個主要是由于細胞體的整體結構決定的,對于一個完整的神經網路來說,卷積和激活函式一起組成了這一層神經元,并且其可以極大增加泛化處理的能力,

F.log_softmax
? ?F.log_softmax一般是應用在網路的輸出層,做一個回歸的處理,其本質也是一個特殊的激活函式,
? ?這里需要理解一下為什么是回歸和應用,首先,我們在做分類問題的時候,其本質是一個概率問題:它大概率屬于哪一類,但是我們的卷積神經網路的輸入和輸出都只是一個或者多個具體的值,而不是一個概率,所以需要用一個激活函式將這個值與某一個概率p映射對應起來,
? ?我們的卷積神經網路用來解決分類問題時,就是先通過卷積層得到特征X,之后通過classifier得到WX+b,再通過Sigmoid、logSoftmax或者Softmax函式得到概率p,最后根據最大概率p得到類別,所以在這里,并不僅僅只有這個函式可以,也可以換做是其他的函式,但是總的來說,卷積神經網路的輸出層需要一個非線性函式將值與概率分布聯系起來,這樣才能完成分類的問題,
? ?如果你還是有些不太明白,那么你可以參考鏈接:神經網路輸出層為什么通常使用softmax?、為什么神經網路架構搜索darts采用了softmax實作由離散到連續的轉換?和多類分類下為什么用softmax而不是用其他歸一化方法?進一步了解一下,如果你選擇的優化器為交叉熵優化器,那可以看一下softmax loss詳解,softmax與交叉熵的關系進一步了解一下它們兩者之間的關系和使用時的注意事項,
nn.Linear
? ?Linear類完整地引數如下所示:
nn.Linear(in_features, out_features, bias=True)
| in_features | 輸入的大小 |
|---|---|
| out_features | 輸出的大小 |
? ?torch.nn.Linear是最麻煩的一個類,他的引數也是本文主要想要研究清楚的地方,它是用于設定全連接層的,但是全連接層的輸入與輸出都是二維張量,一般形狀為[batch_size, size],不同于卷積層要求輸入輸出是四維張量,那么為了達成這種平衡,就會用到view函式,將原來的資料降維成一個一位列向量,不然,在這里是無法正常使用的,
? ?由于現在已經是一維列向量了,所以輸入就需要是其中的某一個sample的大小,已經在這里為了方便大家更好地理解大小這一回事,我們需要再次認識一下卷積神經網路,
? ?首先,在這里的卷積神經網路不是一個二維網路,他是一個三維網路,包含了原始的二維資料以及各層中輸入的通道數(也即是深度),所以,我們往往會采用的 深度x高度x寬度 的形式來表示輸入的大小,那我們再來看代碼,我們會發現,這個里面的引數是 20x10x10,20很好理解,20是Conv2的輸出通道數,就是此時的深度,但是為什么是 10x10 來表示此時的 高度x寬度 呢?按照我們的理解,此時輸入的影像大小應該為 28x28 ,而不應該是 10x10 ,
? ?為了理解10的來歷,首先我們要再回顧基礎知識中卷積核的相關內容,

? ?從本圖來進行理解,我們會發現,每當影像經過一次處理,它的尺寸都會發生相應的變化,這個變化只與卷積核大小、步長還有影像本身大小有關,從這張圖來看,我們可以發現它的放縮滿足 L圖-L核+1 這樣的一個規律,
? ?也就是說,輸入到Linear時,影像的尺寸已經不是28x28了,此時從網路結構上來看,已經經過了兩次卷積和一次池化,池化的作用同理可見,所以10其實是計算后的結果,計算的方式為:
- 第一次卷積(卷積核為5):28-5+1=24
- 第一次池化(池化濾波器為2):24/2=12
- 第二次卷積(卷積核為3):12-3+1=10
? ?到這里也差不多就破案了,所以我們可以概括出來第一次Linear時,其輸入引數為 深度x高度x寬度 ,其中深度為最后一個卷積層輸出的通道數,高度和深度都是根據程序中經歷過的卷積和池化的次數計算得來的,
模型總結
? ?現在概括一下我們之前提到的這些函式以及步驟,我們可以用nn.Sequential函式對每一層進行重構,使得結果更加直觀:
class ConvNet(nn.Module):
def __init__(self):
super(ConvNet, self).__init__()
self.conv1 = nn.Sequential(
nn.Conv2d(1, 10, 5),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2)
)
self.conv2 = nn.Sequential(
nn.Conv2d(10, 20, 3),
nn.ReLU()
)
self.fc = nn.Sequential(
nn.Linear(20 * 10 * 10, 500),
nn.ReLU(inplace=True),
nn.Linear(500, 10)
)
def forward(self, x):
in_size = x.size(0)
out = self.conv1(x)
out = self.conv2(out)
out = out.view(in_size, -1)
out = self.fc(out)
out = F.log_softmax(out, dim = 1)
return out
? ?從這樣的一個新的網路結構來看,其實搭建一個卷積神經網路只需要滿足:確定層數、按照卷積、激活以及是否需要池化等方式定義每一層、層與層之間輸入輸出匹配這幾點,就能夠實作一個卷積神經網路的搭建了,
總結
? ?寫到這里已經寫了三四個小時了,因為也是對機器學習慢慢加深理解的程序,所以可能有些地方用詞不夠嚴謹,也希望如果有我做的不太好的地方可以留言一起研究,一起更改,希望能夠在期末考完之前更完所有~
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/225288.html
標籤:AI
