Pytorch神經(jīng)網(wǎng)絡(luò)參數(shù)管理方法詳細(xì)講解
這里記錄一下pytorch神經(jīng)網(wǎng)絡(luò)參數(shù)管理方法(參數(shù)訪問、參數(shù)初始化、參數(shù)綁定),方便自己和需要的朋友學(xué)習(xí)、查閱。
一、參數(shù)訪問
1.1 訪問指定層的指定參數(shù)
首先構(gòu)建一個(gè)多層感知機(jī)。
import torch from torch import nn net = nn.Sequential(nn.Linear(2, 4), nn.ReLU(), nn.Linear(4, 1)) X = torch.rand(size=(2, 2))
當(dāng)通過nn.Sequential定義模型時(shí), 我們可以通過索引來訪問模型的任意層。 這就像模型是一個(gè)列表一樣,每層的參數(shù)都在其屬性中。 如下所示,我們可以檢查任意一個(gè)全連接層的參數(shù)。
# 1 查看網(wǎng)絡(luò)第一層(即第一個(gè)全連接層)的參數(shù) print(net[0].state_dict()) # 2 查看網(wǎng)絡(luò)第三層(即第二個(gè)全連接層)偏置參數(shù)的類型 print(type(net[2].bias)) # 3 查看網(wǎng)絡(luò)第三層(即第二個(gè)全連接層)偏置參數(shù) print(net[2].bias) # 4 查看網(wǎng)絡(luò)第三層(即第二個(gè)全連接層)偏置參數(shù)的值 print(net[2].bias.data) # 5 查看網(wǎng)絡(luò)第一層(即第一個(gè)全連接層)權(quán)重參數(shù) print(net[0].weight) # 6 查看網(wǎng)絡(luò)第二層 print(net[1])
結(jié)果分別如下所示:
# 1 OrderedDict([('weight', tensor([[ 0.0854, 0.1861], [ 0.5421, 0.2435], [ 0.5745, 0.2469], [ 0.4120, -0.4345]])), ('bias', tensor([ 0.3356, 0.4215, 0.2181, -0.2548]))]) # 2 <class 'torch.nn.parameter.Parameter'> # 3 Parameter containing: tensor([-0.1606], requires_grad=True) # 4 tensor([-0.1606]) # 5 Parameter containing: tensor([[-0.4710, 0.0820], [-0.5563, 0.0728], [ 0.1691, 0.2211], [ 0.4279, -0.5597]], requires_grad=True) # 6 ReLU()
可以看出,每個(gè)參數(shù)都表示為參數(shù)類的一個(gè)實(shí)例,要對參數(shù)執(zhí)行任何操作,首先需要訪問底層的數(shù)值。網(wǎng)絡(luò)層數(shù)從0開始,即net[0]表示網(wǎng)絡(luò)第一層,激活函數(shù)也是網(wǎng)絡(luò)中的一層。
訪問偏置使用basis屬性,訪問權(quán)重使用weight屬性。參數(shù)是復(fù)合的對象,包含值、梯度和額外信息。若只想獲取參數(shù)的值,要在basis或weight后加data屬性。除了值之外,我們還可以訪問每個(gè)參數(shù)的梯度。
print(net[2].weight.grad == None) # 結(jié)果為 True # 原因:由于還沒有調(diào)用反向傳播,所以參數(shù)的梯度處于初始狀態(tài)
1.2 訪問某一層或整個(gè)網(wǎng)絡(luò)的所有參數(shù)
當(dāng)需要對所有參數(shù)執(zhí)行操作時(shí),逐個(gè)訪問它們可能會(huì)很麻煩,此時(shí)我們可以通過遞歸整個(gè)樹來提取每個(gè)子塊的參數(shù)。
# 1 訪問第一層的所有參數(shù) print(*[(name, param.shape) for name, param in net[0].named_parameters()]) # 2 訪問網(wǎng)絡(luò)所有層的全部參數(shù) print(*[(name, param.shape) for name, param in net.named_parameters()])
結(jié)果如下:
# 1
('weight', torch.Size([4, 2])) ('bias', torch.Size([4]))
# 2
('0.weight', torch.Size([4, 2])) ('0.bias', torch.Size([4])) ('2.weight', torch.Size([1, 4])) ('2.bias', torch.Size([1]))
注意:激活函數(shù)沒有參數(shù),所以打印出來的網(wǎng)絡(luò)的所有參數(shù)只有兩個(gè)全連接層的參數(shù)。
此外,我們還可以通過下述方式訪問網(wǎng)絡(luò)參數(shù)。
# 訪問第網(wǎng)絡(luò)第三層的偏置參數(shù)的值 print(net.state_dict()['2.bias'].data)
結(jié)果如下:
tensor([-0.1089])
如果不使用nn.Sequential定義模型,而是自己定義一個(gè)類實(shí)現(xiàn)網(wǎng)絡(luò),則不能使用索引訪問指定層的參數(shù)。如下所示:
class mlp(nn.Module): def __init__(self, input_size, output_size): super().__init__() self.linear1 = nn.Linear(input_size, 4) # 全連接層 self.relu = nn.ReLU() self.linear2 = nn.Linear(4, output_size) # 全連接層 def forward(self, x): x = self.linear1(x) return self.relu(self.linear2(x)) mlp_net= mlp(2, 1) X = torch.rand(size=(2, 2)) # 如果使用索引訪問會(huì)報(bào)錯(cuò) print(mlp_net[0].state_dict())
此時(shí)會(huì)輸出如下結(jié)果:
Traceback (most recent call last):
File "E:/SoftwareLearning/Python/Code/d2l-Train/ParameterManagement.py", line 46, in <module>
print(mlp_net[0].state_dict())
TypeError: 'mlp' object is not subscriptable
但可以通過下述方式進(jìn)行訪問:
# linear2為定義網(wǎng)絡(luò)模型時(shí)自己起的某一全連接層的名稱 print(mlp_net.state_dict()['linear2.bias'].data)
輸出結(jié)果如下:
tensor([0.3709])
1.3 訪問嵌套塊的指定參數(shù)
首先構(gòu)建一個(gè)嵌套塊的網(wǎng)絡(luò)。
def block1(): return nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 4), nn.ReLU()) def block2(): net = nn.Sequential() for i in range(4): # 在這里嵌套 net.add_module(f'block {i}', block1()) return net # 實(shí)例化 rgnet = nn.Sequential(block2(), nn.Linear(4, 1)) # 查看網(wǎng)絡(luò)結(jié)構(gòu) print(rgnet)
輸出結(jié)果如下:
Sequential(
(0): Sequential(
(block 0): Sequential(
(0): Linear(in_features=4, out_features=8, bias=True)
(1): ReLU()
(2): Linear(in_features=8, out_features=4, bias=True)
(3): ReLU()
)
(block 1): Sequential(
(0): Linear(in_features=4, out_features=8, bias=True)
(1): ReLU()
(2): Linear(in_features=8, out_features=4, bias=True)
(3): ReLU()
)
(block 2): Sequential(
(0): Linear(in_features=4, out_features=8, bias=True)
(1): ReLU()
(2): Linear(in_features=8, out_features=4, bias=True)
(3): ReLU()
)
(block 3): Sequential(
(0): Linear(in_features=4, out_features=8, bias=True)
(1): ReLU()
(2): Linear(in_features=8, out_features=4, bias=True)
(3): ReLU()
)
)
(1): Linear(in_features=4, out_features=1, bias=True)
)
因?yàn)槭欠謱忧短椎模晕覀円部梢韵裢ㄟ^嵌套列表索引一樣訪問它們。下面,我們訪問第一個(gè)主要的塊中第二個(gè)子塊的第一層的權(quán)重項(xiàng)。
print(rgnet[0][1][0].weight.data)
輸出結(jié)果如下:
tensor([[ 0.3175, 0.0233, 0.3233, -0.0627],
[-0.0835, -0.3371, -0.4527, 0.0141],
[ 0.1070, 0.3952, 0.4051, 0.3921],
[ 0.1958, -0.3643, 0.4481, -0.3448],
[ 0.0446, -0.0256, 0.1490, 0.4568],
[-0.1352, -0.2099, -0.1225, -0.0413],
[ 0.3027, 0.2114, -0.4063, -0.0288],
[-0.4594, 0.0076, -0.2671, 0.2669]])
二、參數(shù)初始化
初始化對神經(jīng)網(wǎng)絡(luò)來說十分重要,良好的初始化能幫助模型快速收斂,對保持網(wǎng)絡(luò)的數(shù)值穩(wěn)定性至關(guān)重要。默認(rèn)情況下,PyTorch會(huì)根據(jù)一個(gè)范圍均勻地初始化權(quán)重和偏置矩陣,這個(gè)范圍是根據(jù)輸入和輸出維度計(jì)算出的。PyTorch的nn.init
模塊提供了多種預(yù)置初始化方法。
2.1 內(nèi)置初始化
首先調(diào)用內(nèi)置的初始化器對網(wǎng)絡(luò)參數(shù)進(jìn)行初始化。下面的代碼將所有權(quán)重參數(shù)初始化為標(biāo)準(zhǔn)差為0.01的高斯隨機(jī)變量, 且將偏置參數(shù)設(shè)置為0。
# 將所有權(quán)重參數(shù)初始化為標(biāo)準(zhǔn)差為0.01的高斯隨機(jī)變量, 且將偏置參數(shù)設(shè)置為0 def init_normal(m): if type(m) == nn.Linear: # 將權(quán)重參數(shù)初始化為標(biāo)準(zhǔn)差為0.01的高斯隨機(jī)變量 nn.init.normal_(m.weight, mean=0, std=0.01) # 將偏置參數(shù)初始化為0 nn.init.zeros_(m.bias) net.apply(init_normal) print(net[0].weight.data[0], '\n', net[0].bias.data[0])
輸出結(jié)果如下:
tensor([-0.0142, -0.0054])
tensor(0.)
我們還可以將所有參數(shù)初始化為給定的常數(shù),比如初始化為1。
# 將所有參數(shù)初始化為給定的常數(shù) def init_constant(m): if type(m) == nn.Linear: nn.init.constant_(m.weight, 1) nn.init.zeros_(m.bias) net.apply(init_constant) print(net[0].weight.data[0], net[0].bias.data[0])
輸出結(jié)果如下:
tensor([1., 1.])
tensor(0.)
我們還可以對不同層應(yīng)用不同的初始化方法。 例如,下面我們使用Xavier初始化方法初始化第一個(gè)神經(jīng)網(wǎng)絡(luò)層, 然后將第三個(gè)神經(jīng)網(wǎng)絡(luò)層初始化為常量值42。
def xavier(m): if type(m) == nn.Linear: nn.init.xavier_uniform_(m.weight) def init_42(m): if type(m) == nn.Linear: nn.init.constant_(m.weight, 42) # 使用Xavier初始化方法初始化第一個(gè)神經(jīng)網(wǎng)絡(luò)層, 然后將第三個(gè)神經(jīng)網(wǎng)絡(luò)層初始化為常量值42 net[0].apply(xavier) net[2].apply(init_42) print(net[0].weight.data[0]) print(net[2].weight.data)
輸出結(jié)果如下:
tensor([ 0.9265, -0.1521])
tensor([[42., 42., 42., 42.]])
2.2 自定義初始化
有時(shí),深度學(xué)習(xí)框架沒有提供我們需要的初始化方法,此時(shí)就需要我們自定義初始化方法實(shí)現(xiàn)參數(shù)初始化。
# 自定義初始化 def my_init(m): if type(m) == nn.Linear: print("Init", *[(name, param.shape) for name, param in m.named_parameters()][0]) nn.init.uniform_(m.weight, -10, 10) # 均勻分布 # 如果絕對值大于5則參數(shù)不變,如果絕對值小于5則將參數(shù)置0 m.weight.data *= m.weight.data.abs() >= 5 net.apply(my_init) print(net[0].weight[:2])
輸出結(jié)果如下:
Init weight torch.Size([4, 2])
Init weight torch.Size([1, 4])
tensor([[ 6.6987, -5.3545],
[ 6.6684, -6.3039]], grad_fn=<SliceBackward0>)
也可以根據(jù)需要直接對指定層的參數(shù)進(jìn)行設(shè)置。
net[0].weight.data[:] += 1
net[0].weight.data[0, 0] = 42
print(net[0].weight.data[0])
輸出結(jié)果如下:
Init weight torch.Size([4, 2])
Init weight torch.Size([1, 4])
tensor([42.0000, 8.5777])
三、參數(shù)綁定
有時(shí)我們希望在多個(gè)層間共享參數(shù),此時(shí),我們可以定義一個(gè)全連接層,然后使用它的參數(shù)來設(shè)置另一個(gè)層的參數(shù),實(shí)現(xiàn)參數(shù)共享。
shared = nn.Linear(8, 8) net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), shared, nn.ReLU(), shared, nn.ReLU(), nn.Linear(8, 1)) print(net(X)) # 檢查參數(shù)是否相同 print(net[2].weight.data[0] == net[4].weight.data[0]) net[2].weight.data[0, 0] = 100 # 確保它們實(shí)際上是同一個(gè)對象,而不只是有相同的值 print(net[2].weight.data[0] == net[4].weight.data[0])
輸出結(jié)果如下:
tensor([[0.4362],
[0.4562]], grad_fn=<AddmmBackward0>)
tensor([True, True, True, True])
tensor([True, True, True, True])
這個(gè)例子表明第三個(gè)和第五個(gè)神經(jīng)網(wǎng)絡(luò)層的參數(shù)是綁定的。它們不僅值相等,而且由相同的張量表示。因此,如果我們改變其中一個(gè)參數(shù),另一個(gè)參數(shù)也會(huì)改變。你可能會(huì)思考:當(dāng)參數(shù)綁定時(shí),梯度會(huì)發(fā)生什么情況? 答案是由于模型參數(shù)包含梯度,因此在反向傳播期間第二個(gè)隱藏層 (即第三個(gè)神經(jīng)網(wǎng)絡(luò)層)和第三個(gè)隱藏層(即第五個(gè)神經(jīng)網(wǎng)絡(luò)層)的梯度會(huì)加在一起。
四、全部測試代碼
全部測試代碼如下。
import torch from torch import nn class mlp(nn.Module): def __init__(self, input_size, output_size): super().__init__() self.linear1 = nn.Linear(input_size, 4) # 全連接層 self.relu = nn.ReLU() self.linear2 = nn.Linear(4, output_size) # 全連接層 def forward(self, x): x = self.linear1(x) return self.relu(self.linear2(x)) def block1(): return nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 4), nn.ReLU()) def block2(): net = nn.Sequential() for i in range(4): # 在這里嵌套 net.add_module(f'block {i}', block1()) return net net = nn.Sequential(nn.Linear(2, 4), nn.ReLU(), nn.Linear(4, 1)) X = torch.rand(size=(2, 2)) print(net(X)) # 當(dāng)通過Sequential類定義模型時(shí),可以通過索引來訪問模型的任意層。這就像模型是一個(gè)列表一樣,每層的參數(shù)都在其屬性中。 print(net[0].state_dict()) print(type(net[2].bias)) print(net[2].bias) print(net[2].bias.data) print(net[0].weight) print(net[1]) print(net[2].weight.grad == None) # 一次性訪問所有參數(shù) print(*[(name, param.shape) for name, param in net[0].named_parameters()]) print(*[(name, param.shape) for name, param in net.named_parameters()]) # 訪問網(wǎng)絡(luò)參數(shù)的另一種方式 print(net.state_dict()['2.bias'].data) # 不使用nn.Sequential定義模型 mlp_net = mlp(2, 1) X = torch.rand(size=(2, 2)) print(mlp_net(X)) # 不能使用索引訪問某一層的參數(shù),會(huì)報(bào)錯(cuò) # print(mlp_net[0].state_dict()) print(mlp_net.state_dict()['linear2.bias'].data) # 從嵌套塊收集參數(shù) rgnet = nn.Sequential(block2(), nn.Linear(4, 1)) print(rgnet) # 訪問第一個(gè)主要的塊中、第二個(gè)子塊的第一層的權(quán)重 print(rgnet[0][1][0].weight.data) # 參數(shù)初始化 # 將所有權(quán)重參數(shù)初始化為標(biāo)準(zhǔn)差為0.01的高斯隨機(jī)變量, 且將偏置參數(shù)設(shè)置為0 def init_normal(m): if type(m) == nn.Linear: nn.init.normal_(m.weight, mean=0, std=0.01) nn.init.zeros_(m.bias) # 將所有參數(shù)初始化為給定的常數(shù) def init_constant(m): if type(m) == nn.Linear: nn.init.constant_(m.weight, 1) nn.init.zeros_(m.bias) net.apply(init_normal) print(net[0].weight.data[0], '\n', net[0].bias.data[0]) net.apply(init_constant) print(net[0].weight.data[0], '\n', net[0].bias.data[0]) def xavier(m): if type(m) == nn.Linear: nn.init.xavier_uniform_(m.weight) def init_42(m): if type(m) == nn.Linear: nn.init.constant_(m.weight, 42) # 使用Xavier初始化方法初始化第一個(gè)神經(jīng)網(wǎng)絡(luò)層, 然后將第三個(gè)神經(jīng)網(wǎng)絡(luò)層初始化為常量值42 net[0].apply(xavier) net[2].apply(init_42) print(net[0].weight.data[0]) print(net[2].weight.data) # 自定義初始化 def my_init(m): if type(m) == nn.Linear: print("Init", *[(name, param.shape) for name, param in m.named_parameters()][0]) nn.init.uniform_(m.weight, -10, 10) m.weight.data *= m.weight.data.abs() >= 5 net.apply(my_init) print(net[0].weight[:2]) # 也可以直接設(shè)置參數(shù) net[0].weight.data[:] += 1 net[0].weight.data[0, 0] = 42 print(net[0].weight.data[0]) # 參數(shù)綁定 # 有時(shí)我們希望在多個(gè)層間共享參數(shù):我們可以定義一個(gè)稠密層,然后使用它的參數(shù)來設(shè)置另一個(gè)層的參數(shù) # 我們需要給共享層一個(gè)名稱,以便可以引用它的參數(shù) shared = nn.Linear(4, 4) net = nn.Sequential(nn.Linear(2, 4), nn.ReLU(), shared, nn.ReLU(), shared, nn.ReLU(), nn.Linear(4, 1)) print(net(X)) # 檢查參數(shù)是否相同 print(net[2].weight.data[0] == net[4].weight.data[0]) net[2].weight.data[0, 0] = 100 # 確保它們實(shí)際上是同一個(gè)對象,而不只是有相同的值 print(net[2].weight.data[0] == net[4].weight.data[0])
到此這篇關(guān)于Pytorch神經(jīng)網(wǎng)絡(luò)參數(shù)管理方法詳細(xì)講解的文章就介紹到這了,更多相關(guān)Pytorch神經(jīng)網(wǎng)絡(luò)參數(shù)管理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- PyTorch中的神經(jīng)網(wǎng)絡(luò) Mnist 分類任務(wù)
- 使用Pytorch構(gòu)建第一個(gè)神經(jīng)網(wǎng)絡(luò)模型?附案例實(shí)戰(zhàn)
- pytorch簡單實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)功能
- pytorch深度神經(jīng)網(wǎng)絡(luò)入門準(zhǔn)備自己的圖片數(shù)據(jù)
- Pytorch卷積神經(jīng)網(wǎng)絡(luò)遷移學(xué)習(xí)的目標(biāo)及好處
- Pytorch深度學(xué)習(xí)經(jīng)典卷積神經(jīng)網(wǎng)絡(luò)resnet模塊訓(xùn)練
- Pytorch卷積神經(jīng)網(wǎng)絡(luò)resent網(wǎng)絡(luò)實(shí)踐
- PyTorch實(shí)現(xiàn)卷積神經(jīng)網(wǎng)絡(luò)的搭建詳解
相關(guān)文章
基于Opencv圖像識別實(shí)現(xiàn)答題卡識別示例詳解
這篇文章主要為大家詳細(xì)介紹了基于OpenCV如何實(shí)現(xiàn)答題卡識別,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12Python實(shí)現(xiàn)合并同一個(gè)文件夾下所有PDF文件的方法示例
這篇文章主要介紹了Python實(shí)現(xiàn)合并同一個(gè)文件夾下所有PDF文件的方法,涉及Python針對pdf文件的讀取、判斷、解密、寫入合并等相關(guān)操作技巧,需要的朋友可以參考下2018-04-04基于SQLAlchemy實(shí)現(xiàn)操作MySQL并執(zhí)行原生sql語句
這篇文章主要介紹了基于SQLAlchemy實(shí)現(xiàn)操作MySQL并執(zhí)行原生sql語句,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06Python 實(shí)現(xiàn)自動(dòng)化Excel報(bào)表的步驟
這篇文章主要介紹了Python 實(shí)現(xiàn)自動(dòng)化Excel報(bào)表的步驟,幫助大家更好的理解和學(xué)習(xí)使用python,感興趣的朋友可以了解下2021-04-04使用Python操作Excel中的各項(xiàng)頁面設(shè)置功能
在使用Excel進(jìn)行數(shù)據(jù)分析或報(bào)告制作時(shí),頁面設(shè)置是確保最終輸出效果專業(yè)、美觀的關(guān)鍵步驟,合理的頁面設(shè)置不僅能夠優(yōu)化打印效果,還能提升數(shù)據(jù)的可讀性,本文將詳細(xì)介紹如何使用Python操作Excel中的各項(xiàng)頁面設(shè)置功能,需要的朋友可以參考下2024-08-08使用Python字符串訪問與修改局部變量的實(shí)現(xiàn)代碼
這篇文章主要介紹了使用Python字符串訪問與修改局部變量,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06