日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

NNDL实验五 前馈神经网络(2)自动梯度计算 优化问题

發(fā)布時間:2024/5/14 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NNDL实验五 前馈神经网络(2)自动梯度计算 优化问题 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

4.3 自動梯度計算

雖然我們能夠通過模塊化的方式比較好地對神經(jīng)網(wǎng)絡(luò)進(jìn)行組裝,但是每個模塊的梯度計算過程仍然十分繁瑣且容易出錯。在深度學(xué)習(xí)框架中,已經(jīng)封裝了自動梯度計算的功能,我們只需要聚焦模型架構(gòu),不再需要耗費(fèi)精力進(jìn)行計算梯度。

飛槳提供了paddle.nn.Layer類,來方便快速的實(shí)現(xiàn)自己的層和模型。模型和層都可以基于paddle.nn.Layer擴(kuò)充實(shí)現(xiàn),模型只是一種特殊的層。繼承了paddle.nn.Layer類的算子中,可以在內(nèi)部直接調(diào)用其它繼承paddle.nn.Layer類的算子,飛槳框架會自動識別算子中內(nèi)嵌的paddle.nn.Layer類算子,并自動計算它們的梯度,并在優(yōu)化時更新它們的參數(shù)。

4.3.1 利用預(yù)定義算子重新實(shí)現(xiàn)前饋神經(jīng)網(wǎng)絡(luò)

1. 使用pytorch的預(yù)定義算子來重新實(shí)現(xiàn)二分類任務(wù)。(必做)

主要使用到的預(yù)定義算子為torch.nn.Linear

CLASS torch.nn.Linear(in_features, out_features, bias=True, device=None, dtype=None)

torch.nn.Linear算子可以接受一個形狀為[,]的輸入張量,其中""表示張量中可以有任意的其它額外維度,并計算它與形狀為[out_features, in_features]的權(quán)重矩陣的乘積,然后生成形狀為[,]的輸出張量。 torch.nn.Linear算子默認(rèn)有偏置參數(shù),可以通過bias=False設(shè)置不帶偏置。
代碼實(shí)現(xiàn):

import torch import torch.nn as nn import torch.nn.functional as F from torch.nn.init import constant_, normal_, uniform_class Model_MLP_L2_V2(nn.Module):def __init__(self, input_size, hidden_size, output_size):super(Model_MLP_L2_V2, self).__init__()self.fc1 = nn.Linear(input_size, hidden_size)normal_(self.fc1.weight, mean=0., std=1.)constant_(self.fc1.bias, val=0.0)self.fc2 = nn.Linear(hidden_size, output_size)normal_(self.fc2.weight, mean=0., std=1.)constant_(self.fc2.bias, val=0.0)self.act_fn = torch.sigmoid# 前向計算def forward(self, inputs):z1 = self.fc1(inputs)a1 = self.act_fn(z1)z2 = self.fc2(a1)a2 = self.act_fn(z2)return a2

2. 增加一個3個神經(jīng)元的隱藏層,再次實(shí)現(xiàn)二分類,并與1做對比。(必做)

4.3.2 完善Runner類

?基于上一節(jié)實(shí)現(xiàn)的?RunnerV2_1?類,本節(jié)的?RunnerV2_2類在訓(xùn)練過程中使用自動梯度計算;模型保存時,使用state_dict方法獲取模型參數(shù);模型加載時,使用set_state_dict方法加載模型參數(shù).

代碼實(shí)現(xiàn)如下:

#完善Runner類 class RunnerV2_2(object):def __init__(self, model, optimizer, metric, loss_fn, **kwargs):self.model = modelself.optimizer = optimizerself.loss_fn = loss_fnself.metric = metric# 記錄訓(xùn)練過程中的評估指標(biāo)變化情況self.train_scores = []self.dev_scores = []# 記錄訓(xùn)練過程中的評價指標(biāo)變化情況self.train_loss = []self.dev_loss = []def train(self, train_set, dev_set, **kwargs):# 將模型切換為訓(xùn)練模式self.model.train()# 傳入訓(xùn)練輪數(shù),如果沒有傳入值則默認(rèn)為0num_epochs = kwargs.get("num_epochs", 0)# 傳入log打印頻率,如果沒有傳入值則默認(rèn)為100log_epochs = kwargs.get("log_epochs", 100)# 傳入模型保存路徑,如果沒有傳入值則默認(rèn)為"best_model.pdparams"save_path = kwargs.get("save_path", "best_model.pdparams")# log打印函數(shù),如果沒有傳入則默認(rèn)為"None"custom_print_log = kwargs.get("custom_print_log", None)# 記錄全局最優(yōu)指標(biāo)best_score = 0# 進(jìn)行num_epochs輪訓(xùn)練for epoch in range(num_epochs):X, y = train_set# 獲取模型預(yù)測logits = self.model(X)# 計算交叉熵?fù)p失trn_loss = self.loss_fn(logits, y)self.train_loss.append(trn_loss.item())# 計算評估指標(biāo)trn_score = self.metric(logits, y).item()self.train_scores.append(trn_score)# 自動計算參數(shù)梯度trn_loss.backward()if custom_print_log is not None:# 打印每一層的梯度custom_print_log(self)# 參數(shù)更新self.optimizer.step()# 清空梯度self.optimizer.zero_grad()dev_score, dev_loss = self.evaluate(dev_set)# 如果當(dāng)前指標(biāo)為最優(yōu)指標(biāo),保存該模型if dev_score > best_score:self.save_model(save_path)print(f"[Evaluate] best accuracy performence has been updated: {best_score:.5f} --> {dev_score:.5f}")best_score = dev_scoreif log_epochs and epoch % log_epochs == 0:print(f"[Train] epoch: {epoch}/{num_epochs}, loss: {trn_loss.item()}")# 模型評估階段,使用'torch.no_grad()'控制不計算和存儲梯度@torch.no_grad()def evaluate(self, data_set):# 將模型切換為評估模式self.model.eval()X, y = data_set# 計算模型輸出logits = self.model(X)# 計算損失函數(shù)loss = self.loss_fn(logits, y).item()self.dev_loss.append(loss)# 計算評估指標(biāo)score = self.metric(logits, y).item()self.dev_scores.append(score)return score, loss# 模型測試階段,使用'torch.no_grad()'控制不計算和存儲梯度@torch.no_grad()def predict(self, X):# 將模型切換為評估模式self.model.eval()return self.model(X)# 使用'model.state_dict()'獲取模型參數(shù),并進(jìn)行保存def save_model(self, saved_path):torch.save(self.model.state_dict(), saved_path)# 使用'model.set_state_dict'加載模型參數(shù)def load_model(self, model_path):state_dict = torch.load(model_path)self.model.set_state_dict(state_dict)

?4.3.3 模型訓(xùn)練

實(shí)例化RunnerV2類,并傳入訓(xùn)練配置

代碼實(shí)現(xiàn):

from nndl.metric import accuracy input_size = 2 hidden_size = 5 output_size = 1 model = Model_MLP_L2_V2(input_size=input_size, hidden_size=hidden_size, output_size=output_size)# 設(shè)置損失函數(shù) loss_fn = F.binary_cross_entropy# 設(shè)置優(yōu)化器 learning_rate = 0.2 optimizer = torch.optim.SGD(model.parameters(), learning_rate)# 設(shè)置評價指標(biāo) metric = accuracy# 其他參數(shù) epoch_num = 1000 saved_path = 'best_model.pdparams'# 實(shí)例化RunnerV2類,并傳入訓(xùn)練配置 runner = RunnerV2_2(model, optimizer, metric, loss_fn)runner.train([X_train, y_train], [X_dev, y_dev], num_epochs=epoch_num, log_epochs=50, save_path="best_model.pdparams")

??將訓(xùn)練過程中訓(xùn)練集與驗(yàn)證集的準(zhǔn)確率變化情況進(jìn)行可視化。

# 可視化觀察訓(xùn)練集與驗(yàn)證集的指標(biāo)變化情況import matplotlib.pyplot as plt def plot(runner, fig_name):plt.figure(figsize=(10, 5))epochs = [i for i in range(len(runner.train_scores))]plt.subplot(1, 2, 1)plt.plot(epochs, runner.train_loss, color='#e4007f', label="Train loss")plt.plot(epochs, runner.dev_loss, color='#f19ec2', linestyle='--', label="Dev loss")# 繪制坐標(biāo)軸和圖例plt.ylabel("loss", fontsize='large')plt.xlabel("epoch", fontsize='large')plt.legend(loc='upper right', fontsize='x-large')plt.subplot(1, 2, 2)plt.plot(epochs, runner.train_scores, color='#e4007f', label="Train accuracy")plt.plot(epochs, runner.dev_scores, color='#f19ec2', linestyle='--', label="Dev accuracy")# 繪制坐標(biāo)軸和圖例plt.ylabel("score", fontsize='large')plt.xlabel("epoch", fontsize='large')plt.legend(loc='lower right', fontsize='x-large')plt.savefig(fig_name)plt.show()plot(runner, 'fw-acc.pdf')

?

4.3.4 性能評價

?使用測試數(shù)據(jù)對訓(xùn)練完成后的最優(yōu)模型進(jìn)行評價,觀察模型在測試集上的準(zhǔn)確率以及l(fā)oss情況。代碼如下:

#性能評價 # 模型評價 score, loss = runner.evaluate([X_test, y_test]) print("[Test] score/loss: {:.4f}/{:.4f}".format(score, loss))

?

從結(jié)果來看,模型在測試集上取得了較高的準(zhǔn)確率。

增加一個3個神經(jīng)元的隱藏層,再次實(shí)現(xiàn)二分類,并與1做對比。

其中模型定義代碼修改為:

class Model_MLP_L2_V4(nn.Module):def __init__(self, input_size, hidden_size, hidden_size_3, output_size):super(Model_MLP_L2_V4, self).__init__()self.fc1 = nn.Linear(input_size, hidden_size)normal_(self.fc1.weight, mean=0., std=1.)constant_(self.fc1.bias, val=0.0)self.fc2 = nn.Linear(hidden_size, hidden_size_3)normal_(self.fc2.weight, mean=0., std=1.)constant_(self.fc2.bias, val=0.0)self.fc3 = nn.Linear(hidden_size_3, output_size)normal_(self.fc3.weight, mean=0., std=1.)constant_(self.fc3.bias, val=0.0)self.act_fn = torch.sigmoid# 前向計算def forward(self, inputs):z1 = self.fc1(inputs.to(torch.float32))a1 = self.act_fn(z1)z2 = self.fc2(a1)a2 = self.act_fn(z2)z3 = self.fc3(a2)a3 = self.act_fn(z3)return a3

?模型的訓(xùn)練修改:

# 設(shè)置模型 input_size = 2 hidden_size = 5 hidden_size_3 = 3 output_size = 1 model = Model_MLP_L2_V4(input_size=input_size, hidden_size=hidden_size, hidden_size_3=3, output_size=output_size)# 設(shè)置損失函數(shù) loss_fn = F.binary_cross_entropy# 設(shè)置優(yōu)化器 learning_rate = 0.2 optimizer = torch.optim.SGD(model.parameters(), learning_rate)# 設(shè)置評價指標(biāo) metric = accuracy# 其他參數(shù) epoch_num = 1000 saved_path = 'best_model.pdparams'# 實(shí)例化RunnerV2類,并傳入訓(xùn)練配置 runner = RunnerV2_2(model, optimizer, metric, loss_fn) runner.train([X_train, y_train], [X_dev, y_dev], num_epochs=epoch_num, log_epochs=50, save_path="best_model.pdparams")

運(yùn)行結(jié)果:

?

可視化結(jié)果:

模型評價:

通過這兩個實(shí)驗(yàn)結(jié)果的對比可以明顯的看出,在增加了一層隱藏神經(jīng)元后,誤差和學(xué)習(xí)率有了明顯提升。

【思考題】

自定義梯度計算自動梯度計算:

自定義梯度計算:是利用公式手動推導(dǎo)出相應(yīng)的表達(dá)式,然后再將其寫到程序中去,并實(shí)現(xiàn)計算。

自動梯度計算:是利用torch中的相應(yīng)的已經(jīng)定義好的函數(shù)根據(jù)鏈?zhǔn)椒▌t求導(dǎo),計算結(jié)果。
自動梯度計算不管從時間還是結(jié)果上都要優(yōu)于自定義梯度計算。

4.4 優(yōu)化問題

在本節(jié)中,我們通過實(shí)踐來發(fā)現(xiàn)神經(jīng)網(wǎng)絡(luò)模型的優(yōu)化問題,并思考如何改進(jìn)。
4.4.1 參數(shù)初始化

實(shí)現(xiàn)一個神經(jīng)網(wǎng)絡(luò)前,需要先初始化模型參數(shù)。

如果對每一層的權(quán)重和偏置都用0初始化,那么通過第一遍前向計算,所有隱藏層神經(jīng)元的激活值都相同;在反向傳播時,所有權(quán)重的更新也都相同,這樣會導(dǎo)致隱藏層神經(jīng)元沒有差異性,出現(xiàn)對稱權(quán)重現(xiàn)象。

接下來,將模型參數(shù)全都初始化為0,看實(shí)驗(yàn)結(jié)果。這里重新定義了一個類,兩個線性層的參數(shù)全都初始化為0
?

import torch import torch.nn as nn import torch.nn.functional as F from torch.nn.init import constant_, normal_, uniform_class Model_MLP_L2_V4(nn.Module):def __init__(self, input_size, hidden_size, output_size):super(Model_MLP_L2_V4, self).__init__()self.fc1 = nn.Linear(input_size, hidden_size)constant_(self.fc1.weight, val=0.0)constant_(self.fc1.bias, val=0.0)self.fc2 = nn.Linear(hidden_size, output_size)constant_(self.fc2.weight, val=0.0)constant_(self.fc2.bias, val=0.0)self.act_fn = torch.sigmoid# 前向計算def forward(self, inputs):z1 = self.fc1(inputs)a1 = self.act_fn(z1)z2 = self.fc2(a1)a2 = self.act_fn(z2)return a2

?

def print_weights(runner):print('The weights of the Layers:')for item in runner.model.named_parameters():print(item)

利用Runner類訓(xùn)練模型:

# 設(shè)置模型 input_size = 2 hidden_size = 5 output_size = 1 model = Model_MLP_L2_V4(input_size=input_size, hidden_size=hidden_size, output_size=output_size)# 設(shè)置損失函數(shù) loss_fn = F.binary_cross_entropy# 設(shè)置優(yōu)化器 learning_rate = 0.2 # 5e-2 optimizer = torch.optim.SGD(model.parameters(), learning_rate)# 設(shè)置評價指標(biāo) metric = accuracy# 其他參數(shù) epoch = 2000 saved_path = 'best_model.pdparams'# 實(shí)例化RunnerV2類,并傳入訓(xùn)練配置 runner = RunnerV2_2(model, optimizer, metric, loss_fn) runner.train([X_train, y_train], [X_dev, y_dev], num_epochs=5, log_epochs=50, save_path="best_model.pdparams", custom_print_log=print_weights)

?

?可視化訓(xùn)練和驗(yàn)證集上的主準(zhǔn)確率和loss變化:?

def plot(runner, fig_name):plt.figure(figsize=(10, 5))epochs = [i for i in range(len(runner.train_scores))]plt.subplot(1, 2, 1)plt.plot(epochs, runner.train_loss, color='#e4007f', label="Train loss")plt.plot(epochs, runner.dev_loss, color='#f19ec2', linestyle='--', label="Dev loss")# 繪制坐標(biāo)軸和圖例plt.ylabel("loss", fontsize='large')plt.xlabel("epoch", fontsize='large')plt.legend(loc='upper right', fontsize='x-large')plt.subplot(1, 2, 2)plt.plot(epochs, runner.train_scores, color='#e4007f', label="Train accuracy")plt.plot(epochs, runner.dev_scores, color='#f19ec2', linestyle='--', label="Dev accuracy")# 繪制坐標(biāo)軸和圖例plt.ylabel("score", fontsize='large')plt.xlabel("epoch", fontsize='large')plt.legend(loc='lower right', fontsize='x-large')plt.savefig(fig_name)plt.show()plot(runner, "fw-zero.pdf")

?從輸出結(jié)果看,二分類準(zhǔn)確率為50%左右,說明模型沒有學(xué)到任何內(nèi)容。訓(xùn)練和驗(yàn)證loss幾乎沒有怎么下降。

為了避免對稱權(quán)重現(xiàn)象,可以使用高斯分布或均勻分布初始化神經(jīng)網(wǎng)絡(luò)的參數(shù)。

高斯分布和均勻分布采樣的實(shí)現(xiàn)和可視化代碼如下:

gausian_weights = torch.normal(mean=0.0, std=1.0, size=[10000]) uniform_weights = torch.Tensor(10000) uniform_weights.uniform_(-1, 1) # 繪制兩種參數(shù)分布 plt.figure() plt.subplot(1, 2, 1) plt.title('Gausian Distribution') plt.hist(gausian_weights, bins=200, density=True, color='#f19ec2') plt.subplot(1, 2, 2) plt.title('Uniform Distribution') plt.hist(uniform_weights, bins=200, density=True, color='#e4007f') plt.savefig('fw-gausian-uniform.pdf') plt.show()

?

4.4.2 梯度消失問題

在神經(jīng)網(wǎng)絡(luò)的構(gòu)建過程中,隨著網(wǎng)絡(luò)層數(shù)的增加,理論上網(wǎng)絡(luò)的擬合能力也應(yīng)該是越來越好的。但是隨著網(wǎng)絡(luò)變深,參數(shù)學(xué)習(xí)更加困難,容易出現(xiàn)梯度消失問題。

由于Sigmoid型函數(shù)的飽和性,飽和區(qū)的導(dǎo)數(shù)更接近于0,誤差經(jīng)過每一層傳遞都會不斷衰減。當(dāng)網(wǎng)絡(luò)層數(shù)很深時,梯度就會不停衰減,甚至消失,使得整個網(wǎng)絡(luò)很難訓(xùn)練,這就是所謂的梯度消失問題。
在深度神經(jīng)網(wǎng)絡(luò)中,減輕梯度消失問題的方法有很多種,一種簡單有效的方式就是使用導(dǎo)數(shù)比較大的激活函數(shù),如:ReLU。
下面通過一個簡單的實(shí)驗(yàn)觀察前饋神經(jīng)網(wǎng)絡(luò)的梯度消失現(xiàn)象和改進(jìn)方法。
4.4.2.1 模型構(gòu)建

定義一個前饋神經(jīng)網(wǎng)絡(luò),包含4個隱藏層和1個輸出層,通過傳入的參數(shù)指定激活函數(shù)。代碼實(shí)現(xiàn)如下:
?

a = torch.Tensor(10000)class Model_MLP_L5(nn.Module):def __init__(self, input_size, output_size, act='sigmoid', w_init=normal_(a, mean=0.0, std=0.01), b_init=constant_(a, val=1.0)):super(Model_MLP_L5, self).__init__()self.fc1 = torch.nn.Linear(input_size, 3)self.fc2 = torch.nn.Linear(3, 3)self.fc3 = torch.nn.Linear(3, 3)self.fc4 = torch.nn.Linear(3, 3)self.fc5 = torch.nn.Linear(3, output_size)# 定義網(wǎng)絡(luò)使用的激活函數(shù)if act == 'sigmoid':self.act = torch.sigmoidelif act == 'relu':self.act = F.reluelif act == 'lrelu':self.act = F.leaky_reluelse:raise ValueError("Please enter sigmoid relu or lrelu!")# 初始化線性層權(quán)重和偏置參數(shù)self.init_weights(w_init, b_init)# 初始化線性層權(quán)重和偏置參數(shù)def init_weights(self, w_init, b_init):# 使用'named_sublayers'遍歷所有網(wǎng)絡(luò)層for n, m in self.named_parameters():# 如果是線性層,則使用指定方式進(jìn)行參數(shù)初始化if isinstance(m, nn.Linear):w_init(m.weight)b_init(m.bias)def forward(self, inputs):outputs = self.fc1(inputs)outputs = self.act(outputs)outputs = self.fc2(outputs)outputs = self.act(outputs)outputs = self.fc3(outputs)outputs = self.act(outputs)outputs = self.fc4(outputs)outputs = self.act(outputs)outputs = self.fc5(outputs)outputs = torch.sigmoid(outputs)return outputs

?

4.4.2.2 使用Sigmoid型函數(shù)進(jìn)行訓(xùn)練

使用Sigmoid型函數(shù)作為激活函數(shù),為了便于觀察梯度消失現(xiàn)象,只進(jìn)行一輪網(wǎng)絡(luò)優(yōu)化。代碼實(shí)現(xiàn)如下:

定義梯度打印函數(shù)

def print_grads(runner):# 打印每一層的權(quán)重的模print('The gradient of the Layers:')for name, item in runner.model.named_parameters():if len(item.size()) == 2:print(name, torch.norm(input=item, p=2)) # 學(xué)習(xí)率大小 lr = 0.01 # 定義網(wǎng)絡(luò),激活函數(shù)使用sigmoid model = Model_MLP_L5(input_size=2, output_size=1, act='sigmoid') # 定義優(yōu)化器 optimizer = torch.optim.SGD(lr=lr, params=model.parameters()) # 定義損失函數(shù),使用交叉熵?fù)p失函數(shù) loss_fn = F.binary_cross_entropy # 定義評價指標(biāo) metric = accuracy # 指定梯度打印函數(shù) custom_print_log=print_grads # 實(shí)例化Runner類 runner = RunnerV2_2(model, optimizer, metric, loss_fn) # 啟動訓(xùn)練 runner.train([X_train, y_train], [X_dev, y_dev], num_epochs=1, log_epochs=None, save_path="best_model.pdparams", custom_print_log=custom_print_log)

實(shí)例化RunnerV2_2類,并傳入訓(xùn)練配置。代碼實(shí)現(xiàn)如下:

# 實(shí)例化Runner類 runner = RunnerV2_2(model, optimizer, metric, loss_fn)

?模型訓(xùn)練,打印網(wǎng)絡(luò)每層梯度值的?2范數(shù)。代碼實(shí)現(xiàn)如下:

# 啟動訓(xùn)練 runner.train([X_train, y_train], [X_dev, y_dev], num_epochs=1, log_epochs=None, save_path="best_model.pdparams", custom_print_log=custom_print_log)

運(yùn)行結(jié)果:

觀察實(shí)驗(yàn)結(jié)果可以發(fā)現(xiàn),梯度經(jīng)過每一個神經(jīng)層的傳遞都會不斷衰減,最終傳遞到第一個神經(jīng)層時,梯度幾乎完全消失。

4.4.2.3 使用ReLU函數(shù)進(jìn)行模型訓(xùn)練

torch.manual_seed(102) lr = 0.01 # 學(xué)習(xí)率大小 # 定義網(wǎng)絡(luò),激活函數(shù)使用relu model = Model_MLP_L5(input_size=2, output_size=1, act='relu') # 定義優(yōu)化器 optimizer = torch.optim.SGD(lr=lr, params=model.parameters()) # 定義損失函數(shù) # 定義損失函數(shù),這里使用交叉熵?fù)p失函數(shù) loss_fn = F.binary_cross_entropy # 定義評估指標(biāo) metric = accuracy # 實(shí)例化Runner runner = RunnerV2_2(model, optimizer, metric, loss_fn) # 啟動訓(xùn)練 runner.train([X_train, y_train], [X_dev, y_dev], num_epochs=1, log_epochs=None, save_path="best_model.pdparams", custom_print_log=custom_print_log)

?下圖展示了使用不同激活函數(shù)時,網(wǎng)絡(luò)每層梯度值的?2?2范數(shù)情況。從結(jié)果可以看到,5層的全連接前饋神經(jīng)網(wǎng)絡(luò)使用Sigmoid型函數(shù)作為激活函數(shù)時,梯度經(jīng)過每一個神經(jīng)層的傳遞都會不斷衰減,最終傳遞到第一個神經(jīng)層時,梯度幾乎完全消失。改為ReLU激活函數(shù)后,梯度消失現(xiàn)象得到了緩解,每一層的參數(shù)都具有梯度值。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 網(wǎng)絡(luò)每層梯度的L2范數(shù)變化趨勢

4.4.3 死亡ReLU問題

ReLU激活函數(shù)可以一定程度上改善梯度消失問題,但是在某些情況下容易出現(xiàn)死亡ReLU問題,使得網(wǎng)絡(luò)難以訓(xùn)練。

這是由于當(dāng)x<0x<0時,ReLU函數(shù)的輸出恒為0。在訓(xùn)練過程中,如果參數(shù)在一次不恰當(dāng)?shù)母潞?#xff0c;某個ReLU神經(jīng)元在所有訓(xùn)練數(shù)據(jù)上都不能被激活(即輸出為0),那么這個神經(jīng)元自身參數(shù)的梯度永遠(yuǎn)都會是0,在以后的訓(xùn)練過程中永遠(yuǎn)都不能被激活。

一種簡單有效的優(yōu)化方式就是將激活函數(shù)更換為Leaky ReLU、ELU等ReLU的變種。
4.4.3.1 使用ReLU進(jìn)行模型訓(xùn)練 ?

使用第4.4.2節(jié)中定義的多層全連接前饋網(wǎng)絡(luò)進(jìn)行實(shí)驗(yàn),使用ReLU作為激活函數(shù),觀察死亡ReLU現(xiàn)象和優(yōu)化方法。當(dāng)神經(jīng)層的偏置被初始化為一個相對于權(quán)重較大的負(fù)值時,可以想像,輸入經(jīng)過神經(jīng)層的處理,最終的輸出會為負(fù)值,從而導(dǎo)致死亡ReLU現(xiàn)象。
?

# 定義網(wǎng)絡(luò),并使用較大的負(fù)值來初始化偏置 model = Model_MLP_L5(input_size=2, output_size=1, act='relu', b_init=torch.tensor(-8.0))

?實(shí)例化RunnerV2類,啟動模型訓(xùn)練,打印網(wǎng)絡(luò)每層梯度值的?2?2范數(shù)。代碼實(shí)現(xiàn)如下:

# 實(shí)例化Runner類 runner = RunnerV2_2(model, optimizer, metric, loss_fn) # 啟動訓(xùn)練 runner.train([X_train, y_train], [X_dev, y_dev], num_epochs=1, log_epochs=0, save_path="best_model.pdparams", custom_print_log=custom_print_log)

?

從輸出結(jié)果可以發(fā)現(xiàn),使用 ReLU 作為激活函數(shù),當(dāng)滿足條件時,會發(fā)生死亡ReLU問題,網(wǎng)絡(luò)訓(xùn)練過程中 ReLU 神經(jīng)元的梯度始終為0,參數(shù)無法更新。

針對死亡ReLU問題,一種簡單有效的優(yōu)化方式就是將激活函數(shù)更換為Leaky ReLU、ELU等ReLU 的變種。接下來,觀察將激活函數(shù)更換為 Leaky ReLU時的梯度情況。


4.4.3.2 使用Leaky ReLU進(jìn)行模型訓(xùn)練

將激活函數(shù)更換為Leaky ReLU進(jìn)行模型訓(xùn)練,觀察梯度情況。代碼實(shí)現(xiàn)如下:
?

# 重新定義網(wǎng)絡(luò),使用Leaky ReLU激活函數(shù) model = Model_MLP_L5(input_size=2, output_size=1, act='lrelu', b_init=constant_(a, val=-8.0))# 實(shí)例化Runner類 runner = RunnerV2_2(model, optimizer, metric, loss_fn)# 啟動訓(xùn)練 runner.train([X_train, y_train], [X_dev, y_dev], num_epochs=1, log_epochps=None, save_path="best_model.pdparams", custom_print_log=custom_print_log)

?從輸出結(jié)果可以看到,將激活函數(shù)更換為Leaky ReLU后,死亡ReLU問題得到了改善,梯度恢復(fù)正常,參數(shù)也可以正常更新。但是由于 Leaky ReLU 中,x<0x<0 時的斜率默認(rèn)只有0.01,所以反向傳播時,隨著網(wǎng)絡(luò)層數(shù)的加深,梯度值越來越小。如果想要改善這一現(xiàn)象,將 Leaky ReLU 中,x<0x<0 時的斜率調(diào)大即可。

參考

NNDL 實(shí)驗(yàn)4(上) - HBU_DAVID - 博客園

五元錢的博客_CSDN博客-深度學(xué)習(xí),深度學(xué)習(xí)作業(yè)領(lǐng)域博主

pytorch教程之nn.Module類詳解——使用Module類來自定義模型_LoveMIss-Y的博客-CSDN博客_nn.moudle

機(jī)器學(xué)習(xí)中的數(shù)學(xué)——激活函數(shù)(四):Leaky ReLU函數(shù)_von Neumann的博客-CSDN博客_leaky relu函數(shù)

體會

paddlepaddle和pytorch之間一些函數(shù)的轉(zhuǎn)換,以及自定義梯度計算和自動梯度計算之間的區(qū)別。學(xué)習(xí)了神經(jīng)網(wǎng)絡(luò)模型的優(yōu)化問題以及死亡regu問題。更加深入了解了自定義梯度計算和自動梯度計算之間的區(qū)別。有所收獲。

總結(jié)

以上是生活随笔為你收集整理的NNDL实验五 前馈神经网络(2)自动梯度计算 优化问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。