人妖在线一区,国产日韩欧美一区二区综合在线,国产啪精品视频网站免费,欧美内射深插日本少妇

新聞動態(tài)

PyTorch零基礎(chǔ)入門之構(gòu)建模型基礎(chǔ)

發(fā)布日期:2021-12-23 10:29 | 文章來源:源碼之家

一、神經(jīng)網(wǎng)絡(luò)的構(gòu)造

  • PyTorch中神經(jīng)網(wǎng)絡(luò)構(gòu)造一般是基于 Module 類的模型來完成的,它讓模型構(gòu)造更加靈活。Module 類是 nn 模塊里提供的一個模型構(gòu)造類,是所有神經(jīng)網(wǎng)絡(luò)模塊的基類,我們可以繼承它來定義我們想要的模型。
  • 下面繼承 Module 類構(gòu)造多層感知機。這里定義的 MLP 類重載了 Module 類的 init 函數(shù)和 forward 函數(shù)。它們分別用于創(chuàng)建模型參數(shù)和定義前向計算。前向計算也即正向傳播。
# -*- coding: utf-8 -*-
"""
Created on Sat Oct 16 09:43:21 2021
@author: 86493
"""
import torch
from torch import nn
class MLP(nn.Module):
 # 聲明帶有模型參數(shù)的層,此處聲明了2個全連接層
 def __init__(self, **kwargs):
  # 調(diào)用MLP父類Block的構(gòu)造函數(shù)來進行必要的初始化
  # 這樣在構(gòu)造實例時還可以指定其他函數(shù)
  super(MLP, self).__init__(**kwargs)
  self.hidden = nn.Linear(784, 256)
  self.act = nn.ReLU()
  self.output = nn.Linear(256, 10)
  
 # 定義模型的前向計算
 # 即如何根據(jù)輸入x計算返回所需要的模型輸出
 def forward(self, x):
  o = self.act(self.hidden(x))
  return self.output(o)
 
X = torch.rand(2, 784)
net = MLP()
print(net)
print('-' * 60)
print(net(X))

結(jié)果為:

MLP(
(hidden): Linear(in_features=784, out_features=256, bias=True)
(act): ReLU()
(output): Linear(in_features=256, out_features=10, bias=True)
)
------------------------------------------------------------
tensor([[ 0.1836, 0.1946, 0.0924, -0.1163, -0.2914, -0.1103, -0.0839, -0.1274,
0.1618, -0.0601],
[ 0.0738, 0.2369, 0.0225, -0.1514, -0.3787, -0.0551, -0.0836, -0.0496,
0.1481, 0.0139]], grad_fn=<AddmmBackward>)

注意:
(1)上面的MLP類不需要定義反向傳播函數(shù),系統(tǒng)將通過自動求梯度而自動生成反向傳播所需的backward函數(shù)。

(2)將數(shù)據(jù)X傳入實例化MLP類后得到的net對象,會做一次前向計算,并且net(X)會調(diào)用MLP類繼承自父類Modulecall函數(shù)——該函數(shù)調(diào)用我們定義的子類MLPforward函數(shù)完成前向傳播計算。

(3)這里沒將Module類命名為Layer(層)或者Model(模型)等,是因為該類是一個可供自由組建的部件, 它的子類既可以是一個層(如繼承父類nn的子類線性層Linear),也可以是一個模型(如此處的子類MLP),也可以是模型的一部分。

二、神經(jīng)網(wǎng)絡(luò)中常見的層

有全連接層、卷積層、池化層與循環(huán)層等,下面學習使用Module定義層。

2.1 不含模型參數(shù)的層

下面構(gòu)造的 MyLayer 類通過繼承 Module 類自定義了一個將輸入減掉均值后輸出的層,并將層的計算定義在了 forward 函數(shù)里。這個層里不含模型參數(shù)。

# -*- coding: utf-8 -*-
"""
Created on Sat Oct 16 10:19:59 2021
@author: 86493
"""
import torch
from torch import nn
class MyLayer(nn.Module):
 def __init__(self, **kwargs):
  # 調(diào)用父類的方法
  super(MyLayer, self).__init__(**kwargs)
 def forward(self, x):
  return x - x.mean()
 
 
# 測試,實例化該層,做前向計算
layer = MyLayer()
layer1 = layer(torch.tensor([1, 2, 3, 4, 5],
 dtype = torch.float))
print(layer1)

結(jié)果為:

tensor([-2., -1., 0., 1., 2.])

2.2 含模型參數(shù)的層

可以自定義含模型參數(shù)的自定義層。其中的模型參數(shù)可以通過訓練學出。

Parameter 類其實是 Tensor 的子類,如果一個 TensorParameter ,那么它會自動被添加到模型的參數(shù)列表里。所以在自定義含模型參數(shù)的層時,我們應(yīng)該將參數(shù)定義成 Parameter ,除了直接定義成 Parameter 類外,還可以使用 ParameterListParameterDict 分別定義參數(shù)的列表和字典。

PS:下面出現(xiàn)torch.mm是將兩個矩陣相乘,如

# -*- coding: utf-8 -*-
"""
Created on Sat Oct 16 10:56:03 2021
@author: 86493
"""
import torch
a = torch.randn(2, 3)
b = torch.randn(3, 2)
print(torch.mm(a, b))
# 效果相同
print(torch.matmul(a, b))
#tensor([[1.8368, 0.4065],
#  [2.7972, 2.3096]])
#tensor([[1.8368, 0.4065],
#  [2.7972, 2.3096]])

(1)代碼栗子1

# -*- coding: utf-8 -*-
"""
Created on Sat Oct 16 10:33:04 2021
@author: 86493
"""
import torch
from torch import nn
class MyListDense(nn.Module):
 def __init__(self):
  super(MyListDense, self).__init__()
  # 3個randn的意思
  self.params = nn.ParameterList([nn.Parameter(torch.randn(4, 4)) for i in range(3)]) 
  self.params.append(nn.Parameter(torch.randn(4, 1)))
  
 def forward(self, x):
  for i in range(len(self.params)):
# mm是指矩陣相乘
x = torch.mm(x, self.params[i])
  return x
 
net = MyListDense()
print(net)

打印得:

MyListDense(
(params): ParameterList(
(0): Parameter containing: [torch.FloatTensor of size 4x4]
(1): Parameter containing: [torch.FloatTensor of size 4x4]
(2): Parameter containing: [torch.FloatTensor of size 4x4]
(3): Parameter containing: [torch.FloatTensor of size 4x1]
)
)

(2)代碼栗子2

這回用變量字典:

# -*- coding: utf-8 -*-
"""
Created on Sat Oct 16 11:03:29 2021
@author: 86493
"""
import torch
from torch import nn
class MyDictDense(nn.Module):
 def __init__(self):
  super(MyDictDense, self).__init__()
  self.params = nn.ParameterDict({
'linear1': nn.Parameter(torch.randn(4, 4)),
'linear2': nn.Parameter(torch.randn(4, 1))
})
  # 新增
  self.params.update({'linear3':
nn.Parameter(torch.randn(4, 2))})
  
 def forward(self, x, choice = 'linear1'):
  return torch.mm(x, self.params[choice])
 
net = MyDictDense()
print(net)  

打印得:

MyDictDense(
(params): ParameterDict(
(linear1): Parameter containing: [torch.FloatTensor of size 4x4]
(linear2): Parameter containing: [torch.FloatTensor of size 4x1]
(linear3): Parameter containing: [torch.FloatTensor of size 4x2]
)
)

2.3 二維卷積層

二維卷積層將輸入和卷積核做互相關(guān)運算,并加上一個標量偏差來得到輸出。卷積層的模型參數(shù)包括了卷積核和標量偏差。在訓練模型的時候,通常我們先對卷積核隨機初始化,然后不斷迭代卷積核和偏差。

卷積窗口形狀為 p × q p \times q p×q 的卷積層稱為 p × q p \times q p×q 卷積層。同樣, p × q p \times q p×q 卷積或 p × q p \times q p×q 卷積核說明卷積核的高和寬分別為 p p p 和 q q q。

(1)填充可以增加輸出的高和寬。這常用來使輸出與輸入具有相同的高和寬。
(2)步幅可以減小輸出的高和寬,例如輸出的高和寬僅為輸⼊入的高和寬的 ( 為大于1的整數(shù))。

# -*- coding: utf-8 -*-
"""
Created on Sat Oct 16 11:20:57 2021
@author: 86493
"""
import torch
from torch import nn
# 卷積運算(二維互相關(guān))
def corr2d(X, K):
 h, w = K.shape 
 X, K = X.float(), K.float()
 Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))
 for i in range(Y.shape[0]):
  for j in range(Y.shape[1]):
Y[i, j] = (x[i: i + h, j: j + w] * K).sum()
 return Y
 
# 二維卷積層
class Conv2D(nn.Module):
 def __init__(self, kernel_size):
  super(Conv2D, self).__init__()
  self.weight = nn.Parameter(torch.randn(kernel_size))
  self.bias = nn.Parameter(torch.randn(1))
 
 def forward(self, x):
  return corr2d(x, self.weight) + self.bias 
conv2d = nn.Conv2d(in_channels = 1, 
 out_channels = 1,
 kernel_size = 3,
 padding = 1)
print(conv2d)

得:

Conv2d(1, 1, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))

填充(padding)是指在輸入高和寬的兩側(cè)填充元素(通常是0元素)。
下個栗子:創(chuàng)建一個高和寬為3的二維卷積層,設(shè)輸入高和寬兩側(cè)的填充數(shù)分別為1。給定一高和寬都為8的input,輸出的高和寬會也是8。

# -*- coding: utf-8 -*-
"""
Created on Sat Oct 16 11:54:29 2021
@author: 86493
"""
import torch  
from torch import nn
# 定義一個函數(shù)計算卷積層
# 對輸入和輸出左對應(yīng)的升維和降維
def comp_conv2d(conv2d, X):
 # (1, 1)代表批量大小和通道數(shù)
 X = X.view((1, 1) + X.shape)
 Y = conv2d(X)
 # 排除不關(guān)心的前2維:批量和通道
 return Y.view(Y.shape[2:])
# 注意這里是兩側(cè)分別填充1行或列,所以在兩側(cè)共填充2行或列
conv2d = nn.Conv2d(in_channels = 1,
 out_channels = 1,
 kernel_size = 3,
 padding = 1)
X = torch.rand(8, 8)
endshape = comp_conv2d(conv2d, X).shape
print(endshape)
# 使用高為5,寬為3的卷積核,在高和寬兩側(cè)填充數(shù)為2和1
conv2d = nn.Conv2d(in_channels = 1,
 out_channels = 1,
 kernel_size = (5, 3),
 padding = (2, 1))
endshape2 = comp_conv2d(conv2d, X).shape
print(endshape2)

結(jié)果為:

torch.Size([8, 8])
torch.Size([8, 8])

stride

在二維互相關(guān)運算中,卷積窗口從輸入數(shù)組的最左上方開始,按從左往右、從上往下 的順序,依次在輸⼊數(shù)組上滑動。我們將每次滑動的行數(shù)和列數(shù)稱為步幅(stride)。

# 步幅stride
conv2d = nn.Conv2d(in_channels = 1,
 out_channels = 1,
 kernel_size = (3, 5),
 padding = (0, 1),
 stride = (3, 4))
endshape3 = comp_conv2d(conv2d, X).shape
print(endshape3)
# torch.Size([2, 2])

2.4 池化層

  • 池化層每次對輸入數(shù)據(jù)的一個固定形狀窗口(又稱池化窗口)中的元素計算輸出。不同于卷積層里計算輸入和核的互相關(guān)性,池化層直接計算池化窗口內(nèi)元素的最大值或者平均值。該運算也 分別叫做最大池化或平均池化。
  • 在二維最大池化中,池化窗口從輸入數(shù)組的最左上方開始,按從左往右、從上往下的順序,依次在輸入數(shù)組上滑動。當池化窗口滑動到某⼀位置時,窗口中的輸入子數(shù)組的最大值即輸出數(shù)組中相應(yīng)位置的元素。

下面把池化層的前向計算實現(xiàn)在pool2d函數(shù)里。

最大池化:

# -*- coding: utf-8 -*-
"""
Created on Sat Oct 16 18:49:27 2021
@author: 86493
"""
import torch
from torch import nn
def pool2d(x, pool_size, mode = 'max'):
 p_h, p_w = pool_size
 Y = torch.zeros((X.shape[0] - p_h + 1, X.shape[1] - p_w + 1))
 for i in range(Y.shape[0]):
  for j in range(Y.shape[1]):
if mode == 'max':
 Y[i, j] = X[i: i + p_h, j: j + p_w].max()
elif mode == 'avg':
 Y[i, j] = X[i: i + p_h, j: j + p_w].mean()
 return Y
X = torch.Tensor([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
end = pool2d(X, (2, 2)) # 默認是最大池化
# end = pool2d(X, (2, 2), mode = 'avg')
print(end)

tensor([[4., 5.],
[7., 8.]])

平均池化:

# -*- coding: utf-8 -*-
"""
Created on Sat Oct 16 18:49:27 2021
@author: 86493
"""
import torch
from torch import nn
def pool2d(x, pool_size, mode = 'max'):
 p_h, p_w = pool_size
 Y = torch.zeros((X.shape[0] - p_h + 1, X.shape[1] - p_w + 1))
 for i in range(Y.shape[0]):
  for j in range(Y.shape[1]):
if mode == 'max':
 Y[i, j] = X[i: i + p_h, j: j + p_w].max()
elif mode == 'avg':
 Y[i, j] = X[i: i + p_h, j: j + p_w].mean()
 return Y
X = torch.FloatTensor([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
# end = pool2d(X, (2, 2)) # 默認是最大池化
end = pool2d(X, (2, 2), mode = 'avg')
print(end)

結(jié)果如下,注意上面如果mode是avg模式(平均池化)時,不能寫X = torch.tensor([[0, 1, 2], [3, 4, 5], [6, 7, 8]]),否則會報錯Can only calculate the mean of floating types. Got Long instead.。把tensor改成TensorFloatTensor后就可以了(Tensor是FloatTensor的縮寫)。

tensor([[2., 3.],
[5., 6.]])

三、LeNet模型栗子

一個神經(jīng)網(wǎng)絡(luò)的典型訓練過程如下:
1 定義包含一些可學習參數(shù)(或者叫權(quán)重)的神經(jīng)網(wǎng)絡(luò)
2. 在輸入數(shù)據(jù)集上迭代
3. 通過網(wǎng)絡(luò)處理輸入
4. 計算 loss (輸出和正確答案的距離)
5. 將梯度反向傳播給網(wǎng)絡(luò)的參數(shù)
6. 更新網(wǎng)絡(luò)的權(quán)重,一般使用一個簡單的規(guī)則:weight = weight - learning_rate * gradient

# -*- coding: utf-8 -*-
"""
Created on Sat Oct 16 19:21:19 2021
@author: 86493
"""
import torch
import torch.nn as nn
import torch.nn.functional as F
class LeNet(nn.Module):
 # 需要把網(wǎng)絡(luò)中具有可學習參數(shù)的層放在構(gòu)造函數(shù)__init__
 def __init__(self):
  super(LeNet, self).__init__()
  # 輸入圖像channel:1;輸出channel:6
  # 5*5卷積核
  self.conv1 = nn.Conv2d(1, 6, 5)
  self.conv2 = nn.Conv2d(6, 16, 5)
  # an affine operation:y = Wx + b
  self.fc1 = nn.Linear(16 * 5 * 5, 120)
  self.fc2 = nn.Linear(120, 84)
  self.fc3 = nn.Linear(84, 10)
 
 def forward(self, x):
  # 2 * 2 最大池化
  x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
  # 如果是方陣,則可以只使用一個數(shù)字進行定義
  x = F.max_pool2d(F.relu(self.conv2(x)), 2)
  # 做一次flatten
  x = x.view(-1, self.num_flat_features(x))
  x = F.relu(self.fc1(x))
  x = F.relu(self.fc2(x))
  x = self.fc3(x)
  return x 
 def num_flat_features(self, x):
  # 除去批處理維度,得到其他所有維度
  size = x.size()[1:]
  num_features = 1
  # 將剛才得到的維度之間相乘起來
  for s in size:
num_features *= s
  return num_features
net = LeNet()
print(net) 
# 一個模型的可學習參數(shù)可以通過`net.parameters()`返回
params = list(net.parameters())
print("params的len:", len(params))
# print("params:\n", params)
print(params[0].size()) # conv1的權(quán)重
print('-' * 60)
# 隨機一個32×32的input
input = torch.randn(1, 1, 32, 32)
out = net(input)
print("網(wǎng)絡(luò)的output為:", out)
print('-' * 60)
# 隨機梯度的反向傳播
net.zero_grad() # 清零所有參數(shù)的梯度緩存
end = out.backward(torch.randn(1, 10))
print(end)  # None 

print的結(jié)果為:

LeNet(
(conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
(conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
(fc1): Linear(in_features=400, out_features=120, bias=True)
(fc2): Linear(in_features=120, out_features=84, bias=True)
(fc3): Linear(in_features=84, out_features=10, bias=True)
)
params的len: 10
torch.Size([6, 1, 5, 5])
------------------------------------------------------------
網(wǎng)絡(luò)的output為: tensor([[ 0.0904, 0.0866, 0.0851, -0.0176, 0.0198, 0.0530, 0.0815, 0.0284,
-0.0216, -0.0425]], grad_fn=<AddmmBackward>)
------------------------------------------------------------
None

三點提醒:

(1)只需要定義 forward 函數(shù),backward函數(shù)會在使用autograd時自動定義,backward函數(shù)用來計算導數(shù)。我們可以在 forward 函數(shù)中使用任何針對張量的操作和計算。
(2)在backward前最好net.zero_grad(),即清零所有參數(shù)的梯度緩存。
(3)torch.nn只支持小批量處理 (mini-batches)。整個 torch.nn 包只支持小批量樣本的輸入,不支持單個樣本的輸入。比如,nn.Conv2d 接受一個4維的張量,即nSamples x nChannels x Height x Width如果是一個單獨的樣本,只需要使用input.unsqueeze(0) 來添加一個“假的”批大小維度。

  • torch.Tensor:一個多維數(shù)組,支持諸如backward()等的自動求導操作,同時也保存了張量的梯度。
  • nn.Module:神經(jīng)網(wǎng)絡(luò)模塊。是一種方便封裝參數(shù)的方式,具有將參數(shù)移動到GPU、導出、加載等功能。
  • nn.Parameter:張量的一種,當它作為一個屬性分配給一個Module時,它會被自動注冊為一個參數(shù)。
  • autograd.Function:實現(xiàn)了自動求導前向和反向傳播的定義,每個Tensor至少創(chuàng)建一個Function節(jié)點,該節(jié)點連接到創(chuàng)建Tensor的函數(shù)并對其歷史進行編碼。

四、AlexNet模型栗子

# -*- coding: utf-8 -*-
"""
Created on Sat Oct 16 21:00:39 2021
@author: 86493
"""
import torch
from torch import nn
class AlexNet(nn.Module):
 def __init__(self):
  super(AlexNet, self).__init__()
  self.conv = nn.Sequential(
# in_channels,out_channels,kernel_size,stride,padding
nn.Conv2d(1, 96, 11, 4),
nn.ReLU(),
# kernel_size, stride
nn.MaxPool2d(3, 2),  
# 見笑卷積窗口,但使用padding=2來使輸入和輸出的高寬相同
# 且增大輸出通道數(shù)
nn.Conv2d(96, 256, 5, 1, 2),
nn.ReLU(),
nn.MaxPool2d(3, 2),
# 連續(xù)3個卷積層,且后面使用更小的卷積窗口
# 除了最后的卷積層外,進一步增大了輸出
  
# 注:前2個卷積層后不使用池化層來減少輸入的高和寬
nn.Conv2d(256, 384, 3, 1, 1),
nn.ReLU(),
nn.Conv2d(384, 383, 3, 1, 1),
nn.ReLU(),
nn.Conv2d(384, 256, 3, 1, 1),
nn.ReLU(),
nn.MaxPool2d(3, 2)
  )
 # 這里的全連接層的輸出個數(shù)比LeNet中的大數(shù)倍。
 # 使用丟棄層來緩解過擬合
  self.fc = nn.Sequential(
nn.Linear(256 *5 * 5, 4096),
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(4096, 4086),
nn.ReLU(),
nn.Dropout(0.5),
# 輸出層,下次會用到Fash-MNIST,所以此處類別設(shè)為10,
# 而非論文中的1000
nn.Linear(4096, 10),
  )
 
 
 def forward(self, img):
  feature = self.conv(img)
  output = self.fc(feature.view(img.shape[0], -1))
  return output
 
net = AlexNet()
print(net)

可以看到該網(wǎng)絡(luò)的結(jié)構(gòu):

AlexNet(
  (conv): Sequential(
 (0): Conv2d(1, 96, kernel_size=(11, 11), stride=(4, 4))
 (1): ReLU()
 (2): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
 (3): Conv2d(96, 256, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
 (4): ReLU()
 (5): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
 (6): Conv2d(256, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
 (7): ReLU()
 (8): Conv2d(384, 383, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
 (9): ReLU()
 (10): Conv2d(384, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
 (11): ReLU()
 (12): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (fc): Sequential(
 (0): Linear(in_features=6400, out_features=4096, bias=True)
 (1): ReLU()
 (2): Dropout(p=0.5, inplace=False)
 (3): Linear(in_features=4096, out_features=4086, bias=True)
 (4): ReLU()
 (5): Dropout(p=0.5, inplace=False)
 (6): Linear(in_features=4096, out_features=10, bias=True)
  )
)

Reference

(1)官方文檔:https://pytorch.org/docs/stable/_modules/torch/nn/modules/conv.html#Conv2d
(2)datawhale notebook

到此這篇關(guān)于PyTorch零基礎(chǔ)入門之構(gòu)建模型基礎(chǔ)的文章就介紹到這了,更多相關(guān)PyTorch 構(gòu)建模型基礎(chǔ)內(nèi)容請搜索本站以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持本站!

版權(quán)聲明:本站文章來源標注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請保持原文完整并注明來源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學習參考,不代表本站立場,如有內(nèi)容涉嫌侵權(quán),請聯(lián)系alex-e#qq.com處理。

相關(guān)文章

實時開通

自選配置、實時開通

免備案

全球線路精選!

全天候客戶服務(wù)

7x24全年不間斷在線

專屬顧問服務(wù)

1對1客戶咨詢顧問

在線
客服

在線客服:7*24小時在線

客服
熱線

400-630-3752
7*24小時客服服務(wù)熱線

關(guān)注
微信

關(guān)注官方微信
頂部