问题表现
今天跑模型时报了一个非常奇怪的错误:
意思是“你的lstm层输入的参数是无效的,要求接收参数的类型是(Tensor, tuple of (Tensor, Tensor), list of [Parameter, Parameter, Parameter, Parameter], float, int, float, bool, bool, bool),但是实际收到的是(Tensor, tuple of (Tensor, Tensor), list of [Parameter, Parameter, Parameter, Parameter], float, int, float, bool,”
为什么奇怪呢?奇怪在于我检查了我lstm的输入张量,size是正确的,并且我还检查了张量的值,确实都是float32类型:
代码如下
class StudentStatusCommunication(nn.Module):
def __init__(self, embedding_size, add_size, hidden_size, layer_num, dropout):
super(StudentStatusCommunication, self).__init__()
self.linear_layer = nn.Linear(embedding_size+add_size, hidden_size)
self.activate = nn.ReLU()
self.loop_layer = nn.LSTM(hidden_size, hidden_size, layer_num, dropout, batch_first=True)
def forward(self, embedding, add_embedding):
e = torch.cat((embedding, add_embedding), dim=-1)
print(f'cat: {e.shape}')
e = self.linear_layer(e)
print(f'linear: {e.shape}')
e = self.activate(e)
print(f'activate: {e.shape}')
print(e)
h_steps, (h_t, c_t) = self.loop_layer(e)
# 隐藏状态列表, 最后时刻隐藏状态, 最后时刻细胞状态
return h_t
解决方案
问题已经写在报错里了,是参数调用错误,那么参数调用错误在哪呢?
python在调用函数时,接收参数的方式有两种:1、顺序接收(layer_num);2、通过命名接收(batch_first)。
两种方式可以混用,但有两个前提:
- 命名接收方式之后不能再用顺序接收;
- 顺序接收前n个参数会依次传递给函数的前n个形参。
现在我们检查nn.LSTM接收参数的顺序:
可以发现前三个参数都进行了正确地传递,但是第四个参数dropout错误传递给了bias,这就是错误来源!没错,python不支持省略的命名传参方式,即:当实参和形参命名一样时,允许省略命名指向(dropout=dropout可以允许直接写成dropout)。好好好,又是被多语言思维惯性打败的一天。
修正这个错误也非常简单,只要添加命名指向就行:
self.loop_layer = nn.LSTM(hidden_size, hidden_size, layer_num, dropout=dropout, batch_first=True)