FastAi LSTM 前向方法问题

FastAi LSTM forward method issue

尝试创建一个简单的 LSTM 模型并使用 FastAI 框架但失败了。

class SimpleLSTM(nn.Module):
    def __init__(self, input_dim, hidden_dim, output_dim, bs):
        super().__init__()
        self.input_dim = input_dim
        self.hidden_dim = hidden_dim
        self.output_dim = output_dim
        self.bs = bs

        self.lstm = nn.LSTM(self.input_dim, self.hidden_dim)
        self.linear = nn.Linear(self.hidden_dim, self.output_dim)

    def forward(self, *inputs):
        print(f'in[0]: {inputs[0].shape}')
        out, self.h = self.lstm(inputs[0].view(1, self.bs, self.input_dim))
        y_pred = self.linear(out)
        return y_pred[-1]

当我将原始数据提供给模型时,这个模块可以工作,但是当我用 Learner 模块初始化它时,它失败了。

它抱怨数据的形状是错误的。完整代码在这里

import torch
import torch.nn as nn
import pandas as pd
import fastai
from fastai.tabular import *

size = 200
bs = 20
X = torch.randn(size, 5)
y = (X.T[0] * 1.2 + X.T[1] * 4.2 + X.T[2] * 0.56 + X.T[3] * 0.12 + X.T[4] * 7.2 + torch.randn(size)).view(size, 1)
valid_ratio = 0.2
valid_samples = int(valid_ratio * size)
print(f'X.shape: {X.shape}')
print(f'y.shape: {y.shape}')
df = pd.concat([pd.DataFrame(X.data.numpy()), pd.DataFrame(y.data.numpy())], axis=1)
df.columns = ['a', 'b', 'c', 'd', 'e', 'y']

class SimpleLSTM(nn.Module):
    def __init__(self, input_dim, hidden_dim, output_dim, bs):
        super().__init__()
        self.input_dim = input_dim
        self.hidden_dim = hidden_dim
        self.output_dim = output_dim
        self.bs = bs

        self.lstm = nn.LSTM(self.input_dim, self.hidden_dim)
        self.linear = nn.Linear(self.hidden_dim, self.output_dim)

    def forward(self, *inputs):
        print(f'in[0]: {inputs[0].shape}')
        out, self.h = self.lstm(inputs[0].view(1, self.bs, self.input_dim))
        y_pred = self.linear(out)
        return y_pred[-1]

model = SimpleLSTM(input_dim=5, hidden_dim=100, output_dim=1, bs=bs)
model

out = model(X[:bs])
print(f'out shape: {out.shape}')
out = model(X[:bs])
print(f'out shape: {out.shape}')

procs = [Normalize]
valid_idx = range(len(df)-valid_samples, len(df))
dep_var = 'y'
data = TabularDataBunch.from_df('.', df, dep_var, valid_idx=valid_idx, procs=procs, bs=bs)
learner = Learner(data, model)
learner.fit_one_cycle(1)

我的观察是 FastAI 将提供两个参数,一个形状为 (bs, ),另一个形状为 (bs, input_dim)。为什么要传递(bs, )参数?

您的观察是正确的,并且在您的数据束 collate_function 中发生了什么。基本上,当遍历数据加载器时,x 是 data.collate_fn([data.train_ds.x[i] for i in list_indices),其中 list_indices 是您用于该批次的样本的索引列表。 当您查看 data.train_ds.x[0] 时,您会得到

TabularLine d -0.4334; e 1.3458; a 0.4151; c 0.2837; b -0.6355;

data.train_ds.x[0].data 产量:

[tensor(0), tensor([-0.4334,  1.3458,  0.4151,  0.2837, -0.6355])]

之所以会出现这种情况,是因为 fastai 中的表格数据处理的是分类变量和连续变量,这是通过使用 tabular_learner.

创建一个特殊的学习器来处理的。

这里有两种可能的解决方案:

  • 修改数据集,使 data.train_ds.x[i].data 成为形状为 5 的张量。

  • 修改 collate_fn 使其 returns 一个 (bs, input_dim) 形状的张量也应该可以解决问题。

例如添加:

custom_collate_fn = lambda b: data_collate([(x.data[1], y) for (x, y) in b])

并在数据束创建中传递 collate_fn=custom_collate_fn 将起作用。但是,您稍后会收到错误消息,因为您有 200 件商品,而您的模型仅在批量大小为 64 时才有效。如果您在这方面需要帮助,请告诉我。