Task03 梯度消失、梯度爆炸、Kaggle房价预测

梯度消失、梯度爆炸以及Kaggle房价预测

深度模型有关数值稳定性的典型问题是消失(vanishing)和爆炸(explosion)。
当神经网络的层数较多时,模型的数值稳定性容易变差。

假设一个层数为 L 的多层感知机的第 l 层 H(l) 的权重参数为 W(l) ,输出层 H(L) 的权重参数为 W(L) 。为了便于讨论,不考虑偏差参数,且设所有隐藏层的激活函数为恒等映射(identity mapping) ϕ(x)=x 。给定输入 X ,多层感知机的第 l 层的输出 H(l)=XW(1)W(2)…W(l) 。此时,如果层数 l 较大, H(l) 的计算可能会出现衰减或爆炸。举个例子,假设输入和所有层的权重参数都是标量,如权重参数为0.2和5,多层感知机的第30层输出为输入 X 分别与 0.230≈1×10−21 (消失)和 530≈9×1020 (爆炸)的乘积。当层数较多时,梯度的计算也容易出现消失或爆炸。

1.1 随机初始化模型参数

在神经网络中,通常需要随机初始化模型参数。下面我们来解释这样做的原因。

回顾多层感知机一节描述的多层感知机。为了方便解释,假设输出层只保留一个输出单元(删去和以及指向它们的箭头),且隐藏层使用相同的激活函数。如果将每个隐藏单元的参数都初始化为相等的值,那么在正向传播时每个隐藏单元将根据相同的输入计算出相同的值,并传递至输出层。在反向传播中,每个隐藏单元的参数梯度值相等。因此,这些参数在使用基于梯度的优化算法迭代后值依然相等。之后的迭代也是如此。在这种情况下,无论隐藏单元有多少,隐藏层本质上只有1个隐藏单元在发挥作用。因此,正如在前面的实验中所做的那样,我们通常将神经网络的模型参数,特别是权重参数,进行随机初始化。
多层感知机模型

•PyTorch的默认随机初始化

随机初始化模型参数的方法有很多。在线性回归的简洁实现中,我们使用torch.nn.init.normal_()使模型net的权重参数采用正态分布的随机初始化方式。不过,PyTorch中nn.Module的模块参数都采取了较为合理的初始化策略(不同类型的layer具体采样的哪一种初始化方法的可参考源代码),因此一般不用我们考虑。

•Xavier随机初始化

还有一种比较常用的随机初始化方法叫作Xavier随机初始化。 假设某全连接层的输入个数为,输出个数为,Xavier随机初始化将使该层中权重参数的每个元素都随机采样于均匀分布

它的设计主要考虑到,模型参数初始化后,每层输出的方差不该受该层输入个数影响,且每层梯度的方差也不该受该层输出个数影响。

1.2 Kaggle 房价预测实战 pytorch代码

#kaggle 房价预测
# 使用pandas库读入并处理数据

# pip install pandas
import torch
import torch.utils.data as data
import torch.nn as nn
import numpy as np
import d2lzh_pytorch as d2l
import pandas as pd
import sys


# print(torch.__version__)
torch.set_default_tensor_type(torch.FloatTensor)

train_data = pd.read_csv("Dataset/house-prices-dataset/train.csv")
test_data = pd.read_csv("Dataset/house-prices-dataset/test.csv")

# print(train_data.shape)#(1460,81)1460个样本,80个特征,1个标签
# print(test_data.shape)#(1459,80)1459个样本,80个特征
#
# print(train_data.iloc[0:4,[0,1,2,3,-3,-2,-1]])

# print(train_data.iloc[0:4,[1,-1]])
# print(test_data.iloc[0:4,[1,-1]])

#因为训练以及测试数据的第一项特征是ID,它能帮助模型记住每个训练样本,但是难以推广到测试样本,所以不使用它来进行训练
#我们将所有的训练数据和测试数据的79个特征按样本连结
all_features = pd.concat((train_data.iloc[:,1:-1],test_data.iloc[:,1:]))

#数据预处理 ,对连续数值的特征做标准化:设该特征在整个数据集上的均值为u,标准差为q.那么,我们可以将该特征的每个值先减去u,在除以q,得到标准化后的每个特征值;
# 对于缺失的特征值,我们将其替换成该特征的均值
# pandas里面的object是除了非数值之外的数据表示
numeric_features = all_features.dtypes[all_features.dtypes != 'object'].index
# print(numeric_features)
#每列数值数据进行 先减去u,在除以q,得到标准化后数值
all_features[numeric_features] = all_features[numeric_features].apply(lambda x:(x-x.mean())/(x.std()))
# print(all_features[numeric_features[:3]])
#因为标准化后,每个特征的均值变为0,所以可以用0来替换缺失值
all_features = all_features.fillna(0) #fillna 填充nan数据
# print(all_features['Fence'])
#dummy_na = True 将缺失值也当作合法的特征值并为其创建指示特征
#get_dummies 是利用pandas实现one hot encode(独热编码)的方式
all_features = pd.get_dummies(all_features,dummy_na=True)
print(all_features.shape) #get_dummies后,特征数从79,增加到了354
# print(all_features[:3])
#最后,通过values属性,得到numpy格式数据,并转成ndarray方便后面的训练
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)


#训练模型:使用一个基本的线性回归模型和平方损失函数来训练模型
loss = 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的值设成1,使得取对数时的数值更稳定
        clipped_preds = torch.max(net(features),torch.tensor(1.0))
        rmse = torch.sqrt(2*loss(clipped_preds.log(),labels.log()).mean())
    return rmse.item()

def train(net,train_features,train_labels,test_features,test_labels,num_epochs,lr,weight_decay,batch_size):
    train_ls,test_ls = [],[]
    # print("train:",train_features.shape,train_labels.shape)
    dataset = data.TensorDataset(train_features,train_labels)
    train_iter = data.DataLoader(dataset,batch_size,shuffle=True)
    #adam优化算法
    optimizer = torch.optim.Adam(net.parameters(),lr=lr,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_ls

#k折交叉验证函数,返回k折交叉时所需的训练和验证数据
def get_k_fold_data(k,i,x,y):
    assert k>1
    fold_size = x.shape[0] // k
    x_train,y_train = None,None
    for j in range(k):
        idx = slice( j*fold_size,(j+1)*fold_size)
        x_part,y_part = x[idx,:],y[idx]
        # print('x_part:',j,'/',k,x_part.shape)
        if j == i:
            x_valid,y_valid = x_part,y_part
        elif x_train is None:
            x_train,y_train = x_part,y_part
        else:
            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折交叉验证中,我们训练k次并返回训练和验证的平均误差
def k_fold(k,x_train,y_train,num_epochs,lr,weight_decay,batch_size):
    train_ls_sum,valid_ls_sum = 0,0
    # print('x_train shape',x_train.shape)
    for 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, lr, weight_decay, batch_size)
        train_ls_sum += train_ls[-1]
        valid_ls_sum += valid_ls[-1]
        if i == k-1:
            d2l.semilogy(range(1,num_epochs+1),train_ls,'epochs','rmse',
                         range(1,num_epochs+1),valid_ls,['train','valid'])

        print('fold {},train rmse {:.4f},valid rmse {:.4f}'.format(i,train_ls[-1],valid_ls[-1]))
    return train_ls_sum/k ,valid_ls_sum/k

k,num_epochs,lr,weight_decay,batch_size= 5,120,5,0,64
train_l,valid_l = k_fold(k,train_features,train_labels,num_epochs,lr,weight_decay,batch_size)
print(k,'-fold validation,  avg train rmse:',train_l,', avg valid rmse:',valid_l)

def train_and_pred(train_features,test_features,trian_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('trian rmse:',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('./sunmission.csv',index=False)
    submission.to_csv('Dataset/house-prices-dataset/sunmission.csv', index=False)


train_and_pred(train_features,test_features,train_labels,test_data,num_epochs,lr,weight_decay,batch_size)

semilogy代码如下:

def semilogy(x_vals,y_vals,x_label,y_label,x2_vals=None,y2_vals=None,legend = None,figsize=(3.5,3.5)):
    # dl2.set_figsize(figsize)
    plt.xlabel(x_label)
    plt.ylabel(y_label)
    # print(x_vals,y_vals)
    plt.semilogy(x_vals,y_vals)
    if x2_vals and y2_vals:
        plt.semilogy(x2_vals,y2_vals,linestyle=':')
        plt.legend(legend)
    plt.show()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值