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