Pytorch Lightning 模型的输出预测

9

这是一个可能非常简单的问题。我刚刚开始使用PyTorch Lightning,无法弄清楚如何在训练后接收模型的输出。

我对y_train和y_test的预测都很感兴趣,希望以某种形式的数组(PyTorch张量或NumPy数组)返回,以便使用不同的脚本将其与标签一起绘制。

dataset = Dataset(train_tensor)
val_dataset = Dataset(val_tensor)
training_generator = torch.utils.data.DataLoader(dataset, **train_params)
val_generator = torch.utils.data.DataLoader(val_dataset, **val_params)
mynet = Net(feature_len)
trainer = pl.Trainer(gpus=0,max_epochs=max_epochs, logger=logger, progress_bar_refresh_rate=20, callbacks=[early_stop_callback], num_sanity_val_steps=0)
trainer.fit(mynet)

在我的Lightning模块中,我有以下函数:
def __init__(self, random_inputs):

def forward(self, x):

def train_dataloader(self):
    
def val_dataloader(self):

def training_step(self, batch, batch_nb):

def training_epoch_end(self, outputs):

def validation_step(self, batch, batch_nb):

def validation_epoch_end(self, outputs):

def configure_optimizers(self):

我需要一个特定的预测函数吗?还是已经有我看不到的实现方式了?
4个回答

10
我不同意上面的答案:OP的问题似乎关注于他应该如何使用在Lightning中训练的模型进行预测,而不是在训练管道中的特定步骤。在这种情况下,用户不需要接触Trainer对象——那些并不旨在用于一般预测,因此上面的答案在鼓励将Trainer对象带在身边以便进行某些预测的反模式(对于未来阅读这些答案的任何人来说)。
我们可以直接从定义好的Lightning模块获取预测结果,而不是使用trainer。例如,如果我有了我的(已经训练好的)Lightning模块实例model = Net(...) ,则只需调用model(x)即可在输入x上获取预测值(前提是必须已经实现或覆盖了forward方法)。
相比之下,Trainer.predict()并不是用于一般获取训练模型预测结果的方式。Trainer API提供了一系列方法,用于tune, fit, 和test你的LightningModule作为训练流程的一部分,我认为predict方法是为一些不太标准的训练步骤中对单独数据加载器进行即席预测而提供的。
OP的问题(我是否需要特定的预测函数或者已经有一种实现预测的方法我没有看到?)意味着他们不熟悉PyTorch中forward()方法的工作原理,但是询问是否已经存在预测的方法。因此,完整的答案需要进一步解释forward()方法在预测流程中的作用: model(x)之所以起作用是因为Lightning模块是torch.nn.Module的子类,这些子类实现了一个叫做__call__()的神奇方法,这意味着我们可以像调用函数一样调用类实例。而__call__()会调用forward(),这也是为什么我们需要在Lightning模块中重写该方法的原因。
请注意,因为使用model(x)时涉及的逻辑不仅仅是forward方法,除非你有特定的理由,否则始终建议使用model(x)而不是model.forward(x)进行预测。

3
你指出了网络可以直接运行的好处很好,因为当使用 PyTorch Lightning 而没有直接使用 PyTorch 时会隐藏底层机制。我认为,在某些情况下即使进行预测也使用 Trainer 类仍然是合理的,因为它处理将模型和数据放入 GPU 中,并且可以调用某些钩子,何必重复造轮子呢?这并不是一种反模式,将类重命名为Commander,你的大部分观点就无效了。我仍然认为你指出这一点很好,但反模式的说法过于强硬。 - Florian Blume
我认为关于如何从模型中获取预测结果的建议需要包括如何在GPU上运行,model.eval(),关闭梯度以及所有Lightning为用户做的其他事情。仅仅调用model(x)很可能无法满足用户的需求。 - Thomas Ahle

4

2
预测方法似乎已经在此期间添加了。我只是惊讶之前它不可用。 - Tom S
是的,他们似乎非常擅长添加新功能。 - Adrien Forbu
使用 trainer.predict() 和使用 model() 有什么区别?第一个选项是否会自动将调用包装在 eval 模式和 no_grad 中? - Michael
训练器会将您的模型和输入放在图形卡上,限制批次数量(如果设置,请查看训练器的 __init__ 参数),执行分布式计算等操作。 - Florian Blume
有没有什么方法可以将“predict”作为迭代器运行?我不想将所有数据加载到内存中。 - Thomas Ahle

4
你可以尝试两种方式进行预测:
  1. 按照正常流程进行批量预测。
test_dataset = Dataset(test_tensor)
test_generator = torch.utils.data.DataLoader(test_dataset, **test_params)

mynet.eval()
batch = next(iter(test_generator))
with torch.no_grad():
    predictions_single_batch = mynet(**unpacked_batch)
  1. 实例化一个新的 Trainer 对象。Trainerpredict API 允许您传递任意的 DataLoader
test_dataset = Dataset(test_tensor)
test_generator = torch.utils.data.DataLoader(test_dataset, **test_params)

predictor = pl.Trainer(gpus=1)
predictions_all_batches = predictor.predict(mynet, dataloaders=test_generator)

我注意到在第二种情况下,Pytorch Lightning 会处理像将张量和模型移动到(而不是从) GPU 这样的事情,与其执行分布式预测的潜力相一致。 它还不返回任何附有梯度的损失值,这有助于省去编写with torch.no_grad()这样的样板代码的需要。


这个答案的一个重要点是,在某些情况下需要在测试/预测时创建一个新的训练器。 [predict的文档](https://pytorch-lightning.readthedocs.io/en/stable/common/trainer.html#predict)解释了加速器会产生新进程,这些新进程不会返回预测值(因此,如果您以后想要收集它们,它们将不会同步),例如,在DDP下。因此,您可以在DDP下进行训练,但不能在DDP下进行推断,因为它不受支持。 - davzaman
我还没有测试过这个,但是我理解这个语句默认情况下为True,除非使用生成进程的加速器(不支持)。的意思是,如果我们使用ddp_spawn而不是ddp来设置Trainer,那么return_prediction将不被支持。可能会出现一些与mp.spawn()相关的复杂性或瓶颈问题。我确实同意使用Trainer来设置预测器在语义上相当令人困惑。 - Ying Jiang

0

太棒了。我怎么没自己发现这个。很可能是因为我收到了许多错误。但是我已经把一切都解决了。 - Tom S
2
现在似乎有一个预测功能:https://github.com/PyTorchLightning/pytorch-lightning/issues/1853 - Georg Heiler
我不相信.test允许您返回张量(它的主要目的是通过logging API收集日志-目前不接受列表或torch/np.arrays)。因此,.predict()似乎是前进的道路。 - hkh

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