python - PyTorch:是否有类似于 Keras 的 fit() 的明确训练循环?
问题描述
我要从 Keras 转到 PyTorch,我发现的一件令人惊讶的事情是我应该实现自己的训练循环。
在 Keras 中,有一个事实上的fit()
函数:(1)运行梯度下降,(2)收集训练集和验证集的损失和准确性指标的历史记录。
在 PyTorch 中,程序员似乎需要实现训练循环。由于我是 PyTorch 的新手,我不知道我的训练循环实现是否正确。我只想将苹果对苹果的损失和准确性指标与我在 Keras 中看到的进行比较。
我已经通读了:
官方PyTorch 60 分钟闪电战,他们提供了一个示例训练循环。
官方PyTorch 示例代码,我发现训练循环与其他代码一致。
O'Reilly 的书Programming PyTorch for Deep Learning with its own training loop。
斯坦福 CS230示例代码。
所以我想知道:是否有一个明确的、通用的训练循环实现可以做同样的事情并报告与 Keras函数相同的数字?fit()
我的不满点:
从数据加载器中提取数据在图像数据和 NLP 数据之间不一致。
在我见过的任何示例代码中,正确计算损失和准确性都不一致。
一些代码示例使用
Variable
,而其他代码示例不使用。不必要的细节:将数据移入/移出 GPU;知道什么时候打电话
zero_grad()
。
对于它的价值,这是我目前的实现。有没有明显的bug?
import time
def train(model, optimizer, loss_fn, train_dl, val_dl, epochs=20, device='cuda'):
'''
Runs training loop for classification problems. Returns Keras-style
per-epoch history of loss and accuracy over training and validation data.
Parameters
----------
model : nn.Module
Neural network model
optimizer : torch.optim.Optimizer
Search space optimizer (e.g. Adam)
loss_fn :
Loss function (e.g. nn.CrossEntropyLoss())
train_dl :
Iterable dataloader for training data.
val_dl :
Iterable dataloader for validation data.
epochs : int
Number of epochs to run
device : string
Specifies 'cuda' or 'cpu'
Returns
-------
Dictionary
Similar to Keras' fit(), the output dictionary contains per-epoch
history of training loss, training accuracy, validation loss, and
validation accuracy.
'''
print('train() called: model=%s, opt=%s(lr=%f), epochs=%d, device=%s\n' % \
(type(model).__name__, type(optimizer).__name__,
optimizer.param_groups[0]['lr'], epochs, device))
history = {} # Collects per-epoch loss and acc like Keras' fit().
history['loss'] = []
history['val_loss'] = []
history['acc'] = []
history['val_acc'] = []
start_time_sec = time.time()
for epoch in range(epochs):
# --- TRAIN AND EVALUATE ON TRAINING SET -----------------------------
model.train()
train_loss = 0.0
num_train_correct = 0
num_train_examples = 0
for batch in train_dl:
optimizer.zero_grad()
x = batch[0].to(device)
y = batch[1].to(device)
yhat = model(x)
loss = loss_fn(yhat, y)
loss.backward()
optimizer.step()
train_loss += loss.data.item() * x.size(0)
num_train_correct += (torch.max(yhat, 1)[1] == y).sum().item()
num_train_examples += x.shape[0]
train_acc = num_train_correct / num_train_examples
train_loss = train_loss / len(train_dl.dataset)
# --- EVALUATE ON VALIDATION SET -------------------------------------
model.eval()
val_loss = 0.0
num_val_correct = 0
num_val_examples = 0
for batch in val_dl:
x = batch[0].to(device)
y = batch[1].to(device)
yhat = model(x)
loss = loss_fn(yhat, y)
val_loss += loss.data.item() * x.size(0)
num_val_correct += (torch.max(yhat, 1)[1] == y).sum().item()
num_val_examples += y.shape[0]
val_acc = num_val_correct / num_val_examples
val_loss = val_loss / len(val_dl.dataset)
print('Epoch %3d/%3d, train loss: %5.2f, train acc: %5.2f, val loss: %5.2f, val acc: %5.2f' % \
(epoch+1, epochs, train_loss, train_acc, val_loss, val_acc))
history['loss'].append(train_loss)
history['val_loss'].append(val_loss)
history['acc'].append(train_acc)
history['val_acc'].append(val_acc)
# END OF TRAINING LOOP
end_time_sec = time.time()
total_time_sec = end_time_sec - start_time_sec
time_per_epoch_sec = total_time_sec / epochs
print()
print('Time total: %5.2f sec' % (total_time_sec))
print('Time per epoch: %5.2f sec' % (time_per_epoch_sec))
return history
解决方案
简短的回答:对于 PT 和 TF.keras 没有等效的训练循环,而且永远不会有。
首先,训练循环是一种语法糖,它应该让一个人的生活更轻松。从我的角度来看,“让生活更轻松”是 TF.keras 框架的一个主题,这是它拥有它的主要原因。训练循环不能被形式化为定义明确的实践,它可能会根据任务/数据集/过程/度量/you_name_it 的不同而有很大差异,并且需要付出很多努力才能匹配 2 个框架的所有选项。此外,在 Pytorch 中为训练循环创建定义接口对于框架的许多实际用户来说可能过于严格。
匹配网络的输出需要匹配两个框架内每个操作的行为,这是不可能的。首先,框架不一定提供相同的操作集。操作可以不同地分组到更高级别的摘要中。此外,一些常见的函数,如 sigmoid 或 BatchNorm,在纸面上可能看起来在数学上定义得很好,但实际上有几十个实现特定的细节。此外,当对操作进行改进时,社区可以将这些更新集成到主框架发行版中,或者忽略它们。不用说,两个框架的开发人员独立做出这些决定,并且可能有不同的动机。
总而言之,匹配 2 个框架的高级细节需要付出巨大的努力,并且可能会对现有用户造成很大的破坏。
推荐阅读
- r - 在谷歌地图上覆盖克里格数据时遇到问题
- javascript - 从 HTML 中删除多个元素的字符串
- html - 基于 url 或子目录突出显示当前导航选项卡
- php - 添加指向 scandir() 结果的链接
- java - Java & MongoDB - 如何在 MongoDB 文档中获取 _id 的值?
- google-cloud-sql - 如何将 datalab 与 Google Cloud SQL 连接起来?
- python - threading.Timer 用基本的清理控制杀死长时间运行的任务
- c++ - 如何在c ++中获取5到-5之间的随机数
- r - 调用 lmer 函数中的回归变量
- java - 从 Java 中的 Servlett 输出流创建 gif