本節簡單總結Pytorch中常見的4大歸一化、模型如何保存並加載、以及模型如何實現微調,pytorch中多GPU的使用。【文中思維導圖采用MindMaster軟件,Latex公式采用在線編碼器】 |
1.Pytorch中封裝的4大歸一化(BN、LN、IN、GN)
(1)為什么要采用Normalization?
都是為了解決Internal Covariate Shift(ICS)問題,即數據分布出現異常而導致的網絡訓練困難。ICS問題在《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》中提到。
(2)這4大歸一化之間的異同點
① 相同點
都是類似 \(\gamma \times \frac{x- mean }{\sqrt{var^{2}+ \varepsilon } } + \beta\) 這樣的形式,即“加減乘除”。
② 不同點
均值mean和方差variance的求取方式不同,具體以下圖為例:
- (a)BN:對一個batch下的樣本數的同一channel下的特征做歸一化;
- (b)LN:由於BN不適合變長的網絡,例如RNN,所以LN對單獨一個樣本計算均值和方差,注意:gamma和beta是逐元素計算 ;
- (c)IN:由於BN不適合圖像生成領域,即每一個圖像都有自身的風格,不能夠將一個batch中的樣本混為一談,,故對每一個樣本逐通道歸一化;
- (d)GN:對於某些情況,batch_size較小,此時易知歸一化值不准(類似無法代替全局),故GN對通道數(很多的通道)進行分組,用通道數去彌補小數據量問題。注意:gamma和beta是逐通道計算 。
(3)具體使用
① BatchNorm
'''BatchNorm基類'''
class _BatchNorm(Module):
def __init__(self, num_features, eps=1e-5, momentum=0.1, affine=True,
track_running_stats=True):
# 參數:
# num_features:一個樣本的特征數量(或者是通道數量)
# eps:分母修正項
# momentum:指數加權平均 估計當前的mean/var!!!!!!!
# affine:是否進行affine transform,即gamma、beta的scale and shift
# track_running_stats:是訓練狀態 or 測試狀態
注意:測試狀態,mean和var只計算當前batch,而訓練狀態,當前時刻的mean和var會受之前的batch影響,即下面的公式(momentum):
上面的momentum在之前的博客中有提及。
② LayerNorm
class LayerNorm(Module):
def __init__(self, normalized_shape, eps=1e-5, elementwise_affine=True):
# 參數:
# normalized_shape:該樣本的特征形狀
# eps:分母修正項
# elementwise_affine:是否要對每個元素進行affine transform操作
注意:實例化LN時,要注意normalized_shape這一項,必須從size末尾寫,例如:
>>> input = torch.randn(20, 5, 4, 10)
>>> m = nn.LayerNorm(input.size()[1:])
>>> m = nn.LayerNorm([4, 10])
>>> m = nn.LayerNorm([5,4, 10])
>>> m = nn.LayerNorm([10])
# 但不可以是:
>>> m = nn.LayerNorm([5, 4])
③ InstanceNorm
'''InstanceNorm基類'''
class _InstanceNorm(_BatchNorm):
def __init__(self, num_features, eps=1e-5, momentum=0.1, affine=False,
track_running_stats=False):
# 參數:
# num_features:一個樣本的特征數量(或者是通道數量)
# eps:分母修正項
# momentum:指數加權平均 估計當前的mean/var!!!!!!!
# affine:是否進行affine transform,即gamma、beta的scale and shift
# track_running_stats:是訓練狀態 or 測試狀態
④ GroupNorm
class GroupNorm(Module):
def __init__(self, num_groups, num_channels, eps=1e-5, affine=True):
# 參數:
# num_groups:分組數(必須能將通道數整除!!!!!!!!)
# num_channels:通道數
# eps:分母修正項
# affine:是否進行affine transform,這里是對每個通道進行!!!!
2.模型的保存與加載
(1)pytorch提供的方法
主要參數如下:(torch.save(obj, f))
-
obj:要保存的對象(模型,張量,參數等);
-
f:保存的路徑。
(2)模型的加載 torch.load
主要參數如下:(torch.load(f, map_location=None))
-
f:讀取路徑;
-
map_location:決定該文件放在GPU上還是cpu位置上,當想要加載的模型是GPU上的,此時機子沒有GPU,則可以將map_location='cpu',將該模型放置在cpu上。
(3)兩種保存模型的方式
# 保存整個網絡
torch.save(net, PATH)
# 保存網絡中的參數
torch.save(net.state_dict(),PATH)
#對應上面的保存方式:
model_dict=torch.load(PATH)
model_dict=model.load_state_dict(torch.load(PATH))
(4)端點續訓練(其實就是把優化器、模型的參數,以及epoch保存下來,方便后續繼續訓練)
'''保存'''
checkpoint = {"model_state_dict": net.state_dict(),
"optimizer_state_dict": optimizer.state_dict(),
"epoch": epoch}
torch.save(checkpoint, path_checkpoint)
'''加載'''
checkpoint = torch.load(path_checkpoint)
net.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
start_epoch = checkpoint['epoch']
scheduler.last_epoch = start_epoch # 用於更新優化器的學習率
3.模型微調
模型finetune感覺這里對python編程較高 ( 咳咳。。。——>_——>。。。只怪本人python編程目前還比較low ),其實主要要對字典、列表等高級操作要熟悉!!
在這里本人就不獻丑了,等后續用到finetune再來補充。大家可以先參考知乎Pytorch自由載入部分模型參數並凍結。
4.GPU的使用
(1)to函數 (用於轉換類型/設備)
to函數可以用於tensor和module:
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
'''對於張量tensor'''
# 張量不執行原位inplace操作,即必須賦值
a = torch.ones((5, 5))
a = a.to(torch.float64)
a = a.to(device)
'''對於模型moudle'''
# 模型執行inplace
net.to(device)
(2)torch.cuda常用方法
上圖中設置GPU對應的os.environ.setdefault操作,可以有效防止多人使用GPU沖突問題,或者多程序在不同的GPU上跑的沖突,可用可見的GPU(邏輯GPU)與物理GPU的對應關系可以理解為:(對應圖3os.environ粉色方塊的操作)
(3)多GPU運算的分發並行機制
① 發並行機制的流程圖
這里一般利用torch.nn.DataParallel操作,例如batch_size=16,我們利用4個GPU並行運算,分配給每個GPU的batch_size=4該機制主要流程為:
② torch.nn.DataParallel
class DataParallel(Module):
def __init__(self, module, device_ids=None, output_device=None, dim=0):
# 參數:
# module:需要處理的模型;
# device_ids:可分法的GPU,默認分發到所有可見可用的GPU;(通過設置上述os.environ.setdefault操作,即可默認)
# output_device:最后結果輸出設備,默認主GPU
(4)在多GPU上運行的模型,其pkl文件加載時的問題
當我們加載多GPU模型參數,print load后的pkl文件,會發現較原始模型linear.0,多了一個module,即module.linear.0,因而我們需要取出module之后的參數,可以參考github上的代碼:
'''state_dict_load為OrderedDict形式,無法直接操作'''
state_dict_load = torch.load(path, map_location="cpu")
from collections import OrderedDict
new_state_dict = OrderedDict()
for k, v in state_dict_load.items():
namekey = k[7:] if k.startswith('module.') else k # 重新取出module后層信息,作為新的key
new_state_dict[namekey] = v
net.load_state_dict(new_state_dict)