上一篇讲了防止过拟合的一种方式,权重衰减,也即在loss上加上一部分\(\frac{\lambda}{2n} \|\boldsymbol{w}\|^2\),从而使得w不至于过大,即不过分偏向某个特征.

这一篇介绍另一种防止过拟合的方法,dropout,即丢弃某些神经元的输出.由于每次训练的过程里,丢弃掉哪些神经元的输出都是随机的,从而可以使得模型不过分依赖于某些神经元的输出,从而达到防止过拟合的目的.

需要注意的一点是:并不是简单地丢弃掉某些神经元的输出,对留下的输出,我们要改变他们的值,以保证丢弃前后的输出的期望不变

比如,对如下神经网络:输入个数为4,隐藏单元个数为5,且隐藏单元\(h_i\)(\(i=1, \ldots, 5\))的计算表达式为

\[h_i = \phi\left(x_1 w_{1i} + x_2 w_{2i} + x_3 w_{3i} + x_4 w_{4i} + b_i\right)
\]

这里\(\phi\)是激活函数,\(x_1, \ldots, x_4\)是输入,隐藏单元\(i\)的权重参数为\(w_{1i}, \ldots, w_{4i}\),偏差参数为\(b_i\)。当对该隐藏层使用丢弃法时,该层的隐藏单元将有一定概率被丢弃掉。设丢弃概率为\(p\),那么有\(p\)的概率\(h_i\)会被清零,有\(1-p\)的概率\(h_i\)会除以\(1-p\)做拉伸。丢弃概率是丢弃法的超参数。具体来说,设随机变量\(\xi_i\)为0和1的概率分别为\(p\)和\(1-p\)。使用丢弃法时我们计算新的隐藏单元\(h_i'\)

\[h_i' = \frac{\xi_i}{1-p} h_i
\]

由于\(E(\xi_i) = 1-p\),因此

\[E(h_i') = \frac{E(\xi_i)}{1-p}h_i = h_i
\]

从零开始实现

以一个三层的多层感知机为例.两个隐藏层的输出个数都是256.

导入必要的包

import torch
import torch.nn as nn
import numpy as np
import sys
import utils

utils.py中包含了常用的一些实现,比如数据集加载,sgd实现之类的函数都实现在了这个文件里.

实现dropout

def dropout(X,drop_prob):
X = X.float()
keep_prob = 1 - drop_prob if keep_prob == 0:
return torch.zeros_like(X)
mask = (torch.rand(X.shape) < keep_prob).float()
#print("mask:\n",mask) return mask * X/keep_prob X=torch.arange(16).view(2,8)

drop_prob是丢弃的概率.其中我们用torch.rand(X.shape) < keep_prob去生成一组bool数,表明要丢弃哪些,保留哪些.

关于torch中几种分布的用法,参考https://zhuanlan.zhihu.com/p/31231210

  • 均匀分布

    torch.rand(*sizes, out=None) → Tensor

    返回一个张量,包含了从区间[0, 1)的均匀分布中抽取的一组随机数。张量的形状由参数sizes定义。

  • 标准正态分布

    torch.randn(*sizes, out=None) → Tensor

    返回一个张量,包含了从标准正态分布(均值为0,方差为1,即高斯白噪声)中抽取的一组随机数。张量的形状由参数sizes定义。

  • 离散正态分布

    torch.normal(means, std, out=None) → → Tensor

    返回一个张量,包含了从指定均值means和标准差std的离散正态分布中抽取的一组随机数。

    标准差std是一个张量,包含每个输出元素相关的正态分布标准差。

  • 线性间距向量

    torch.linspace(start, end, steps=100, out=None) → Tensor

    返回一个1维张量,包含在区间start和end上均匀间隔的step个点。

    输出张量的长度由steps决定。

加载数据

batch_size,num_workers = 256,4
train_iter,test_iter = utils.load_data(batch_size,num_workers)

utils.py中

def load_data(batch_size,num_workers):
mnist_train = torchvision.datasets.FashionMNIST(root='/home/sc/disk/keepgoing/learn_pytorch/Datasets/FashionMNIST',
train=True, download=True,
transform=transforms.ToTensor())
mnist_test = torchvision.datasets.FashionMNIST(root='/home/sc/disk/keepgoing/learn_pytorch/Datasets/FashionMNIST',
train=False, download=True,
transform=transforms.ToTensor()) train_iter = torch.utils.data.DataLoader(
mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
test_iter = torch.utils.data.DataLoader(
mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers) return train_iter,test_iter

定义模型参数

num_inputs, num_outputs, num_hiddens1, num_hiddens2 = 784, 10, 256, 256

W1 = torch.tensor(np.random.normal(0, 0.01, size=(num_inputs, num_hiddens1)), dtype=torch.float, requires_grad=True)
b1 = torch.zeros(num_hiddens1, requires_grad=True)
W2 = torch.tensor(np.random.normal(0, 0.01, size=(num_hiddens1, num_hiddens2)), dtype=torch.float, requires_grad=True)
b2 = torch.zeros(num_hiddens2, requires_grad=True)
W3 = torch.tensor(np.random.normal(0, 0.01, size=(num_hiddens2, num_outputs)), dtype=torch.float, requires_grad=True)
b3 = torch.zeros(num_outputs, requires_grad=True) params = [W1, b1, W2, b2, W3, b3]

模型定义

drop_prob1, drop_prob2 = 0.2, 0.5
def net(X, is_training=True):
X = X.view(-1, num_inputs)
H1 = (torch.matmul(X, W1) + b1).relu()
if is_training: # 只在训练模型时使用丢弃法
H1 = dropout(H1, drop_prob1) # 在第一层全连接后添加丢弃层
H2 = (torch.matmul(H1, W2) + b2).relu()
if is_training:
H2 = dropout(H2, drop_prob2) # 在第二层全连接后添加丢弃层
return torch.matmul(H2, W3) + b3

通常,倾向于在更靠近原始输入的层,drop概率更低.

精度评估函数定义

def evaluate_accuracy(data_iter, net):
acc_sum, n = 0.0, 0
for X, y in data_iter:
if isinstance(net, torch.nn.Module):
net.eval() # 评估模式, 这会关闭dropout
acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
net.train() # 改回训练模式
else: # 自定义的模型
if('is_training' in net.__code__.co_varnames): # 如果有is_training这个参数
# 将is_training设置成False
acc_sum += (net(X, is_training=False).argmax(dim=1) == y).float().sum().item()
else:
acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
n += y.shape[0]
return acc_sum / n

损失函数

loss = nn.CrossEntropyLoss()

优化算法

def sgd(params, lr, batch_size):
for param in params:
param.data -= lr * param.grad / batch_size # 注意这里更改param时用的param.data

训练

注意一点:这里的loss我们是直接用的torch的nn.CrossEntropyLoss(),这个loss已经是除了batch_size,是求了均值的.所以l很小,相应的梯度也很小.

而更新参数的时候用的是手写的sgd,又除了一次batch_size.所以为了快速收敛,lr要设置大一点.

def train(train_iter,test_iter,num_epochs,net,loss,params,sgd,batch_size,lr):
for epoch in range(num_epochs):
train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
for X,y in train_iter:
y_hat=net(X) #前向传播
"""
这里的loss我们是直接用的torch的nn.CrossEntropyLoss(),这个loss是求了均值的.l很小,相应的梯度也很小.
更新参数的时候用的是手写的sgd. lr要设置大一点.
"""
l = loss(y_hat,y).sum()#计算loss #清空梯度
if params is not None and params[0].grad is not None:
for param in params:
param.grad.data.zero_() l.backward()#反向传播
sgd(params, lr, batch_size) #更新参数 train_l_sum += l.item()
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
n += y.shape[0]
test_acc = evaluate_accuracy(test_iter, net)
print('epoch %d, loss %.4f, train_acc %.3f,test_acc %.3f'
% (epoch + 1, train_l_sum / n, train_acc_sum/n, test_acc)) num_epochs,lr=5,100
train(train_iter,test_iter,num_epochs,net,loss,params,sgd,batch_size,lr)

输出如下:

epoch 1, loss 0.0045, train_acc 0.558,test_acc 0.736
epoch 2, loss 0.0023, train_acc 0.786,test_acc 0.792
epoch 3, loss 0.0019, train_acc 0.825,test_acc 0.796
epoch 4, loss 0.0017, train_acc 0.839,test_acc 0.845
epoch 5, loss 0.0016, train_acc 0.850,test_acc 0.823

简洁实现

数据加载

同前

模型定义及模型参数初始化

net = nn.Sequential(
utils.FlattenLayer(),
nn.Linear(num_inputs,num_hiddens1),
nn.ReLU(),
nn.Dropout(drop_prob1),
nn.Linear(num_hiddens1,num_hiddens2),
nn.ReLU(),
nn.Dropout(drop_prob2),
nn.Linear(num_hiddens2,10)
)

直接使用nn.Dropout()类.

for param in net.parameters():
nn.init.normal_(param,mean=0,std=0.01)

精度评估函数定义

同前. 要注意的是在测试模式下,我们不想做dropout.要net.eval()关闭dropout.

def evaluate_accuracy(data_iter, net):
acc_sum, n = 0.0, 0
for X, y in data_iter:
if isinstance(net, torch.nn.Module):
net.eval() # 评估模式, 这会关闭dropout
acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
net.train() # 改回训练模式
else: # 自定义的模型
if('is_training' in net.__code__.co_varnames): # 如果有is_training这个参数
# 将is_training设置成False
acc_sum += (net(X, is_training=False).argmax(dim=1) == y).float().sum().item()
else:
acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
n += y.shape[0]
return acc_sum / n

损失函数

loss = nn.CrossEntropyLoss()

优化算法

optimizer = torch.optim.SGD(net.parameters(),lr=0.5)

用torch.optim.SGD(net.parameters(),lr=0.5)表明用随机梯度下降法去计算net.parameters()的梯度.

训练

  • 前向传播,计算输出
  • 计算loss
  • 反向传播,计算梯度
  • 根据梯度,更新参数
  • 循环往复,输入下一个batch的数据
def train_use_torch(train_iter,test_iter,num_epochs,net,loss,params,optimizer,batch_size,lr):
for epoch in range(num_epochs):
train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
for X,y in train_iter:
y_hat = net(X)  #前向传播,计算输出
l = loss(y_hat,y).sum()#计算loss
optimizer.zero_grad()#梯度清空 l.backward()#反向传播,计算梯度 optimizer.step()#根据梯度,更新参数 train_l_sum += l.item()
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
n += y.shape[0] test_acc = evaluate_accuracy(test_iter, net)
print('epoch %d, loss %.4f, train_acc %.3f,test_acc %.3f'
% (epoch + 1, train_l_sum / n, train_acc_sum/n, test_acc)) num_epochs,lr=5,0.5
train_use_torch(train_iter,test_iter,num_epochs,net,loss,params,optimizer,batch_size,lr)

输出

epoch 1, loss 0.0045, train_acc 0.556,test_acc 0.745
epoch 2, loss 0.0023, train_acc 0.787,test_acc 0.817
epoch 3, loss 0.0019, train_acc 0.821,test_acc 0.803
epoch 4, loss 0.0017, train_acc 0.837,test_acc 0.809
epoch 5, loss 0.0016, train_acc 0.847,test_acc 0.840

从头学pytorch(七):dropout防止过拟合的更多相关文章

  1. 从头学pytorch(一):数据操作

    跟着Dive-into-DL-PyTorch.pdf从头开始学pytorch,夯实基础. Tensor创建 创建未初始化的tensor import torch x = torch.empty(5,3 ...

  2. 从头学pytorch(十五):AlexNet

    AlexNet AlexNet是2012年提出的一个模型,并且赢得了ImageNet图像识别挑战赛的冠军.首次证明了由计算机自动学习到的特征可以超越手工设计的特征,对计算机视觉的研究有着极其重要的意义 ...

  3. 从头学pytorch(六):权重衰减

    深度学习中常常会存在过拟合现象,比如当训练数据过少时,训练得到的模型很可能在训练集上表现非常好,但是在测试集上表现不好. 应对过拟合,可以通过数据增强,增大训练集数量.我们这里先不介绍数据增强,先从模 ...

  4. 从头学pytorch(三) 线性回归

    关于什么是线性回归,不多做介绍了.可以参考我以前的博客https://www.cnblogs.com/sdu20112013/p/10186516.html 实现线性回归 分为以下几个部分: 生成数据 ...

  5. 从头学pytorch(十六):VGG NET

    VGG AlexNet在Lenet的基础上增加了几个卷积层,改变了卷积核大小,每一层输出通道数目等,并且取得了很好的效果.但是并没有提出一个简单有效的思路. VGG做到了这一点,提出了可以通过重复使⽤ ...

  6. 从头学pytorch(二十):残差网络resnet

    残差网络ResNet resnet是何凯明大神在2015年提出的.并且获得了当年的ImageNet比赛的冠军. 残差网络具有里程碑的意义,为以后的网络设计提出了一个新的思路. googlenet的思路 ...

  7. 从头学pytorch(二) 自动求梯度

    PyTorch提供的autograd包能够根据输⼊和前向传播过程⾃动构建计算图,并执⾏反向传播. Tensor Tensor的几个重要属性或方法 .requires_grad 设为true的话,ten ...

  8. 从头学pytorch(九):模型构造

    模型构造 nn.Module nn.Module是pytorch中提供的一个类,是所有神经网络模块的基类.我们自定义的模块要继承这个基类. import torch from torch import ...

  9. 从头学pytorch(十二):模型保存和加载

    模型读取和存储 总结下来,就是几个函数 torch.load()/torch.save() 通过python的pickle完成序列化与反序列化.完成内存<-->磁盘转换. Module.s ...

随机推荐

  1. 【NS2】trace 文件格式(转载)

    本文档是对 http://nsnam.isi.edu/nsnam/index.php/NS-2_Trace_Formats > 的翻译. 译注:本文描述的无线格Trace格式已经有些陈旧,现在一 ...

  2. Android Tween和Frame 动画

    关于动画的实现,Android提供了Animation,在Android SDK介绍了2种Animation模式: 1. Tween Animation:通过对场景里的对象不断做图像变换(平移.缩放. ...

  3. python 实现A*算法

    A*作为最常用的路径搜索算法,值得我们去深刻的研究.路径规划项目.先看一下维基百科给的算法解释:https://en.wikipedia.org/wiki/A*_search_algorithm A ...

  4. ACM感想、

    从15年10月开始搞ACM,到如今的16年4月底,已经接近半年了. 大一下学期开始ACM组的人员就不断减少,有的因为坚持不下,有的因为不喜欢,有的没留下理由就走了, 瞬间感觉实验室空了很多很多.现在常 ...

  5. functiils.lru_cache缩短递归时间

    力扣上看到一道题: 假设你正在爬楼梯.需要 n 阶你才能到达楼顶. 每次你可以爬 1 或 2 个台阶.你有多少种不同的方法可以爬到楼顶呢? 注意:给定 n 是一个正整数. 使用普通递归解决,超出时间限 ...

  6. java三大循环结构

    用于处理需要重复执行的操作: 根据判断条件的成立与否,决定程序段落的执行次数,而这个程序段落我们称为循环体: while:事先不需要知道循环执行多少次: do  while:同上,只是至少要执行一次( ...

  7. layer/layui弹出层插件bug

    <button class="layui-btn" lay-submit lay-filter="formDemo" id="layui-btn ...

  8. python3快捷键

    原文:https://blog.csdn.net/pipisorry/article/details/39909057 PyCharm3.0默认快捷键(翻译的) PyCharm Default Key ...

  9. P1105 数列

    题目描述 给定一个正整数 \(k(2 \le k \le 15)\) ,把所有k的方幂及所有有限个互不相等的k的方幂之和构成一个递增的序列,例如,当 \(k = 3\) 时,这个序列是: 1,3,4, ...

  10. Vue vue-resource三种请求数据方式pet,post,jsonp

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...