.. _sec_transposed_conv: Devrik Evrişim ============== Bugüne kadar gördüğümüz CNN katmanları, evrişimli katmanlar (:numref:`sec_conv_layer`) ve ortaklama katmanları (:numref:`sec_pooling`) gibi, genellikle girdinin uzamsal boyutlarını (yükseklik ve genişlik) azaltır (örnek seyreltme) veya değişmeden tutar. Piksel düzeyinde sınıflandırılan anlamsal bölümlemede, girdi ve çıktının mekansal boyutları aynı ise uygun olacaktır. Örneğin, bir çıktı pikselindeki kanal boyutu, girdi pikselinin sınıflandırma sonuçlarını aynı uzamsal konumda tutabilir. Bunu başarmak için, özellikle uzamsal boyutlar CNN katmanları tarafından azaltıldıktan sonra, ara öznitelik haritalarının mekansal boyutlarını artırabilecek (örnek sıklaştırma) yapabilen başka bir CNN katmanlarını kullanabiliriz. Bu bölümde, evrişim tarafından örnek seyreltme işlemlerini tersine çevirmek için *kesirli adımlı evrişim* :cite:`Dumoulin.Visin.2016` olarak da adlandırılan *devrik evrişimi* tanıtacağız. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python from d2l import mxnet as d2l from mxnet import init, np, npx from mxnet.gluon import nn npx.set_np() .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python import torch from torch import nn from d2l import torch as d2l .. raw:: html
.. raw:: html
Temel İşlem ----------- Şimdilik kanalları görmezden gelerek, 1 adımlı ve dolgusuz temel devrik evrişim işlemiyle başlayalım. Bir :math:`n_h \times n_w` girdi tensörü ve bir :math:`k_h \times k_w` çekirdeği verildiğini varsayalım. Çekirdek penceresini her satırda :math:`n_w` kez ve her sütundaki :math:`n_h` kez 1 adımıyla kaydırılması toplam :math:`n_h n_w` ara sonuç verir. Her ara sonuç sıfır olarak ilklenen bir :math:`(n_h + k_h - 1) \times (n_w + k_w - 1)` tensördür. Her ara tensörün hesaplanması için, girdi tensöründe bulunan her eleman çekirdek ile çarpılır, böylece sonuçta ortaya çıkan :math:`k_h \times k_w` tensörü her ara tensörde bir kısmın yerini alır. Her ara tensördeki değiştirilen kısmın konumunun, hesaplama için kullanılan girdi tensöründe elemanın konumuna karşılık geldiğini unutmayın. Sonunda, çıktı üretmek için tüm ara sonuçlar toplanır. Örneğin, :numref:`fig_trans_conv`, :math:`2\times 2` girdi tensörü için :math:`2\times 2` çekirdeği ile devrik evrişimin nasıl hesaplandığını göstermektedir. .. _fig_trans_conv: .. figure:: ../img/trans_conv.svg :math:`2\times 2` çekirdek ile devrik evrişim. Gölgeli kısımlar, bir ara tensörün bir kısmı ile hesaplama için kullanılan girdi ve çekirdek tensör elemanlarıdır. Bir girdi matrisi ``X`` ve bir çekirdek matrisi ``K`` için bu temel devrik evrişim işlemi ``trans_conv``'u uygulayabiliriz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def trans_conv(X, K): h, w = K.shape Y = np.zeros((X.shape[0] + h - 1, X.shape[1] + w - 1)) for i in range(X.shape[0]): for j in range(X.shape[1]): Y[i: i + h, j: j + w] += X[i, j] * K return Y .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def trans_conv(X, K): h, w = K.shape Y = torch.zeros((X.shape[0] + h - 1, X.shape[1] + w - 1)) for i in range(X.shape[0]): for j in range(X.shape[1]): Y[i: i + h, j: j + w] += X[i, j] * K return Y .. raw:: html
.. raw:: html
Çekirdek yoluyla girdi öğelerini *azaltan* olağan evrişimin (:numref:`sec_conv_layer`) aksine, devrik evrişim, girdi öğelerini çekirdek yoluyla *yayınlar*, böylece girdiden daha büyük bir çıktı üretir. Temel iki boyutlu devrik evrişim işleminin yukarıdaki uygulamanın çıktısını doğrulamak için :numref:`fig_trans_conv` şeklindeki girdi tensörü ``X``'i ve çekirdek tensörü ``K``'yi oluşturabiliriz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python X = np.array([[0.0, 1.0], [2.0, 3.0]]) K = np.array([[0.0, 1.0], [2.0, 3.0]]) trans_conv(X, K) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output array([[ 0., 0., 1.], [ 0., 4., 6.], [ 4., 12., 9.]]) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python X = torch.tensor([[0.0, 1.0], [2.0, 3.0]]) K = torch.tensor([[0.0, 1.0], [2.0, 3.0]]) trans_conv(X, K) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output tensor([[ 0., 0., 1.], [ 0., 4., 6.], [ 4., 12., 9.]]) .. raw:: html
.. raw:: html
Alternatif olarak, girdi ``X`` ve çekirdek ``K`` dört boyutlu tensörler olduğunda, aynı sonuçları elde etmek için üst seviye API'leri kullanabiliriz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python X, K = X.reshape(1, 1, 2, 2), K.reshape(1, 1, 2, 2) tconv = nn.Conv2DTranspose(1, kernel_size=2) tconv.initialize(init.Constant(K)) tconv(X) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output array([[[[ 0., 0., 1.], [ 0., 4., 6.], [ 4., 12., 9.]]]]) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python X, K = X.reshape(1, 1, 2, 2), K.reshape(1, 1, 2, 2) tconv = nn.ConvTranspose2d(1, 1, kernel_size=2, bias=False) tconv.weight.data = K tconv(X) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output tensor([[[[ 0., 0., 1.], [ 0., 4., 6.], [ 4., 12., 9.]]]], grad_fn=) .. raw:: html
.. raw:: html
Dolgu, Adım ve Çoklu Kanallar ----------------------------- Dolgunun girdiye uygulandığı düzenli evrişimden farklı olarak, devrik evrişim içindeki çıktıya uygulanır. Örneğin, yükseklik ve genişliğin her iki tarafındaki dolgu sayısı 1 olarak belirtilirken, ilk ve son satırlar ve sütunlar devrik evrişim çıktısından kaldırılır. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python tconv = nn.Conv2DTranspose(1, kernel_size=2, padding=1) tconv.initialize(init.Constant(K)) tconv(X) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output array([[[[4.]]]]) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python tconv = nn.ConvTranspose2d(1, 1, kernel_size=2, padding=1, bias=False) tconv.weight.data = K tconv(X) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output tensor([[[[4.]]]], grad_fn=) .. raw:: html
.. raw:: html
Devrik evrişimde, girdi için değil, ara sonuçlar (böylece çıktı) için adımlar belirtilir. :numref:`fig_trans_conv` içinde bahsedilen aynı girdi ve çekirdek tensörleri kullanılırken, adımın 1'den 2'ye değiştirilmesi, ara tensörlerin, dolayısıyla :numref:`fig_trans_conv_stride2` şeklinde gösterilen çıktı tensörünün, hem yüksekliğini hem de ağırlığını artırır. .. _fig_trans_conv_stride2: .. figure:: ../img/trans_conv_stride2.svg 2 uzun adımlı :math:`2\times 2` çekirdekli devrik evrişim. Gölgeli kısımlar, hesaplama için kullanılan girdi ve çekirdek tensör elemanlarının yanı sıra bir ara tensörün bir kısmıdır. Aşağıdaki kod parçacığı, :numref:`fig_trans_conv_stride2`'te 2 uzun adım için devrik evrişim çıktısını doğrulayabilir. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python tconv = nn.Conv2DTranspose(1, kernel_size=2, strides=2) tconv.initialize(init.Constant(K)) tconv(X) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output array([[[[0., 0., 0., 1.], [0., 0., 2., 3.], [0., 2., 0., 3.], [4., 6., 6., 9.]]]]) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python tconv = nn.ConvTranspose2d(1, 1, kernel_size=2, stride=2, bias=False) tconv.weight.data = K tconv(X) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output tensor([[[[0., 0., 0., 1.], [0., 0., 2., 3.], [0., 2., 0., 3.], [4., 6., 6., 9.]]]], grad_fn=) .. raw:: html
.. raw:: html
Çoklu girdi ve çıktı kanalı için, devrik evrişim normal evrişim ile aynı şekilde çalışır. Girdinin :math:`c_i` kanallara sahip olduğunu ve devrik evrişimin her girdi kanalına bir :math:`k_h\times k_w` çekirdek tensörü atadığını varsayalım. Birden fazla çıktı kanalı belirtildiğinde, her çıktı kanalı için bir :math:`c_i\times k_h\times k_w` çekirdeğine sahip olacağız. Her durumda, :math:`\mathsf{X}`'i :math:`\mathsf{Y}=f(\mathsf{X})` çıktısı almak için bir :math:`f` evrişim katmanına beslersek ve :math:`\mathsf{X}` içindeki kanalların sayısı olan çıktı kanallarının sayısı dışında :math:`f` ile aynı hiper parametrelere sahip bir :math:`g` devrik evrimsel katman oluşturursak, o zaman :math:`g(Y)` :math:`\mathsf{X}` ile aynı şekle sahip olacaktır. Bu, aşağıdaki örnekte gösterilebilir. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python X = np.random.uniform(size=(1, 10, 16, 16)) conv = nn.Conv2D(20, kernel_size=5, padding=2, strides=3) tconv = nn.Conv2DTranspose(10, kernel_size=5, padding=2, strides=3) conv.initialize() tconv.initialize() tconv(conv(X)).shape == X.shape .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output True .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python X = torch.rand(size=(1, 10, 16, 16)) conv = nn.Conv2d(10, 20, kernel_size=5, padding=2, stride=3) tconv = nn.ConvTranspose2d(20, 10, kernel_size=5, padding=2, stride=3) tconv(conv(X)).shape == X.shape .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output True .. raw:: html
.. raw:: html
.. _subsec-connection-to-mat-transposition: Matris Devirme ile Bağlantı --------------------------- Devrik evrişim, matris devirme sonrasından adlandırılmıştır. Açıklamak için, önce matris çarpımlarını kullanarak evrişimlerin nasıl uygulanacağını görelim. Aşağıdaki örnekte, :math:`3\times 3`'lük girdi ``X`` ve :math:`2\times 2`'lik evrişim çekirdeği ``K`` tanımlıyoruz ve ``Y`` evrişim çıktısını hesaplamak için ``corr2d`` işlevini kullanıyoruz. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python X = np.arange(9.0).reshape(3, 3) K = np.array([[1.0, 2.0], [3.0, 4.0]]) Y = d2l.corr2d(X, K) Y .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output array([[27., 37.], [57., 67.]]) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python X = torch.arange(9.0).reshape(3, 3) K = torch.tensor([[1.0, 2.0], [3.0, 4.0]]) Y = d2l.corr2d(X, K) Y .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output tensor([[27., 37.], [57., 67.]]) .. raw:: html
.. raw:: html
Daha sonra, çok sayıda sıfır içeren seyrek ağırlık matrisi ``W`` olarak evrişim çekirdeği ``K``'yi yeniden yazıyoruz. Ağırlık matrisinin şekli (:math:`4`, :math:`9`) olup sıfır olmayan elemanlar evrişim çekirdeği ``K``'den gelmektedir. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def kernel2matrix(K): k, W = np.zeros(5), np.zeros((4, 9)) k[:2], k[3:5] = K[0, :], K[1, :] W[0, :5], W[1, 1:6], W[2, 3:8], W[3, 4:] = k, k, k, k return W W = kernel2matrix(K) W .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output array([[1., 2., 0., 3., 4., 0., 0., 0., 0.], [0., 1., 2., 0., 3., 4., 0., 0., 0.], [0., 0., 0., 1., 2., 0., 3., 4., 0.], [0., 0., 0., 0., 1., 2., 0., 3., 4.]]) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python def kernel2matrix(K): k, W = torch.zeros(5), torch.zeros((4, 9)) k[:2], k[3:5] = K[0, :], K[1, :] W[0, :5], W[1, 1:6], W[2, 3:8], W[3, 4:] = k, k, k, k return W W = kernel2matrix(K) W .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output tensor([[1., 2., 0., 3., 4., 0., 0., 0., 0.], [0., 1., 2., 0., 3., 4., 0., 0., 0.], [0., 0., 0., 1., 2., 0., 3., 4., 0.], [0., 0., 0., 0., 1., 2., 0., 3., 4.]]) .. raw:: html
.. raw:: html
9 uzunluğunda bir vektör elde etmek için girdi ``X``'i satır satır bitiştirin. Daha sonra ``W`` ve vektörleştirilmiş ``X``'in matris çarpımı, 4 uzunluğunda bir vektör verir. Yeniden şekillendirdikten sonra, yukarıdaki orijinal evrişim işlemindeki aynı ``Y`` sonucunu elde edebiliriz: Matris çarpımlarını kullanarak evrişimleri uyguladık. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python Y == np.dot(W, X.reshape(-1)).reshape(2, 2) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output array([[ True, True], [ True, True]]) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python Y == torch.matmul(W, X.reshape(-1)).reshape(2, 2) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output tensor([[True, True], [True, True]]) .. raw:: html
.. raw:: html
Aynı şekilde, matris çarpımlarını kullanarak devrik evrişimleri de uygulayabiliriz. Aşağıdaki örnekte, yukarıdaki olağan evrişimin :math:`2 \times 2` çıktısı ``Y``'yi devrik evrişime girdi olarak alıyoruz. Bu işlemi matrisleri çarparak uygulamak için, ``W`` ağırlık matrisini yeni şekli :math:`(9, 4)` ile devirmemiz yeterlidir. .. raw:: html
mxnetpytorch
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python Z = trans_conv(Y, K) Z == np.dot(W.T, Y.reshape(-1)).reshape(3, 3) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output array([[ True, True, True], [ True, True, True], [ True, True, True]]) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python Z = trans_conv(Y, K) Z == torch.matmul(W.T, Y.reshape(-1)).reshape(3, 3) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output tensor([[True, True, True], [True, True, True], [True, True, True]]) .. raw:: html
.. raw:: html
Matrisleri çarparak evrişimi uygulamayı düşünün. Bir girdi vektörü :math:`\mathbf{x}` ve bir ağırlık matrisi :math:`\mathbf{W}` göz önüne alındığında, evrişimin ileri yayma fonksiyonu, girdi ağırlık matrisi ile çarpımından bir :math:`\mathbf{y}=\mathbf{W}\mathbf{x}` vektör çıktısı verilerek uygulanabilir. Geriye yayma zincir kuralını ve :math:`\nabla_{\mathbf{x}}\mathbf{y}=\mathbf{W}^\top`'i izlediğinden, evrişimin geriye yayma fonksiyonu, girdisini devrik ağırlık matrisi :math:`\mathbf{W}^\top` ile çarparak uygulanabilir. Bu nedenle, devrik evrişim katmanı sadece ileri yayma fonksiyonunu ve evrişim tabakasının geri yayma fonksiyonunu değiştirebilir: İleri yayma ve geri yayma fonksiyonları sırasıyla :math:`\mathbf{W}^\top` ve :math:`\mathbf{W}` ile girdi vektörünü çarpar. Özet ---- - Çekirdek üzerinden girdi elemanlarını azaltan olağan evrişimin aksine, devrik evrişim çekirdek üzerinden girdi öğelerini yayınlar ve böylece girdiden daha büyük bir çıktı üretir. - :math:`\mathsf{X}`'i :math:`\mathsf{Y}=f(\mathsf{X})` çıktısı almak için bir :math:`f` evrişim katmanına beslersek ve çıktı kanallarının sayısının :math:`\mathsf{X}` içindeki kanalların sayısı olması dışında :math:`f` ile aynı hiper parametrelere sahip bir devrik evrişim katmanı :math:`g` oluşturursak, :math:`g(Y)` :math:`\mathsf{X}` ile aynı şekle sahip olacaktır. - Matris çarpımlarını kullanarak evrişimleri gerçekleştirebiliriz. Devrik evrişimli katman sadece ileri yayma fonksiyonunu ve evrişimli katmanın geri yayma işlevini değiştirebilir. Alıştırmalar ------------ 1. :numref:`subsec-connection-to-mat-transposition` içinde, evrişim girdisi ``X`` ve devrik evrişim çıktısı ``Z`` aynı şekle sahiptir. Aynı değere sahipler mi? Neden? 2. Evrişimleri uygulamak için matris çarpımlarını kullanmak verimli midir? Neden? .. raw:: html
mxnetpytorch
.. raw:: html
`Tartışmalar `__ .. raw:: html
.. raw:: html
`Tartışmalar `__ .. raw:: html
.. raw:: html