梯度消失和梯度爆炸_知识干货-动手学深度学习-05 梯度消失和梯度爆炸以及Kaggle房价预测...
梯度消失和梯度爆炸
深度模型有關(guān)數(shù)值穩(wěn)定性的典型問題是消失(vanishing)和爆炸(explosion)。
當(dāng)神經(jīng)網(wǎng)絡(luò)的層數(shù)較多時(shí),模型的數(shù)值穩(wěn)定性容易變差。
PyTorch的默認(rèn)隨機(jī)初始化
隨機(jī)初始化模型參數(shù)的方法有很多。在線性回歸的簡(jiǎn)潔實(shí)現(xiàn)中,我們使用torch.nn.init.normal_()使模型net的權(quán)重參數(shù)采用正態(tài)分布的隨機(jī)初始化方式。不過(guò),PyTorch中nn.Module的模塊參數(shù)都采取了較為合理的初始化策略(不同類型的layer具體采樣的哪一種初始化方法的可參考源代碼),因此一般不用我們考慮。
鏈接:https://github.com/pytorch/pytorch/tree/master/torch/nn/modules
Xavier隨機(jī)初始化
還有一種比較常用的隨機(jī)初始化方法叫作Xavier隨機(jī)初始化。 假設(shè)某全連接層的輸入個(gè)數(shù)為aa,輸出個(gè)數(shù)為bb,Xavier隨機(jī)初始化將使該層中權(quán)重參數(shù)的每個(gè)元素都隨機(jī)采樣于均勻分布
它的設(shè)計(jì)主要考慮到,模型參數(shù)初始化后,每層輸出的方差不該受該層輸入個(gè)數(shù)影響,且每層梯度的方差也不該受該層輸出個(gè)數(shù)影響。
考慮環(huán)境因素?
協(xié)變量偏移
這里我們假設(shè),雖然輸入的分布可能隨時(shí)間而改變,但是標(biāo)記函數(shù),即條件分布P(y∣x)不會(huì)改變。雖然這個(gè)問題容易理解,但在實(shí)踐中也容易忽視。
想想?yún)^(qū)分貓和狗的一個(gè)例子。我們的訓(xùn)練數(shù)據(jù)使用的是貓和狗的真實(shí)的照片,但是在測(cè)試時(shí),我們被要求對(duì)貓和狗的卡通圖片進(jìn)行分類。
?
顯然,這不太可能奏效。訓(xùn)練集由照片組成,而測(cè)試集只包含卡通。在一個(gè)看起來(lái)與測(cè)試集有著本質(zhì)不同的數(shù)據(jù)集上進(jìn)行訓(xùn)練,而不考慮如何適應(yīng)新的情況,這是不是一個(gè)好主意。不幸的是,這是一個(gè)非常常見的陷阱。
統(tǒng)計(jì)學(xué)家稱這種協(xié)變量變化是因?yàn)閱栴}的根源在于特征分布的變化(即協(xié)變量的變化)。數(shù)學(xué)上,我們可以說(shuō)P(x)改變了,但P(y∣x)保持不變。盡管它的有用性并不局限于此,當(dāng)我們認(rèn)為x導(dǎo)致y時(shí),協(xié)變量移位通常是正確的假設(shè)。
標(biāo)簽偏移
當(dāng)我們認(rèn)為導(dǎo)致偏移的是標(biāo)簽P(y)上的邊緣分布的變化,但類條件分布是不變的P(x∣y)時(shí),就會(huì)出現(xiàn)相反的問題。當(dāng)我們認(rèn)為y導(dǎo)致x時(shí),標(biāo)簽偏移是一個(gè)合理的假設(shè)。例如,通常我們希望根據(jù)其表現(xiàn)來(lái)預(yù)測(cè)診斷結(jié)果。在這種情況下,我們認(rèn)為診斷引起的表現(xiàn),即疾病引起的癥狀。有時(shí)標(biāo)簽偏移和協(xié)變量移位假設(shè)可以同時(shí)成立。例如,當(dāng)真正的標(biāo)簽函數(shù)是確定的和不變的,那么協(xié)變量偏移將始終保持,包括如果標(biāo)簽偏移也保持。有趣的是,當(dāng)我們期望標(biāo)簽偏移和協(xié)變量偏移保持時(shí),使用來(lái)自標(biāo)簽偏移假設(shè)的方法通常是有利的。這是因?yàn)檫@些方法傾向于操作看起來(lái)像標(biāo)簽的對(duì)象,這(在深度學(xué)習(xí)中)與處理看起來(lái)像輸入的對(duì)象(在深度學(xué)習(xí)中)相比相對(duì)容易一些。
病因(要預(yù)測(cè)的診斷結(jié)果)導(dǎo)致 癥狀(觀察到的結(jié)果)。
訓(xùn)練數(shù)據(jù)集,數(shù)據(jù)很少只包含流感p(y)的樣本。
而測(cè)試數(shù)據(jù)集有流感p(y)和流感q(y),其中不變的是流感癥狀p(x|y)。
概念偏移
另一個(gè)相關(guān)的問題出現(xiàn)在概念轉(zhuǎn)換中,即標(biāo)簽本身的定義發(fā)生變化的情況。這聽起來(lái)很奇怪,畢竟貓就是貓。的確,貓的定義可能不會(huì)改變,但我們能不能對(duì)軟飲料也這么說(shuō)呢?事實(shí)證明,如果我們周游美國(guó),按地理位置轉(zhuǎn)移數(shù)據(jù)來(lái)源,我們會(huì)發(fā)現(xiàn),即使是如圖所示的這個(gè)簡(jiǎn)單術(shù)語(yǔ)的定義也會(huì)發(fā)生相當(dāng)大的概念轉(zhuǎn)變。
Kaggle 房?jī)r(jià)預(yù)測(cè)實(shí)戰(zhàn)?
作為深度學(xué)習(xí)基礎(chǔ)篇章的總結(jié),我們將對(duì)本章內(nèi)容學(xué)以致用。下面,讓我們動(dòng)手實(shí)戰(zhàn)一個(gè)Kaggle比賽:房?jī)r(jià)預(yù)測(cè)。本節(jié)將提供未經(jīng)調(diào)優(yōu)的數(shù)據(jù)的預(yù)處理、模型的設(shè)計(jì)和超參數(shù)的選擇。我們希望讀者通過(guò)動(dòng)手操作、仔細(xì)觀察實(shí)驗(yàn)現(xiàn)象、認(rèn)真分析實(shí)驗(yàn)結(jié)果并不斷調(diào)整方法,得到令自己滿意的結(jié)果。
%matplotlib inline import torch import torch.nn as nn import numpy as np import pandas as pd import sys sys.path.append("/home/kesci/input") import d2lzh1981 as d2l print(torch.__version__) torch.set_default_tensor_type(torch.FloatTensor)#設(shè)置默認(rèn)值獲取和讀取數(shù)據(jù)集
比賽數(shù)據(jù)分為訓(xùn)練數(shù)據(jù)集和測(cè)試數(shù)據(jù)集。兩個(gè)數(shù)據(jù)集都包括每棟房子的特征,如街道類型、建造年份、房頂類型、地下室狀況等特征值。這些特征值有連續(xù)的數(shù)字、離散的標(biāo)簽甚至是缺失值“na”。只有訓(xùn)練數(shù)據(jù)集包括了每棟房子的價(jià)格,也就是標(biāo)簽。我們可以訪問比賽網(wǎng)頁(yè),點(diǎn)擊“Data”標(biāo)簽,并下載這些數(shù)據(jù)集。
我們將通過(guò)pandas庫(kù)讀入并處理數(shù)據(jù)。在導(dǎo)入本節(jié)需要的包前請(qǐng)確保已安裝pandas庫(kù)。 假設(shè)解壓后的數(shù)據(jù)位于/home/kesci/input/houseprices2807/目錄,它包括兩個(gè)csv文件。下面使用pandas讀取這兩個(gè)文件
test_data = pd.read_csv("/home/kesci/input/houseprices2807/house-prices-advanced-regression-techniques/test.csv") train_data = pd.read_csv("/home/kesci/input/houseprices2807/house-prices-advanced-regression-techniques/train.csv")#訓(xùn)練數(shù)據(jù)集包括1460個(gè)樣本、80個(gè)特征和1個(gè)標(biāo)簽。 train_data.shape #(1460, 81)#查看前4個(gè)樣本的前4個(gè)特征、后2個(gè)特征和標(biāo)簽(SalePrice): train_data.iloc[0:4, [0, 1, 2, 3, -3, -2, -1]]可以看到第一個(gè)特征是Id,它能幫助模型記住每個(gè)訓(xùn)練樣本,但難以推廣到測(cè)試樣本,所以我們不使用它來(lái)訓(xùn)練。我們將所有的訓(xùn)練數(shù)據(jù)和測(cè)試數(shù)據(jù)的79個(gè)特征按樣本連結(jié)。
all_features = pd.concat((train_data.iloc[:, 1:-1], test_data.iloc[:, 1:])) #(2919, 79)預(yù)處理數(shù)據(jù)
我們對(duì)連續(xù)數(shù)值的特征做標(biāo)準(zhǔn)化(standardization):設(shè)該特征在整個(gè)數(shù)據(jù)集上的均值為μ,標(biāo)準(zhǔn)差為σ。那么,我們可以將該特征的每個(gè)值先減去μ再除以σ得到標(biāo)準(zhǔn)化后的每個(gè)特征值。對(duì)于缺失的特征值,我們將其替換成該特征的均值。
numeric_features = all_features.dtypes[all_features.dtypes != 'object'].index #參考鏈接:https://pandas.pydata.org/pandas-#docs/stable/reference/api/pandas.DataFrame.dtypes.html?#highlight=dtypes#pandas.DataFrame.dtypes all_features[numeric_features] = all_features[numeric_features].apply(lambda x: (x - x.mean()) / (x.std())) # 標(biāo)準(zhǔn)化后,每個(gè)數(shù)值特征的均值變?yōu)?,所以可以直接用0來(lái)替換缺失值 all_features[numeric_features] = all_features[numeric_features].fillna(0) #Fill NA/NaN values using the specified method.接下來(lái)將離散數(shù)值轉(zhuǎn)成指示特征。舉個(gè)例子,假設(shè)特征MSZoning里面有兩個(gè)不同的離散值RL和RM,那么這一步轉(zhuǎn)換將去掉MSZoning特征,并新加兩個(gè)特征MSZoning_RL和MSZoning_RM,其值為0或1。如果一個(gè)樣本原來(lái)在MSZoning里的值為RL,那么有MSZoning_RL=1且MSZoning_RM=0。
# dummy_na=True將缺失值也當(dāng)作合法的特征值并為其創(chuàng)建指示特征 all_features = pd.get_dummies(all_features, dummy_na=True) #Convert categorical variable into dummy/indicator variables. all_features.shape#(2919, 331) #可以看到這一步轉(zhuǎn)換將特征數(shù)從79增加到了331。 #最后,通過(guò)values屬性得到NumPy格式的數(shù)據(jù),并轉(zhuǎn)成Tensor方便后面的訓(xùn)練。 n_train = train_data.shape[0] train_features = torch.tensor(all_features[:n_train].values, dtype=torch.float) test_features = torch.tensor(all_features[n_train:].values, dtype=torch.float) train_labels = torch.tensor(train_data.SalePrice.values, dtype=torch.float).view(-1, 1)#訓(xùn)練模型 loss = torch.nn.MSELoss()def get_net(feature_num):net = nn.Linear(feature_num, 1)for param in net.parameters():nn.init.normal_(param, mean=0, std=0.01)return net??def log_rmse(net, features, labels):with torch.no_grad():# 將小于1的值設(shè)成1,使得取對(duì)數(shù)時(shí)數(shù)值更穩(wěn)定clipped_preds = torch.max(net(features), torch.tensor(1.0))rmse = torch.sqrt(2 * loss(clipped_preds.log(), labels.log()).mean())#感覺寫的有點(diǎn)問題return rmse.item()#下面的訓(xùn)練函數(shù)跟本章中前幾節(jié)的不同在于使用了Adam優(yōu)化算法。相對(duì)之前使用的小批量隨機(jī)梯度下降,它對(duì)學(xué)習(xí)率相對(duì)不那么敏感。我們將在之后的“優(yōu)化算法”一章里詳細(xì)介紹它。def train(net, train_features, train_labels, test_features, test_labels,num_epochs, learning_rate, weight_decay, batch_size):train_ls, test_ls = [], []#定義訓(xùn)練和測(cè)試的loss值dataset = torch.utils.data.TensorDataset(train_features, train_labels)train_iter = torch.utils.data.DataLoader(dataset, batch_size, shuffle=True)# 這里使用了Adam優(yōu)化算法optimizer = torch.optim.Adam(params=net.parameters(), lr=learning_rate, weight_decay=weight_decay) net = net.float()for epoch in range(num_epochs):for X, y in train_iter:l = loss(net(X.float()), y.float())optimizer.zero_grad()l.backward()optimizer.step()train_ls.append(log_rmse(net, train_features, train_labels))if test_labels is not None:test_ls.append(log_rmse(net, test_features, test_labels))return train_ls, test_lsK折交叉驗(yàn)證
我們?cè)谀P瓦x擇、欠擬合和過(guò)擬合中介紹了K折交叉驗(yàn)證。它將被用來(lái)選擇模型設(shè)計(jì)并調(diào)節(jié)超參數(shù)。下面實(shí)現(xiàn)了一個(gè)函數(shù),它返回第i折交叉驗(yàn)證時(shí)所需要的訓(xùn)練和驗(yàn)證數(shù)據(jù)。
def get_k_fold_data(k, i, X, y):# 返回第i折交叉驗(yàn)證時(shí)所需要的訓(xùn)練和驗(yàn)證數(shù)據(jù)assert k > 1fold_size = X.shape[0] // kX_train, y_train = None, Nonefor j in range(k):#這里面還定義了一個(gè)循環(huán),用來(lái)取數(shù)據(jù)idx = slice(j * fold_size, (j + 1) * fold_size)X_part, y_part = X[idx, :], y[idx]if j == i:#當(dāng)相等的時(shí)候就是驗(yàn)證集X_valid, y_valid = X_part, y_partelif X_train is None:#第一次不等的時(shí)候就是訓(xùn)練X_train, y_train = X_part, y_partelse:#第2次以及之后不等的時(shí)候,就累加測(cè)試集X_train = torch.cat((X_train, X_part), dim=0)y_train = torch.cat((y_train, y_part), dim=0)return X_train, y_train, X_valid, y_valid# 在K折交叉驗(yàn)證中我們訓(xùn)練K次并返回訓(xùn)練和驗(yàn)證的平均誤差 def k_fold(k, X_train, y_train, num_epochs,learning_rate, weight_decay, batch_size):train_l_sum, valid_l_sum = 0, 0for i in range(k):data = get_k_fold_data(k, i, X_train, y_train)net = get_net(X_train.shape[1])train_ls, valid_ls = train(net, *data, num_epochs, learning_rate,weight_decay, batch_size)train_l_sum += train_ls[-1]valid_l_sum += valid_ls[-1]if i == 0:d2l.semilogy(range(1, num_epochs + 1), train_ls, 'epochs', 'rmse',range(1, num_epochs + 1), valid_ls,['train', 'valid'])print('fold %d, train rmse %f, valid rmse %f' % (i, train_ls[-1], valid_ls[-1]))return train_l_sum / k, valid_l_sum / k#輸出train 和 test的loss模型選擇
我們使用一組未經(jīng)調(diào)優(yōu)的超參數(shù)并計(jì)算交叉驗(yàn)證誤差。可以改動(dòng)這些超參數(shù)來(lái)盡可能減小平均測(cè)試誤差。 有時(shí)候你會(huì)發(fā)現(xiàn)一組參數(shù)的訓(xùn)練誤差可以達(dá)到很低,但是在KK折交叉驗(yàn)證上的誤差可能反而較高。這種現(xiàn)象很可能是由過(guò)擬合造成的。因此,當(dāng)訓(xùn)練誤差降低時(shí),我們要觀察KK折交叉驗(yàn)證上的誤差是否也相應(yīng)降低。
k, num_epochs, lr, weight_decay, batch_size = 5, 100, 5, 0, 64 train_l, valid_l = k_fold(k, train_features, train_labels, num_epochs, lr, weight_decay, batch_size) print('%d-fold validation: avg train rmse %f, avg valid rmse %f' % (k, train_l, valid_l))預(yù)測(cè)并在Kaggle中提交結(jié)果
下面定義預(yù)測(cè)函數(shù)。在預(yù)測(cè)之前,我們會(huì)使用完整的訓(xùn)練數(shù)據(jù)集來(lái)重新訓(xùn)練模型,并將預(yù)測(cè)結(jié)果存成提交所需要的格式。
def train_and_pred(train_features, test_features, train_labels, test_data,num_epochs, lr, weight_decay, batch_size):net = get_net(train_features.shape[1])train_ls, _ = train(net, train_features, train_labels, None, None,num_epochs, lr, weight_decay, batch_size)d2l.semilogy(range(1, num_epochs + 1), train_ls, 'epochs', 'rmse')print('train rmse %f' % train_ls[-1])preds = net(test_features).detach().numpy()test_data['SalePrice'] = pd.Series(preds.reshape(1, -1)[0])submission = pd.concat([test_data['Id'], test_data['SalePrice']], axis=1)submission.to_csv('./submission.csv', index=False)# sample_submission_data = pd.read_csv("../input/house-prices-advanced-regression-techniques/sample_submission.csv")#設(shè)計(jì)好模型并調(diào)好超參數(shù)之后,下一步就是對(duì)測(cè)試數(shù)據(jù)集上的房屋樣本做價(jià)格預(yù)測(cè)。如果我們得到與交叉驗(yàn)證時(shí)差不多的訓(xùn)練誤差, #那么這個(gè)結(jié)果很可能是理想的,可以在Kaggle上提交結(jié)果。 train_and_pred(train_features, test_features, train_labels, test_data, num_epochs, lr, weight_decay, batch_size)歡迎大家指正和留言評(píng)論
總結(jié)
以上是生活随笔為你收集整理的梯度消失和梯度爆炸_知识干货-动手学深度学习-05 梯度消失和梯度爆炸以及Kaggle房价预测...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python读取mysql以html形式
- 下一篇: sigmoid函数_常用的激活(激励)函