5.4. Özelleştirilmiş Katmanlar
Open the notebook in Colab
Open the notebook in Colab
Open the notebook in Colab
Open the notebook in SageMaker Studio Lab

Derin öğrenmenin başarısının ardındaki faktörlerden biri, çok çeşitli görevlere uygun mimariler tasarlamak için yaratıcı yollarla oluşturulabilen çok çeşitli katmanların mevcut olmasıdır. Örneğin, araştırmacılar özellikle imgelerle baş etmek, metin, dizili veriler üzerinde döngü yapmak ve dinamik programlama yapmak için katmanlar icat ettiler. Er ya da geç, derin öğrenme çerçevesinde henüz var olmayan bir katmanla karşılaşacaksınız (veya onu icat edeceksiniz). Özel bir katman oluşturmanız gerekebilir. Bu bölümde size bunu nasıl yapacağınızı gösteriyoruz.

5.4.1. Paramatresiz Katmanlar

Başlangıç olarak, kendi parametresi olmayan özelleştirilmiş bir katman oluşturalım. Bloğu tanıttığımızı bölümü hatırlarsanız, Section 5.1 içindeki, burası size tanıdık gelecektir. Aşağıdaki CenteredLayer sınıfı, girdiden ortalamayı çıkarır. Bunu inşa etmek için, temel katman sınıfından kalıtımla üretmemiz ve ileri yayma işlevini uygulamamız gerekir.

from mxnet import np, npx
from mxnet.gluon import nn

npx.set_np()

class CenteredLayer(nn.Block):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def forward(self, X):
        return X - X.mean()
import torch
from torch import nn
from torch.nn import functional as F


class CenteredLayer(nn.Module):
    def __init__(self):
        super().__init__()

    def forward(self, X):
        return X - X.mean()
import tensorflow as tf


class CenteredLayer(tf.keras.Model):
    def __init__(self):
        super().__init__()

    def call(self, inputs):
        return inputs - tf.reduce_mean(inputs)

Katmanımızın amaçlandığı gibi çalıştığını, ona biraz veri besleyerek doğrulayalım.

layer = CenteredLayer()
layer(np.array([1, 2, 3, 4, 5]))
array([-2., -1.,  0.,  1.,  2.])
layer = CenteredLayer()
layer(torch.FloatTensor([1, 2, 3, 4, 5]))
tensor([-2., -1.,  0.,  1.,  2.])
layer = CenteredLayer()
layer(tf.constant([1, 2, 3, 4, 5]))
<tf.Tensor: shape=(5,), dtype=int32, numpy=array([-2, -1,  0,  1,  2], dtype=int32)>

Artık katmanımızı daha karmaşık modeller oluşturmada bir bileşen olarak kullanabiliriz.

net = nn.Sequential()
net.add(nn.Dense(128), CenteredLayer())
net.initialize()
net = nn.Sequential(nn.Linear(8, 128), CenteredLayer())
net = tf.keras.Sequential([tf.keras.layers.Dense(128), CenteredLayer()])

Ekstra bir makulluk kontrolü olarak, ağa rastgele veri gönderebilir ve ortalamanın gerçekte 0 olup olmadığına bakabiliriz. Kayan virgüllü sayılarla uğraştığımız için, nicemlemeden dolayı çok küçük sıfır olmayan bir sayı görebiliriz.

Y = net(np.random.uniform(size=(4, 8)))
Y.mean()
array(3.783498e-10)
Y = net(torch.rand(4, 8))
Y.mean()
tensor(-7.4506e-09, grad_fn=<MeanBackward0>)
Y = net(tf.random.uniform((4, 8)))
tf.reduce_mean(Y)
<tf.Tensor: shape=(), dtype=float32, numpy=1.1641532e-09>

5.4.2. Parametreli Katmanlar

Artık basit katmanları nasıl tanımlayacağımızı bildiğimize göre, eğitim yoluyla ayarlanabilen parametrelerle katmanları tanımlamaya geçelim. Bazı temel idari işlevleri sağlayan parametreler oluşturmak için yerleşik işlevleri kullanabiliriz. Özellikle erişim, ilkleme, paylaşma, modeli kaydetme ve yükleme parametrelerini yönetirler. Bu şekilde, diğer faydaların yanı sıra, her özel katman için özel serileştirme (serialization) rutinleri yazmamız gerekmeyecek.

Şimdi tam bağlı katman sürümümüzü uygulayalım. Bu katmanın iki parametreye ihtiyaç duyduğunu hatırlayınız, biri ağırlığı ve diğeri ek girdiyi temsil etmek için. Bu uygulamada, varsayılan olarak ReLU etkinleştirmesini kullanıyoruz. Bu katman, sırasıyla girdilerin ve çıktıların sayısını gösteren in_units ve units girdi argümanlarının girilmesini gerektirir.

class MyDense(nn.Block):
    def __init__(self, units, in_units, **kwargs):
        super().__init__(**kwargs)
        self.weight = self.params.get('weight', shape=(in_units, units))
        self.bias = self.params.get('bias', shape=(units,))

    def forward(self, x):
        linear = np.dot(x, self.weight.data(ctx=x.ctx)) + self.bias.data(
            ctx=x.ctx)
        return npx.relu(linear)

Daha sonra, MyDense sınıfını ilkliyoruz ve model parametrelerine erişiyoruz.

dense = MyDense(units=3, in_units=5)
dense.params
mydense0_ (
  Parameter mydense0_weight (shape=(5, 3), dtype=<class 'numpy.float32'>)
  Parameter mydense0_bias (shape=(3,), dtype=<class 'numpy.float32'>)
)
class MyLinear(nn.Module):
    def __init__(self, in_units, units):
        super().__init__()
        self.weight = nn.Parameter(torch.randn(in_units, units))
        self.bias = nn.Parameter(torch.randn(units,))
    def forward(self, X):
        linear = torch.matmul(X, self.weight.data) + self.bias.data
        return F.relu(linear)

Daha sonra, MyLinear sınıfını ilkliyoruz ve model parametrelerine erişiyoruz.

linear = MyLinear(5, 3)
linear.weight
Parameter containing:
tensor([[-0.4603,  0.5533,  0.0135],
        [-1.0431, -1.7814,  0.5278],
        [-0.9134,  0.1051, -0.5784],
        [ 1.8552,  0.6963,  0.8525],
        [-0.5034,  1.6521, -1.1436]], requires_grad=True)
class MyDense(tf.keras.Model):
    def __init__(self, units):
        super().__init__()
        self.units = units

    def build(self, X_shape):
        self.weight = self.add_weight(name='weight',
            shape=[X_shape[-1], self.units],
            initializer=tf.random_normal_initializer())
        self.bias = self.add_weight(
            name='bias', shape=[self.units],
            initializer=tf.zeros_initializer())

    def call(self, X):
        linear = tf.matmul(X, self.weight) + self.bias
        return tf.nn.relu(linear)

Daha sonra, MyDense sınıfını ilkliyoruz ve model parametrelerine erişiyoruz.

dense = MyDense(3)
dense(tf.random.uniform((2, 5)))
dense.get_weights()
[array([[ 0.03330242,  0.0457952 , -0.01465737],
        [ 0.05158572, -0.02191112,  0.03810618],
        [-0.0173367 , -0.06167042, -0.02427012],
        [ 0.04562289,  0.01620819,  0.04083775],
        [ 0.10629302,  0.045753  , -0.07166722]], dtype=float32),
 array([0., 0., 0.], dtype=float32)]

Özel kesim katmanları kullanarak doğrudan ileri yayma hesaplamaları yapabiliriz.

dense.initialize()
dense(np.random.uniform(size=(2, 5)))
array([[0.        , 0.01633355, 0.        ],
       [0.        , 0.01581812, 0.        ]])
linear(torch.rand(2, 5))
tensor([[0.6806, 1.0990, 1.5497],
        [0.0000, 0.0619, 0.8226]])
dense(tf.random.uniform((2, 5)))
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[0.111458  , 0.04671677, 0.        ],
       [0.03075667, 0.        , 0.        ]], dtype=float32)>

Özelleştirilmiş kesim katmanlar kullanarak da modeller oluşturabiliriz. Bir kere ona sahip olduğumuzda, onu tıpkı yerleşik tam bağlı katman gibi kullanabiliriz.

net = nn.Sequential()
net.add(MyDense(8, in_units=64),
        MyDense(1, in_units=8))
net.initialize()
net(np.random.uniform(size=(2, 64)))
array([[0.06508517],
       [0.0615553 ]])
net = nn.Sequential(MyLinear(64, 8), MyLinear(8, 1))
net(torch.rand(2, 64))
tensor([[9.2351],
        [7.0112]])
net = tf.keras.models.Sequential([MyDense(8), MyDense(1)])
net(tf.random.uniform((2, 64)))
<tf.Tensor: shape=(2, 1), dtype=float32, numpy=
array([[0.03537374],
       [0.02933387]], dtype=float32)>

5.4.3. Özet

  • Temel katman sınıfı üzerinden özel kesim katmanlar tasarlayabiliriz. Bu, kütüphanedeki mevcut katmanlardan farklı davranan yeni esnek katmanlar tanımlamamıza olanak tanır.

  • Tanımlandıktan sonra, özel kesim katmanlar keyfi bağlamlarda ve mimarilerde çağrılabilir.

  • Katmanlar, yerleşik işlevler aracılığıyla yaratılabilen yerel parametrelere sahip olabilirler.

5.4.4. Alıştırmalar

  1. Bir girdi alan ve bir tensör indirgemesi hesaplayan bir katman tasarlayınız, yani \(y_k = \sum_{i, j} W_{ijk} x_i x_j\) döndürsün.

  2. Verilerin Fourier katsayılarının ilk baştaki yarısını döndüren bir katman tasarlayınız.