# 卷积神经网络参数优化策略（一）

#### 前言

###### 1. 普通的全连接神经网络的效果

>>> import network3
>>> from network3 import Network
>>> from network3 import ConvPoolLayer, FullyConnectedLayer, SoftmaxLayer
>>> training_data, validation_data, test_data = network3.load_data_shared()
>>> mini_batch_size = 10
>>> net = Network([
FullyConnectedLayer(n_in=784, n_out=100),
SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(training_data, 60, mini_batch_size, 0.1,
validation_data, test_data)

###### 2.使用卷积神经网络 — 仅一个卷积层

CNN结构

>>> net = Network([
ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28),
filter_shape=(20, 1, 5, 5),
poolsize=(2, 2)),
FullyConnectedLayer(n_in=20*12*12, n_out=100),
SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(training_data, 60, mini_batch_size, 0.1,
validation_data, test_data)


###### 3.使用卷积神经网络 — 两个卷积层

>>> net = Network([
ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28),
filter_shape=(20, 1, 5, 5),
poolsize=(2, 2)),
ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12),
filter_shape=(40, 20, 5, 5),
poolsize=(2, 2)),
FullyConnectedLayer(n_in=40*4*4, n_out=100),
SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(training_data, 60, mini_batch_size, 0.1,
validation_data, test_data)


###### 4.使用卷积神经网络 — 两个卷积层+线性修正单元(ReLU)+正则化

f(z)=max(0,z)
,我们选择60个迭代期，学习速率η=0.03, ，使用L2正则化，正则化参数λ=0.1,代码如下

>>> from network3 import ReLU
>>> net = Network([
ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28),
filter_shape=(20, 1, 5, 5),
poolsize=(2, 2),
activation_fn=ReLU),
ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12),
filter_shape=(40, 20, 5, 5),
poolsize=(2, 2),
activation_fn=ReLU),
FullyConnectedLayer(n_in=40*4*4, n_out=100, activation_fn=ReLU),
SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(training_data, 60, mini_batch_size, 0.03,
validation_data, test_data, lmbda=0.1)


###### 5.使用卷积神经网络 — 两个卷基层+线性修正单元(ReLU)+正则化+拓展数据集

>>> expanded_training_data, _, _ = network3.load_data_shared(
"../data/mnist_expanded.pkl.gz")
>>> net = Network([
ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28),
filter_shape=(20, 1, 5, 5),
poolsize=(2, 2),
activation_fn=ReLU),
ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12),
filter_shape=(40, 20, 5, 5),
poolsize=(2, 2),
activation_fn=ReLU),
FullyConnectedLayer(n_in=40*4*4, n_out=100, activation_fn=ReLU),
SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(expanded_training_data, 60, mini_batch_size, 0.03,
validation_data, test_data, lmbda=0.1)


###### 6.使用卷积神经网络 — 两个卷基层+线性修正单元(ReLU)+正则化+拓展数据集+继续插入额外的全连接层

>>> net = Network([
ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28),
filter_shape=(20, 1, 5, 5),
poolsize=(2, 2),
activation_fn=ReLU),
ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12),
filter_shape=(40, 20, 5, 5),
poolsize=(2, 2),
activation_fn=ReLU),
FullyConnectedLayer(n_in=40*4*4, n_out=100, activation_fn=ReLU),
FullyConnectedLayer(n_in=100, n_out=100, activation_fn=ReLU),
SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(expanded_training_data, 60, mini_batch_size, 0.03,
validation_data, test_data, lmbda=0.1)


###### 7.使用卷积神经网络 — 两个卷基层+线性修正单元(ReLU)+拓展数据集+继续插入额外的全连接层+弃权技术

>>> net = Network([
ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28),
filter_shape=(20, 1, 5, 5),
poolsize=(2, 2),
activation_fn=ReLU),
ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12),
filter_shape=(40, 20, 5, 5),
poolsize=(2, 2),
activation_fn=ReLU),
FullyConnectedLayer(
n_in=40*4*4, n_out=1000, activation_fn=ReLU, p_dropout=0.5),
FullyConnectedLayer(
n_in=1000, n_out=1000, activation_fn=ReLU, p_dropout=0.5),
SoftmaxLayer(n_in=1000, n_out=10, p_dropout=0.5)],
mini_batch_size)
>>> net.SGD(expanded_training_data, 40, mini_batch_size, 0.03,
validation_data, test_data)


###### 8.使用卷积神经网络 — 两个卷基层+线性修正单元(ReLU)+正则化+拓展数据集+继续插入额外的全连接层+弃权技术+组合网络

1. 使用卷积层极大地减小了全连接层中的参数的数目，使学习的问题更容易
2. 使用更多强有力的规范化技术（尤其是弃权和卷积）来减小过度拟合，
3. 使用修正线性单元而不是S型神经元，来加速训练-依据经验，通常是3-5倍，
4. 使用GPU来计算
5. 利用充分大的数据集，避免过拟合
6. 使用正确的代价函数，避免学习减速
7. 使用好的权重初始化，避免因为神经元饱和引起的学习减速

CNN超参数优化和可视化技巧详解

Dropout方法

当使用多层更深的隐藏层全连接网络时，参数量会变得非常巨大，达到数十亿量级；而采用CNN结构，则可以层间共享权重，极大减小待训练的参数量；同时可采用二维卷积，保留图像的空间结构信息；采用池化层，进一步减少参数计算。
一般来说，提高泛化能力的方法主要有： 正则化、增加神经网络层数、改变激活函数与代价函数、使用好的权重初始化技术、人为扩展训练集、弃权技术。
下面以MNIST为例，结合CNN、Pooling、Fc结构，通过不同的网络结构变化，给这些参数优化理论一个直观的验证结果。

CNN不同网络结构性能比较CNN不同网络结构性能比较

1、使用L2正则化，dropout技术，扩展数据集等，有效缓解过拟合，提升了性能；
2、使用ReLU，导数为常量，可以缓解梯度下降问题，并加速训练；
3、增加Conv/Pooling与Fc层，可以改善性能。（我自己实测也是如此）

Note：
1、网络并非越深越好，单纯的Conv/Pooling/Fc结构，增加到一定深度后由于过拟合性能反而下降。

Xavier初始法论文：http://jmlr.org/proceedings/papers/v9/glorot10a/glorot10a.pdf

He初始化论文：https://arxiv.org/abs/1502.01852

uniform均匀分布初始化： w = np.random.uniform(low=-scale, high=scale, size=[n_in,n_out])
Xavier初始法，适用于普通激活函数(tanh,sigmoid)：scale = np.sqrt(3/n)
He初始化，适用于ReLU：scale = np.sqrt(6/n)
normal高斯分布初始化： w = np.random.randn(n_in,n_out) * stdev # stdev为高斯分布的标准差，均值设为0
Xavier初始法，适用于普通激活函数 (tanh,sigmoid)：stdev = np.sqrt(n)
He初始化，适用于ReLU：stdev = np.sqrt(2/n)
svd初始化：对RNN有比较好的效果。参考论文：https://arxiv.org/abs/1312.6120

zero-center ,这个挺常用的. X -= np.mean(X, axis = 0) # zero-center X /= np.std(X, axis = 0) # normalize
PCA whitening,这个用的比较少.

clip c(梯度裁剪): 限制最大梯度,其实是value = sqrt(w12+w22….),如果value超过了阈值,就算一个衰减系系数,让value的值等于阈值: 5,10,15

rnn的dim和embdding size,一般从128上下开始调整. batch size,一般从128左右开始调整.batch size合适最重要,并不是越大越好.
word2vec初始化,在小数据上,不仅可以有效提高收敛速度,也可以可以提高结果.

LSTM 的forget gate的bias,用1.0或者更大的值做初始化,可以取得更好的结果,来自这篇论文:http://jmlr.org/proceedings/papers/v37/jozefowicz15.pdf, 我这里实验设成1.0,可以提高收敛速度.实际使用中,不同的任务,可能需要尝试不同的值.
Batch Normalization据说可以提升效果，不过我没有尝试过，建议作为最后提升模型的手段，参考论文：Accelerating Deep Network Training by Reducing Internal Covariate Shift

Ensemble

Ensemble是论文刷结果的终极核武器,深度学习中一般有以下几种方式

（1）relu+bn。这套好基友组合是万精油，可以满足95%的情况，除非有些特殊情况会用identity，比如回归问题，比如resnet的shortcut支路，sigmoid什么的都快从我世界里消失了

（2）dropout 。分类问题用dropout ，只需要最后一层softmax 前用基本就可以了，能够防止过拟合，可能对accuracy提高不大，但是dropout 前面的那层如果是之后要使用的feature的话，性能会大大提升（例如max pool进入fc，实测发现加BN效果非常明显）

（3）数据的shuffle 和augmentation 。这个没啥好说的，aug也不是瞎加，比如行人识别一般就不会加上下翻转的，因为不会碰到头朝下的异型种

（4）降学习率。随着网络训练的进行，学习率要逐渐降下来，如果你有tensorboard，你有可能发现，在学习率下降的一瞬间，网络会有个巨大的性能提升，同样的fine-tuning也要根据模型的性能设置合适的学习率，比如一个训练的已经非常好的模型你上来就1e-3的学习率，那之前就白训练了，就是说网络性能越好，学习率要越小

（5）tensorboard。以前不怎么用，用了之后发现太有帮助，帮助你监视网络的状态，来调整网络参数

（6）随时存档模型，要有validation 。这就跟打游戏一样存档，把每个epoch和其对应的validation 结果存下来，可以分析出开始overfitting的时间点，方便下次加载fine-tuning

（7）网络层数，参数量什么的都不是大问题，在性能不丢的情况下，减到最小

（8）batchsize通常影响没那么大，塞满卡就行，除了特殊的算法需要batch大一点

（9）输入减不减mean归一化在有了bn之后已经不那么重要了

（1）卷积核的分解。从最初的5×5分解为两个3×3，到后来的3×3分解为1×3和3×1，再到resnet的1×1，3×3，1×1，再xception的3×3 channel-wise conv+1×1，网络的计算量越来越小，层数越来越多，性能越来越好，这些都是设计网络时可以借鉴的

（2）不同尺寸的feature maps的concat，只用一层的feature map一把梭可能不如concat好，pspnet就是这种思想，这个思想很常用

（3）resnet的shortcut确实会很有用，重点在于shortcut支路一定要是identity，主路是什么conv都无所谓，这是我亲耳听resnet作者所述

（4）针对于metric learning，对feature加个classification 的约束通常可以提高性能加快收敛

1.样本要足够随机
2.样本要做归一化
3.激活函数要视样本输入选择
4.minibatch很重要，几百到几千是比较合适的(很大数据量的情况下)
6.权重初始化，可用高斯分布乘上一个很小的数

2. Dropout的放置位置以及大小非常重要，求大神能分享经验…
3. Relu并不是一定比Tanh好，如果不太懂的话，用的不合适，可能会导致梯度消失？(不知道是不是网络结构问题，为什么一用relu梯度一会儿就变成Nan)
4. pretrain 的 Embedding在训练中不调优泛化能力要更好一些，调优的话参数会增加好多啊。
另：心得体会
5. 深度学习真是一门实验科学，很多地方解释不了为什么好，为什么不好。
2.如果你机器配置很不到位，也没有人带你，毕业设计千万别选深度学习，天天愁，好坑啊。

Model Ensembles:

1.增加每个step的轮数
2.early stop
3.用小一些的学习率warmup
4.回退到更大的学习率
5.nesterov momentum sgd
6.搜索初始学习率

1.better initialization helps a lot

2.use minibatch and choose batch_size(must)

3.use batch_norm &dropout

5.plot the learning rate curve

6.plot the loss curve.

7.lstm &gru are almost always better than sample RNN

8.use better framework(like tensorflow with tensorboard)

9.find hyper parameters used most often in paper

10 pray

cnn的调参主要是在优化函数、embedding的维度还要残差网络的层数几个方面。

embedding随着维度的增大会出现一个最大值点，也就是开始时是随维度的增加效果逐渐变好，到达一个点后，而后随维度的增加，效果会变差。

1.无论是cnn还是rnn，batch normalization都有用，不一定结果提高几个点，收敛快多了
2.数据初始时normalize得好，有时候直接提高2个点，比如cifar10，转到yuv下normalize再scn
3.loss不降了lr就除10

1. 训练数据
许多 ML 开发者习惯把原始训练数据直接扔给 DNN——为什么不这么做呢？既然任何 DNN （大多数人的假设）仍然能够给出不错的结果，不是吗？但是，有句老话叫“给定恰当的数据类型，一个简单的模型能比复杂 DNN 提供更好、更快的结果”。虽然这有一些例外，但在今天，这句话仍然没有过时。因此，不管你是在计算机视觉（ CV），自然语言处理（NLP）还是统计建模（Statistical Modelling）等领域，想要对原始数据预处理，有几个方法可以得到更好的训练数据：

Data Augmentation（数据扩张）——生成新样例。以图像为例，重新调节，增加噪声等等。

1. 选择恰当的激励函数（activation function）
激励函数是所有神经网络的核心部分之一。

1. 隐藏单元和隐层（Hidden Units and Layers）的数量
保留超出最优数量的隐藏单元，一般是比较保险的做法。这是因为任何正则化方法（ regularization method）都会处理好超出的单元，至少在某种程度上是这样。在另一方面，保留比最优数量更少的隐藏单元，会导致更高的模型欠拟合（underfitting）几率。

“你只需不停增加层，直到测试误差不再减少。”

1. 权重初始化 （Weight Initialization）
永远用小的随机数字初始化权重，以打破不同单元间的对称性（symmetry）。但权重应该是多小呢？推荐的上限是多少？用什么概率分布产生随机数字？

1. 学习率
这或许是最重要的超参数之一，调节着学习过程。如果学习率设置得太小，你的模型很可能需要 n 年来收敛。设置得太大，再加上不多的初始训练样本，你的损失可能会极高。一般来说，0.01 的学习率比较保险

1. 超参数调参：扔掉网格搜索，拥抱随机搜索
网格搜索（Grid Search ）在经典机器学习中十分普遍。但它在寻找 DNN 的最优超参数方面一点也不高效。这主要是由于 DNN 尝试不同超参数组合所耗费的时间。随着超参数不断增长，网格搜索需要的计算性能会指数级增长。

1. 学习方法

2. 权重的维度保持为 2 的幂
即便是运行最先进的深度学习模型，使用最新、最强大的计算硬件，内存管理仍然在字节（byte）级别上进行。所以，把参数保持在 64, 128, 512, 1024 等 2 的次方永远是件好事。这也许能帮助分割矩阵和权重，导致学习效率的提升。当用 GPU 运算，这变得更明显。

3. 无监督预训练（Unsupervised Pretraining ）
不管你进行的是 NLP（自然语言处理）、计算机视觉还是语音识别等任务，无监督预训练永远能帮助你训练监督、或其他无监督模型：NLP 中词向量就（Word Vectors）无所不在；你可以用 ImageNet 的数据库，使用无监督方式对你的模型预训练，或是对于两个类别的监督分类；或是更大频域的音频样本，来在扬声器消崎模型（speaker disambiguation model）中使用该信息。

4. Mini-Batch（小批量） 对比随机学习（Stochastic Learning）
训练一个模型的主要目的是学习合适的参数，即产生输入到输出的最优映射。这些参数利用每个训练样本进行调参，不管你决定使用 batch, mini-batch 还是随机学习。当采用随机学习方法时，学习每个训练样本后权重的梯度都会进行调参，向梯度加入噪音（随机学习中“随机”的由来）。这样做的结果十分理想，比如说，训练中加入的噪音使得模型更不容易过拟合。

1. 打乱训练样本
这来自于信息理论（Information Theory）——“学习到一件不太可能发生的事却发生了，比学习一件很可能发生的事已经发生，包含更多的信息。”同样的，把训练样例的顺序随机化（在不同周期，或者 mini-batch），会导致更快的收敛。如果模型看到的很多样例不在同一种顺序下，运算速度会有小幅提升。

2. 使用 Dropout 正则化
如果有数百万的参数需要学习，正则化就是避免 DNN 过拟合的必须手段。你也可以继续使用 L1/L2 正则化，但 Dropout 是检查 DNN 过拟合的更好方式（雷锋网按：Dropout 是指随机让网络某些隐层节点的权重不工作，不工作的那些节点可以暂时认为不是网络结构的一部分，但是它的权重会保留下来）。执行 Dropout 很容易，并且通常能带来更快地学习。0.5 的默认值是一个不错的选择，当然，这取决于具体任务。如果模型不太复杂，0.2 的 Dropout 值或许就够了。

1. 周期 / 训练迭代次数
“对深度学习模型进行多个周期的训练，会得到更好的模型”——我们经常听到这句话。但多少周期才是“多”呢？其实，这里有一个简单的策略：继续按照一个固定的样例数或者周期训练模型，比如两万个样例或者一个周期。在每批样例之后，比较测试误差（test error）和训练误差（train error），如果它们的差距在缩小，那么继续训练。另外，记得在每批训练之后，保存模型的参数，所以训练好之后你可以从多个模型中做选择。

2. 可视化
训练深度学习模型有上千种出差错的方式。我猜大家都遇到过这样的场景：模型已经训练了几个小时或者好几天，然而在训练完成之后，才意识到某个地方出问题了。为了不让你自己神经错乱，一定要对训练过程作可视化处理。比较显而易见的措施是保存或打印损失值、训练误差、测试误差等项目的日志。

1. 使用支持 GPU 和自动微分法 (Automatic Differentiation）的库
谢天谢地，对于快速创建原型模型，我们已经有了相当不错的库，比如 Theano, Tensorflow, Keras 等等。几乎所有这些深度学习库支持 GPU 计算和自动微分法。所以，你不需要深入研究核心 GPU 编程技术（除非你想——这绝对很有意思）。你也不需要写自己的微分代码——在非常复杂的模型上这相当费劲（但若需要，你应该有能力去做）。 Tensorflow还提供了分布式计算的支持——如果你是土豪的话.
07-01 5125

07-08 4545
10-15 8202
10-23 1万+
01-03 7796
05-24 3
04-30 148
11-24 745
12-02 1316
03-17 1135