1.dropout原理
(1)Dropout出现的原因
在机器学习的模型中,如果模型的参数太多,而训练样本又太少,训练出来的模型很容易产生过拟合的现象。在训练神经网络的时候经常会遇到过拟合的问题,过拟合具体表现在:模型在训练数据上损失函数较小,预测准确率较高;但是在测试数据上损失函数比较大,预测准确率较低。
过拟合是很多机器学习的通病。如果模型过拟合,那么得到的模型几乎不能用。为了解决过拟合问题,一般会采用模型集成的方法,即训练多个模型进行组合。此时,训练模型费时就成为一个很大的问题,不仅训练多个模型费时,测试多个模型也是很费时。
综上所述,训练深度神经网络的时候,总是会遇到两大缺点:
(1)容易过拟合
(2)费时
Dropout可以比较有效的缓解过拟合的发生,在一定程度上达到正则化的效果。
(2)什么是dropout
在2012年,Hinton在其论文《Improving neural networks by preventing co-adaptation of feature detectors》中提出Dropout。当一个复杂的前馈神经网络被训练在小的数据集时,容易造成过拟合。为了防止过拟合,可以通过阻止特征检测器的共同作用来提高神经网络的性能。
在2012年,Alex、Hinton在其论文《ImageNet Classification with Deep Convolutional Neural Networks》中用到了Dropout算法,用于防止过拟合。并且,这篇论文提到的AlexNet网络模型引爆了神经网络应用热潮,并赢得了2012年图像识别大赛冠军,使得CNN成为图像分类上的核心算法模型。
随后,又有一些关于Dropout的文章《Dropout:A Simple Way to Prevent Neural Networks from Overfitting》、《Improving Neural Networks with Dropout》、《Dropout as data augmentation》。
从上面的论文中,我们能感受到Dropout在深度学习中的重要性。那么,到底什么是Dropout呢?
Dropout可以作为训练深度神经网络的一种trick供选择。在每个训练批次中,通过忽略一半的特征检测器(让一半的隐层节点值为0),可以明显地减少过拟合现象。这种方式可以减少特征检测器(隐层节点)间的相互作用,检测器相互作用是指某些检测器依赖其他检测器才能发挥作用。
Dropout说的简单一点就是:我们在前向传播的时候,让某个神经元的激活值以一定的概率p停止工作,这样可以使模型泛化性更强,因为它不会太依赖某些局部的特征,如图1所示。
![](https://i-blog.csdnimg.cn/blog_migrate/cc75436f47bf6ef1a7b77459a54ea166.png)
(3)原理
假设我们要训练这样一个神经网络,如图2所示。
![](https://i-blog.csdnimg.cn/blog_migrate/b8e5d9034b71f4b7aae3c5244ed93063.png)
输入是x输出是y,正常的流程是:我们首先把x通过网络前向传播,然后把误差反向传播以决定如何更新参数让网络进行学习。使用Dropout之后,过程变成如下:
(1)首先随机(临时)删掉网络中一半的隐藏神经元,输入输出神经元保持不变(图3中虚线为部分临时被删除的神经元)
![](https://i-blog.csdnimg.cn/blog_migrate/a15170777720497aea551a145ee684f1.png)
(2) 然后把输入x通过修改后的网络前向传播,然后把得到的损失结果通过修改的网络反向传播。一小批训练样本执行完这个过程后,在没有被删除的神经元上按照随机梯度下降法更新对应的参数(w,b)。
(3)然后继续重复这一过程:
. 恢复被删掉的神经元(此时被删除的神经元保持原样,而没有被删除的神经元已经有所更新)
. 从隐藏层神经元中随机选择一个一半大小的子集临时删除掉(备份被删除神经元的参数)。
. 对一小批训练样本,先前向传播然后反向传播损失并根据随机梯度下降法更新参数(w,b) (没有被删除的那一部分参数得到更新,删除的神经元参数保持被删除前的结果)。
不断重复这一过程。
以上内容出自:https://blog.csdn.net/program_developer/article/details/80737724
2.用代码实现正则化(L1、L2、Dropout)
不用pytorch:
import numpy as np
import matplotlib.pyplot as plt
sigmod = lambda x: 1 / (1 + np.exp(-x))
layer_sizes = lambda x, y: (x.shape[0], 4, y.shape[0])
initialize_parameters = lambda n_x, n_h, n_y: {
"w1": np.random.rand(n_h, n_x) * 0.01,
"b1": np.zeros((n_h, 1)),
"w2": np.random.rand(n_y, n_h) * 0.01,
"b2": np.zeros((n_y, 1))
}
parameters = initialize_parameters(2, 4, 1)
# 前向传播,得到最后的activate and 各种中间变量
def forward_propagetion_with_dropout(x, paramters, keep_prod=0.7):
# 取出参数
w1 = parameters['w1']
b1 = parameters['b1']
w2 = parameters['w2']
b2 = parameters['b2']
# 向前传播
z1 = np.matmul(w1, x) + b1
a1 = np.tanh(z1)
# 这里加入mask,使得其中一些神经元的活动变为0,在反向传播中,不再更新这些节点
# 先创建一个mask
mask1 = (np.random.rand(a1.shape[0], 1) < keep_prod)
a1 = a1 * mask1
a1 = a1 / keep_prod
z2 = np.matmul(w2, a1) + b2
a2 = sigmod(z2)
cache = {
"z1": z1,
"a1": a1,
"mask1": mask1,
"z2": z2,
"a2": a2
}
return a2, cache
# 计算cost
# 一般情况下,与输出层,标签,以及正则项相关。
def compute_cost(a2, y, parameters, lambd=0):
m = y.shape[1]
logprobs = (np.log(a2) * y) + np.log(1 - a2) * (1 - y)
cost = -np.sum(logprobs) / m
l2_loss = np.sum(np.square(parameters['w1'])) + np.sum(np.square(parameters['w2']))
cost = np.squeeze(cost)
return cost + lambd * l2_loss / (2 * m)
# 反向传播
# 根据当前参数下,向前传播产生的中间变量缓存值,以及当前数据x,y得到cost关于每个参数的偏导数
def backward_propagetion_with_dropout(parameters, cache, x, y, lambd=0, keep_prob=0.7):
m = x.shape[1]
w1 = parameters['w1']
w2 = parameters['w2']
a1 = cache['a1']
a2 = cache['a2']
mask1 = cache['mask1']
dz2 = a2 - y
dw2 = np.dot(dz2, a1.T) / m + lambd / m * w2
db2 = np.sum(dz2, axis=1, keepdims=True) / m
# Dropout的关键操作
da1 = np.dot(w2.T, dz2)
da1 = da1 * mask1
da1 = da1 / keep_prob
dz1 = np.multiply(np.dot(w2.T, dz2), (1 - np.power(a1, 2)))
dw1 = np.dot(dz1, x.T) / m + lambd / m * w1
db1 = np.sum(dz1, axis=1, keepdims=True) / m
grads = {
"dw1": dw1,
"db1": db1,
"dw2": dw2,
"db2": db2,
}
return grads
# 参数更新
# 向后传播得到梯度以后就可以更新参数了
def update_parameters(parameters, grads, learning_rate=0.01):
w1 = parameters['w1']
b1 = parameters['b1']
w2 = parameters['w2']
b2 = parameters['b2']
dw1 = learning_rate * grads["dw1"]
db1 = learning_rate * grads["db1"]
dw2 = learning_rate * grads["dw2"]
db2 = learning_rate * grads["db2"]
w1 = w1 - dw1
b1 = b1 - db1
w2 = w2 - dw2
b2 = b2 - db2
parameters = {
"w1": w1,
"b1": b1,
"w2": w2,
"b2": b2
}
return parameters
np.random.seed(1)
m = 200
x = np.random.randn(2, m)
y = (1 + (2 * (x[0, :] > 0) - 1) * (2 * (x[1, :] > 0) - 1)) / 2
y = y.reshape(1, x.shape[1])
num_iterations = 10000
learning_rate = 0.1
n_x, n_h, n_y = 2, 30, 1
costs = []
keep_prob = 0.7
parameters = initialize_parameters(n_x, n_h, n_y)
w1 = parameters['w1']
b1 = parameters['b1']
w2 = parameters['w2']
b2 = parameters['b2']
# loop 每次传递都会选出不同的神经元drop掉,相当于在所有可能的网络构型中进行抽样,这样更容易泛化,减少模型
# 方差
for i in range(num_iterations):
a2, cache = forward_propagetion_with_dropout(x, parameters)
cost = compute_cost(a2, y, parameters)
grads = backward_propagetion_with_dropout(parameters, cache, x, y)
# 关键步骤,参数更新
parameters = update_parameters(parameters, grads, learning_rate)
if i % 1000 == 0:
print("Cost after iteration %i:%f" % (i, cost))
costs.append(cost)
plt.plot(np.squeeze(costs))
plt.ylabel('cost')
plt.ylabel('iterations(per 1000)')
plt.title("learning rate =" + str(learning_rate))
plt.show()
pytorch:只需要一行代码,如下:
from torch import optim
# weight_decay = 0.001 就是l2范数
optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay=0.001)
3.pytorch实现dropout
import numpy as np
import torch
from torch.autograd import Variable
from torch import nn
from torch import optim
xy = np.loadtxt('./data/diabletes.csv.gz', delimiter=',', dtype=np.float32)
x_data = Variable(torch.from_numpy(xy[:, 0: -1]))
y_data = Variable(torch.from_numpy(xy[:, [-1]]))
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
self.l1 = nn.Linear(8, 60)
self.l2 = nn.Linear(60, 4)
self.l3 = nn.Linear(4, 1)
self.sigmoid = nn.Sigmoid()
# 直接调用torch.nn.Dropout
self.dropout = nn.Dropout(p=0.4)
def foward(self, x):
out1 = self.sigmoid(self.l1(x))
out2 = self.dropout(out1)
out3 = self.sigmoid(self.l2(out2))
y_pred = self.sigmoid(self.l3(out3))
return y_pred
model = Model()
# 选择优化器
criterion = nn.BCELoss(size_average=True)
# weight_decay是l2正则
optimizer = optim.Adam(model.parameters(), lr=0.1, weight_decay=0.1)
for epoch in range(100):
y_pred = model(x_data)
loss = criterion(y_pred, y_data)
if epoch % 20 == 0:
print("epoch =", epoch, "loss = ", loss.data[0])
optimizer.zero_grad()
loss.backward()
optimizer.step()
hour_var = Variable(torch.randn(1, 8))
print("predict", model(hour_var).data[0] > 0.5)