#machine-learning #deep-learning #neural-network #pytorch
#машинное обучение #глубокое обучение #нейронная сеть #pytorch
Вопрос:
Я действительно новичок в pytorch. И я действительно запутался весь день, пока пытался выяснить, почему мой nn работает медленнее на GPU, чем на CPU. Я не понимаю, когда я рассчитывал время выполнения с помощью time.time(), время всего цикла сильно отличается от суммы каждого отдельного времени выполнения. Вот часть моего кода. Кто-нибудь может мне помочь? Ценю это!
time_out = 0
time_in = 0
for epoch in tqdm(range(self.n_epoch)):
running_loss = 0
running_error = 0
running_acc = 0
if self.cuda:
torch.cuda.synchronize() #time_out_start
epst1 = time.time()
for step, (batch_x, batch_y) in enumerate(self.normal_loader):
if self.cuda:
torch.cuda.synchronize() #time_in_start
t1 = time.time()
batch_x, batch_y = batch_x.to(self.device), batch_y.to(self.device)
b_x = Variable(batch_x)
b_y = Variable(batch_y)
pred_y = self.model(b_x)
#print (pred_y)
loss = self.criterion(pred_y, b_y)
error = mae(pred_y.detach().cpu().numpy(),b_y.detach().cpu().numpy())
acc = r2(b_y.detach().cpu().numpy(),pred_y.detach().cpu().numpy())
#print (loss)
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
running_acc = acc
running_loss = loss.item()
running_error = error
if self.cuda:
torch.cuda.synchronize() #time_in_end
t6 = time.time()
time_in = t6-t1
if self.cuda:
torch.cuda.synchronize() #time_out_end
eped1 = time.time()
time_out = eped1-epst1
print ('loop time(out)',time_out)
print ('loop time(in)',time_in)
Результат:
ПРОЦЕССОР:
ЭПОХА 10: выход: 1.283 с, вход: 0.695 с
ЭПОХА 50: выход: 6,43 с, вход: 3,288 с
ЭПОХА 100: выход: 12,646 с, вход: 6,386 с
Графический процессор:
ЭПОХА 10: выход: 3,92 с, вход: 1,471 с
ЭПОХА 50: выход: 9,35 с, вход: 3,04 с
ЭПОХА 100: выход: 18,418 с, вход: 5,655
Я понимаю, что передача данных с cpu на gpu требует некоторого времени. Таким образом, по мере увеличения эпох время вычисления GPU должно становиться меньше времени процессора. Мой вопрос:
- почему время, которое я записываю вне цикла, так отличается от внутреннего? Есть ли какой-либо шаг, который я пропустил, чтобы записать время выполнения?
- И почему GPU стоит больше времени вне времени, даже если время внутри меньше времени процессора?
Сеть действительно проста, а именно:
class Model(nn.Module):
def __init__(self,n_input,n_nodes1,n_nodes2):
super(Model, self).__init__()
self.n_input = n_input
self.n_nodes1 = n_nodes1
self.n_nodes2 = n_nodes2
self.l1 = nn.Linear(self.n_input, self.n_nodes1)
self.l2 = nn.Linear(self.n_nodes1, self.n_nodes2)
self.l3 = nn.Linear(self.n_nodes2, 1)
def forward(self,x):
h1 = F.relu(self.l1(x))
h2 = F.relu(self.l2(h1))
h = self.l3(h2)
return h
обучающие данные формируются следующим образом: (задача регрессии, input_x — это дескрипторы, а y — целевое значение)
def load_train_normal(self,x,y,batch_size = 100):
if batch_size:
self.batch_size = batch_size
self.x_train_n, self.y_train_n = Variable(torch.from_numpy(x).float()), Variable(torch.from_numpy(y).float())
#x, y = Variable(torch.from_numpy(x).float()), Variable(torch.from_numpy(y).float())
self.dataset = Data.TensorDataset(self.x_train_n,self.y_train_n)
self.normal_loader = Data.DataLoader(
dataset = self.dataset,
batch_size = self.batch_size,
shuffle = True, num_workers=2,)
Комментарии:
1. Лучше приведите полный пример, чтобы мы могли воспроизвести проблему. Время выполнения иногда может зависеть от размера ваших данных, что не отражается в вашем коде.
Ответ №1:
почему время, которое я записываю вне цикла, так отличается от внутреннего? Есть ли какой-либо шаг, который я пропустил, чтобы записать время выполнения?
self.normal_loader
это не просто простой словарь, вектор или что-то такое простое. Итерация по нему занимает значительное количество времени.
И почему GPU стоит больше времени вне времени, даже если время внутри меньше времени процессора?
torch.cuda.synchronize()
это тяжелая операция. Даже когда он даже не делал ничего полезного, например, в этом случае, поскольку pred_y.detach().cpu()
уже была принудительная синхронизация.
Как получить их быстрее? Отбросьте synchronize()
вызовы, они не принесут вам никакой пользы.
А затем отложите обработку pred_y
до более позднего времени. Намного позже. Вы хотите вызвать модель как минимум 2 или 3 раза, прежде чем запускать первую загрузку результатов. Чем проще модель и чем меньше данных, тем больше итераций вам придется ждать.
Поскольку передача данных на GPU и обратно не просто «занимает время», они подразумевают синхронизацию. Без синхронизации модель выполнения на GPU в основном «отстает», при этом загрузка данных на GPU уже выполняется асинхронно за кулисами, а фактическое выполнение ставится в очередь только за ними. Если вы случайно или явно не синхронизируете, рабочие нагрузки начинают перекрываться, материал (загрузка, выполнение, работа процессора) начинает выполняться параллельно. Ваше эффективное время выполнения приближается max(upload, download, GPU execution, CPU execution)
.
При синхронизации нет задач для перекрытия и нет пакетов для формирования из однотипных задач. Загрузка, выполнение, загрузка, часть процессора, все это происходит последовательно. Ваше время выполнения заканчивается upload download GPU execution CPU execution
. Некоторые дополнительные накладные расходы для прерывания пакетной обработки на уровне драйвера сверху. Так легко в 5-10 раз медленнее, чем должно быть.
Комментарии:
1. Привет, Ext3h, спасибо за ваше отличное объяснение. Я понял это. Большое спасибо!