pytorch模型的定義、修改、讀取、斷點續(xù)訓(xùn)深入解析
在機器學(xué)習(xí)和深度學(xué)習(xí)領(lǐng)域,PyTorch是一種廣泛使用的開源深度學(xué)習(xí)框架。它提供了豐富的工具和函數(shù),方便用戶定義、訓(xùn)練和部署各種深度學(xué)習(xí)模型。本篇博客將詳細(xì)介紹PyTorch中模型定義的方式,并結(jié)合原理和代碼示例進行講解,旨在幫助讀者深入理解PyTorch的模型定義過程。
前言
模型定義的基本原理
在PyTorch中,模型定義是通過定義一個繼承自torch.nn.Module類的Python類來實現(xiàn)的。torch.nn.Module是PyTorch中模型定義的基礎(chǔ),它提供了一組豐富的工具和函數(shù),用于定義和操作神經(jīng)網(wǎng)絡(luò)模型。
模型定義的基本原理如下:
創(chuàng)建一個繼承自torch.nn.Module的子類,這個子類將成為我們定義的模型。
在子類的構(gòu)造函數(shù)中,首先調(diào)用super().__init__()來初始化父類torch.nn.Module,然后在構(gòu)造函數(shù)中定義模型的各個層和模塊。
在子類中實現(xiàn)forward方法,該方法定義了模型的前向傳播過程,即定義了輸入數(shù)據(jù)如何經(jīng)過各個層進行計算得到輸出。
可選地,在子類中實現(xiàn)__str__方法,用于打印模型的結(jié)構(gòu)信息。
接下來,我們將通過一個簡單的神經(jīng)網(wǎng)絡(luò)模型的定義和代碼示例來進一步解釋以上原理。
模型參數(shù)和層的概念
在深入了解模型定義之前,讓我們先來了解一些基本概念:模型參數(shù)和層。
模型參數(shù)
模型參數(shù)是模型內(nèi)部可學(xué)習(xí)的參數(shù),它們會在訓(xùn)練過程中自動更新以優(yōu)化模型的性能。常見的模型參數(shù)包括權(quán)重(weights)和偏置(biases)。權(quán)重是連接不同神經(jīng)元的連接強度,而偏置是每個神經(jīng)元的激活閾值。
層
在PyTorch中,層是模型中的構(gòu)建塊,它們接受輸入數(shù)據(jù)并將其轉(zhuǎn)換為輸出數(shù)據(jù)。層通常包含一些可學(xué)習(xí)的參數(shù),例如全連接層中的權(quán)重和偏置。常見的層類型包括全連接層、卷積層、池化層等。
pytorch的模型定義
pytorch有3種模型定義方式,三種方式都是基于nn.Module建立的,Module是所有網(wǎng)絡(luò)的基礎(chǔ)。
- Sequential
- ModuleList
- ModuleDict
1) Sequential
該方法與tf2很相似,使用也很簡單
以數(shù)字作為層的名稱
import torch
import torch.nn as nn
model = nn.Sequential(
nn.Linear(56,512),
nn.ReLU(),
nn.Linear(512,8)
)
print(model)對層的名稱進行自定義
import collections
import torch
import torch.nn as nn
model = nn.Sequential(collections.OrderedDict([
('layer_1',nn.Linear(56,512)),
('layer_2',nn.ReLU()),
('layer_3',nn.Linear(512,8)),
]))2) ModuleList
ModuleList接收一個子模塊的列表作為輸入(一個列表中不同的module,并把參數(shù)注冊到網(wǎng)絡(luò)中),可以使用append,extend進行操作。
class ModuleListExample(nn.module):
def __init__(self):
super().__init__()
self.model = nn.ModuleList([nn.Linear(56,512),nn.ReLU(),nn.Linear(512,8)])
def forward(self,x):
for layers in self.model:
x=f(x)
return x
- nn.Sequential內(nèi)部實現(xiàn)了forward函數(shù),因此可以不用寫forward函數(shù),而nn.ModuleList則沒有實現(xiàn)內(nèi)部forward函數(shù),需要人工實現(xiàn)。
- nn.Sequential初始調(diào)用時就決定了里面Module的調(diào)用順序,因此需要保證上一個Module的輸出大小能符合下一個Module輸入大小的要求。nn.ModuleList只是簡單的將Module打包并注冊到網(wǎng)絡(luò)中,需要在人工實現(xiàn)的forward里面去決定調(diào)用順序。
3)ModuleDict
該方法與ModuleList類似,只是能加入網(wǎng)絡(luò)層的名稱
class ModuleDictExample(nn.module):
def __init__(self):
super().__init__()
self.choices = nn.ModuleDict([
'conv': nn.Conv2d(10,10,3),
'pool': nn.MaxPool2d(3)
])
self.activations = nn.ModuleDict([
['lrelu',nn.LeakyReLU()],
['prelu',nn.PReLU()]
])
def forward(self,x,choice,act):
x = self.choices[choice](x)
x = self.activations[act](x)
return x模型的修改
1)修改模型
這里以resnet50作為修改模型,修改全連接層的輸出大小
import torch.nn as nn
import torchvision.models as models
from collections import OrderedDict
resnet = models.resnet50()
classifier_ten = nn.Sequential(OrderedDict([
('layer_1',nn.Linear(56,512)),
('layer_2',nn.ReLU()),
('layer_3',nn.Linear(512,8)),
]))
net.fc = classifier_ten2)添加額外輸入
將原模型添加輸入位置前的部分作為一個整體,同時在forward中定義好原模型不變的部分,添加輸入和后續(xù)層之間的鏈接關(guān)系;
class myModel(nn.Module):
def __init__(self,net):
super().__init__()
self.net = net
self.relu = nn.Relu()
self.dropout = nn.Dropout(0.5)
def forward(self,x,add_variable):
x = self.net(x)
x = torch.cat((self.drop(self.relu(x)),add_variable.unsqueeze(1)))
x = self.fc_add(x)
x = self.output(x)
return x
net = models.resnet50()
print('添加額外輸入之前:\n', net)
model = Model(net)
print('添加額外輸入之后:\n', model)3)額外輸出
import torch.nn as nn
import torch
import torchvision.models as models
from collections import OrderedDict
# 添加額外輸出
class Model(nn.Module):
def __init__(self, net):
super(Model, self).__init__()
self.net = net
self.relu = nn.ReLU()
self.dropout = nn.Dropout(0.5)
self.fc1 = nn.Linear(1000, 10, bias=True)
self.output = nn.Softmax(dim=1)
def forward(self, x):
x1000 = self.net(x)
x10 = self.dropout(self.relu(x1000))
x10 = self.fc1(x10)
x10 = self.output(x10)
# 輸出倒數(shù)第二層x1000,和最后一層x10
return x10, x1000
net = models.resnet50()
print('添加額外輸出之前:\n', net)
model = Model(net)
print('添加額外輸出之后:\n', model)模型的保存與讀取
保存的格式:pkl,pt,pth
多卡模型存儲:torch.nn.DataParallel(model).cuda()
1)模型結(jié)構(gòu)與模型參數(shù)的保存
保存
save_dir = './models/resnet50.pkl' model = models.resnet50(pretrained=True) torch.save(model, save_dir)
加載
loaded_model = toch.load(save_dir) loaded_model.eval()
2)只保存權(quán)重
保存
save_dir = './models/resnet50_state_dict.pkl' torch.save(model.state_dict(),save_dir)
model.state_dict()返回的事一個orderdict,存儲了網(wǎng)絡(luò)結(jié)構(gòu)的名字和對應(yīng)的參數(shù)
加載
loaded_dict = torch.load(save_dir) #加載參數(shù) loaded_model = models.resnet50() #加載模型 loaded_model.state_dict = loaded_dict #還原模型中的變量值 loaded_model.eval()
loaded_model.state_dict = loaded_dict
可以使用下面的代碼進行替換
loaded_model.load_statie_dict(loaded_dict,strict=True)
load_state_dict()中還有一個關(guān)鍵的參數(shù)strict, 當(dāng)strict=True,要求預(yù)訓(xùn)練權(quán)重層數(shù)的鍵值與新構(gòu)建的模型中的權(quán)重層數(shù)名稱完全吻合;如果新構(gòu)建的模型在層數(shù)上進行了部分微調(diào),則上述代碼就會報錯:key對應(yīng)不上, 此時,采用strict=False就能夠解決這個問題
斷點續(xù)訓(xùn)
模型訓(xùn)練過程中的保存
checkpoint ={
'net':model.state_dict(),
'optimzer':optimizer.state_dict(),
'epoch':epoch
}
torch.save(checkpoint, './models/checkpoint/ckpt_weight_12.pth')斷點回復(fù)
1)首先加載最近的權(quán)重信息到模型中
2)配置epoch
3)讓模型開始訓(xùn)練
path_checkpoint = "./models/checkpoint/ckpt_weight_12.pth"
checkpoint = torch.load(path_checkpoint)
model.load_state_dict(checkpoint['net'])
optimizer.load_state_dict(checkpoint['optimizer'])
start_epoch = checkpoint['epoch']
for epoch in range(start_epoch+1,100):
for step,(b_img,b_label) in enumerate(train_loader):
train_output = model(b_img)
loss = loss_func(train_output,b_label)
optimizer.zero_grad()
loss.backward()
optimizer.step()學(xué)習(xí)率自動下降
TensorFlow提供了一種更加靈活的學(xué)習(xí)率設(shè)置方法-指數(shù)衰減法,使用tf.train.exponential_decay實現(xiàn)。指數(shù)衰減法的核心思想是,先使用較大的學(xué)習(xí)率來快速得到一個比較優(yōu)的解,然后隨著迭代的繼續(xù)逐步減小學(xué)習(xí)率,使得模型更加穩(wěn)定。tf.train.exponential_decay函數(shù)可以用以下代碼實現(xiàn)的功能來展示:
decayed_learning_rate = learning_rate*decay_rate^(global_step/decay_steps) #decayed_learning_rate為每一輪優(yōu)化時使用的學(xué)習(xí)率,learning_rate為事先設(shè)定的初始學(xué)習(xí)率,decay_rate為衰減系數(shù),global_step為迭代次數(shù),decay_steps為衰減速度(即迭代多少次進行衰減)
可見使用此函數(shù),隨著迭代次數(shù)的增加,學(xué)習(xí)率逐步降低。
tf.train.exponential_decay可以通過設(shè)置參數(shù)staircase選擇不同的衰減方式,其默認(rèn)值為False,既每一次迭代都進行學(xué)習(xí)率的優(yōu)化,
global_step = tf.Variable(0) learning_rate = tf.train.exponential_decay(0.1,global_step,100,0.96,staircase=True) #0.1表示初始學(xué)習(xí)率,global_step表示迭代次數(shù),100表示衰減速度,0.96表示衰減率 #使用指數(shù)衰減的學(xué)習(xí)率,在minimize函數(shù)中傳入global_step,它將自動更新,learning_rate也隨即被更新 learning_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step) #神經(jīng)網(wǎng)絡(luò)反向傳播算法,使用梯度下降算法GradientDescentOptimizer來優(yōu)化權(quán)重值,learning_rate為學(xué)習(xí)率,minimize中參數(shù)loss是損失函數(shù),global_step表明了當(dāng)前迭代次數(shù)(會被自動更新)
pytorch中進行使用
import torch
import torch.nn as nn
from torch.optim.lr_scheduler import StepLR
import itertools
initial_lr = 0.1
class model(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3)
def forward(self, x):
pass
net_1 = model()
optimizer_1 = torch.optim.Adam(net_1.parameters(),lr=initial_lr)
#對優(yōu)化方法進行優(yōu)化
scheduler_1 = StepLR(optimizer_1, step_sie=3, gamma=0.1)
for epoch in range(1, 11):
optimizer_1.zero_grad()
optimizer_1.step()
print("第%d個epoch的學(xué)習(xí)率:%f" % (epoch, optimizer_1.param_groups[0]['lr']))
scheduler_1.step()import torch
import torch.nn as nn
from torch.optim.lr_scheduler import LambdaLR
class model(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3)
def forward(self, x):
pass
net_1 = model()
optimizer_1 = torch.optim.Adam(net_1.parameters(), lr = initial_lr)
scheduler_1 = LambdaLR(optimizer_1, lr_lambda=lambda epoch: 1/(epoch+1))
print("初始化的學(xué)習(xí)率:", optimizer_1.defaults['lr'])
for epoch in range(1, 11):
# train
optimizer_1.zero_grad()
optimizer_1.step()
print("第%d個epoch的學(xué)習(xí)率:%f" % (epoch, optimizer_1.param_groups[0]['lr']))
scheduler_1.step()多步長SGD繼續(xù)訓(xùn)練
在簡單的任務(wù)中,我們使用固定步長(也就是學(xué)習(xí)率LR)進行訓(xùn)練,但是如果學(xué)習(xí)率lr設(shè)置的過小的話,則會導(dǎo)致很難收斂,如果學(xué)習(xí)率很大的時候,就會導(dǎo)致在最小值附近,總會錯過最小值,loss產(chǎn)生震蕩,無法收斂。所以這要求我們要對于不同的訓(xùn)練階段使用不同的學(xué)習(xí)率,一方面可以加快訓(xùn)練的過程,另一方面可以加快網(wǎng)絡(luò)收斂。
所以我們在保存網(wǎng)絡(luò)中的訓(xùn)練的參數(shù)的過程中,還需要保存lr_scheduler的state_dict,然后斷點繼續(xù)訓(xùn)練的時候恢復(fù)
optimizer = torch.optim.SGD(model.parameters(),lr=0.1)
#這里我設(shè)置了不同的epoch對應(yīng)不同的學(xué)習(xí)率衰減,在10->20->30,學(xué)習(xí)率依次衰減為原來的0.1,即一個數(shù)量級
lr_schedule = torch.optim.lr_scheduler.MultiStepLR(optimizer,milestones=[10,20,30,40,50],gamma=0.1)
for epoch in range(start_epoch+1,80):
optimizer.zero_grad()
optimizer.step()
lr_schedule.step()
if epoch %10 ==0:
print('epoch:',epoch)
print('learning rate:',optimizer.state_dict()['param_groups'][0]['lr'])我們在保存的時候,也需要對lr_scheduler的state_dict進行保存,斷點繼續(xù)訓(xùn)練的時候也需要恢復(fù)lr_scheduler
if RESUME:
path_checkpoint = "./model_parameter/test/ckpt_best_50.pth" # 斷點路徑
checkpoint = torch.load(path_checkpoint) # 加載斷點
model.load_state_dict(checkpoint['net']) # 加載模型可學(xué)習(xí)參數(shù)
optimizer.load_state_dict(checkpoint['optimizer']) # 加載優(yōu)化器參數(shù)
start_epoch = checkpoint['epoch'] # 設(shè)置開始的epoch
lr_schedule.load_state_dict(checkpoint['lr_schedule'])#加載lr_scheduler
for epoch in range(start_epoch+1,80):
optimizer.zero_grad()
optimizer.step()
lr_schedule.step()
if epoch %10 ==0:
print('epoch:',epoch)
print('learning rate:',optimizer.state_dict()['param_groups'][0]['lr'])
checkpoint = {
"net": model.state_dict(),
'optimizer': optimizer.state_dict(),
"epoch": epoch,
'lr_schedule': lr_schedule.state_dict()
}
if not os.path.isdir("./model_parameter/test"):
os.mkdir("./model_parameter/test")
torch.save(checkpoint, './model_parameter/test/ckpt_best_%s.pth' % (str(epoch)))optimizer = torch.optim.SGD(model.parameters(),lr=0.1)
lr_schedule = torch.optim.lr_scheduler.MultiStepLR(optimizer,milestones=[10,20,30,40,50],gamma=0.1)
start_epoch = 9
# print(schedule)
if RESUME:
path_checkpoint = "./model_parameter/test/ckpt_best_50.pth" # 斷點路徑
checkpoint = torch.load(path_checkpoint) # 加載斷點
model.load_state_dict(checkpoint['net']) # 加載模型可學(xué)習(xí)參數(shù)
optimizer.load_state_dict(checkpoint['optimizer']) # 加載優(yōu)化器參數(shù)
start_epoch = checkpoint['epoch'] # 設(shè)置開始的epoch
lr_schedule.load_state_dict(checkpoint['lr_schedule'])
for epoch in range(start_epoch+1,80):
optimizer.zero_grad()
optimizer.step()
lr_schedule.step()
if epoch %10 ==0:
print('epoch:',epoch)
print('learning rate:',optimizer.state_dict()['param_groups'][0]['lr'])
checkpoint = {
"net": model.state_dict(),
'optimizer': optimizer.state_dict(),
"epoch": epoch,
'lr_schedule': lr_schedule.state_dict()
}
if not os.path.isdir("./model_parameter/test"):
os.mkdir("./model_parameter/test")
torch.save(checkpoint, './model_parameter/test/ckpt_best_%s.pth' % (str(epoch)))總結(jié)
模型定義是深度學(xué)習(xí)中重要的一環(huán),PyTorch提供了強大而靈活的工具和函數(shù),使我們能夠輕松定義各種類型的深度學(xué)習(xí)模型。通過深入理解模型定義的原理和應(yīng)用,我們能夠更好地理解和設(shè)計自己的模型,從而提升深度學(xué)習(xí)任務(wù)的性能和效果。
到此這篇關(guān)于pytorch模型的定義、修改、讀取、斷點續(xù)訓(xùn)深入解析的文章就介紹到這了,更多相關(guān)pytorch模型的定義、修改、讀取、斷點續(xù)訓(xùn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
pycharm 2019 最新激活方式(pycharm破解、激活)
這篇文章主要介紹了最新2019pycharm激活方式(pycharm破解、激活),本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2020-01-01
python遍歷 truple list dictionary的幾種方法總結(jié)
下面小編就為大家?guī)硪黄猵ython遍歷 truple list dictionary的幾種方法總結(jié)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-09-09
Python入門(六)Python數(shù)據(jù)類型
這篇文章主要介紹了Python入門(六)Python數(shù)據(jù)類型,Python是一門非常強大好用的語言,也有著易上手的特性,本文為入門教程,需要的朋友可以參考下2023-04-04
ruff check文件目錄檢測--exclude參數(shù)設(shè)置路徑詳解
這篇文章主要為大家介紹了ruff check文件目錄檢測exclude參數(shù)如何設(shè)置多少路徑詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10

