我们正在三胞胎上训练推荐系统的网络。 fit方法的核心代码如下:
for e in range(epochs):
start = time.time()
cumulative_loss = 0
for i, batch in enumerate(train_iterator):
# Forward + backward.
with autograd.record():
output = self.model(batch.data[0])
loss = loss_fn(output, batch.label[0])
# Calculate gradients
loss.backward()
# Update parameters of the network.
trainer_fn.step(batch_size)
# Calculate training metrics. Sum losses of every batch.
cumulative_loss += nd.mean(loss).asscalar()
train_iterator.reset()
其中train_iterator
是自mx.io.DataIter
继承的自定义迭代器类,并在适当的上下文中已返回数据(三元组),如下所示:
data = [mx.nd.array(data[:, :-1], self.ctx, dtype=np.int)]
labels = [mx.nd.array(data[:, -1], self.ctx)]
return mx.io.DataBatch(data, labels)
在运行self.model.initialize(ctx=mx.gpu(0))
方法之前还调用了 fit
。 loss_fn = gluon.loss.L1Loss()
。
问题在于nvidia-smi
报告该进程已正确分配到GPU中。但是,在GPU中运行fit
的速度并不比在CPU中运行速度快多少。此外,将batch_size
从50000增加到500000,每批处理时间增加了10倍(考虑到GPU并行化,这不是我所期望的)。
具体来说,对于50k批次:
* output = self.model(batch.data[0])
在GPU上花费0.03秒,在CPU上花费0.08秒。
* loss.backward()
耗时0.11秒,在CPU上耗时0.39。
都用nd.waitall()
进行了评估,以避免异步调用导致错误的测量结果。
此外,在普通MXNet上运行的非常相似的代码所用的相应部分用时不到0.03秒,这导致完整的时期从MXNet稍稍超过一分钟到Gluon长达15分钟。
关于这里可能发生什么的任何想法?
谢谢!
答案 0 :(得分:1)
问题在以下几行:
cumulative_loss += nd.mean(loss).asscalar()
调用asscalar()
时,MXNet必须隐式进行同步调用才能将结果从GPU复制到CPU:本质上与调用nd.waitall()
相同。由于您是在每次迭代中都这样做,因此它将在每次迭代中进行同步,从而大大降低了挂钟时间。
您可以做的是将cumulative_loss
保存在GPU中并进行更新,仅在实际需要显示时才将其复制到CPU中-可以是每N次迭代或在实际完成之后,具体取决于如何每次迭代都需要很长时间。