最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

PyTorch權(quán)值初始化原理解析

 更新時(shí)間:2023年07月17日 09:07:45   作者:YOLO  
這篇文章主要為大家介紹了PyTorch權(quán)值初始化原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

Pytorch:權(quán)值初始化

在搭建好網(wǎng)絡(luò)模型之后,首先需要對(duì)網(wǎng)絡(luò)模型中的權(quán)值進(jìn)行初始化。權(quán)值初始化的作用有很多,通常,一個(gè)好的權(quán)值初始化將會(huì)加快模型的收斂,而比較差的權(quán)值初始化將會(huì)引發(fā)梯度爆炸或者梯度消失。下面將具體解釋其中的原因:

梯度消失與梯度爆炸

考慮一個(gè) 3 層的全連接網(wǎng)絡(luò)。

$H_{1}=X \times W_{1}$,$H_{2}=H_{1} \times W_{2}$,$Out=H_{2} \times W_{3}$,如下圖所示,

其中第 2 層的權(quán)重梯度如下:

$\begin{array}{l}
\mathrm{H}_{2}=\mathrm{H}_{1} * \mathrm{~W}_{\mathbf{2}} \
\Delta \mathrm{W}_{\mathbf{2}}=\frac{\partial \text { Loss }}{\partial \mathrm{W}_{2}}=\frac{\partial \mathrm{Loss}}{\partial \text { out }} \frac{\partial \text { out }}{\partial \mathrm{H}_{2}} \frac{\partial \mathrm{H}_{2}}{\partial \mathrm{W}_{2}} \ =\frac{\partial \text { Loss }}{\partial \text { out }} \frac{\partial \text { out }}{\partial \mathrm{H}_{2}} \mathrm{H}_{1} \
\end{array}$

由上式化簡可知,如果H\_1發(fā)生以下變化,那么對(duì)應(yīng)的梯度也就會(huì)發(fā)生變化:

梯度消失: $\mathrm{H}_{1} \rightarrow 0 \Rightarrow \Delta \mathrm{W}_{2} \rightarrow 0$

梯度爆炸: $\mathrm{H}_{1} \rightarrow \infty \Rightarrow \Delta \mathrm{W}_{2} \rightarrow \infty $

因此,為了避免以上兩種情況,就必須嚴(yán)格控制網(wǎng)絡(luò)層輸出的數(shù)值范圍。

具體可以通過構(gòu)建 100 層全連接網(wǎng)絡(luò),先不使用非線性激活函數(shù),每層的權(quán)重初始化為服從 $N(0,1)$ 的正態(tài)分布,輸出數(shù)據(jù)使用隨機(jī)初始化的數(shù)據(jù),這樣的例子來直觀地感受影響:

import torch
import torch.nn as nn
from common_tools import set_seed
set_seed(1)  # 設(shè)置隨機(jī)種子
class MLP(nn.Module):
    def __init__(self, neural_num, layers):
        super(MLP, self).__init__()
        self.linears = nn.ModuleList([nn.Linear(neural_num, neural_num, bias=False) for i in range(layers)])
        self.neural_num = neural_num
    def forward(self, x):
        for (i, linear) in enumerate(self.linears):
            x = linear(x)
        return x
    def initialize(self):
        for m in self.modules():
            # 判斷這一層是否為線性層,如果為線性層則初始化權(quán)值
            if isinstance(m, nn.Linear):
                nn.init.normal_(m.weight.data)    # normal: mean=0, std=1
# 網(wǎng)絡(luò)的層數(shù)
layer_nums = 100
# 神經(jīng)元的個(gè)數(shù)
neural_nums = 256
batch_size = 16
net = MLP(neural_nums, layer_nums)
net.initialize()
# 設(shè)置隨機(jī)初始化的輸入
inputs = torch.randn((batch_size, neural_nums))  # normal: mean=0, std=1
output = net(inputs)
print(output)

輸出為:

tensor([[nan, nan, nan,  ..., nan, nan, nan],
        [nan, nan, nan,  ..., nan, nan, nan],
        [nan, nan, nan,  ..., nan, nan, nan],
        ...,
        [nan, nan, nan,  ..., nan, nan, nan],
        [nan, nan, nan,  ..., nan, nan, nan],
        [nan, nan, nan,  ..., nan, nan, nan]], grad_fn=<MmBackward>)

通過輸出可知,輸出值均為nan,即非數(shù)字類型,原因可能是數(shù)據(jù)太大(梯度爆炸)或者太小(梯度消失)。

為了具體知道是在哪一層開始出現(xiàn)nan的,我們可以在forward函數(shù)中添加判斷得知,查看每一次前向轉(zhuǎn)播的標(biāo)準(zhǔn)差是否是nan,若是,則停止前向傳播并輸出。

這里判斷是否為nan時(shí)采用了 torch.isnan 函數(shù)

def forward(self, x):
        for (i, linear) in enumerate(self.linears):
            x = linear(x)
            print("layer:{}, std:{}".format(i, x.std()))
            if torch.isnan(x.std()):
                print("output is nan in {} layers".format(i))
                break
        return x

輸出如下:

layer:0, std:15.959932327270508
layer:1, std:256.6237487792969
layer:2, std:4107.24560546875
.
.
.
layer:29, std:1.322983152787379e+36
layer:30, std:2.0786820453988485e+37
layer:31, std:nan
output is nan in 31 layers

可見,之際上輸出的標(biāo)準(zhǔn)差是逐層遞增的,具體為什么會(huì)導(dǎo)致這種情況:

  • $E(X \times Y)=E(X) \times E(Y)$:兩個(gè)相互獨(dú)立的隨機(jī)變量的乘積的期望等于它們的期望的乘積。
  • $D(X)=E(X^{2}) - [E(X)]^{2}$:一個(gè)隨機(jī)變量的方差等于它的平方的期望減去期望的平方
  • $D(X+Y)=D(X)+D(Y)$:兩個(gè)相互獨(dú)立的隨機(jī)變量之和的方差等于它們的方差的和。

可以推導(dǎo)出兩個(gè)隨機(jī)變量的乘積的方差如下:

$D(X \times Y)=E[(XY)^{2}] - [E(XY)]^{2}=D(X) \times D(Y) + D(X) \times [E(Y)]^{2} + D(Y) \times [E(X)]^{2}$

又由于輸入變量是符合標(biāo)準(zhǔn)的正態(tài)分布的,因此 $E(X)=0$,$E(Y)=0$,可知 $D(X \times Y)=D(X) \times D(Y)$

我們以輸入層第一個(gè)神經(jīng)元為例:

$\mathrm{H}_{11}=\sum_{i=0}^{n} X_{i} * W_{1 i}$

其中輸入 X 和權(quán)值 W 都是服從 $N(0,1)$ 的正態(tài)分布,且由公式$D(X \times Y)=D(X) \times D(Y)$, 因此這個(gè)神經(jīng)元的方差為:

$\begin{aligned}
\mathbf{D}\left(\mathrm{H}_{11}\right) &=\sum_{i=0}^{n} D\left(X_{i}\right) * D\left(W_{1 i}\right) \
&=n (1 1) \
&=n
\end{aligned}$

可以求其標(biāo)準(zhǔn)差:

$\operatorname{std}\left(\mathrm{H}_{11}\right)=\sqrt{\mathrm{D}\left(\mathrm{H}_{11}\right)}=\sqrt{n}$

可見,經(jīng)過第一層網(wǎng)絡(luò),方差就會(huì)擴(kuò)大 n 倍,標(biāo)準(zhǔn)差就擴(kuò)大 $\sqrt{n}$ 倍,n 為每層神經(jīng)元個(gè)數(shù),直到超出數(shù)值表示范圍。

從前面的輸出中也可以看出來,n = 256,因此每一層的標(biāo)準(zhǔn)差輸出都是16倍。再由公式可知,每一層網(wǎng)絡(luò)輸出的方差與神經(jīng)元個(gè)數(shù)、輸入數(shù)據(jù)的方差、權(quán)值方差有關(guān)(見上式),通過觀察可知,比較好改變的是權(quán)值的方差 $D(W)$,要控制每一層輸出的方差仍然為1左右,因此需要 $D(W)= \frac{1}{n}$,可知標(biāo)準(zhǔn)差為 $std(W)=\sqrt\frac{1}{n}$。因此修改權(quán)值初始化代碼為nn.init.normal_(m.weight.data, std=np.sqrt(1/self.neural_num))

再次輸出時(shí),結(jié)果如下:

layer:0, std:0.9974957704544067
layer:1, std:1.0024365186691284
layer:2, std:1.002745509147644
.
.
.
layer:94, std:1.031973123550415
layer:95, std:1.0413124561309814
layer:96, std:1.0817031860351562

修改之后,沒有出現(xiàn)梯度消失或者梯度爆炸的情況,每層神經(jīng)元輸出的方差均在 1 左右。通過恰當(dāng)?shù)臋?quán)值初始化,可以保持權(quán)值在更新過程中維持在一定范圍之內(nèi)

但是上述的實(shí)驗(yàn)前提為未使用非線性函數(shù)的前提下,如果在forward()中添加非線性變換例如tanh,每一層的輸出方差會(huì)越來越小,會(huì)導(dǎo)致梯度消失。

為了解決這個(gè)問題,進(jìn)一步有了著名的 Xavier 初始化與 Kaiming 初始化。

Xavier 方法與 Kaiming 方法

Xavier 方法

Xavier 是 2010 年提出的,針對(duì)有非線性激活函數(shù)時(shí)的權(quán)值初始化方法。

  • 目標(biāo)是保持?jǐn)?shù)據(jù)的方差維持在 1 左右
  • 針對(duì)飽和激活函數(shù)如 sigmoid 和 tanh 等。

同時(shí)考慮前向傳播和反向傳播,需要滿足兩個(gè)等式

$\begin{array}{l}
\boldsymbol{n}_{\boldsymbol{i}} * \boldsymbol{D}(\boldsymbol{W})=\mathbf{1} \
\boldsymbol{n}_{\boldsymbol{i}+\mathbf{1}} * \boldsymbol{D}(\boldsymbol{W})=\mathbf{1} \
\end{array}$

通過計(jì)算可知:

$D(W)=\frac{2}{n_{i}+n_{i+1}}$。

為了使 Xavier 方法初始化的權(quán)值服從均勻分布,假設(shè) $W$ 服從均勻分布 $U[-a, a]$,那么方差 $D(W)=\frac{(-a-a)^{2}}{12}=\frac{(2 a){2}}{12}=\frac{a{2}}{3}$,令 $\frac{2}{n_{i}+n_{i+1}}=\frac{a^{2}}{3}$,解得:$\boldsymbol{a}=\frac{\sqrt{6}}{\sqrt{n_{i}+n_{i+1}}}$,所以 $W$ 服從分布 $U\left[-\frac{\sqrt{6}}{\sqrt{n_{i}+n_{i+1}}}, \frac{\sqrt{6}}{\sqrt{n_{i}+n_{i+1}}}\right]$

所以初始化方法改為:

a = np.sqrt(6 / (self.neural_num + self.neural_num))
# 把 a 變換到 tanh,計(jì)算增益
tanh_gain = nn.init.calculate_gain('tanh')
a *= tanh_gain
nn.init.uniform_(m.weight.data, -a, a)

并且每一層的激活函數(shù)都使用 tanh,輸出如下:

layer:0, std:0.7571136355400085
layer:1, std:0.6924336552619934
layer:2, std:0.6677976846694946
.
.
.
layer:97, std:0.6426210403442383
layer:98, std:0.6407480835914612
layer:99, std:0.6442216038703918

可以看到每層輸出的方差都維持在 0.6 左右。

也可以直接調(diào)用PyTorch 中 Xavier 初始化方法:

tanh_gain = nn.init.calculate_gain('tanh')
nn.init.xavier_uniform_(m.weight.data, gain=tanh_gain)

nn.init.calculate\_gain()

這里重點(diǎn)介紹一下nn.init.calculate_gain(nonlinearity,param=**None**)方法。

主要功能是經(jīng)過一個(gè)分布的方差經(jīng)過激活函數(shù)后的變化尺度,主要有兩個(gè)參數(shù):

  • nonlinearity:激活函數(shù)名稱
  • param:激活函數(shù)的參數(shù),如 Leaky ReLU 的 negative\_slop等等。

下面是計(jì)算標(biāo)準(zhǔn)差經(jīng)過激活函數(shù)的變化尺度的代碼。

x = torch.randn(10000)
out = torch.tanh(x)
# 計(jì)算變化尺度(也可以稱為變化倍數(shù))
gain = x.std() / out.std()
print('gain:{}'.format(gain))
tanh_gain = nn.init.calculate_gain('tanh')
print('tanh_gain in PyTorch:', tanh_gain)

輸出如下:

gain:1.5982500314712524
tanh_gain in PyTorch: 1.6666666666666667

結(jié)果表示,原有數(shù)據(jù)分布的方差經(jīng)過 tanh 之后,標(biāo)準(zhǔn)差會(huì)變小 1.6 倍左右。

Kaiming 方法

雖然 Xavier 方法提出了針對(duì)飽和激活函數(shù)的權(quán)值初始化方法,但是 AlexNet 出現(xiàn)后,大量網(wǎng)絡(luò)開始使用非飽和的激活函數(shù)如 ReLU 等,這時(shí) Xavier 方法不再適用。2015 年針對(duì) ReLU 及其變種等激活函數(shù)提出了 Kaiming 初始化方法。

針對(duì) ReLU,方差應(yīng)該滿足:$\mathrm{D}(W)=\frac{2}{n_{i}}$;

針對(duì) ReLu 的變種,方差應(yīng)該滿足:$D(W)=\frac{2}{\left(1+\mathrm{a}^{2}\right) * n_{i}}$,a 表示負(fù)半軸的斜率,如 PReLU 方法,標(biāo)準(zhǔn)差滿足 $\operatorname{std}(W)=\sqrt{\frac{2}{\left(1+a^{2}\right) * n_{i}}}$。

代碼如下:nn.init.normal_(m.weight.data, std=np.sqrt(2 / self.neural_num)),或者使用 PyTorch 提供的初始化方法:nn.init.kaiming_normal_(m.weight.data)

常用初始化方法

PyTorch 中提供了 10 中初始化方法

  • Xavier 均勻分布
  • Xavier 正態(tài)分布
  • Kaiming 均勻分布
  • Kaiming 正態(tài)分布
  • 均勻分布
  • 正態(tài)分布
  • 常數(shù)分布
  • 正交矩陣初始化
  • 單位矩陣初始化
  • 稀疏矩陣初始化

綜上, 常用初始化的目標(biāo)就是要保證每一層輸出的方差不能太大,也不能太小,維持在一個(gè)穩(wěn)定的范圍內(nèi)。

以上就是PyTorch: 權(quán)值初始化的詳細(xì)內(nèi)容,更多關(guān)于PyTorch 權(quán)值初始化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Flask?數(shù)據(jù)庫遷移詳情

    Flask?數(shù)據(jù)庫遷移詳情

    本文給大家分享的是?Flask?數(shù)據(jù)庫遷移詳情,db.create_all()不會(huì)重新創(chuàng)建表或是更新表,需要先使用db.drop_all()刪除數(shù)據(jù)庫中所有的表之后再調(diào)用db.create_all()才能重新創(chuàng)建表,但是這樣的話,原來表中的數(shù)據(jù)就都被刪除了,這肯定是不行的,這時(shí)就出現(xiàn)了數(shù)據(jù)庫遷移的概念
    2021-11-11
  • Python對(duì)圖片進(jìn)行二值化的實(shí)現(xiàn)方法

    Python對(duì)圖片進(jìn)行二值化的實(shí)現(xiàn)方法

    圖像的二值化處理是將圖像上的像素點(diǎn)的灰度值設(shè)置為0或255,也就是將整個(gè)圖像呈現(xiàn)出明顯的只有黑和白的視覺效果,二值化是圖像分割的一種最簡單的方法,可以把灰度圖像轉(zhuǎn)換成二值圖像,本文給大家介紹了Python對(duì)圖片進(jìn)行二值化的實(shí)現(xiàn)方法,需要的朋友可以參考下
    2025-10-10
  • Python 項(xiàng)目轉(zhuǎn)化為so文件實(shí)例

    Python 項(xiàng)目轉(zhuǎn)化為so文件實(shí)例

    今天小編就為大家分享一篇Python 項(xiàng)目轉(zhuǎn)化為so文件實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2019-12-12
  • python讀寫文件write和flush的實(shí)現(xiàn)方式

    python讀寫文件write和flush的實(shí)現(xiàn)方式

    今天小編就為大家分享一篇python讀寫文件write和flush的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-02-02
  • Python使用urllib模塊的urlopen超時(shí)問題解決方法

    Python使用urllib模塊的urlopen超時(shí)問題解決方法

    這篇文章主要介紹了Python使用urllib模塊的urlopen超時(shí)問題解決方法,本文使用socket模塊中的setdefaulttimeout函數(shù)解決了超時(shí)問題,需要的朋友可以參考下
    2014-11-11
  • python處理cookie詳解

    python處理cookie詳解

    Cookie用于服務(wù)器實(shí)現(xiàn)會(huì)話,用戶登錄及相關(guān)功能時(shí)進(jìn)行狀態(tài)管理,本文介紹了使用python處理cookie的方法,需要的朋友可以參考下
    2014-02-02
  • Python拼接字符串的7種方法總結(jié)

    Python拼接字符串的7種方法總結(jié)

    這篇文章主要給大家總結(jié)介紹了關(guān)于Python拼接字符串的7種方法,分別是來自C語言的%方式、format()拼接方式、() 類似元組方式、面向?qū)ο竽0迤唇?、join()拼接方式以及f-string方式,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2018-11-11
  • Django values()和value_list()的使用

    Django values()和value_list()的使用

    這篇文章主要介紹了Django values()和value_list()的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-03-03
  • Python函數(shù)的作用域及內(nèi)置函數(shù)詳解

    Python函數(shù)的作用域及內(nèi)置函數(shù)詳解

    這篇文章主要介紹了python函數(shù)的作用域及內(nèi)置函數(shù)詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2021-09-09
  • Python中對(duì)對(duì)象列表進(jìn)行排序的多種方式實(shí)現(xiàn)

    Python中對(duì)對(duì)象列表進(jìn)行排序的多種方式實(shí)現(xiàn)

    在軟件開發(fā)中,我們經(jīng)常需要對(duì)數(shù)據(jù)進(jìn)行排序以滿足特定的需求,在 Python 中,排序是一個(gè)常見的操作,可以通過多種方式實(shí)現(xiàn),需要的朋友可以參考下
    2025-01-01

最新評(píng)論

额尔古纳市| 新田县| 株洲市| 天镇县| 武穴市| 无极县| 广平县| 敦化市| 濮阳县| 博罗县| 邻水| 乃东县| 元朗区| 台中市| 平昌县| 清流县| 集贤县| 霍林郭勒市| 阿图什市| 郧西县| 连平县| 铁力市| 温泉县| 临澧县| 兴隆县| 南充市| 商河县| 介休市| 泗水县| 健康| 康马县| 苏州市| 阿合奇县| 青田县| 临夏市| 昌乐县| 儋州市| 梨树县| 兴仁县| 稷山县| 新安县|