深度学习实践

前言

从开始接触mxnet的gluon教程开始,我就一直跟着官方教程自己动手做,也参加了kaggle的两个竞赛项目,到目前为止对训练调试过的神经网络是有更直观的了解和认识,对这些网络的优化和调试也多多少少有了一些的经验,所以乘着还没有忘记,赶紧总结记录出来(到目前为止做到的都是监督学习内容,所以本文也是以监督学习为对象说明)。

使用神经网络训练的步骤

这些天看过不少的深度神经网络的设计,也使用mxnet的gluon或者是直接手动编写过一些相对简单的网络来训练一些标准的数据集,体会最深的一点就是不管多么复杂的网络,变化的部分只是网络模型的本身,其他的部分做下来基本上都是属于同一个套路。

实现一个使用神经网络训练数据的项目,一般会有如下步骤:

  1. 整理、导入数据,这是准备工作
  2. 定义网络结构和代价函数,这是算法的核心
  3. 训练,将数据输入网络中,循环迭代
  4. 测试,使用训练好的模型测试实际的数据

以下会对上面四个步骤分别说明。

数据整理和导入

虽然当我们在编写代码时,这只是准备性质的工作,但是在处理实际的机器学习、深度学习的问题时,数据才是核心:不同类型的数据决定了你采用哪一种算法会更好,而在实际训练时,数据的质量直接关系到最终结果的好坏。

深度学习需要大量的数据,然而收集大量的数据是一件很困难的事情,而这也是以前制约深度学习发展的一个很重要的因素。所幸的是,在大数据的今天,数据的收集和存储已经方便很多,对我们普通学习者而言更有利的是,前人已经整理出了不少标准的数据库提供我们使用和研究,如我所用过的:MNIST(手写数字图像)、FishionMNIST(服装图像)、CIFAR-10图像数据集。

在数据整理和导入时,有以下几点要注意。

1. 图像数据的整理

对于图像数据在整理时,训练的数据为了方便导入,一般都是根据类别新建文件夹,各自存放各自的的类型。像mxnet就很提供的图像文件夹导入函数接口,可以很方便的导入图片数据和标签。

2. 一些预处理方法

对于非图像类的原始数据,可能有:无效数据Nan,数据属性取值范围相差过大等等问题。这是我们往往需要做预处理,这就属于数据处理方面的内容了,很多数据处理的方法可以使用。

  • 对于无效数据:我们可以自己差值补全(如插平均值),如果属性Nan值过多,直接删掉也可以这一列
  • 对于属性值范围差异大的问题:对数据做归一化(或者说是标准化)处理

在归一化处理时:对于连续数值型的数据归一化,通常选择减平均值除以标准差的方法——(x-x.mean())/x.std());在处理非数值非连续型的数据时,直接使用one-hot coding进行编码将一个属性变为多个0、1的属性,这样做的原因是可以统一用欧氏距离来评价数据点位置的远近。

定义网络模型和代价函数

目前,我所接触到的神经网络层类型有两种:全连接层或卷积模块,我们定义网络就是将这些层或者模块叠加组合起来。在最简单的单层全连接网络的时候,我有用python手动编写,这在有提供矩阵运算的编程语言中实现是比较简单:

  • 直接用矩阵运算的线性方程组表示网络
  • 根据问题定义代价函数
  • 编写使用微分法求近似偏导函数
  • 编写梯度下降(或随机梯度下降)函数

以上,虽然后面两点都是优化的内容,但是我还是觉得把这些核心算法放在一起比较好。

1. 使用深度学习框架的原因

单层全连接网络可以简单的手动实现,主要还是网络结构简单,求偏导容易。复杂的网络结构就不方便手动编写了,最好是借助各种深度学习框架,这些框架都有自动求导功能,这些基于计算图的求导更准确,且借助BP算法能够计算更深层次网络节点的偏导。另外一点就是复杂的模型,定义其本身就很麻烦,如深层的卷积神经网络,每一层的权重参数、卷积核的大小都需要自己指定这些参数且相互之间还有影响,输入的图片经过这一层一层的网络,形状不断的在改变,而这些网络的参数本身就很让人混淆,再经过一系列变换,就更难确定了。

2. 代价函数

代价函数(Cost Fuction)也可能被称为损失函数(Loss Function),是我们衡量模型预测准确性的衡量的方法,虽然对于不同的问题类型和算法模型可能需要考虑设计不同的代价函数,不过实际代价函数除了需要能够表示预测和真实之间的差距外,还需要考虑要方便做最小化优化,因此常用的代价函数也就并不多。对于回归问题,最常用的代价函数就是均方差了,而对与分类问题最常用的是softmax交叉熵。

3. 梯度下降和随机梯度下降(SGD)

说起优化算法,梯度下降应该算是最简单最经典的吧,虽然有更高级的优化算法存在,但是梯度下降仍然是得非常广泛。不过在实际中梯度下降一般都是以随机梯度下降的形式来使用的。随机梯度下降是梯度下降应用的一种形式,相比普通的梯度下降算法以整个数据集作为输入来作为优化对象(批梯度下降),他每次迭代只在数据集中随机选取一部分输入来优化,它的收敛会更快。

4. 正规方程

当然优化的方法,除了像梯度下降这样逐步迭代,一步一步逼近到最有解,还有可以用解析法直接求解线性方程,不过在基本上都是非线性的深度学习中就不适用了,即使在线性回归这样的简单机器学习问题中,我们也得考虑:输入矩阵是否可逆、求逆计算量大小的问题。

训练与测试

1. 泛化能力的测试

基本上所有的机器学习和深度学习都需要考虑一个问题,那就是模型的泛化能力,它是以判断一个模型好坏一个最重要的标准。为了测试模型的泛化能力,我们通常会将整个过程分为两个步骤:训练、测试。训练是指在训练集上不断循环迭代的使用优化算法将代价函数结果的值不断降低,测试是用训练好的模型测试一批已知结果的数据,检查模型的精度,我们希望模型在测试中的结果能够和在训练中的结果尽量的接近。

训练的数据和测试的数据集通常是不重合的,所以我们需要将原始的输入数据集划分为:训练集和测试集。这其中有不少方法,不过最基本最简单的就是,从原始数据集中按比例随机采样出一部分作为测试集用(通常训练集占绝大多数,测试集占小部分)。在花费时间可以接受的情况下,也可以采取K折验证法,即把原始的数据集随机分成相等的K份,每次取一份作为测试集,其他作为训练集,重复K次让测试集能够选完所有的份数。当然这样时间的开销就比之前多了K倍。

2. 过拟合

导致模型泛化能力弱的很大一部分原因来自过拟合,过拟合的现象就是,模型在训练集上表现非常好,但是在测试集上表现很差。导致过拟合的原理比较多,如:原始数据集量太小,模型过于复杂等。过拟合的情况非常常见,基本上调过机器学习/深度学习算法的可定会遇到它,不过对于过拟合,我们也有很多方法来降低它的影响。

  • Data Augmentation:数据增强,人工增加训练集的大小. 通过平移, 翻转, 加噪声等方法从已有数据中创造出一批”新”的数据
  • Regularization:正则化,通过在Loss Function 后面加上正则项可以抑制过拟合的产生,常见的如L0, L1, L2,他们能够减小大多数特征的影响来,而将影响集中到少数特征上 缺点是引入了一个需要手动调整的hyper-parameter
  • Dropout:这也是一种正则化手段. 不过跟以上不同的是它通过随机将部分神经元的输出置零来实现

3. GPU并行训练

当模型复杂、数据量大的时候,你会深深感受到GPU的可贵,在做cifar-10的实验的时候,我用CPU训练一次迭代就要半个多小时,换上GPU时间立刻缩短到1分半钟。能够很方便的使用GPU、甚至多GPU并行训练,这也是使用深度学习框架很大的优点。

Compartir