梯度消失和梯度爆炸_从零开始学Pytorch之梯度消失、梯度爆炸
本文首发于微信公众号“计算机视觉cv”
深度模型有关数值稳定性的典型问题是消失(vanishing)和爆炸(explosion)。
当神经网络的层数较多时,模型的数值稳定性容易变差。
假设一个层数为
随机初始化模型参数
在神经网络中,通常需要随机初始化模型参数。下面我们来解释这样做的原因。
回顾多层感知机一节描述的多层感知机。为了方便解释,假设输出层只保留一个输出单元
![](/assets/blank.gif)
PyTorch的默认随机初始化
随机初始化模型参数的方法有很多。在线性回归的简洁实现中,我们使用torch.nn.init.normal_()
使模型net
的权重参数采用正态分布的随机初始化方式。不过,PyTorch中nn.Module
的模块参数都采取了较为合理的初始化策略(不同类型的layer具体采样的哪一种初始化方法的可参考源代码),因此一般不用我们考虑。
Xavier随机初始化
还有一种比较常用的随机初始化方法叫作Xavier随机初始化。 假设某全连接层的输入个数为
它的设计主要考虑到,模型参数初始化后,每层输出的方差不该受该层输入个数影响,且每层梯度的方差也不该受该层输出个数影响。
考虑环境因素
协变量偏移
这里我们假设,虽然输入的分布可能随时间而改变,但是标记函数,即条件分布P(y∣x)不会改变。虽然这个问题容易理解,但在实践中也容易忽视。
想想区分猫和狗的一个例子。我们的训练数据使用的是猫和狗的真实的照片,但是在测试时,我们被要求对猫和狗的卡通图片进行分类。
catcatdogdog
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
测试数据:
catcatdogdog
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
显然,这不太可能奏效。训练集由照片组成,而测试集只包含卡通。在一个看起来与测试集有着本质不同的数据集上进行训练,而不考虑如何适应新的情况,这是不是一个好主意。不幸的是,这是一个非常常见的陷阱。
统计学家称这种协变量变化是因为问题的根源在于特征分布的变化(即协变量的变化)。数学上,我们可以说P(x)改变了,但P(y∣x)保持不变。尽管它的有用性并不局限于此,当我们认为x导致y时,协变量移位通常是正确的假设。
标签偏移
当我们认为导致偏移的是标签P(y)上的边缘分布的变化,但类条件分布是不变的P(x∣y)时,就会出现相反的问题。当我们认为y导致x时,标签偏移是一个合理的假设。例如,通常我们希望根据其表现来预测诊断结果。在这种情况下,我们认为诊断引起的表现,即疾病引起的症状。有时标签偏移和协变量移位假设可以同时成立。例如,当真正的标签函数是确定的和不变的,那么协变量偏移将始终保持,包括如果标签偏移也保持。有趣的是,当我们期望标签偏移和协变量偏移保持时,使用来自标签偏移假设的方法通常是有利的。这是因为这些方法倾向于操作看起来像标签的对象,这(在深度学习中)与处理看起来像输入的对象(在深度学习中)相比相对容易一些。
病因(要预测的诊断结果)导致 症状(观察到的结果)。
训练数据集,数据很少只包含流感p(y)的样本。
而测试数据集有流感p(y)和流感q(y),其中不变的是流感症状p(x|y)。
概念偏移
另一个相关的问题出现在概念转换中,即标签本身的定义发生变化的情况。这听起来很奇怪,毕竟猫就是猫。的确,猫的定义可能不会改变,但我们能不能对软饮料也这么说呢?事实证明,如果我们周游美国,按地理位置转移数据来源,我们会发现,即使是如图所示的这个简单术语的定义也会发生相当大的概念转变。
![](/assets/blank.gif)
如果我们要建立一个机器翻译系统,分布P(y∣x)可能因我们的位置而异。这个问题很难发现。另一个可取之处是P(y∣x)通常只是逐渐变化。
Kaggle 房价预测实战
让我们动手实战一个Kaggle比赛:房价预测。本节将提供未经调优的数据的预处理、模型的设计和超参数的选择。
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
torch.set_default_tensor_type(torch.FloatTensor)
获取和读取数据集
比赛数据分为训练数据集和测试数据集。两个数据集都包括每栋房子的特征,如街道类型、建造年份、房顶类型、地下室状况等特征值。这些特征值有连续的数字、离散的标签甚至是缺失值“na”。只有训练数据集包括了每栋房子的价格,也就是标签。我们可以访问比赛网页,点击“Data”标签,并下载这些数据集。
我们将通过pandas
库读入并处理数据。在导入本节需要的包前请确保已安装pandas
库。 假设解压后的数据位于/home/kesci/input/houseprices2807/
目录,它包括两个csv文件。下面使用pandas
读取这两个文件。
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")
预处理数据
我们对连续数值的特征做标准化(standardization):设该特征在整个数据集上的均值为
numeric_features = all_features.dtypes[all_features.dtypes != 'object'].index
all_features[numeric_features] = all_features[numeric_features].apply(lambda x: (x - x.mean()) / (x.std()))
# 标准化后,每个数值特征的均值变为0,所以可以直接用0来替换缺失值
all_features[numeric_features] = all_features[numeric_features].fillna(0)
接下来将离散数值转成指示特征。举个例子,假设特征MSZoning里面有两个不同的离散值RL和RM,那么这一步转换将去掉MSZoning特征,并新加两个特征MSZoning_RL和MSZoning_RM,其值为0或1。如果一个样本原来在MSZoning里的值为RL,那么有MSZoning_RL=1且MSZoning_RM=0。
# dummy_na=True将缺失值也当作合法的特征值并为其创建指示特征
all_features = pd.get_dummies(all_features, dummy_na=True) #利用pandas实现one hot encode的方式
#最后,通过`values`属性得到NumPy格式的数据,并转成`Tensor`方便后面的训练。
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 = 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) #normal_为正态分布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()
下面的训练函数跟本章中前几节的不同在于使用了Adam优化算法。相对之前使用的小批量随机梯度下降,它对学习率相对不那么敏感。
def train(net, train_features, train_labels, test_features, test_labels,num_epochs, learning_rate, weight_decay, batch_size):train_ls, test_ls = [], []dataset = torch.utils.data.TensorDataset(train_features, train_labels)train_iter = torch.utils.data.DataLoader(dataset, batch_size, shuffle=True)# 这里使用了Adam优化算法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_ls
K折交叉验证
我们在模型选择、欠拟合和过拟合中介绍了
i
折交叉验证时所需要的训练和验证数据。
def get_k_fold_data(k, i, X, y):# 返回第i折交叉验证时所需要的训练和验证数据assert k > 1fold_size = X.shape[0] // kX_train, y_train = None, Nonefor j in range(k):idx = slice(j * fold_size, (j + 1) * fold_size)X_part, y_part = X[idx, :], y[idx]if j == i:X_valid, y_valid = X_part, y_partelif X_train is None:X_train, y_train = X_part, y_partelse: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_validdef 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
模型选择
我们使用一组未经调优的超参数并计算交叉验证误差。可以改动这些超参数来尽可能减小平均测试误差。 有时候你会发现一组参数的训练误差可以达到很低,但是在
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))
![](/assets/blank.gif)
预测并在Kaggle中提交结果
下面定义预测函数。在预测之前,我们会使用完整的训练数据集来重新训练模型,并将预测结果存成提交所需要的格式。
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")
设计好模型并调好超参数之后,下一步就是对测试数据集上的房屋样本做价格预测。如果我们得到与交叉验证时差不多的训练误差,那么这个结果很可能是理想的,可以在Kaggle上提交结果。
train_and_pred(train_features, test_features, train_labels, test_data, num_epochs, lr, weight_decay, batch_size)
![](/assets/blank.gif)
梯度消失和梯度爆炸_从零开始学Pytorch之梯度消失、梯度爆炸相关推荐
- mpandroidchart y轴从0开始_从零开始学Pytorch(十七)之目标检测基础
目标检测和边界框 %matplotlib inline from PIL import Imageimport sys sys.path.append('/home/input/') #数据集路径 i ...
- 从零开始学Pytorch(第5天)
从零开始学Pytorch(第5天) 前言 一.模块类的构建 1. nn.Module 2.构建一个线性回归类 二.计算图和自动求导机制 1.计算图 2.自动求导 总结 前言 今天主要了解和学习Pyto ...
- 从零开始学Pytorch(零)之安装Pytorch
本文首发于公众号"计算机视觉cv" Pytorch优势 聊聊为什么使用Pytorch,个人觉得Pytorch比Tensorflow对新手更为友善,而且现在Pytorch在学术界 ...
- 跟我从零开始学python之一_从零开始学 Python 之运算符
从零开始学 Python 之运算符 前言 大家好,这里是「痴海」从零开始学习 Python 系列教程.此文首发于「痴海」公众号,欢迎大家去关注.学习一门语言最好的办法,就是教懂别人.在这公众号,我会从 ...
- r语言中矩阵QR分解_从零开始学R语言Day4|向量、矩阵和数组
从零开始学R语言Day4|向量.矩阵和数组 1.1向量 1.1.1向量 在Day2中我们提及过用和c()函数来构建向量,具体实例如下. 我们还可以采用vector("类型",长度) ...
- 从零开始学python人工智能课程_从零开始学人工智能(12)--Python · 决策树(零)· 简介...
原标题:从零开始学人工智能(12)--Python · 决策树(零)· 简介 感谢关注天善智能,走好数据之路↑↑↑ 欢迎关注天善智能,我们是专注于商业智能BI,人工智能AI,大数据分析与挖掘领域的垂直 ...
- xenserver 虚拟机扩容lvm磁盘分区的方法_从零开始学Linux运维|35.LVM(逻辑卷管理)的创建...
简单来说 LVM是建立在硬盘和分区之上的一个逻辑层,提高磁盘分区管理的灵活性 如果没有使用LVM,我们直接访问文件系统读取硬盘内容 使用了LVM我们是通过逻辑卷的东西来读取硬盘内容 使用LVM增加一个 ...
- python实现素数筛选法_从零开始学Python系列-第6讲:循环结构
应用场景 我们在写程序的时候,一定会遇到需要重复执行某条或某些指令的场景.例如用程序控制机器人踢足球,如果机器人持球而且还没有进入射门范围,那么我们就要一直发出让机器人向球门方向移动的指令.在这个场景 ...
- linux apache设置web访问重定向_从零开始学Linux运维|30.Linux的目录结构
1.tree命令 linux下目录结构跟一个倒过来的树一样的,最顶层就是根目录 / tree这个命令就很形象 它够很方便的查看目录结构 使用"yum install tree -y" ...
最新文章
- oracle如何复制dept,[oracle]表复制的sql语句
- java也可以做黑客?
- android闹钟详细设计,基于LabVIEW的闹钟设计 详细文档+程序
- 【渝粤教育】国家开放大学2018年春季 0699-22T阅读与写作 参考试题
- 基于Java+SpringBoot+vue+element实现物流管理系统
- linux+下c语言编程项目,精通UNIX下C语言编程与项目实践
- 计算机打印服务设置s,Print Spooler 服务
- cesium绘制网格_Cesium (五) 栅格图层
- 抖音seo,抖音优化系统,抖音seo矩阵系统源码技术搭建
- 双系统时间不一致问题
- 基于R的飞机航线数据可视化(行政区划)
- 篮球爱好和程序的结合:C#生成NBA赛事预告页面
- 快手用最简单的方式感动每一个独特的你 原来背后的黑科技才是真的666
- BDL程序搬迁环境应注意的问题
- java毕业设计旅拍平台源码+lw文档+mybatis+系统+mysql数据库+调试
- 维基百科创建需要注意哪些问题?
- ES5 ES6相关内容 day15
- 云计算需要python吗_国内python云计算是啥
- Math.atan和Math.atan2函数
- 济南计算机学院排名,济南市最好的10所大学排名,山大第1,山东师范第3,济南大学第7...
热门文章
- 计算机软件硬件边界如何定义,软件定义汽车,但别忘了硬件定义了软件的边界...
- 如何使用amplifycolor调色插件
- 【论文阅读】DeepJS: Job Scheduling Based on DRL in Cloud Data Center
- TOOLFK工具-在线OCR图片文字识别工具
- python123平台作业答案进制转换_各种进制转换详解-python
- 爬取马蜂窝用户评论界面中的用户ID、用户名以及评分
- 原子类型:AtomicReference详解
- python列表元素零的移动
- go-micro接口调用
- 如何成为web前端大神以及进阶之路