Руководство по восхождению на яму PyTorch

PyTorch

Пользуюсь PyTorch давно, но изредка будут различные баги.Когда багов накопится до определенного количества, они будут вызывать качественные изменения - пишите заметку на долгую память...

Переменная и тензор

0.4Более поздние версии могут напрямую использовать Tensor вместо Variable, нет необходимости обертывать громоздкий слойVariable.

Транспонировать тензор

Есть много способов транспонировать тензор, например.transpose,.transpose_Это наиболее часто используемый (подчеркнуто, чтобы внести изменения в исходный тензор), но этот метод транспонирования имеет ограничение, заключающееся в том, что он может работать только в двух измерениях, что является функцией транспонирования «корневой положительный красный росток».

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

Размер вверх и вниз

Вознесение.unsqueezeзаключается в том, чтобы вставить измерение в указанное измерение, но данные фактически не изменяются. Например следующий пример:

In [1]: a
Out[1]:
tensor([[[-0.7908, -1.4576, -0.3251],
         [-1.2053,  0.3667,  0.9423],
         [ 0.0517,  0.6051, -0.1360],
         [ 0.8666, -1.4679, -0.4511]]])

In [2]: a.shape
Out[2]: torch.Size([1, 4, 3])

In [3]: a.unsqueeze(2).shape
Out[3]: torch.Size([1, 4, 1, 3])

Снижение размерности.squeezeявляется обратным восходящему измерению, которое исключает все измерения размерности 1:

In [1]: a.shape
Out[1]: torch.Size([1, 4, 3])

In [2]: a.squeeze().shape
Out[2]: torch.Size([4, 3])

Тензор смежный

После того, как тензор выполняет операцию транспонирования, данные на самом деле не меняются, но меняется порядок чтения данных. Это метод экономии места и вычислений.

Однако, если транспонированные данные необходимо.viewДля других операций, поскольку данные не являются логически непрерывными, их необходимо вызывать вручную.contiguousПусть данные восстанавливают вид непрерывного хранения.

Каков формат параметров функций потерь?

Это ситуация, которая часто встречается в первые дни.FloatTensor, скороLongTensorуже? Чтобы решить эту проблему, вам все еще нужно иметь глубокое понимание того, что делают различные функции потерь.

BCELoss

Взаимная энтропия двух классификаций рассчитывается следующим образом:

\sum_{i}{-y_ilog\hat{y_i}-(1-y_i)log(1-\hat{y_i})}

Это не трудно увидетьy_iи\hat{y_i}оба должны бытьFloatTensor. Если операцию выполнить на другом тензоре, возникнет ошибка:

Expected object of type torch.FloatTensor but found type torch.LongTensor

CrossEntropyLoss

вышеBCELossЭто расчет функции потерь в случае двух классификаций, поэтому, когда вы сталкиваетесь с проблемами с несколькими классификациями, если вы хотите использовать перекрестную энтропию для расчета, вам нужно использоватьCrossEntropyLoss.

Будь то двоичный или мультиклассовый, его можно рассчитать с простейшей потерей L2, но если вы используетеSigmoidВ качестве функции активации градиент легко исчезает, что приводит к плохим результатам.

Пользовательская сеть должна инициализировать веса

Если веса не инициализированы, что-то произойдет...

Иногда вы сталкиваетесь с некоторыми сетями со странной структурой, и различные параметры перепутаны, поэтому функций, которые идут в комплекте с факелом, может быть недостаточно, тогда вам нужно настроить структуру сети. Как правило, он строится из самой низкой матрицы, как и тензорный поток. Например, чтобы определить полносвязный слой без условий смещения, вы можете использоватьnn.Linear(xxx, xxx, bias=False). Однако в особых случаях даже внутренние детали полносвязного слоя должны быть определены вами самостоятельно.В это время вам необходимо обратиться к исходному коду torch, например, к части исходного кода полносвязного слоя:

class Linear(Module):
    def __init__(self, in_features, out_features, bias=True):
        super(Linear, self).__init__()
        self.in_features = in_features
        self.out_features = out_features
        self.weight = Parameter(torch.Tensor(out_features, in_features))
        if bias:
            self.bias = Parameter(torch.Tensor(out_features))
        else:
            self.register_parameter('bias', None)
        self.reset_parameters()

    def reset_parameters(self):
        stdv = 1. / math.sqrt(self.weight.size(1))
        self.weight.data.uniform_(-stdv, stdv)
        if self.bias is not None:
            self.bias.data.uniform_(-stdv, stdv)

    def forward(self, input):
        return F.linear(input, self.weight, self.bias)

На этом примере уже можно многому научиться, вот ключевая ссылкаParametersИспользование (сначала out_feature) и функции инициализации весаreset_parametersдетали интерьера.

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

Некоторые ямы CUDA

Пользовательская сеть не будет автоматически конвертироваться в cuda? ?

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

Expected object of type torch.FloatTensor but found type torch.cuda.FloatTensor for argument

Позже при отладке выяснилось, что сеть верхнего уровня называлась.cudaПосле этого базовая сеть все еще находится вdevice(type='cpu')! Хотя самый простой способ решить эту проблему — поместить определение базовой сети в верхнюю сеть, это не способствует сопровождению кода, и будет очень сложно совместно использовать модули в разных сетях.

Миграция PyTorch на данные ЦП и ГП действительно не так удобна, как тензорный поток...

Я видел на GitHub много обходных путей для подобных проблем, и один из них — хорошая идея:

if torch.cuda.available():
    import torch.cuda as t
else:
    import torch as t

Если вы рассматриваете автоматический CUDA при написании кода в начале, это действительно хороший способ, если вы уже написали раздутый код, его все равно будет очень обременительно переписывать.

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

def cuda(self, device=None):
    r"""Moves all model parameters and buffers to the GPU..."""
    return self._apply(lambda t: t.cuda(device))

существует.cudaПодмодуль также вызывается в функции.cudaВот и все.

Данные CUDA не могут быть напрямую преобразованы в данные numpy.

Если данные были перенесены на графический процессор, преобразовать результат операции в данные, соответствующие характеристикам numpy, не так просто, и обычно будет сообщено об ошибке:

TypeError: can't convert CUDA tensor to numpy. Use Tensor.cpu() to copy the tensor to host memory first.

Выдано сообщение об ошибке решения. Конечно, вы также можете изменить операции, которые нуждаются в помощи numpy в будущем, на операции, поддерживаемые PyTorch, так скорость будет еще быстрее, ведь все дороги ведут в Рим.