Я хочу использовать линейный полностью подключенный слой в качестве одного из входных слоев в моей сети. Вход имеет форму (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 Я буду благодарен за любую интуицию, извините, что пост получил это долго.