深度学习中的Dropout简介及实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/fengbingchun/article/details/89286485

在训练神经网络模型时候,如果模型相对复杂即参数较多而训练样本相对较少,这时候训练出的模型可能对训练集中的数据拟合的比较好,但在测试集上的表现较差,即出现了过拟合的情况。这种情况下可以使用Dropout来降低过拟合的可能性进而提高模型的泛化能力。过拟合指的是模型在训练数据上损失函数比较小,预测准确率较高,但是在测试数据上损失函数比较大,预测准确率较低。Dropout可以随机地临时选择一些中间层中的神经元,使这些神经元不起作用,即在本次迭代中输出为零,同时保持输入层和输出层的神经元数目不变。在反向传播并更新参数的过程中,与这些节点相连的权值也不需要更新。但是这些节点并不从网络中删除,并且其权值也保留下来,以使这些节点在下一次迭代时重新被选中作为起作用点而参与权值的更新。Dropout由Hinton等人于2012年提出,论文名字为《Improving neural networks by preventing co-adaptation of feature detetors》,关于Dropout更详细的介绍可参考另一篇论文,论文名字为《Dropout: A Simple Way to Prevent Neural Networks from Overfitting》,下面的一些截图均来自此论文,论文下载地址:http://jmlr.org/papers/volume15/srivastava14a/srivastava14a.pdf

由于每次使用输入网络的样本进行权值更新时,隐含层的节点都是以一定的概率随机出现,因此不能保证每2个隐含节点每次都能同时出现,这样权值更新将不再依赖于具有固定关系隐含节点的共同作用,阻止了某些特征仅仅在其它特定特征下才有效果的情况。消除或减弱了神经元节点间的联合适应性,增强了泛化能力。也可以将Dropout看做是模型平均的一种。所谓模型平均,就是把来自不同模型的估计或者预测通过一定的权重平均起来,在一些文献中也称为模型组合。因为对于每次输入到网络的样本,由于隐藏层节点的随机性,其对应的网络结构都是不同的,但所有的这些不同的网络结构又同时共享了隐藏层之间的权值。

Dropout神经网络结构如下图所示:

应用Dropout的多层神经网络中,在训练阶段的前向计算过程如下所示:

标准网络与Dropout网络的基本操作比较如下图所示:

Dropout在训练阶段和测试阶段描述如下图所示:

Dropout适合用于数据量大的大型网络中。Dropout应用在训练阶段,用于减少过拟合。Dropout可以与神经网络的大多数层一起使用,如全连接层、卷积层、循环层。Dropout能被实现在任何隐含层或可见层或输入层,Dropout不能被应用在输出层。

Dropout一般执行过程:

(1). 首先随机(临时)删掉网络中一些隐藏神经元即使某些神经元输出为0,输入输出神经元保持不变

(2). 然后把输入通过修改后的网络前向传播,然后把得到的损失结果通过修改的网络反向传播。一小批训练样本执行完这个过程后,在没有被删除的神经元上按照随机梯度下降法更新对应的参数(w, b)。

(3). 然后继续重复这一过程:恢复被删掉的神经元(此时被删除的神经元参数保持不变,而没有被删除的神经元已经有所更新);从隐藏层神经元中再次随机选择一些神经元临时删除掉(备份被删除神经元的参数);对一小批训练样本,先前向传播然后反向传播损失并根据随机梯度下降法更新参数(w,b)(没有被删除的那一部分参数得到更新,删除的神经元参数保持被删除前的结果)。

不断重复这一过程。

以下是测试代码:

#include <time.h>
#include <cmath>
#include <vector>
#include <limits>
#include <string>
#include <tuple>
#include <random>
#include <memory>
#include <random>
#include <opencv2/opencv.hpp>

#define EXP 1.0e-5

namespace fbc {

// ============================ Dropout ================================
template<class T>
int dropout(const T* bottom, int width, int height,  T* top, float dropout_ratio = 0.5f)
{
	if (dropout_ratio <= 0.f || dropout_ratio >= 1.f) {
		fprintf(stderr, "Error: dropout_ratio's value should be: (0., 1.): %f\n", dropout_ratio);
		return -1;
	}

	std::random_device rd; std::mt19937 gen(rd());
	std::bernoulli_distribution d(1. - dropout_ratio);

	int size = height * width;
	std::unique_ptr<int[]> mask(new int[size]);
	for (int i = 0; i < size; ++i) {
		mask[i] = (int)d(gen);
	}

	float scale = 1. / (1. - dropout_ratio);
	for (int i = 0; i < size; ++i)	{
		top[i] = bottom[i] * mask[i] * scale;
	}

	return 0;
}

} // namespace fbc

int test_dropout()
{
	std::random_device rd; std::mt19937 gen(rd());
	int height = 4, width = 8, size = height * width;

	std::unique_ptr<float[]> bottom(new float[size]), top(new float[size]);	
	std::uniform_real_distribution<float> distribution(-10.f, 10.f);
	for (int i = 0; i < size; ++i) {
		bottom[i] = distribution(gen);
	}

	float dropout_ratio = 0.8f;
	if (fbc::dropout(bottom.get(), width, height, top.get(), dropout_ratio) != 0) {
		fprintf(stderr, "Error: fail to dropout\n");
		return -1;
	}

	fprintf(stdout, "bottom data:\n");
	for (int h = 0; h < height; ++h) {
		for (int w = 0; w < width; ++w) {
			fprintf(stdout, " %f ", bottom[h * width + w]);
		}
		fprintf(stdout, "\n");
	}
	
	fprintf(stdout, "top data:\n");
	for (int h = 0; h < height; ++h) {
		for (int w = 0; w < width; ++w) {
			fprintf(stdout, " %f ", top[h * width + w]);
		}
		fprintf(stdout, "\n");
	}

	return 0;
}

执行结果如下图所示:

GitHub: https://github.com/fengbingchun/NN_Test 

猜你喜欢

转载自blog.csdn.net/fengbingchun/article/details/89286485