为什么我的模型能跑,但效果很差:错误处理了dataloader返回的数据

为什么我的模型能跑,但效果很差?(这个效果差指的是模型的结果接近于random)

或许你和我一样错误的处理了数据的 shape。

这里以 RNN 为例,但这个问题是普适的。

事情的起因源于做李宏毅机器学习 HW02 的 Boss baseline,最后的步骤是转向使用 RNN,而 sample code 中的代码并不是 RNN 形式的,所以需要修改 Module 类和修改输入以适配模型。

这里是一段错误样例代码(这里 reshape 可以换成 view),简单列一下以便后面讲解:

class Classifier(nn.Module):
    def __init__(self, input_dim, output_dim=41, hidden_layers=1, hidden_dim=256):
        super(Classifier, self).__init__()
        
        self.input_size = 39	# 这一项是RNN的"input_dim",RNN需要对"单"个数据进行处理
        self.hidden_size = 512	# 这一项是RNN的"hidden_dim"
        self.num_layers = 3		# 这一项是RNN的"hidden_layers"
        
        # 创建一个RNN层,输入维度为39,隐藏状态维度为512,层数为3,input.shape=(seq_len, batch_size, input_size)
        self.rnn = nn.RNN(input_size=self.input_size, hidden_size=self.hidden_size, num_layers=self.num_layers)
		# 后接一层全连接的神经网络
        self.fc = nn.Linear(self.hidden_size, output_dim)

    def forward(self, x):
        # 通过RNN层,得到输出和最后一个隐藏状态
        # x.shape = (seq_len, batch_size, RNN_input_size)
        x, _ = self.rnn(x)		# => (seq_len, batch_size, RNN_hidden_size)
        # 取最后一个时间步的输出作为分类的输入
        x = x[-1] 			# => (batch_size, RNN_hidden_size)
        # 通过线性层,得到最终的分类结果
        x = self.fc(x) 			# =>(batch_size, labels)
        return x

...
train_loader = DataLoader(train_set, batch_size=batch_size, shuffle=True)
for i, batch in enumerate(tqdm(train_loader)):
    features, labels = batch	# features.shape: (batch_size, seq_len * input_size)
    features = features.reshape(seq_len, batch_size, input_size).to(device) # 这里是错误的,正确处理顺序应当为:(batch_size, seq_len, input_size),更正确的处理是 (-1, seq_len, input_size),-1 表示自动计算该维度,因为最后可能不够一个 batch_size,会报错。
	...
    outputs = model(features) 	# outputs.shape: (batch_size, labels)
    loss = criterion(outputs, labels)
    ...

dataloader 返回的数据 shape(batch_size, input_dim)

所以应该修改成 features = features.reshape(batch_size, seq_len, input_size).to(device)

如果你和我一开始一样:因为知道 rnninput.shape=(seq_len, batch_size, input_size),所以懒得使用 batch_first=True,但不熟悉 dataloader 具体返回的数据 shape

那么,你很有可能会使用 features = features.reshape(seq_len, batch_size, input_size).to(device),这样可以训练,但效果奇差,而且:) 不会报错。

因为 features.shape = (batch_size, input_dim),而你选择了将它 reshape(seq_len, batch_size, input_size)

你需要格外注意上面这点,必须正确处理 featurereshape,可以不用设置 batch_first=True,因为这样顶多就需要把 self.rnn(x) 改成 self.rnn(x.permute(1, 0, 2))

下面是正确处理的代码:

class Classifier(nn.Module):
    def __init__(self, input_dim, output_dim=41, hidden_layers=1, hidden_dim=256):
        super(Classifier, self).__init__()
        
        self.input_size = 39	# 这一项是RNN的"input_dim",RNN需要对"单"个数据进行处理
        self.hidden_size = 512	# 这一项是RNN的"hidden_dim"
        self.num_layers = 3		# 这一项是RNN的"hidden_layers"
        
        # 创建一个RNN层,输入维度为39,隐藏状态维度为1024,层数为3,input.shape=(seq_len, batch_size, input_size)
        self.rnn = nn.RNN(input_size=self.input_size, hidden_size=self.hidden_size, num_layers=self.num_layers)
		# 后接一层全连接的神经网络
        self.fc = nn.Linear(self.hidden_size, output_dim)

    def forward(self, x):
        # 通过RNN层,得到输出和最后一个隐藏状态
        # x.shape = (batch_size, seq_len, RNN_input_size)
        x, _ = self.rnn(x.permute(1, 0, 2))		# => (seq_len, batch_size, RNN_hidden_size)
        # 取最后一个时间步的输出作为分类的输入
        x = x[-1] 			# => (batch_size, RNN_hidden_size)
        # 通过线性层,得到最终的分类结果
        x = self.fc(x) 			# =>(batch_size, labels)
        return x

...
train_loader = DataLoader(train_set, batch_size=batch_size, shuffle=True)
for i, batch in enumerate(tqdm(train_loader)):
    features, labels = batch	# features.shape: (batch_size, seq_len * input_size)
    features = features.reshape(-1, seq_len, input_size)
	...
    outputs = model(features) 	# outputs.shape: (batch_size, labels)
    loss = criterion(outputs, labels)
    ...

PyTorchrnn()/lstm()... 有一个参数是 batch_first,这个参数如果设置成 True,那么意味着 inputshape(seq_len, batch_size, input_size) => (batch_size, seq_len, input_size),一开始我并不理解为什么有这个参数,直到我发现了 features.shape = (batch_size, input_dim),设置成 True 能少去些弯绕。

P.S. 当 batch_first = True 时,如果要取最后一个状态,forward() 中的代码需要从 x = x[-1] 改成 x = x[:, -1]

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hoper.J

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值