线性层的 PyTorch 和 Chainer 实现 - 它们是否等效?

PyTorch and Chainer implementations of the Linear layer- are they equivalent?

我想使用线性全连接层作为网络中的输入层之一。输入具有形状 (batch_size、in_channels、num_samples)。它基于 Tacotron 论文:https://arxiv.org/pdf/1703.10135.pdf,Enocder prenet 部分。 我感觉好像 Chainer 和 PyTorch 有不同的线性层实现——它们真的执行相同的操作还是我误解了什么?

在 PyTorch 中,线性层的行为遵循文档:https://pytorch.org/docs/0.3.1/nn.html#torch.nn.Linear 据此,输入输出数据的形状如下:

Input: (N,∗,in_features) where * means any number of additional dimensions

Output: (N,∗,out_features) where all but the last dimension are the same shape as the input.

现在,让我们尝试在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) 并调用它会导致大小不匹配。所以对于 chainer,我得到了完全不同的结果,因为这次我有一个形状为 (8, 12) 的权重矩阵,而我的结果的形状为 (1, 8)。所以现在,这是我的问题:由于结果明显不同,权重矩阵和输出都有不同的形状,我怎样才能使它们相等以及期望的输出应该是什么?在 Tacotron 的 PyTorch 实现中,PyTorch 方法似乎按原样使用 (https://github.com/mozilla/TTS/blob/master/layers/tacotron.py) - Prenet。如果是这样的话,我怎样才能使 Chainer 产生相同的结果(我必须在 Chainer 中实现它)。我将不胜感激,抱歉 post 拖了这么久。

Chainer Linear 层(有点令人沮丧)没有将变换应用于最后一个轴。 Chainer 压平了其余的轴。相反,您需要提供有多少批处理轴,documentation 在您的情况下为 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 等中的默认行为。