pytorch模型的定義、修改、讀取、斷點(diǎn)續(xù)訓(xùn)深入解析
在機(jī)器學(xué)習(xí)和深度學(xué)習(xí)領(lǐng)域,PyTorch是一種廣泛使用的開源深度學(xué)習(xí)框架。它提供了豐富的工具和函數(shù),方便用戶定義、訓(xùn)練和部署各種深度學(xué)習(xí)模型。本篇博客將詳細(xì)介紹PyTorch中模型定義的方式,并結(jié)合原理和代碼示例進(jìn)行講解,旨在幫助讀者深入理解PyTorch的模型定義過程。
前言
模型定義的基本原理
在PyTorch中,模型定義是通過定義一個繼承自torch.nn.Module類的Python類來實(shí)現(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ù)中定義模型的各個層和模塊。
在子類中實(shí)現(xiàn)forward方法,該方法定義了模型的前向傳播過程,即定義了輸入數(shù)據(jù)如何經(jīng)過各個層進(jìn)行計算得到輸出。
可選地,在子類中實(shí)現(xiàn)__str__方法,用于打印模型的結(jié)構(gòu)信息。
接下來,我們將通過一個簡單的神經(jīng)網(wǎng)絡(luò)模型的定義和代碼示例來進(jìn)一步解釋以上原理。
模型參數(shù)和層的概念
在深入了解模型定義之前,讓我們先來了解一些基本概念:模型參數(shù)和層。
模型參數(shù)
模型參數(shù)是模型內(nèi)部可學(xué)習(xí)的參數(shù),它們會在訓(xùn)練過程中自動更新以優(yōu)化模型的性能。常見的模型參數(shù)包括權(quán)重(weights)和偏置(biases)。權(quán)重是連接不同神經(jīng)元的連接強(qiá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)
對層的名稱進(jìn)行自定義
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進(jìn)行操作。
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)部實(shí)現(xiàn)了forward函數(shù),因此可以不用寫forward函數(shù),而nn.ModuleList則沒有實(shí)現(xiàn)內(nèi)部forward函數(shù),需要人工實(shí)現(xiàn)。
- nn.Sequential初始調(diào)用時就決定了里面Module的調(diào)用順序,因此需要保證上一個Module的輸出大小能符合下一個Module輸入大小的要求。nn.ModuleList只是簡單的將Module打包并注冊到網(wǎng)絡(luò)中,需要在人工實(shí)現(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_ten
2)添加額外輸入
將原模型添加輸入位置前的部分作為一個整體,同時在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
可以使用下面的代碼進(jìn)行替換
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ù)上進(jìn)行了部分微調(diào),則上述代碼就會報錯:key
對應(yīng)不上, 此時,采用strict=False
就能夠解決這個問題
斷點(diǎn)續(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')
斷點(diǎn)回復(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實(shí)現(xiàn)。指數(shù)衰減法的核心思想是,先使用較大的學(xué)習(xí)率來快速得到一個比較優(yōu)的解,然后隨著迭代的繼續(xù)逐步減小學(xué)習(xí)率,使得模型更加穩(wěn)定。tf.train.exponential_decay函數(shù)可以用以下代碼實(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為衰減速度(即迭代多少次進(jìn)行衰減)
可見使用此函數(shù),隨著迭代次數(shù)的增加,學(xué)習(xí)率逐步降低。
tf.train.exponential_decay
可以通過設(shè)置參數(shù)staircase
選擇不同的衰減方式,其默認(rèn)值為False,既每一次迭代都進(jìn)行學(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中進(jìn)行使用
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)化方法進(jìn)行優(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)進(jìn)行訓(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,然后斷點(diǎn)繼續(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進(jìn)行保存,斷點(diǎn)繼續(xù)訓(xùn)練的時候也需要恢復(fù)lr_scheduler
if RESUME: path_checkpoint = "./model_parameter/test/ckpt_best_50.pth" # 斷點(diǎn)路徑 checkpoint = torch.load(path_checkpoint) # 加載斷點(diǎn) 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" # 斷點(diǎn)路徑 checkpoint = torch.load(path_checkpoint) # 加載斷點(diǎn) 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提供了強(qiáng)大而靈活的工具和函數(shù),使我們能夠輕松定義各種類型的深度學(xué)習(xí)模型。通過深入理解模型定義的原理和應(yīng)用,我們能夠更好地理解和設(shè)計自己的模型,從而提升深度學(xué)習(xí)任務(wù)的性能和效果。
到此這篇關(guān)于pytorch模型的定義、修改、讀取、斷點(diǎn)續(xù)訓(xùn)深入解析的文章就介紹到這了,更多相關(guān)pytorch模型的定義、修改、讀取、斷點(diǎn)續(xù)訓(xùn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
pycharm 2019 最新激活方式(pycharm破解、激活)
這篇文章主要介紹了最新2019pycharm激活方式(pycharm破解、激活),本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2020-01-01python遍歷 truple list dictionary的幾種方法總結(jié)
下面小編就為大家?guī)硪黄猵ython遍歷 truple list dictionary的幾種方法總結(jié)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-09-09PyTorch中Tensor的維度變換實(shí)現(xiàn)
這篇文章主要介紹了PyTorch中Tensor的維度變換實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08Python入門(六)Python數(shù)據(jù)類型
這篇文章主要介紹了Python入門(六)Python數(shù)據(jù)類型,Python是一門非常強(qiáng)大好用的語言,也有著易上手的特性,本文為入門教程,需要的朋友可以參考下2023-04-04ruff check文件目錄檢測--exclude參數(shù)設(shè)置路徑詳解
這篇文章主要為大家介紹了ruff check文件目錄檢測exclude參數(shù)如何設(shè)置多少路徑詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10