Глюонный код не намного быстрее на GPU, чем на CPU - PullRequest
0 голосов
/ 04 апреля 2019

мы тренируем сеть для рекомендательной системы, по триплетам. Основной код для метода подбора следующий:

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. Однако запуск fit в графическом процессоре не намного быстрее, чем запуск в процессоре. Кроме того, увеличение batch_size с 50000 до 500000 увеличивает время на пакет в 10 раз (чего я не ожидал, учитывая распараллеливание GPU).

В частности, для партии 50 КБ: * output = self.model(batch.data[0]) занимает 0.03 секунды на GPU и 0.08 на CPU. * loss.backward() занимает 0,11 секунды и 0,39 на процессоре.

оба оцениваются с помощью nd.waitall(), чтобы избежать асинхронных вызовов, приводящих к неправильным измерениям.

Кроме того, очень похожему коду, который выполнялся на простом MXNet, потребовалось менее 0,03 секунды для соответствующей части, что приводит к полной эпохе, занимающей чуть более минуты с MXNet, до 15 минут с Gluon.

Есть идеи о том, что здесь может происходить?

Заранее спасибо!

1 Ответ

1 голос
/ 04 апреля 2019

Проблема в следующей строке:

cumulative_loss += nd.mean(loss).asscalar()

Когда вы вызываете asscalar(), MXNet вынужден неявно выполнять синхронизированный вызов, чтобы скопировать результат из графического процессора в центральный процессор: это по сути то же самое, что и вызов nd.waitall(). Поскольку вы делаете это для каждой итерации, она будет выполнять синхронизацию для каждой итерации, что значительно снизит время ваших настенных часов.

Что вы можете сделать, это сохранить и обновить cumulative_loss в графическом процессоре и копировать его в ЦП только тогда, когда вам действительно нужно его отобразить - это может быть каждые N итераций или после того, как эпоха фактически завершена, в зависимости от того, как долго требуется, чтобы сделать каждую итерацию.

...