Dropout丢弃法是为了防止模型过拟合,它会每个批次随机的将部分数据置为0,在网络传递的过程不起任何作用,经过多个批次的dropout的作用形成最终的模型参数
失活一定的权重就不会造成过拟合,缺少部分权重的影响导致模型的容量变小,进而本次学习的任务不会过多,不会学习到个别的噪音
我们假设有一个向量为X=[x1,x2,x3,x4,x5]
丢弃法虽然会将一部分数据置为0,但是它不会改变数据的期望
我们假设丢弃的概率为p
现在的期望就变成了E(x1)=0*p+(1-p)*x1/(1-p)=x1
可以看到并不会发生改变
而且有一个地方,经常有的图会标记,dropout就是让每个隐藏层的部分节点失活不发生作用,我感觉应该是不对的,dropout是将部分数据进行置为0,那么说明失活的应该是层与层之间的权重连线,应该是部分权重不起作用了,而不是整个神经元全部失活
因为每次的传播都会选择性失活一定的权重,如果第一次某个权重不起作用,它可能在第二次的传播中进行更新权重,经过多个epoch即不同批次的迭代,每个权重都会被训练到
import torch
import torch.nn as nn
import torchvision
from torchvision import transforms
from torch.utils import data
# 将图片数据转化成张量
data_transform=transforms.ToTensor()
# 加载图片数据集
train_dataset=torchvision.datasets.FashionMNIST(root='./data',train=True,download=False,transform=data_transform)
val_dataset=torchvision.datasets.FashionMNIST(root='./data',train=False,download=False,transform=data_transform)
batch_size=128
# 加载批次数据
train_loader=data.DataLoader(train_dataset,batch_size,shuffle=True)
val_loader=data.DataLoader(val_dataset,batch_size,shuffle=False)
从零实现Dropout
这里定义dropout函数
如果是0,说明不丢弃任何数据,返回原数据
如果为1,则说明全部丢弃,返回同样形状的0矩阵
否则随机生成一个符合标准正态分布的随机张量,然后按位置进行判断和概率p的大小
然后将布尔张量转化成浮点,即1.0或0.0
之后将每个位置的值进行相应的改变,丢弃的就变为0,否则将其放大(除以1-p)
def dropout(x,p):
if p==0:
return x
if p==1:
return torch.zeors_like(x)
mask=torch.randn_like(x).gt(p).float()
return mask*x/(1-p)
定义模型需要指定是否使用dropout,在forward传播函数中进行判断
class Net(nn.Module):
def __init__(self,is_train):
super(Net,self).__init__()
self.is_train=is_train
self.layer1=nn.Linear(784,256)
self.layer2=nn.Linear(256,256)
self.layer3=nn.Linear(256,10)
self.relu=nn.ReLU()
def forward(self,x):
x=x.reshape(-1,784)
out=self.layer1(x)
out=self.relu(out)
if self.is_train:
out=dropout(out,0.3)
out=self.layer2(out)
out=self.relu(out)
if self.is_train:
out=dropout(out,0.5)
out=self.layer3(out)
return out
定义损失函数为交叉熵
优化器为SGD,且学习率为0.03
然后扫描整个数据集3次
net=Net(True)
loss_function=nn.CrossEntropyLoss()
optimizer=torch.optim.SGD(net.parameters(),lr=0.03)
epochs=3
for epoch in range(epochs):
net.train()
for x,y in train_loader:
y_hat=net(x)
loss=loss_function(y_hat,y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
accuracy=0.0
with torch.no_grad():
net.eval()
for x,y in val_loader:
y_hat=net(x)
y_pred=torch.argmax(y_hat,dim=1)
accuracy+=torch.eq(y_pred,y).sum().item()
accuracy=accuracy/len(val_dataset)
print('epoch {},accuracy {:.4f},loss {:.4f}'.format(epoch+1,accuracy,loss.item()))
epoch 1,accuracy 0.8254,loss 0.2865
epoch 2,accuracy 0.8447,loss 0.4892
epoch 3,accuracy 0.8438,loss 0.2258
简洁实现
简洁实现较为方便,利用Sequential将各个层与层之间的操作容纳起来
这里在两个隐藏层定义了两个dropout函数
dropout1=0.3
dropout2=0.5
net=nn.Sequential(nn.Flatten(),
nn.Linear(784,256),
nn.ReLU(),
nn.Dropout(dropout1),
nn.Linear(256,256),
nn.ReLU(),
nn.Dropout(dropout2),
nn.Linear(256,10))
loss_function=nn.CrossEntropyLoss()
optimizer=torch.optim.SGD(net.parameters(),lr=0.03)
epochs=3
for epoch in range(epochs):
net.train()
for x,y in train_loader:
y_hat=net(x)
loss=loss_function(y_hat,y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
accuracy=0.0
with torch.no_grad():
net.eval()
for x,y in val_loader:
y_hat=net(x)
y_pred=torch.argmax(y_hat,dim=1)
accuracy+=torch.eq(y_pred,y).sum().item()
accuracy=accuracy/len(val_dataset)
print('epoch {},accuracy {:.4f},loss {:.4f}'.format(epoch+1,accuracy,loss.item()))
epoch 1,accuracy 0.7176,loss 0.7700
epoch 2,accuracy 0.7732,loss 0.6294
epoch 3,accuracy 0.8057,loss 0.5933