模型保存與提取
- 1. 整個模型 保存-載入
- 2. 僅模型參數 保存-載入
- 3. GPU/CPU模型保存與導入
- 4. net.eval()--固定模型隨機項
神經網絡模型在線訓練完之后需要保存下來,以便下次使用時可以直接導入已經訓練好的模型。pytorch 提供兩種方式保存模型:
方式1:保存整個網絡,載入時直接載入整個網絡,優點:代碼簡單,缺點需要的存儲空間大
方式2:只保存網絡參數,載入時需要先建立與原來網絡一樣結構的網絡,然后將網絡參數導入到該網絡中,方式2的優缺點與方式1相反。
1. 整個模型 保存-載入
模型的結構參數都保存下來了
# 保存模型:設置 保存目錄 和 保存文件名.擴展名,常用擴展名: .pkl .pth (擴展名只要好辨識就即可)
PATH="./model/mynet1.pkl"
# 導入官方提供的預訓練模型
net1=torchvision.models.alexnet(pretrainend=True)
# 用數據集訓練網絡
.....
# 保存訓練好的網絡
torch.save(net1, PATH)
-----------------------------------------------------------
# 載入模型:設置載入路徑,即模型保存的路徑
PATH="./model/mynet1.pkl"
net1_1=torch.load(PATH)
2. 僅模型參數 保存-載入
保存時–只保存網絡中的參數 (速度快, 占內存少), 載入時–需要提前創建好結構和net2是一樣的
# 保存模型:設置 保存目錄 和 保存文件名.擴展名,常用擴展名: .pkl .pth (擴展名只要好辨識就即可)
PATH="./model/mynet2.pkl"
# 導入官方提供的預訓練模型
net2=torchvision.models.alexnet(pretrainend=True)
# 用數據集訓練網絡
.....
# 保存訓練好的網絡
torch.save(net1.state_dict(), PATH)
-----------------------------------------------------------
# 載入模型:設置載入路徑,即模型保存的路徑
PATH="./model/net2.pkl"
# 新建一個網絡
net2_2=torchvision.models.alexnet(pretrained=True)
# 載入模型參數
net2_2.load_state_dict(torch.load(PATH))
迷糊的現象
在使用莫煩的文檔做實驗時,保存的兩個文件:net.pkl,net_params.pkl大小差異比較大。保證在導入模型是比較快。
但是使用torchvision.models.模塊中的一系列網絡時,因為網絡的參數很大,所以實驗過程中用兩種方法保存模型的文件大小是一致的。(猜測是內置模型使用torch.save(net1, ‘net.pkl’)時默認保存的是模型參數)
提供一個神經網絡模型占用空間大小的計算方法:
參考文檔:經典CNN模型計算量與內存需求分析
3. GPU/CPU模型保存與導入
在訓練是模型是GPU/CPU,決定了模型載入時的模型原型。可以分為下面三種情況
(只展示導入整個網絡模型的情況,具體實驗還沒做過):
1.CPU(原型)->CPU, GPU(原型)->GPU
torch.load( ‘net.pkl’)
2.GPU(原型)->CPU
torch.load(‘model_dict.pkl’, map_location=lambda storage, loc: storage)
3.CPU(模型文件)->GPU
torch.load(‘model_dic.pkl’, map_location=lambda storage, loc: storage.cuda)
參考文檔:https://blog.csdn.net/u012135425/article/details/85217542
4. net.eval()–固定模型隨機項
兩種模型載入方式、.eval() 作用實驗demo
step1: 載入模型
# 20191204 pytorch 模型載入測試
import torchvision as tvt
import torch
net1=tvt.models.alexnet(pretrained=True) # 1.自動從網上下載的預先訓練模型
net2=torch.load("./model/mynet1.pkl") # 2.導入事先訓練好的保存的整個網絡net3=tvt.models.alexnet(pretrained=True) # 3.導入只保存模型參數的網絡,需要新建一個網絡
net3.load_state_dict(torch.load("../model/mynet2.pkl"))
net3.eval() # 固定dropout和歸一化層,否則每次推理會生成不同的結果。
step2:輸出三個網絡同一層參數的和,net2 和net3 對應參數相等。可以看出來,兩種模型保存和導入方式是等價的。
net1 tensor(-21257.7656, grad_fn=<SumBackward0>)
net2 tensor(-21253.9473, device='cuda:0', grad_fn=<SumBackward0>)
net3 tensor(-21253.9551, grad_fn=<SumBackward0>)
step3: 產生一個隨機輸入a,輸入到網絡1,2,3,打印輸出結果。
a=torch.randn([1,3,224,224])
y1=net1(a)
y2=net2(a)
y3=net3(a)
# 第二次輸入
y11=net1(a)
y22=net2(a)
y33=net3(a)
# 打印y1,y2,y3,y11,y22,y33(1000維的和)
y1: tensor(-5.2689, grad_fn=<SumBackward0>)
y2: tensor(-1.6695, device='cuda:0', grad_fn=<SumBackward0>)
y3: tensor(-4.4349, device='cuda:0', grad_fn=<SumBackward0>)y11: tensor(-4.4205, grad_fn=<SumBackward0>)
y22: tensor(-5.9475, device='cuda:0', grad_fn=<SumBackward0>)
y33: tensor(-4.4349, device='cuda:0', grad_fn=<SumBackward0>)
只有net3的輸出是固定的,因為在模型導入的時候執行了net3.eval().
結論:無論采用 方式1 還是 方式2 導入的模型, 在模型測試時,都需要用.eval()方法固定一下網絡在訓練過程中的隨機項目,如dropout 等,避免網絡在同一個輸入下產生不一樣的結果。