PyTorch - 如何在评估模式下停用辍学
Posted
技术标签:
【中文标题】PyTorch - 如何在评估模式下停用辍学【英文标题】:PyTorch - How to deactivate dropout in evaluation mode 【发布时间】:2019-05-21 15:03:36 【问题描述】:这是我定义的模型,它是一个带有 2 个全连接层的简单 lstm。
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_train
和x_val
是形状为(4478,30)
的float 数据框,而y_train
和y_val
是形状为(4478,10)
的float df
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)
定义变量并做一次bp,我可以发现验证损失为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 的随机性,我再次调用了 forward 函数,我得到了一个不同的数字
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来控制dropout:
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'
但他们都没有工作。
【问题讨论】:
model.eval() 应该可以工作。您确定您没有引入错误或更改输入张量的值吗? 是的,我尝试移除 dropout 层,结果证明无论我投入多少时间都是不变的。所以我认为只是应用了 dropout 的情况,我得到了不同的结果。 【参考方案1】:您必须在您的__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()
时立即处于非活动状态。
注意:如果您想在之后继续训练,您需要在您的模型上调用 train()
以退出评估模式。
您还可以在此处找到使用eval()
评估模式的辍学小工作示例:
nn.Dropout vs. F.dropout pyTorch
【讨论】:
在一个模型中多次使用同一个 dropout 层是不是很酷? 看来,在 Pytorch 中,如果你想让事情顺利进行,你必须将所有层定义为类中的字段。我对吗?当我将图层分配到列表中时(因为我希望事情是动态的),它们没有包含在.model_dict()
中,所以我无法保存网络。通过在网络的__init__
函数中调用setattr(self, layer_name, layer)
来解决它。 Pytorch 似乎不会在非 pytorch 组件中递归查找其他组件,例如列表或其他数据结构。
@SomethingSomething 不确定我是否正确,但您可能想看看:torch.nn.ModuleList
谢谢@blue-phoenox,这很有帮助。所以ModuleList
是一个列表,指定用于包含将在调用model.eval()
、model.train()
等方法时递归更新的组件,如果我没记错的话。
@SomethingSomething 是的,使用nn.ModuleList
将确保其中的所有参数/模块都将正确注册,因此所有Module
方法都可以看到它们比如train()
。【参考方案2】:
正如其他答案所说,希望在模型的 __init__
方法中定义 dropout 层,以便您的模型可以跟踪每个预定义层的所有信息。当模型的状态发生变化时,它会通知所有层并做一些相关的工作。例如,在调用 model.eval()
时,您的模型将停用 dropout 层,但直接传递所有激活。一般来说,如果你想停用你的dropout层,你最好在__init__
方法中使用nn.Dropout
模块定义dropout层。
【讨论】:
【参考方案3】:我添加这个答案只是因为我现在在尝试通过 dropout 分歧重现深度贝叶斯主动学习时面临同样的问题。 如果您需要保持 dropout 处于活动状态(例如为相同的测试实例引导一组不同的预测),您只需让模型处于训练模式,无需定义自己的 dropout 层。
由于在pytorch中你需要定义你自己的预测函数,你可以像这样添加一个参数:
def predict_class(model, test_instance, active_dropout=False):
if active_dropout:
model.train()
else:
model.eval()
【讨论】:
以上是关于PyTorch - 如何在评估模式下停用辍学的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Pytorch 中实现 dropout,以及在哪里应用它
使用Pytorch实现Transformer,如何巧妙的使用或者停用 optimizer.zero_grad()来训练大模型?