5.4. Özelleştirilmiş Katmanlar¶ 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¶
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.
Verilerin Fourier katsayılarının ilk baştaki yarısını döndüren bir katman tasarlayınız.