pytorch task5: PyTorch实现L1,L2正则化以及Dropout

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所示。
               

图1:使用Dropout的神经网络模型

(3)原理

假设我们要训练这样一个神经网络,如图2所示。

                                                       

图2:标准的神经网络

输入是x输出是y,正常的流程是:我们首先把x通过网络前向传播,然后把误差反向传播以决定如何更新参数让网络进行学习。使用Dropout之后,过程变成如下:

(1)首先随机(临时)删掉网络中一半的隐藏神经元,输入输出神经元保持不变(图3中虚线为部分临时被删除的神经元)

                                                                                   

图3:部分临时被删除的神经元


(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)

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值