PyTorch框架学习十——基础网络层(卷积、转置卷积、池化、反池化、线性、激活函数)
PyTorch框架學(xué)習(xí)十——基礎(chǔ)網(wǎng)絡(luò)層(卷積、轉(zhuǎn)置卷積、池化、反池化、線性、激活函數(shù))
- 一、卷積層
- 二、轉(zhuǎn)置卷積層
- 三、池化層
- 1.最大池化nn.MaxPool2d
- 2.平均池化nn.AvgPool2d
- 四、反池化層
- 最大值反池化nn.MaxUnpool2d
- 五、線性層
- 六、激活函數(shù)層
- 1.nn.Sigmoid
- 2.nn.Tanh
- 3.nn.ReLU
- 4.nn.LeakyReLU、nn.PReLU、nn.RReLU
- (1)nn.LeakyReLU
- (2)nn.PReLU
- (3)nn.RReLU
上次筆記介紹了PyTorch中如何進(jìn)行網(wǎng)絡(luò)搭建,是從宏觀上來(lái)學(xué)習(xí),這次筆記介紹一個(gè)網(wǎng)絡(luò)內(nèi)部的具體網(wǎng)絡(luò)層,從微觀拆解。
一、卷積層
因?yàn)閷?duì)卷積運(yùn)算有基礎(chǔ),就不從0開(kāi)始寫了,下面概括地介紹相關(guān)概念后,重點(diǎn)放在卷積層在PyTorch的實(shí)現(xiàn)上。
卷積運(yùn)算:卷積核在輸入信號(hào)(圖像)上滑動(dòng),相應(yīng)位置上進(jìn)行乘加。網(wǎng)上有很多圖例,清晰地介紹了卷積運(yùn)算的過(guò)程,這里不贅述。
卷積核:又稱為濾波器、過(guò)濾器,可以認(rèn)為是某種模式或特征。
卷積維度:一般,卷積核在幾個(gè)維度上滑動(dòng),就是幾維卷積,如下圖所示:
上圖有個(gè)小問(wèn)題,沒(méi)有考慮通道數(shù),圖中所示的都是一個(gè)通道的情況,比如二維灰度圖像,通道數(shù)為1,如果是三通道的RGB二維圖像呢?那卷積核也會(huì)變成3通道,每個(gè)通道進(jìn)行乘加之后得到的結(jié)果再相加,才是最后的卷積結(jié)果。
PyTorch上的實(shí)現(xiàn):
torch.nn.Conv2d(in_channels: int, out_channels: int, kernel_size: Union[int, Tuple[int, int]], stride: Union[int, Tuple[int, int]] = 1, padding: Union[int, Tuple[int, int]] = 0, dilation: Union[int, Tuple[int, int]] = 1, groups: int = 1, bias: bool = True, padding_mode: str = 'zeros')參數(shù)如下圖所示:
有一個(gè)需要注意的地方:
就是說(shuō),kernel_size,stride,padding和dilation四個(gè)參數(shù),如果是一個(gè)值(int類),則對(duì)H和W兩個(gè)維度都是這個(gè)值;如果是兩個(gè)int組成的元組,則第一個(gè)數(shù)對(duì)應(yīng)H維度,第二個(gè)數(shù)對(duì)應(yīng)W維度,下面舉幾個(gè)例子:
這里kernel_size等于一個(gè)值3,即H維和W維的尺寸都是3,等價(jià)于(3, 3);stride等于一個(gè)值2,即滑動(dòng)時(shí)在H和W兩個(gè)維度上步長(zhǎng)都是2。
>>> m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2))而這個(gè)卷積層,kernel_size為(3, 5),即H維是3,W維是5,即代表大小為3×5的卷積核;步長(zhǎng)為(2, 1),即H維上滑動(dòng)步長(zhǎng)為2,W維上滑動(dòng)步長(zhǎng)為1;padding為(4, 2),即H維(上下各)填充4個(gè)像素,W維(左右各)填充2個(gè)像素。
還有一個(gè)需要注意的地方:輸入輸出圖像尺寸的關(guān)系,這個(gè)對(duì)于分析非常重要。
如果不考慮padding和dilation這兩個(gè)的影響,那么輸出圖像的尺寸等于:
如果全都考慮的話,就等于:
最后用一個(gè)例子來(lái)形象地體現(xiàn)卷積層的作用:
構(gòu)建了一個(gè)3×3×3的卷積核來(lái)對(duì)圖像lena.png進(jìn)行卷積,將卷積結(jié)果可視化如下:
左邊是原圖,右邊是卷積之后的結(jié)果,查看兩圖的尺寸:
卷積后的結(jié)果,其實(shí)就是某一種模式或者說(shuō)是特征圖,這是該卷積核卷積之后的結(jié)果,我們將卷積核內(nèi)的權(quán)重?fù)Q一下,就可以得到另一種不同的特征圖:
所以用多個(gè)卷積核就可以得到多個(gè)特征圖。
二、轉(zhuǎn)置卷積層
用于對(duì)圖像進(jìn)行上采樣Upsample,即可以將小尺寸的圖像轉(zhuǎn)置卷積為大尺寸。
因?yàn)榫矸e操作是通過(guò)矩陣相乘實(shí)現(xiàn)的,而轉(zhuǎn)置卷積之所以得名,就是它的卷積核的尺寸與相應(yīng)的卷積操作的卷積核的尺寸是一個(gè)轉(zhuǎn)置的關(guān)系,只是尺寸有轉(zhuǎn)置的關(guān)系,元素是不相同的,轉(zhuǎn)置卷積也無(wú)法恢復(fù)到原始圖像了。
PyTorch中轉(zhuǎn)置卷積的實(shí)現(xiàn):
torch.nn.ConvTranspose2d(in_channels: int, out_channels: int, kernel_size: Union[int, Tuple[int, int]], stride: Union[int, Tuple[int, int]] = 1, padding: Union[int, Tuple[int, int]] = 0, output_padding: Union[int, Tuple[int, int]] = 0, groups: int = 1, bias: bool = True, dilation: int = 1, padding_mode: str = 'zeros')參數(shù)如下所示:
它的輸入輸出尺寸的關(guān)系與卷積層正好相反:
最后來(lái)看一個(gè)轉(zhuǎn)置卷積的例子(輸入圖像還是卷積時(shí)用的lena.png):
可以得到尺寸更大的特征圖,在圖像分割時(shí)經(jīng)常用到,看一下兩圖的尺寸:
三、池化層
池化運(yùn)算:對(duì)信號(hào)進(jìn)行“收集”并“總結(jié)”,類似水池收集水資源,因而得名池化層。
“收集”:像素個(gè)數(shù)由多變少,“總結(jié)”:最大值池化/平均值池化。
具體池化操作不再贅述,給一張最大值池化和平均值池化的圖:
主要看一下PyTorch中池化層的實(shí)現(xiàn):
1.最大池化nn.MaxPool2d
功能:對(duì)二維圖像進(jìn)行最大值池化。
torch.nn.MaxPool2d(kernel_size: Union[int, Tuple[int, ...]], stride: Union[int, Tuple[int, ...], None] = None, padding: Union[int, Tuple[int, ...]] = 0, dilation: Union[int, Tuple[int, ...]] = 1, return_indices: bool = False, ceil_mode: bool = False)參數(shù)如下所示:
池化前后的圖像尺寸關(guān)系如下:
若不考慮padding、dilation,且默認(rèn)狀態(tài)下 kernel_size = stride 則簡(jiǎn)化版公式為:
最后看一個(gè)例子:
看起來(lái)好像差不多,但是很明顯池化后圖片質(zhì)量下降了一點(diǎn),畢竟池化是一個(gè)抽象特征的過(guò)程,看一下池化前后尺寸:
尺寸變成原來(lái)的一半。
2.平均池化nn.AvgPool2d
功能:對(duì)二維圖像進(jìn)行平均值池化。
torch.nn.AvgPool2d(kernel_size: Union[int, Tuple[int, int]], stride: Union[int, Tuple[int, int], None] = None, padding: Union[int, Tuple[int, int]] = 0, ceil_mode: bool = False, count_include_pad: bool = True, divisor_override: bool = None)參數(shù)如下所示:
平均池化前后圖像的尺寸計(jì)算公式如下:
若不考慮padding,默認(rèn)情況下kernel_size=stride,則簡(jiǎn)化計(jì)算公式與最大池化一樣:
看兩個(gè)例子:
和最大值池化類似,看起來(lái)差不多,但是質(zhì)量上下降了一點(diǎn);而且與最大值池化相比,平均池化后的圖偏暗一點(diǎn),因?yàn)樽畲笾党鼗〉孟袼囟际亲畲笾?#xff0c;而平均值池化是取得平均,像素值要普遍小于最大值,所以偏暗。
第二個(gè)例子著重關(guān)注除法因子:
img_tensor = torch.ones((1, 1, 4, 4)) avgpool_layer = nn.AvgPool2d((2, 2), stride=(2, 2), divisor_override=3) img_pool = avgpool_layer(img_tensor)print("raw_img:\n{}\npooling_img:\n{}".format(img_tensor, img_pool))輸出為:
raw_img: tensor([[[[1., 1., 1., 1.],[1., 1., 1., 1.],[1., 1., 1., 1.],[1., 1., 1., 1.]]]]) pooling_img: tensor([[[[1.3333, 1.3333],[1.3333, 1.3333]]]])如果不設(shè)置除法因子,應(yīng)該平均計(jì)算的時(shí)候是除以4,得到的結(jié)果全為1,而設(shè)置除法因子之后就不除以4而是除以除法因子3,所以得到4/3=1.3333。
四、反池化層
是池化的一個(gè)“逆”過(guò)程,但這個(gè)“逆”只是通過(guò)上采樣將尺寸恢復(fù)到原來(lái),像素值是不能恢復(fù)成原來(lái)一模一樣的,因?yàn)橄褡畲蟪鼗?#xff0c;除最大值之外的像素都已經(jīng)丟棄了。所以嚴(yán)格來(lái)說(shuō)池化是不可逆的,反池化只是“近似”逆過(guò)程。
最大值反池化nn.MaxUnpool2d
功能:對(duì)二維圖像進(jìn)行最大值池化上采樣。
torch.nn.MaxUnpool2d(kernel_size: Union[int, Tuple[int, int]], stride: Union[int, Tuple[int, int], None] = None, padding: Union[int, Tuple[int, int]] = 0)參數(shù)如下所示:
它是依據(jù)最大值池化層的輸出以及最大值的索引來(lái)恢復(fù)原圖像,非最大值的地方全都設(shè)置為0。
看一個(gè)例子:
輸出結(jié)果為:
raw_img: tensor([[[[0., 4., 4., 3.],[3., 3., 1., 1.],[4., 2., 3., 4.],[1., 3., 3., 0.]]]]) img_pool: tensor([[[[4., 4.],[4., 4.]]]]) img_pool: tensor([[[[4., 4.],[4., 4.]]]]) img_unpool: tensor([[[[0., 4., 4., 0.],[0., 0., 0., 0.],[4., 0., 0., 4.],[0., 0., 0., 0.]]]])當(dāng)然你可以用得到的索引去unpool其他的數(shù)據(jù),如下所示:
# pooling img_tensor = torch.randint(high=5, size=(1, 1, 4, 4), dtype=torch.float) maxpool_layer = nn.MaxPool2d((2, 2), stride=(2, 2), return_indices=True) img_pool, indices = maxpool_layer(img_tensor)# unpooling img_reconstruct = torch.randn_like(img_pool, dtype=torch.float) maxunpool_layer = nn.MaxUnpool2d((2, 2), stride=(2, 2)) img_unpool = maxunpool_layer(img_reconstruct, indices)print("raw_img:\n{}\nimg_pool:\n{}".format(img_tensor, img_pool)) print("img_reconstruct:\n{}\nimg_unpool:\n{}".format(img_reconstruct, img_unpool)) raw_img: tensor([[[[0., 4., 4., 3.],[3., 3., 1., 1.],[4., 2., 3., 4.],[1., 3., 3., 0.]]]]) img_pool: tensor([[[[4., 4.],[4., 4.]]]]) img_reconstruct: tensor([[[[-1.0276, -0.5631],[-0.8923, -0.0583]]]]) img_unpool: tensor([[[[ 0.0000, -1.0276, -0.5631, 0.0000],[ 0.0000, 0.0000, 0.0000, 0.0000],[-0.8923, 0.0000, 0.0000, -0.0583],[ 0.0000, 0.0000, 0.0000, 0.0000]]]])這個(gè)演示的目的就更接近真實(shí)情況,因?yàn)槌鼗笫遣粫?huì)立即反池化的,還會(huì)經(jīng)過(guò)一些其他的網(wǎng)絡(luò)層計(jì)算,從而原來(lái)的像素值已經(jīng)發(fā)生了變化,接下來(lái)的反池化就是要將已經(jīng)變化的數(shù)據(jù)上采樣到原來(lái)的尺寸,像素值肯定不再是原來(lái)的了。
平均池化的反池化PyTorch中沒(méi)有,估計(jì)是無(wú)法反池化。
五、線性層
線性層又稱為全連接層,其每個(gè)神經(jīng)元與上一層所有神經(jīng)元相連。實(shí)現(xiàn)對(duì)前一層的線性組合,線性變換。
PyTorch中線性層的實(shí)現(xiàn)是nn.Linear:
torch.nn.Linear(in_features: int, out_features: int, bias: bool = True)所做的操作:
參數(shù)如下所示:
下面看一個(gè)例子,構(gòu)建一個(gè)3×4的線性層:
inputs = torch.tensor([[1., 2, 3]]) linear_layer = nn.Linear(3, 4) linear_layer.weight.data = torch.tensor([[1., 1., 1.],[2., 2., 2.],[3., 3., 3.],[4., 4., 4.]]) linear_layer.bias.data.fill_(0.5) output = linear_layer(inputs) print(inputs, inputs.shape) print(linear_layer.weight.data, linear_layer.weight.data.shape) print(output, output.shape) tensor([[1., 2., 3.]]) torch.Size([1, 3]) tensor([[1., 1., 1.],[2., 2., 2.],[3., 3., 3.],[4., 4., 4.]]) torch.Size([4, 3]) tensor([[ 6.5000, 12.5000, 18.5000, 24.5000]], grad_fn=<AddmmBackward>) torch.Size([1, 4])六、激活函數(shù)層
激活函數(shù)對(duì)特征進(jìn)行非線性變換,賦予多層神經(jīng)網(wǎng)絡(luò)有深度的意義,如果沒(méi)有非線性變換,多少層網(wǎng)絡(luò)層都可以等效為一層。
下面介紹幾個(gè)常用的激活函數(shù):
1.nn.Sigmoid
計(jì)算公式:
該函數(shù)圖像及其導(dǎo)數(shù)如下所示:
特點(diǎn):
- 輸出在(0,1)之間,符合概率的意義;
- 導(dǎo)數(shù)范圍[0, 0.25],非常小,在不斷地導(dǎo)數(shù)迭代中容易導(dǎo)致梯度消失;
- 輸出的均值非零,破壞了數(shù)據(jù)分布。
注意維度:
用法如下:
2.nn.Tanh
計(jì)算公式:
該函數(shù)及其導(dǎo)數(shù)如下圖所示:
特點(diǎn):
- 輸出值在(-1, 1),輸出數(shù)據(jù)符合0均值。
- 導(dǎo)數(shù)范圍是(0,1),如今網(wǎng)絡(luò)層數(shù)很深,也容易導(dǎo)致梯度消失。
用法如下:
>>> m = nn.Tanh() >>> input = torch.randn(2) >>> output = m(input)3.nn.ReLU
計(jì)算公式:
該函數(shù)及其導(dǎo)數(shù)如下圖所示:
特點(diǎn):
- 輸出值均為正數(shù),負(fù)半軸導(dǎo)致死神經(jīng)元。
- 導(dǎo)數(shù)為1,緩解梯度消失,但容易引發(fā)梯度爆炸。
4.nn.LeakyReLU、nn.PReLU、nn.RReLU
針對(duì)上述ReLU負(fù)半軸為0導(dǎo)致死神經(jīng)元的問(wèn)題,提出了一些解決方案,下面介紹其中三種常用的:
(1)nn.LeakyReLU
給負(fù)半軸一個(gè)很小的固定斜率。
torch.nn.LeakyReLU(negative_slope: float = 0.01, inplace: bool = False)計(jì)算公式:
參數(shù):
(2)nn.PReLU
負(fù)半軸斜率不再人為固定,而是可學(xué)習(xí)的。
torch.nn.PReLU(num_parameters: int = 1, init: float = 0.25)計(jì)算公式:
參數(shù):
(3)nn.RReLU
斜率從均勻分布中隨機(jī)采樣。
torch.nn.RReLU(lower: float = 0.125, upper: float = 0.3333333333333333, inplace: bool = False)
參數(shù):
下圖展示了三種ReLU的圖像:
總結(jié)
以上是生活随笔為你收集整理的PyTorch框架学习十——基础网络层(卷积、转置卷积、池化、反池化、线性、激活函数)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Python数据类型-元组类型
- 下一篇: LeetCode题——最长无重复子串