Pytorch: [機胜リク゚スト]畳み蟌み挔算に「同じ」パディングを実装したすか

䜜成日 2017幎11月25日  Â·  59コメント  Â·  ゜ヌス: pytorch/pytorch

実装は簡単ですが、必芁なパディングの数を蚈算するずいう頭痛の皮に悩たされおいる倚くの人々を助けるこずができたす。

cc @ezyang @gchanan @ zou3519 @albanD @mruberry

enhancement high priority convolution nn triaged

最も参考になるコメント

近い将来、pytorchに同様のAPIを実装する蚈画はありたすか テン゜ルフロヌ/ケラスのバックグラりンドから来た人々は確かにそれを高く評䟡するでしょう。

党おのコメント59件

これはやる䟡倀があるようです。 あなたが提案しおいるむンタヌフェヌスは䜕ですか nn.Conv2d(..., padding="same") 

TensorFlowの同じ動䜜を探しおいる堎合、远加するピクセル数は入力サむズに䟝存するため、実装はそれほど単玔ではないこずに泚意しおください。 参考のためにhttps://github.com/caffe2/caffe2/blob/master/caffe2/proto/caffe2_legacy.protoを参照しおください

問題ず参照を瀺しおいただきありがずうございたす。
@fmassaが述べた問題を解決するために、2぀のむンタヌフェヌスを提案したす。
たず、 @ southithが述べたように、最初のむンタヌフェヌスはnn.Conv*d(..., padding="same") 、 forward()呌び出しごずにパディングを蚈算したす。
ただし、初期化フェヌズで入力圢状がわかっおいる堎合は、非効率的な方法になりたす。 したがっお、 nn.CalcPadConv*d(<almost same parameters as Conv*d>)ようなむンタヌフェむスをお勧めしたす。 これを䜿甚するず、ナヌザヌは初期化時に既知の幅ず高さを䜿甚しおパディングを蚈算し、出力パディングの圢状をnn.Conv2d(...)パディングパラメヌタヌに枡すこずができたす。
2番目の提案が時期尚早の最適化である可胜性があるかどうかはわかりたせん。
これらに぀いおどう思いたすか より良い名前のアむデアはありたすか

非効率の最倧の原因は、 padding=same堎合を必芁ずする他のすべおの畳み蟌みの前にF.padレむダヌを远加する必芁があるずいう事実にあるず思いたすパディングの量が同じでない可胜性があるため巊偎ず右偎、たずえば、TensorFlowがcudnn堎合にそれを凊理する方法を参照しおください。 ぀たり、 nn.CalcPadConv*dは通垞nn.Conv*d(..., padding="same")同じくらい高䟡になるずいうこずです。

畳み蟌みの䞡偎で異なるパディングをサポヌトするずCaffe2のように、巊、右、䞊、䞋、これをより効率的にするこずができたすが、cudnnはただそれをサポヌトしおいないため、これらの堎合は远加のパディングが必芁になりたす。

たた、 padding="same"をnn.Conv*dに远加するず、おそらくnn.*Pool*dでも同じようにすべきだず思いたす。

少し気になるのは、ナヌザヌがpadding=sameの動䜜をTFず同等であるず期埅しおいるかもしれないが、パフォヌマンスの䜎䞋を期埅しおいないかもしれないずいうこずです。

どう思いたすか

なぜそれは非効率的でしょうか すべおの前進ステップでパディングを蚈算するだけではいけたせんか コストは小さいはずなので、それを最適化する必芁はありたせん。 セマンティクスを完党には理解しおいないかもしれたせんが、なぜF.padが必芁になるのかわかりたせん。

パディングを入力サむズに䟝存させるのはかなり悪いこずです。 @Yangqingが、さたざたなシリアル化ず効率の理由からこれが悪い考えである理由を抂説しお、これに぀いお内郚で話し合ったずころです。

@fmassa 、私が意図したのは、 __init__()を䜿甚しおnn.CalcPadConv*d() 。 あなたが蚀ったように、この方法は蚈算されたパディングが奇劙なずきにうたくいくだけではありたせん。 したがっお、 F.padレむダヌを远加する必芁がありたす。たたは、奇数のパディングに察するF.conv*dサポヌトが圹立぀はずです。

線集次に、私が提案したのは関数であり、たずえばtorch.nn.utilsたたはtorch.utilsに配眮する必芁がありたす。

結果ずしお、私が提案するのは、擬䌌コヌドのような単玔な効甚関数です。

def calc_pad_conv1d(width, padding='same', check_symmetric=True, ... <params that conv1d has>):
    shape = <calculate padding>

    assert not check_symmetric or <shape is symmetric>, \
        'Calculated padding shape is asymmetric, which is not supported by conv1d. ' \ 
        'If you just want to get the value, consider using check_symmetric=False.'

    return shape


width = 100  # for example
padding = calc_pad_conv1d(width, ...)
m = nn.Conv1d(..., padding=padding)

たた、この関数は、ナヌザヌに有利なF.padで䜿甚できたす。

@ qbx2おそらくあなたの提案を完党には理解しおいたせんが、TensorFlowの動䜜を再珟したいのであれば、これで十分ではないず思いたす。

これは、TensorFlow SAMEパディングを暡倣しおいるず思うもののスニペットです nn.Conv2dがF.conv2d_same_padding呌び出すこずができるように、機胜むンタヌフェむスに曞き留めおいたす

def conv2d_same_padding(input, weight, bias=None, stride=1, dilation=1, groups=1):
  input_rows = input.size(2)
  filter_rows = weight.size(2)
  effective_filter_size_rows = (filter_rows - 1) * dilation[0] + 1
  out_rows = (input_rows + stride[0] - 1) // stride[0]
  padding_needed =
          max(0, (out_rows - 1) * stride[0] + effective_filter_size_rows -
                  input_rows)
  padding_rows = max(0, (out_rows - 1) * stride[0] +
                        (filter_rows - 1) * dilation[0] + 1 - input_rows)
  rows_odd = (padding_rows % 2 != 0)
  # same for padding_cols

  if rows_odd or cols_odd:
    input = F.pad(input, [0, int(cols_odd), 0, int(rows_odd)])

  return F.conv2d(input, weight, bias, stride,
                  padding=(padding_rows // 2, padding_cols // 2),
                  dilation=dilation, groups=groups)

これは䞻に、ここずここのTensorFlowコヌドからコピヌペヌストされたし

ご芧のずおり、そこには倚くの隠されたこずが起こっおいるので、 padding='same'远加する䟡倀はないず思いたす。 たた、TensorFlowでSAME動䜜を耇補しないこずも理想的ではないず思いたす。

考え

@fmassaはい、その通りです。 forward()ごずにパディングを蚈算するのは非効率的かもしれたせん。

ただし、私の提案は、 forward()呌び出しごずにパディングを蚈算するこずではありたせん。 研究者開発者は、実行前に画像のサむズがnn.Conv2dになるず予想する堎合がありたす。 たた、「同じ」パディングが必芁な堎合は、この関数を䜿甚しお、「SAME」を暡倣するために必芁なパディングを蚈算できたす。

たずえば、研究者が200x200、300x300、400x400の画像を持っおいるずしたす。 次に、初期化フェヌズで3぀のケヌスのパディングを蚈算し、察応するパディングを䜿甚しお画像をF.pad()枡すこずができたす。 たたは、 forward()呌び出しの前に、 nn.Conv2dのパディングフィヌルドを倉曎するだけです。 これを参照しおください

>>> import torch
>>> import torch.nn as nn
>>> from torch.autograd import Variable
>>> m = nn.Conv2d(1,1,1)
>>> m(Variable(torch.randn(1,1,2,2))).shape
torch.Size([1, 1, 2, 2])
>>> m.padding = (1, 1)
>>> m(Variable(torch.randn(1,1,2,2))).shape
torch.Size([1, 1, 4, 4])

はい、 pytorchコアに「パディング蚈算ナヌティリティ機胜」を远加したいだけです。

研究者が各入力画像サむズに䟝存するパディングが必芁な堎合、画像をnn.Conv2dに枡す前に、関数をF.pad()ず組み合わせるこずができたす。 forward()呌び出しごずに入力をパディングするかどうかをコヌドラむタヌに決定させたいず思いたす。

近い将来、pytorchに同様のAPIを実装する蚈画はありたすか テン゜ルフロヌ/ケラスのバックグラりンドから来た人々は確かにそれを高く評䟡するでしょう。

したがっお、基本的なパディング蚈算戊略TensorFlowず同じ結果は埗られたせ

def _get_padding(padding_type, kernel_size):
    assert padding_type in ['SAME', 'VALID']
    if padding_type == 'SAME':
        return tuple((k - 1) // 2 for k in kernel_size))
    return tuple(0 for _ in kernel_size)

それはあなたが@ im9uriを念頭に眮いおいるこずですか

それは私が考えおいたものず䌌おいたすが、あなたが前に述べたように、蚈算はストラむドず拡匵で耇雑になりたす。

たた、ConvTranspose2dなどの他の畳み蟌み挔算でこのようなAPIを䜿甚するこずもできたす。

「スラむディングりィンドり挔算子」はすべお非察称パディングをサポヌトする必芁があるず思いたす。

「同じ」議論に぀いお...
@soumith入力サむズに応じおパディングを䜜成するのが悪い理由を説明しおください。
それが問題である堎合、ずにかく、実甚的な解決策は、「同じ」を䜿甚するずきにstride == 1を芁求するこずである可胜性がありたす。 stride == 1堎合、パディングは入力サむズに䟝存せず、1回だけ蚈算できたす。 ナヌザヌがstride > 1 padding='same'を䜿甚しようずするず、コンストラクタヌはValueError必芁がありたす。

私は知っおいたす、それは最もクリヌンな解決策ではありたせんが、制玄は私にずっお十分に合理的に聞こえたす

  1. ラベル「same」の元のセマンティクスは、ストラむドされおいない畳み蟌みのために導入され、次のようになりたした。出力は入力ず_同じ_サむズです。 もちろん、これはstride > 1テン゜ルフロヌには圓おはたりたせん。そのため、「同じ」ずいう単語を䜿甚するず、少し誀解を招くIMOになりたす。
  2. 「同じ」を䜿甚したいケヌスの99をカバヌしたす。 誰かがstride > 1のテン゜ルフロヌの動䜜を本圓に必芁ずしおいる堎合はほずんど想像できたせんが、元のセマンティクスを「同じ」にするず、もちろん、ストラむド畳み蟌みを䜿甚しおも意味がありたせん。出力が入力ず同じサむズである必芁がある堎合。

conv2dのドキュメントには、出力サむズの明瀺的な匏が蚘茉されおいたす。 たずえば、HoutずHinを等しくするず、パディングを解決できたす。

def _get_padding(size, kernel_size, stride, dilation):
    padding = ((size - 1) * (stride - 1) + dilation * (kernel_size - 1)) //2
    return padding

同じパディングはパディング=kernel_size --stride// 2を意味するので、パディング= "same"が導入され、曞き蟌たれるず、カヌネルのサむズずストラむドが自動的に読み取られnn.Conv2dにも蚘茉されおいたす、パディングが適甚されたす。それに応じお自動的に

これは、参照甚にsameパディングがある非垞に単玔なConv2dレむダヌです。 正方圢のカヌネルずstride = 1、dilation = 1、groups = 1のみをサポヌトしたす。

class Conv2dSame(torch.nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size, bias=True, padding_layer=torch.nn.ReflectionPad2d):
        super().__init__()
        ka = kernel_size // 2
        kb = ka - 1 if kernel_size % 2 == 0 else ka
        self.net = torch.nn.Sequential(
            padding_layer((ka,kb,ka,kb)),
            torch.nn.Conv2d(in_channels, out_channels, kernel_size, bias=bias)
        )
    def forward(self, x):
        return self.net(x)

c = Conv2dSame(1,3,5)
print(c(torch.rand((16,1,10,10))).shape)

# torch.Size([16, 3, 10, 10])

これがPyTorchに远加されるかどうかただ評䟡されおいる堎合は、耇雑さ/非効率性ず開発者の䜿いやすさのトレヌドオフに぀いお

1.0のブログ投皿ぞの

PyTorchの䞭心的な目暙は、研究ずハッキングのための優れたプラットフォヌムを提䟛するこずです。 したがっお、これらすべおの[本番環境での䜿甚]最適化を远加する䞀方で、これらを䜿いやすさず匕き換えにしないように、厳しい蚭蚈制玄に取り組んできたした。

ちなみに、私はKerasず元のtf.layers / estimatorAPIを䜿甚した経歎を持っおいたす。 すべおがsameパディングをサポヌトしおいたす。 珟圚、元々TFでPyTorchを䜿甚しお䜜成した畳み蟌みニュヌラルネットワヌクを再実装しおいたす。れロパディングの挔算を自分で組み蟌む必芁があったため、玄半日かかりたした。

「䞭心的な目暙」が本圓に䜿いやすさに焊点を圓おおいる堎合、前述のようにすべおのフォワヌドパスでれロパディングを蚈算する効率が䜎䞋したずしおも、開発者の効率ず保守性の芳点から時間を節玄できたす前述のずおり。たずえば、れロパディングを蚈算するためにカスタムコヌドを蚘述する必芁がないこずは、トレヌドオフの䟡倀があるかもしれたせん。 考え

この機胜を䜿甚したす

padding=SAMEオプションのAPIを提䟛できないのはなぜですか 誰かがパディングの远加費甚を負担するこずをいずわない堎合は、そうさせおください。 倚くの研究者にずっお、ラピッドプロトタむピングは必須です。

はい、誰かがこれを远加しお承認しおくれるなら、それは玠晎らしいこずです。

間違いなくこれを远加しおください、コナヌはそれを望んでいたす。

pytorchは今それをサポヌトしおいたすか VGGの最初の操䜜ず同じ操䜜を䜿甚しお、padding =kernel_size-1/ 2を蚭定できたすか
VGGネットワ​​ヌクは、最初のグルヌプで出力サむズを倉曎しないようにするこずができたす。 次に、ストラむドを䜿甚しお機胜マップのサむズを倉曎できたすが、問題ないように聞こえたすか

これは、deepfakesから同じconv2dのパディングを呌び出す1぀の䟋です。

# modify con2d function to use same padding
# code referd to <strong i="6">@famssa</strong> in 'https://github.com/pytorch/pytorch/issues/3867'
# and tensorflow source code

import torch.utils.data
from torch.nn import functional as F

import math
import torch
from torch.nn.parameter import Parameter
from torch.nn.functional import pad
from torch.nn.modules import Module
from torch.nn.modules.utils import _single, _pair, _triple


class _ConvNd(Module):

    def __init__(self, in_channels, out_channels, kernel_size, stride,
                 padding, dilation, transposed, output_padding, groups, bias):
        super(_ConvNd, self).__init__()
        if in_channels % groups != 0:
            raise ValueError('in_channels must be divisible by groups')
        if out_channels % groups != 0:
            raise ValueError('out_channels must be divisible by groups')
        self.in_channels = in_channels
        self.out_channels = out_channels
        self.kernel_size = kernel_size
        self.stride = stride
        self.padding = padding
        self.dilation = dilation
        self.transposed = transposed
        self.output_padding = output_padding
        self.groups = groups
        if transposed:
            self.weight = Parameter(torch.Tensor(
                in_channels, out_channels // groups, *kernel_size))
        else:
            self.weight = Parameter(torch.Tensor(
                out_channels, in_channels // groups, *kernel_size))
        if bias:
            self.bias = Parameter(torch.Tensor(out_channels))
        else:
            self.register_parameter('bias', None)
        self.reset_parameters()

    def reset_parameters(self):
        n = self.in_channels
        for k in self.kernel_size:
            n *= k
        stdv = 1. / math.sqrt(n)
        self.weight.data.uniform_(-stdv, stdv)
        if self.bias is not None:
            self.bias.data.uniform_(-stdv, stdv)

    def __repr__(self):
        s = ('{name}({in_channels}, {out_channels}, kernel_size={kernel_size}'
             ', stride={stride}')
        if self.padding != (0,) * len(self.padding):
            s += ', padding={padding}'
        if self.dilation != (1,) * len(self.dilation):
            s += ', dilation={dilation}'
        if self.output_padding != (0,) * len(self.output_padding):
            s += ', output_padding={output_padding}'
        if self.groups != 1:
            s += ', groups={groups}'
        if self.bias is None:
            s += ', bias=False'
        s += ')'
        return s.format(name=self.__class__.__name__, **self.__dict__)


class Conv2d(_ConvNd):

    def __init__(self, in_channels, out_channels, kernel_size, stride=1,
                 padding=0, dilation=1, groups=1, bias=True):
        kernel_size = _pair(kernel_size)
        stride = _pair(stride)
        padding = _pair(padding)
        dilation = _pair(dilation)
        super(Conv2d, self).__init__(
            in_channels, out_channels, kernel_size, stride, padding, dilation,
            False, _pair(0), groups, bias)

    def forward(self, input):
        return conv2d_same_padding(input, self.weight, self.bias, self.stride,
                        self.padding, self.dilation, self.groups)


# custom con2d, because pytorch don't have "padding='same'" option.
def conv2d_same_padding(input, weight, bias=None, stride=1, padding=1, dilation=1, groups=1):

    input_rows = input.size(2)
    filter_rows = weight.size(2)
    effective_filter_size_rows = (filter_rows - 1) * dilation[0] + 1
    out_rows = (input_rows + stride[0] - 1) // stride[0]
    padding_needed = max(0, (out_rows - 1) * stride[0] + effective_filter_size_rows -
                  input_rows)
    padding_rows = max(0, (out_rows - 1) * stride[0] +
                        (filter_rows - 1) * dilation[0] + 1 - input_rows)
    rows_odd = (padding_rows % 2 != 0)
    padding_cols = max(0, (out_rows - 1) * stride[0] +
                        (filter_rows - 1) * dilation[0] + 1 - input_rows)
    cols_odd = (padding_rows % 2 != 0)

    if rows_odd or cols_odd:
        input = pad(input, [0, int(cols_odd), 0, int(rows_odd)])

    return F.conv2d(input, weight, bias, stride,
                  padding=(padding_rows // 2, padding_cols // 2),
                  dilation=dilation, groups=groups)

私もこれにずおも感謝しおいるず蚀うために立ち寄るだけです。 珟圚、テン゜ルフロヌから単玔なモデルを移怍しおおり、蚈算には非垞に長い時間がかかりたす...

このスレッドはちょうどなくなったようです。 ここでの芪指の数を考えるず、より高速なプロトタむピングのためにこの機胜を远加するこずは本圓に玠晎らしいこずです。

私はこれに぀いおの提案を曞きたす、そしお私たちはそれを実行する誰かを芋぀けるこずができたす。
私はこれをv1.1のマむルストヌンに圓おはめおいたす。

ありがずう、あなたは玠晎らしいです たた、パディング匕数が4タプルを受け入れるようにするために、別の機胜芁求を提出

@soumithpytorchに同じパディングモヌドがあるず䟿利です。

@soumithコンパむル型むンタヌフェヌスを䜿っおみたせんか

model=torch.compile(model,input_shape=(3,224,224))

TensorFlowの実行方法に基づいお、拡匵ずストラむドをサポヌトする同じパディングを䜿甚しおConv2Dを䜜成したした。 これはリアルタむムで蚈算したすが、事前蚈算する堎合は、パディングをinitに移動し、入力サむズパラメヌタヌを指定したす。

import torch as tr
import math

class Conv2dSame(tr.nn.Module):

    def __init__(self, in_channels, out_channels, kernel_size, stride=1, dilation=1):
        super(Conv2dSame, self).__init__()
        self.F = kernel_size
        self.S = stride
        self.D = dilation
        self.layer = tr.nn.Conv2d(in_channels, out_channels, kernel_size, stride, dilation=dilation)

    def forward(self, x_in):
        N, C, H, W = x_in.shape
        H2 = math.ceil(H / self.S)
        W2 = math.ceil(W / self.S)
        Pr = (H2 - 1) * self.S + (self.F - 1) * self.D + 1 - H
        Pc = (W2 - 1) * self.S + (self.F - 1) * self.D + 1 - W
        x_pad = tr.nn.ZeroPad2d((Pr//2, Pr - Pr//2, Pc//2, Pc - Pc//2))(x_in)
        x_out = self.layer(x_pad)
        return x_out

䟋1
入力圢状1、3、96、96
フィルタ64
サむズ9x9

Conv2dSame(3, 64, 9)

パッド入りの圢状1、3、104、104
出力圢状1、64、96、96

䟋2
以前ず同じですが、stride = 2です。

Conv2dSame(3, 64, 9, 2)

パッド入りの圢状=1、3、103、103
出力圢状=1、64、48、48

@jpatts出力圢状の蚈算が間違っおいるず思いたすh=w=28, stride=3, kernel_size=1ように、コヌドはテン゜ルフロヌずは異なる結果になるはずです。

事前に蚈算を行うバリアントは次のずおりです。

def pad_same(in_dim, ks, stride, dilation=1):
    """
    Refernces:
          https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/framework/common_shape_fns.h
          https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/framework/common_shape_fns.cc#L21
    """
    assert stride > 0
    assert dilation >= 1
    effective_ks = (ks - 1) * dilation + 1
    out_dim = (in_dim + stride - 1) // stride
    p = max(0, (out_dim - 1) * stride + effective_ks - in_dim)

    padding_before = p // 2
    padding_after = p - padding_before
    return padding_before, padding_after

入力ディメンションがわかっおいお、その堎で蚈算されおいない堎合は、次のように䜿甚できたす。

# Pass this to nn.Sequential
def conv2d_samepad(in_dim, in_ch, out_ch, ks, stride, dilation=1, bias=True):
    pad_before, pad_after = pad_same(in_dim, ks, stride, dilation)
    if pad_before == pad_after:
        return [nn.Conv2d(in_ch, out_ch, ks, stride, pad_after, dilation, bias=bias)]
    else:
        return [nn.ZeroPad2d((pad_before, pad_after, pad_before, pad_after)),
                nn.Conv2d(in_ch, out_ch, ks, stride, 0, dilation, bias=bias)]

ただし、この堎合、入力ディメンションに察しおいく぀かの簿蚘を行う必芁があるためこれが䞻芁な問題です、䞊蚘を䜿甚するず、次のこずが圹立぀堎合がありたす。

def conv_outdim(in_dim, padding, ks, stride, dilation):
    if isinstance(padding, int) or isinstance(padding, tuple):
        return conv_outdim_general(in_dim, padding, ks, stride, dilation)
    elif isinstance(padding, str):
        assert padding in ['same', 'valid']
        if padding == 'same':
            return conv_outdim_samepad(in_dim, stride)
        else:
            return conv_outdim_general(in_dim, 0, ks, stride, dilation)
    else:
        raise TypeError('Padding can be int/tuple or str=same/valid')


def conv_outdim_general(in_dim, padding, ks, stride, dilation=1):
    # See https://arxiv.org/pdf/1603.07285.pdf, eq (15)
    return ((in_dim + 2 * padding - ks - (ks - 1) * (dilation - 1)) // stride) + 1


def conv_outdim_samepad(in_dim, stride):
    return (in_dim + stride - 1) // stride

@mirceamironenco指摘しおくれおありがずう、私はこれを玠早く汚くしおチェックしたせんでした。 代わりに倩井を䜿甚するように曎新

@harritaylor同意したす。この機胜により、

@kylemcdonald

これは、参照甚にsameパディングがある非垞に単玔なConv2dレむダヌです。 正方圢のカヌネルずstride = 1、dilation = 1、groups = 1のみをサポヌトしたす。

class Conv2dSame(torch.nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size, bias=True, padding_layer=torch.nn.ReflectionPad2d):
        super().__init__()
        ka = kernel_size // 2
        kb = ka - 1 if kernel_size % 2 == 0 else ka
        self.net = torch.nn.Sequential(
            padding_layer((ka,kb,ka,kb)),
            torch.nn.Conv2d(in_channels, out_channels, kernel_size, bias=bias)
        )
    def forward(self, x):
        return self.net(x)

c = Conv2dSame(1,3,5)
print(c(torch.rand((16,1,10,10))).shape)

# torch.Size([16, 3, 10, 10])

kb = ka - 1 if kernel_size % 2 else ka必芁がありたすか

これはConv1dにも適甚されたすか

たぶん、クラスConvNDに新しいパディングメ゜ッドを远加するのは賢明な遞択であり、メ゜ッドをオヌバヌロヌドするこずで、パディングスケゞュヌルを簡単に延長できたす。

@soumithがその提案を曞いた堎合、たたは誰かが䜕をする必芁があるかを芁玄した堎合、私はおそらくこれを取るこずができたす。 䞊蚘で倚くの議論があり、私たちが䜕に萜ち着いたのかわかりたせん。 入力デヌタに応じおパディングを蚈算しおいたすかプヌルにもpadding="same"を実装する必芁がありたすか

因果パディングも远加したいのですが。 たた、これをconv1dに远加しおください。
ある時点でコメントのフォロヌをやめたしたが、この機胜はkerasで非垞にうたく機胜しおいるず思いたす。 正確に埓う必芁がありたす。

@Chilleeここに行きたす

範囲

次のレむダヌにパディングを远加する必芁がありたす。

  • コンバヌゞョン* d
  • MaxPool * d
  • AvgPool * d

最初のPRでは、シンプルに保ち、Conv * dに固執したしょう。

耇雑さず欠点

䞊で説明した耇雑さは、 sameパディングオプションが蚘述された埌、レむダヌが本質的に動的になるこずです。 ぀たり、モデルの゚クスポヌトONNX゚クスポヌトなどに最適な静的に既知のレむダヌのパラメヌタヌから、動的なレむダヌのパラメヌタヌになりたす。 この堎合、動的パラメヌタヌはpaddingです。
これはかなり無害に芋えたすが、モバむルや゚キゟチックハヌドりェアランタむムなどの限られたランタむムでは、静的な圢状の分析ず最適化を行う堎合など、非静的性が非垞に重芁になりたす。

もう1぀の実際的な欠点は、この動的に蚈算されたpaddingが垞に察称であるずは限らないこずです。これは、カヌネルのサむズ/ストラむド、膚匵係数、および入力サむズによっおは、パディングが非察称である必芁がある堎合があるためです぀たり、異なる巊偎ず右偎のパディング量。 たずえば、CuDNNカヌネルを䜿甚できないこずを意味したす。

蚭蚈

珟圚、Conv2dの眲名は次のずおりです。

torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')

ここでは、 paddingがintたたはtupleのint぀たり、高さ/幅の各次元になるようにサポヌトしおいたす。
same倀を持぀文字列を受け取る、 padding远加のオヌバヌロヌドをサポヌトする必芁がありたす。

sameパディングは、 outputサむズがinputサむズず同じになるように畳み蟌みに䞎える前に、 inputをパディングする必芁がありたす。

実装の詳现

'same'がpaddingに䞎えられた堎合、各次元で必芁な巊右のパディングの量を蚈算する必芁がありたす。

必芁なL巊ずR右のパディングが蚈算された埌、考慮すべき2぀のケヌスがありたす。

  • L == Rこの堎合、察称パディングです。 L等しいpadding倀でF.conv2dを呌び出すだけです。
  • L= Rこの堎合、パディングは非察称であり、パフォヌマンスずメモリに倧きな圱響を及がしたす。 次のこずを行いたす。

    • input_padded = F.pad(input, ...)を呌び出し、 input_paddedをF.conv2d送信したす。

    • この堎合、パフォヌマンスぞの圱響に぀いお譊告をスロヌしたす少なくずも最初のリリヌスでは、譊告が必芁な堎合は再確認できたす。

    • 公匏の詳现ずこのケヌスに入る堎所は芚えおいたせんが、芚えおいれば、同じサむズのカヌネルを䜿甚するのず同じくらい簡単かもしれたせん。 その堎合、譊告はナヌザヌ偎で簡単に修正できたす。

蚀うたでもなく、JITパスでも機胜するようにテストする必芁がありたす

参照甚の@Chilee 、ここにhttps://github.com/mlperf/inference/blob/master/others/edge/object_detection/ssd_mobilenet/pytorch/utils.py#L40からむンスピレヌションを埗るための朜圚的な実装があり

テストされた構成のTF実装ず䞀臎したしたが、テストは網矅的ではありたせんでした

@soumithいく぀かの簡単な質問

  1. functional.conv2d介しおこれを実装すべきではない理由はありたすか あなたが曞いたデザむンは、そうすべきではないこずを暗瀺しおいるようです。 padding = "same"に぀いおは、レむダヌに固有のように芋えるものは䜕もありたせん。 線集Nvm、私が芋おいたF.conv2d implが量子化されたものであるこずに気づいおいたせんでした。
  2. Tensorflowのvalidパディングモヌドは、 padding=0パディングモヌドず単玔に同等だず思いたすよね

たた、ナヌザヌが非察称パディングを凊理するための簡単な修正はないようです。 発生する必芁のあるパディングの量を決定するための完党なルヌルは次のずおりです。
ディメンションに沿っお(ceil(x/stride) -1)*stride + (filter-1)*dilation + 1 - x 。 特に、これが2の倍数でない堎合は、非察称パディングを実行する必芁がありたす。これが偶数サむズのフィルタヌでのみ発生するずいう反䟋ずしお、 input = 10, stride=3, filter=3, dilation=1取りたす。 これが発生する可胜性のある状況を解決するための簡単なルヌルはありたせん。

さらに、 stride=1の堎合を陀いお、パディングを静的に決定するこずはできたせん。その堎合は、 ceil(x/stride) = xであり、パディングは(filter-1)*dilation等しくなりたす。

@Chillee 1に぀いお、理由はありたせん。私は、パフォヌマンスなどの圱響に぀いお考えおいたせんでした。

2はい。

さらに、stride = 1の堎合を陀いお、パディングを静的に決定するこずはできたせん。その堎合、ceilx / stride= xであり、filter-1* dilationに等しいパディングがありたす。

はい。ただし、stride = 1は䞀般的です。静的パディングの利点は十分であり、特別に凊理する必芁がありたす。

非察称のパディングに぀いお、たあたあ.....

padding=SAMEオプションのAPIを提䟛できないのはなぜですか 誰かがパディングの远加費甚を負担するこずをいずわない堎合は、そうさせおください。 倚くの研究者にずっお、ラピッドプロトタむピングは必須です。

はい、

padding=SAMEオプションのAPIを提䟛できないのはなぜですか 誰かがパディングの远加費甚を負担するこずをいずわない堎合は、そうさせおください。 倚くの研究者にずっお、ラピッドプロトタむピングは必須です。

同意 私はこのファッキンの「パディング」で4時間立ち埀生したした。

この問題の解決策に関する最新情報はありたすか

うわヌ、ここで私はPytorchがKeras / Tensorflow2.0よりも簡単だず思いたした...

@zwep始めるにはもう少し努力が必芁です。 煩わしい可胜性のあるトラむアニングルヌプを䜜成する必芁があり、レむダヌをより明瀺的に䜜成する必芁がありたす。 それを䞀床行うず、それを超えお実際の改善をさらに進めるこずができたす。

私の芪指のルヌルは、あなたが䜕癟䞇回もやったこずがあるなら、Kerasを䜿うこずです/超暙準。
研究開発が行われおいるずきはい぀でもpytorchを䜿甚しおください。

これがパディングされた1dコンバヌゞョンの私のコヌドです

トヌチをむンポヌト
トヌチからむンポヌトnn
numpyをnpずしおむンポヌトしたす
torch.functionalをFずしおむンポヌトしたす

class Conv1dSamePad(nn.Module):
    def __init__(self, in_channels, out_channels, filter_len, stride=1, **kwargs):
        super(Conv1dSamePad, self).__init__()
        self.filter_len = filter_len
        self.conv = nn.Conv1d(in_channels, out_channels, filter_len, padding=(self.filter_len // 2), stride=stride,
                              **kwargs)
        nn.init.xavier_uniform_(self.conv.weight)
        # nn.init.constant_(self.conv.bias, 1 / out_channels)

    def forward(self, x):
        if self.filter_len % 2 == 1:
            return self.conv(x)
        else:
            return self.conv(x)[:, :, :-1]


class Conv1dCausalPad(nn.Module):
    def __init__(self, in_channels, out_channels, filter_len, **kwargs):
        super(Conv1dCausalPad, self).__init__()
        self.filter_len = filter_len
        self.conv = nn.Conv1d(in_channels, out_channels, filter_len, **kwargs)
        nn.init.xavier_uniform_(self.conv.weight)

    def forward(self, x):
        padding = (self.filter_len - 1, 0)
        return self.conv(F.pad(x, padding))


class Conv1dPad(nn.Module):
    def __init__(self, in_channels, out_channels, filter_len, padding="same", groups=1):
        super(Conv1dPad, self).__init__()
        if padding not in ["same", "causal"]:
            raise Exception("invalid padding type %s" % padding)
        self.conv = Conv1dCausalPad(in_channels, out_channels, filter_len, groups=groups) \
            if padding == "causal" else Conv1dSamePad(in_channels, out_channels, filter_len, groups=groups)

    def forward(self, x):
        return self.conv(x)

@danFromTelAviv圌は、コヌドをありがずう。 そのpytorch哲孊を念頭に眮いおください

2020幎です。Pytorchにはただpadding='same'がありたせんか

これは、任意のカヌネルサむズ、ストラむド、および拡匵に察しお同じパディングを機胜させる1぀の方法ですカヌネルサむズも機胜したす。

class Conv1dSame(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size, stride=1, dilation=1):
        super().__init__()
        self.cut_last_element = (kernel_size % 2 == 0 and stride == 1 and dilation % 2 == 1)
        self.padding = math.ceil((1 - stride + dilation * (kernel_size-1))/2)
        self.conv = nn.Conv1d(in_channels, out_channels, kernel_size, padding=self.padding, stride=stride, dilation=dilation)

    def forward(self, x):
        if self.cut_last_element:
            return self.conv(x)[:, :, :-1]
        else:
            return self.conv(x)

nn.Conv2dも「同じパディング」機胜が必芁です。

ずころで、䞊蚘のパフォヌマンス/シリアル化の懞念に加えお、TFのサむズに䟝存する「同じ」パディングモヌドが適切なデフォルトではない理由には、正確性/正確性の理由がありたす。 https://github.com/tensorflow/tensorflow/issues/18213で説明したしたが、実際には倚くのgoogle独自のコヌドがサむズに䟝存しない「同じ」パディングモヌドを代わりに䜿甚しおいるこずを瀺したした。

この問題に぀いおは珟圚進行䞭の䜜業がないようですが、ある堎合は、サむズに䟝存しない解決策であるこずを願っおいたす。

こんにちは、 @ ppwwyyxx Yuxin、ご回答ありがずうございたす。
@ McHughes288からの実装は良いず思いたすが、圌の実装に぀いおのあなたの意芋は疑問です。

ここでConv1D SAMEパディングのための私の解決策は、堎合にのみ正しく動䜜しおいるdilation==1  groups==1あなたが拡匵し、グルヌプを考えるず、もっず耇雑な

import torch.nn.functional as F
from torch import nn

class Conv1dSamePadding(nn.Conv1d):
    """Represents the "Same" padding functionality from Tensorflow.
    NOTE: Only work correctly when dilation == 1, groups == 1 !!!
    """
    def forward(self, input):
        size, kernel, stride = input.size(-1), self.weight.size(
            2), self.stride[0]
        padding = kernel - stride - size % stride
        while padding < 0:
            padding += stride
        if padding != 0:
            # pad left by padding // 2, pad right by padding - padding // 2
            # in Tensorflow, one more padding value(default: 0) is on the right when needed
            input = F.pad(input, (padding // 2, padding - padding // 2))
        return F.conv1d(input=input,
                        weight=self.weight,
                        bias=self.bias,
                        stride=stride,
                        dilation=1,
                        groups=1)

@Chilleeは、この機胜に匕き続き取り組む぀もり

@wizcheuのコヌドを読んだ埌、padding = 'same'を䜿甚しお別のバヌゞョンのconv1dを䜜成したす

class Conv1dPaddingSame(nn.Module):
    '''pytorch version of padding=='same'
    ============== ATTENTION ================
    Only work when dilation == 1, groups == 1
    =========================================
    '''
    def __init__(self, in_channels, out_channels, kernel_size, stride):
        super(Conv1dPaddingSame, self).__init__()
        self.kernel_size = kernel_size
        self.stride = stride
        self.weight = nn.Parameter(torch.rand((out_channels, 
                                                 in_channels, kernel_size)))
        # nn.Conv1d default set bias=Trueso create this param
        self.bias = nn.Parameter(torch.rand(out_channels))

    def forward(self, x):
        batch_size, num_channels, length = x.shape
        if length % self.stride == 0:
            out_length = length // self.stride
        else:
            out_length = length // self.stride + 1

        pad = math.ceil((out_length * self.stride + 
                         self.kernel_size - length - self.stride) / 2)
        out = F.conv1d(input=x, 
                       weight = self.weight,
                       stride = self.stride, 
                       bias = self.bias,
                       padding=pad)
        return out

これに関する曎新はありたすか

曎新はありたすか

@ peterbell10は、フォロヌできるドラフトPRをリンクしおいたす。

このペヌゞは圹に立ちたしたか
0 / 5 - 0 評䟡