PyTorch - 如何在评估模式下关闭Dropout

29

这是我定义的模型,它是一个简单的LSTM,包含2个全连接层。

import copy
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

class mylstm(nn.Module):
    def __init__(self,input_dim, output_dim, hidden_dim,linear_dim):
        super(mylstm, self).__init__()
        self.hidden_dim=hidden_dim
        self.lstm=nn.LSTMCell(input_dim,self.hidden_dim)
        self.linear1=nn.Linear(hidden_dim,linear_dim)
        self.linear2=nn.Linear(linear_dim,output_dim)
    def forward(self, input):
        out,_=self.lstm(input)
        out=nn.Dropout(p=0.3)(out)
        out=self.linear1(out)
        out=nn.Dropout(p=0.3)(out)
        out=self.linear2(out)
        return out

x_trainx_val 是形状为 (4478,30) 的浮点数数据框,而 y_trainy_val 是形状为 (4478,10) 的浮点数数据框。

    x_train.head()
Out[271]: 
       0       1       2       3    ...        26      27      28      29
0  1.6110  1.6100  1.6293  1.6370   ...    1.6870  1.6925  1.6950  1.6905
1  1.6100  1.6293  1.6370  1.6530   ...    1.6925  1.6950  1.6905  1.6960
2  1.6293  1.6370  1.6530  1.6537   ...    1.6950  1.6905  1.6960  1.6930
3  1.6370  1.6530  1.6537  1.6620   ...    1.6905  1.6960  1.6930  1.6955
4  1.6530  1.6537  1.6620  1.6568   ...    1.6960  1.6930  1.6955  1.7040

[5 rows x 30 columns]

x_train.shape
Out[272]: (4478, 30)

定义变量并进行一次反向传播,我可以发现验证损失为1.4941

model=mylstm(30,10,200,100).double()
from torch import optim
optimizer=optim.RMSprop(model.parameters(), lr=0.001, alpha=0.9)
criterion=nn.L1Loss()
input_=torch.autograd.Variable(torch.from_numpy(np.array(x_train)))
target=torch.autograd.Variable(torch.from_numpy(np.array(y_train)))
input2_=torch.autograd.Variable(torch.from_numpy(np.array(x_val)))
target2=torch.autograd.Variable(torch.from_numpy(np.array(y_val)))
optimizer.zero_grad()
output=model(input_)
loss=criterion(output,target)
loss.backward()
optimizer.step()
moniter=criterion(model(input2_),target2)

moniter
Out[274]: tensor(1.4941, dtype=torch.float64, grad_fn=<L1LossBackward>)

但是当我再次调用前向函数时,由于dropout的随机性,我得到了不同的数字。

moniter=criterion(model(input2_),target2)
moniter
Out[275]: tensor(1.4943, dtype=torch.float64, grad_fn=<L1LossBackward>)

我应该做什么才能消除在预测短句中所有的掉线问题?

我尝试使用eval():

moniter=criterion(model.eval()(input2_),target2)
moniter
Out[282]: tensor(1.4942, dtype=torch.float64, grad_fn=<L1LossBackward>)

moniter=criterion(model.eval()(input2_),target2)
moniter
Out[283]: tensor(1.4945, dtype=torch.float64, grad_fn=<L1LossBackward>)

并传递一个额外的参数p以控制丢失:

import copy
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
class mylstm(nn.Module):
    def __init__(self,input_dim, output_dim, hidden_dim,linear_dim,p):
        super(mylstm, self).__init__()
        self.hidden_dim=hidden_dim
        self.lstm=nn.LSTMCell(input_dim,self.hidden_dim)
        self.linear1=nn.Linear(hidden_dim,linear_dim)
        self.linear2=nn.Linear(linear_dim,output_dim)
    def forward(self, input,p):
        out,_=self.lstm(input)
        out=nn.Dropout(p=p)(out)
        out=self.linear1(out)
        out=nn.Dropout(p=p)(out)
        out=self.linear2(out)
        return out

model=mylstm(30,10,200,100,0.3).double()

output=model(input_)
loss=criterion(output,target)
loss.backward()
optimizer.step()
moniter=criterion(model(input2_,0),target2)
Traceback (most recent call last):

  File "<ipython-input-286-e49b6fac918b>", line 1, in <module>
    output=model(input_)

  File "D:\Users\shan xu\Anaconda3\lib\site-packages\torch\nn\modules\module.py", line 489, in __call__
    result = self.forward(*input, **kwargs)

TypeError: forward() missing 1 required positional argument: 'p'

但是它们都没有起作用。


1
model.eval() 应该可以正常工作。你确定没有引入错误或更改了输入张量的值吗? - harveyslash
是的,我尝试去掉了dropout层,结果无论我投入多少时间,都保持不变。因此,我认为只是应用了dropout导致我得到了不同的结果。 - Tommy Yu
3个回答

31
你需要在 __init__ 中定义你的 nn.Dropout 层,并将其分配给你的模型,以便在调用 eval() 时对其进行响应。因此,像这样更改你的模型应该适合你:
class mylstm(nn.Module):
    def __init__(self,input_dim, output_dim, hidden_dim,linear_dim,p):
        super(mylstm, self).__init__()
        self.hidden_dim=hidden_dim
        self.lstm=nn.LSTMCell(input_dim,self.hidden_dim)
        self.linear1=nn.Linear(hidden_dim,linear_dim)
        self.linear2=nn.Linear(linear_dim,output_dim)

        # define dropout layer in __init__
        self.drop_layer = nn.Dropout(p=p)
    def forward(self, input):
        out,_= self.lstm(input)

        # apply model dropout, responsive to eval()
        out= self.drop_layer(out)
        out= self.linear1(out)

        # apply model dropout, responsive to eval()
        out= self.drop_layer(out)
        out= self.linear2(out)
        return out

如果您这样更改,当您调用eval()时,dropout将不再起作用。

注意:如果您想在之后继续训练,需要在模型上调用train()以退出评估模式。


您还可以在此处找到使用eval()进行评估模式的dropout的小型工作示例: nn.Dropout vs. F.dropout pyTorch


3
在一个模型中多次使用相同的dropout层是否合适? - bgenchel
在Pytorch中,如果您想要使一切正常,似乎必须将所有层定义为类中的字段。我对吗?由于我希望事情可以动态变化,所以有一次我将这些层分配到了一个列表中,但它们没有包含在.model_dict()中,因此我无法保存网络。通过在net的__init__函数中调用setattr(self, layer_name, layer)也解决了这个问题。似乎Pytorch不会递归查找非Pytorch组件(例如列表或其他数据结构)中的其他组件。 - SomethingSomething
1
@SomethingSomething 不确定我是否理解您的意思,但您可能想要查看:torch.nn.ModuleList - MBT
谢谢@blue-phoenox,这非常有帮助。所以ModuleList是一个列表,用于包含组件,在调用model.eval()model.train()等方法时会进行递归更新,如果我理解正确的话。 - SomethingSomething
1
@SomethingSomething 是的,使用 nn.ModuleList 将确保其中所有的参数/模块都能得到正确的注册,因此它们将被所有 Module 方法(如 train())看到。 - MBT
@bgenchel,很抱歉错过了您的评论。当然可以多次使用相同的层,因为dropout层没有需要学习的参数。它只是在给定的droprate上执行dropout操作。当您多次使用它时,它的效果也同样好。 - MBT

2
我添加这个答案是因为我正在尝试通过丢失不一致性来复现深度贝叶斯主动学习。如果您需要保持丢失活动(例如引导同一测试实例的不同预测集),则只需将模型保持在训练模式下,无需定义自己的丢失层。由于在pytorch中需要定义自己的预测函数,您可以像这样添加一个参数:
def predict_class(model, test_instance, active_dropout=False):
    if active_dropout:
        model.train()
    else:
        model.eval()

0

正如其他答案所述,丢弃层应该在模型的__init__方法中定义,以便您的模型可以跟踪每个预定义层的所有信息。当模型状态发生变化时,它会通知所有层并执行一些相关工作。例如,在调用model.eval()时,您的模型将停用丢弃层,但直接传递所有激活值。通常情况下,如果您想要停用丢弃层,最好使用nn.Dropout模块在__init__方法中定义丢弃层。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接