PyTorch и Chainer реализации линейного слоя - они эквивалентны? - PullRequest
1 голос
/ 08 мая 2019

Я хочу использовать линейный полностью подключенный слой в качестве одного из входных слоев в моей сети. Вход имеет форму (batch_size, in_channels, num_samples). Он основан на бумаге Такотрон: https://arxiv.org/pdf/1703.10135.pdf, часть предварительной сети Enocder. Мне кажется, что Chainer и PyTorch имеют разные реализации уровня Linear - они действительно выполняют одни и те же операции или я что-то неправильно понимаю?

В PyTorch поведение линейного слоя соответствует документации: https://pytorch.org/docs/0.3.1/nn.html#torch.nn.Linear в соответствии с которым форма входных и выходных данных имеет следующий вид:

Ввод: (N, ∗, in_features) где * означает любое количество дополнительных измерений

Выход: (N, ∗, out_features), где все, кроме последнего измерения, имеют ту же форму, что и вход.

Теперь давайте попробуем создать линейный слой в pytorch и выполнить операцию. Мне нужен выход с 8 каналами, а входные данные будут иметь 3 канала.

import numpy as np
import torch
from torch import nn
linear_layer_pytorch = nn.Linear(3, 8)

Давайте создадим некоторые фиктивные входные данные формы (1, 4, 3) - (batch_size, num_samples, in_channels:

data = np.array([1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4], dtype=np.float32).reshape(1, 4, 3)
data_pytorch = torch.from_numpy(data)

и, наконец, выполните операцию:

results_pytorch = linear_layer_pytorch(data_pytorch)
results_pytorch.shape

Форма вывода выглядит следующим образом: Out[27]: torch.Size([1, 4, 8]) Взглянем на источник реализации PyTorch:

def linear(input, weight, bias=None):
    # type: (Tensor, Tensor, Optional[Tensor]) -> Tensor
    r"""
    Applies a linear transformation to the incoming data: :math:`y = xA^T + b`.

    Shape:

        - Input: :math:`(N, *, in\_features)` where `*` means any number of
          additional dimensions
        - Weight: :math:`(out\_features, in\_features)`
        - Bias: :math:`(out\_features)`
        - Output: :math:`(N, *, out\_features)`
    """
    if input.dim() == 2 and bias is not None:
        # fused op is marginally faster
        ret = torch.addmm(bias, input, weight.t())
    else:
        output = input.matmul(weight.t())
        if bias is not None:
            output += bias
        ret = output
    return ret

Транспонирует весовую матрицу, которая передается ей, транслирует ее по оси batch_size и выполняет умножения матриц. Имея в виду, как работает линейный слой, я представляю его как 8 узлов, соединенных через синапс, содержащий вес, с каждым каналом во входной выборке, поэтому в моем случае он имеет вес 3 * 8. И именно эту форму я вижу в отладчике (8, 3).

Теперь давайте перейдем к Chainer. Документация по линейному слою Chainer доступна здесь: https://docs.chainer.org/en/stable/reference/generated/chainer.links.Linear.html#chainer.links.Linear. Согласно этой документации, слой Linear охватывает функцию linear , которая согласно документам сглаживает ввод по размеры партии и форма ее весовой матрицы (output_size, flattend_input_size)

import chainer 
linear_layer_chainer = chainer.links.Linear(8)
results_chainer = linear_layer_chainer(data)
results_chainer.shape
Out[21]: (1, 8)

Создание слоя как linear_layer_chainer = chainer.links.Linear(3, 8) и его вызов вызывает несоответствие размера. Так что в случае с цепью я получил совершенно другие результаты, потому что на этот раз у меня есть весовая матрица, имеющая форму (8, 12), а мои результаты имеют форму (1, 8). Итак, вот мой вопрос: поскольку результаты явно различаются, и матрицы весов, и выходные данные имеют разные формы, как я могу сделать их эквивалентными и каким должен быть желаемый результат? В реализации Tacotron в PyTorch кажется, что подход PyTorch используется как есть (https://github.com/mozilla/TTS/blob/master/layers/tacotron.py) - Prenet. Если это так, как я могу заставить Chainer выдавать те же результаты (я должен реализовать это в Chainer Я буду благодарен за любую интуицию, извините, что пост получил это долго.

1 Ответ

0 голосов
/ 08 мая 2019
Слой

Chainer Linear (немного разочаровывает) не применяет преобразование к последней оси.Chainer выравнивает остальную часть осей.Вместо этого вам нужно указать количество осей дозирования, документация , что в вашем случае равно 2:

# data.shape == (1, 4, 3)
results_chainer = linear_layer_chainer(data, n_batch_axes=2)
# 2 batch axes (1,4) means you apply linear to (..., 3)
# results_chainer.shape == (1, 4, 8)

Вы также можете использовать l(data, n_batch_axes=len(data.shape)-1), чтобы всегда применять к последнему измерению, котороеэто поведение по умолчанию в PyTorch, Keras и т. д.

...