Работа с большим замедлением при перемещении кода PyTorch в графический процессор - PullRequest
1 голос
/ 12 февраля 2020

У меня есть модель нейросети Graph, которую я написал с использованием Pytorch. На моем процессоре у меня не получается фантастическая производительность c, поэтому я попытался перенести ее на графический процессор V100, к которому у меня есть доступ. В этом процессе я получил огромное снижение производительности (примерно в 10 раз медленнее).

У меня есть две идеи о том, где может быть проблема, но я хотел бы получить некоторый вклад, чтобы попытаться получить оптимальную производительность от моей модели. Первая проблема может исходить от моего пользовательского графового сверточного слоя:

class GraphConvLayer(torch.nn.Module):
    """
    Based, basically, on https://arxiv.org/abs/1609.02907
    Have some modifications:
    https://towardsdatascience.com/how-to-do-deep-learning-on-graphs-with-graph-convolutional-networks-7d2250723780
    This helped:
    https://pytorch.org/docs/master/notes/extending.html

    """
    def __init__(self, input_features, output_features, device, bias=True):
        super(GraphConvLayer, self).__init__()

        self.input_features = input_features
        self.output_features = output_features
        self.device = device

        self.weight = nn.Parameter(torch.FloatTensor(self.input_features, self.output_features))

        if bias:
            self.bias = nn.Parameter(torch.FloatTensor(self.output_features))
        else:
            self.register_parameter('bias', None)

        # Not a very smart way to initialize weights
        self.weight.data.uniform_(-0.1, 0.1)
        if bias is not None:
            self.bias.data.uniform_(-0.1, 0.1)

    def forward(self,input, adj):
        # Here, we put in the forward pass:
        # Our forward pass needs to be:
        # D^-1 * (A + 1) * X * weights
        input, adj = input.float(), adj.float()

        Identity = torch.eye( len(adj[0]), device = self.device)
        A_hat = adj + Identity

        D = torch.sum(A_hat, dim=0)
        len_D = len(D)
        zero = torch.zeros(len_D,len_D, device = self.device)
        mask = torch.diag(torch.ones_like(D, device = self.device))
        D = mask*torch.diag(D) + (1. - mask)*zero

        D_inv = torch.inverse(D)
        out = torch.mm(input, self.weight)
        out = torch.spmm(A_hat,out)
        out = torch.spmm(D_inv, out)

        if self.bias is not None:
            return out + self.bias
        else:
            return out

        return out

    def extra_repr(self):
        # (Optional)Set the extra information about this module. You can test
        # it by printing an object of this class.
        return 'node_features={}, length of weights={}, bias={}'.format(
            self.node_features, self.input_features, self.bias is not None
        )

В частности, на прямом проходе я делаю выбор трансформаций, которые описаны в ссылке по направлению к данным в классе. Есть ли здесь что-то, что вызывает такое большое замедление? Мне кажется, что все тензоры инициализируются на GPU.

Во-вторых, так как все мои графики имеют разные размеры, я вынужден использовать пакет размером 1. В моем обучении l oop У меня есть это:

        for batch in tqdm(train_loader):
            opt.zero_grad()
            adjacency, features, _, nodes = batch
            adjacency = adjacency.to(device)
            features = features.to(device)
            nodes = nodes.to(device)

            output = model(features[0], adjacency[0])

            loss = F.nll_loss(output, nodes[0])
            loss.backward()
            opt.step()

Это значит ( насколько я понимаю), что каждый отдельный фрагмент данных перемещается в GPU индивидуально, каждый l oop. Это кажется очевидной причиной неэффективности. Есть ли способ переместить все данные в память GPU одновременно, вне обучающей l oop, позволяя мне удалить adjacency = adjacency.to(device) строк?

Любая помощь будет очень признательна.

1 Ответ

1 голос
/ 12 февраля 2020

Ваша проблема почти гарантированно связана с перемещением памяти к графическому процессору, тем более что вы упоминаете свои отдельные пакеты.

Единственными способами, которые могут помочь вам ускорить текущую реализацию, может быть просмотр карты памяти , которые мы не можем увидеть, используете ли вы их уже на основе предоставленного кода.

Кроме этого, даже с матрицей смежности другого размера, Заполнение может быть правильной стратегией, если вам удастся отсортировать партии по несколько равным размерам.

Ваша forward() функция также явно не оптимизирована и может обеспечить некоторое ускорение, но Я ожидал бы, что оптимизация в сторону лучшего дозирования будет намного лучше.

...