5.2. Parametre Yönetimi¶ Open the notebook in SageMaker Studio Lab
Bir mimari seçip hiper parametrelerimizi belirledikten sonra, hedefimiz yitim işlevimizi en aza indiren parametre değerlerini bulmak olduğu eğitim döngüsüne geçiyoruz. Eğitimden sonra, gelecekteki tahminlerde bulunmak için bu parametrelere ihtiyacımız olacak. Ek olarak, bazen parametreleri başka bir bağlamda yeniden kullanmak, modelimizi başka bir yazılımda yürütülebilmesi için diske kaydetmek veya bilimsel anlayış kazanma umuduyla incelemek için ayıklamak isteyeceğiz.
Çoğu zaman, ağır işlerin üstesinden gelmek için derin öğrenme çerçevelerine dayanarak, parametrelerin nasıl beyan edildiğine ve değiştirildiğine dair işin esas ayrıntıları görmezden gelebileceğiz. Bununla birlikte, standart katmanlara sahip yığılmış mimarilerden uzaklaştığımızda, bazen parametreleri bildirme ve onların üstünde oynama yabani otlarına girmemizi gerekecektir. Bu bölümde aşağıdakileri ele alıyoruz:
Hata ayıklama, teşhis ve görselleştirmeler için parametrelere erişim.
Parametre ilkletme.
Parametreleri farklı model bileşenleri arasında paylaşma.
Tek bir gizli katmana sahip bir MLP’ye odaklanarak başlıyoruz.
from mxnet import init, np, npx
from mxnet.gluon import nn
npx.set_np()
net = nn.Sequential()
net.add(nn.Dense(8, activation='relu'))
net.add(nn.Dense(1))
net.initialize() # Varsayılan ilkleme yöntemini kullan
X = np.random.uniform(size=(2, 4))
net(X) # İleri hesaplama
array([[0.0054572 ],
[0.00488594]])
import torch
from torch import nn
net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 1))
X = torch.rand(size=(2, 4))
net(X)
tensor([[-0.4043],
[-0.3974]], grad_fn=<AddmmBackward0>)
import tensorflow as tf
net = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(4, activation=tf.nn.relu),
tf.keras.layers.Dense(1),
])
X = tf.random.uniform((2, 4))
net(X)
<tf.Tensor: shape=(2, 1), dtype=float32, numpy=
array([[-0.41335705],
[-0.75329673]], dtype=float32)>
5.2.1. Parametre Erişimi¶
Zaten bildiğiniz modellerden parametrelere nasıl erişileceğiyle
başlayalım. Bir model Sequential
sınıfı aracılığıyla
tanımlandığında, ilk olarak herhangi bir katmana, bir listeymiş gibi
modelde üzerinden indeksleme ile erişebiliriz. Her katmanın
parametreleri, özelliklerinde uygun bir şekilde bulunur. Tam bağlı
ikinci katmanın parametrelerini aşağıdaki gibi irdeleyebiliriz.
print(net[1].params)
dense1_ (
Parameter dense1_weight (shape=(1, 8), dtype=float32)
Parameter dense1_bias (shape=(1,), dtype=float32)
)
print(net[2].state_dict())
OrderedDict([('weight', tensor([[ 0.0882, -0.0851, -0.0998, -0.0520, 0.1348, 0.3360, -0.1585, -0.1034]])), ('bias', tensor([-0.3121]))])
print(net.layers[2].weights)
[<tf.Variable 'dense_1/kernel:0' shape=(4, 1) dtype=float32, numpy=
array([[ 0.34046865],
[-0.7770064 ],
[-0.46259564],
[-0.86495763]], dtype=float32)>, <tf.Variable 'dense_1/bias:0' shape=(1,) dtype=float32, numpy=array([0.], dtype=float32)>]
Çıktı bize birkaç önemli şey gösteriyor. İlk olarak, bu tam bağlı katman, sırasıyla o katmanın ağırlıklarına ve ek girdilerine karşılık gelen iki parametre içerir. Her ikisi de tek hassas basamaklı kayan virgüllü sayı (float32) olarak saklanır. Parametrelerin adlarının, yüzlerce katman içeren bir ağda bile, her katmanın parametrelerini benzersiz şekilde tanımlamamıza izin verdiğini unutmayın.
5.2.1.1. Hedeflenen Parametreler¶
Her parametrenin, parametre sınıfının bir örneği olarak temsil edildiğine dikkat edin. Parametrelerle yararlı herhangi bir şey yapmak için önce altta yatan sayısal değerlere erişmemiz gerekir. Bunu yapmanın birkaç yolu var. Bazıları daha basitken diğerleri daha geneldir. Aşağıdaki kod, bir parametre sınıfı örneği döndüren ikinci sinir ağı katmanından ek girdiyi dışarı çıkarır ve dahası bu parametrenin değerine erişim sağlar.
print(type(net[1].bias))
print(net[1].bias)
print(net[1].bias.data())
<class 'mxnet.gluon.parameter.Parameter'>
Parameter dense1_bias (shape=(1,), dtype=float32)
[0.]
Parametreler; değer, gradyanlar ve ek bilgiler içeren karmaşık nesnelerdir. Bu nedenle değerleri açıkça talep etmemiz gerekiyor.
Değere ek olarak, her parametre gradyana erişmemize de izin verir. Henüz bu ağ için geri yaymayı çağırmadığımız için, ağ ilk durumundadır.
net[1].weight.grad()
array([[0., 0., 0., 0., 0., 0., 0., 0.]])
print(type(net[2].bias))
print(net[2].bias)
print(net[2].bias.data)
<class 'torch.nn.parameter.Parameter'>
Parameter containing:
tensor([-0.3121], requires_grad=True)
tensor([-0.3121])
Parametreler; değer, gradyanlar ve ek bilgiler içeren karmaşık nesnelerdir. Bu nedenle değerleri açıkça talep etmemiz gerekiyor.
Değere ek olarak, her parametre gradyana erişmemize de izin verir. Henüz bu ağ için geri yaymayı çağırmadığımız için, ağ ilk durumundadır.
net[2].weight.grad == None
True
print(type(net.layers[2].weights[1]))
print(net.layers[2].weights[1])
print(tf.convert_to_tensor(net.layers[2].weights[1]))
<class 'tensorflow.python.ops.resource_variable_ops.ResourceVariable'>
<tf.Variable 'dense_1/bias:0' shape=(1,) dtype=float32, numpy=array([0.], dtype=float32)>
tf.Tensor([0.], shape=(1,), dtype=float32)
5.2.1.2. Bütün Parametrelere Bir Kerede Erişim¶
Tüm parametrelerde işlem yapmamız gerektiğinde, bunlara tek tek erişmek bıktırıcı olabilir. Her bir alt bloğun parametrelerini dışarı çıkarmayı tüm ağaç boyunca tekrarlamamız gerekeceğinden, daha karmaşık bloklarla (örneğin, iç içe geçmiş bloklarla) çalıştığımızda durum özellikle kullanışsızca büyüyebilir. Aşağıda, ilk tam bağlı katmanın parametrelerine erişmeye karşılık tüm katmanlara erişmeyi gösteriyoruz.
print(net[0].collect_params())
print(net.collect_params())
dense0_ (
Parameter dense0_weight (shape=(8, 4), dtype=float32)
Parameter dense0_bias (shape=(8,), dtype=float32)
)
sequential0_ (
Parameter dense0_weight (shape=(8, 4), dtype=float32)
Parameter dense0_bias (shape=(8,), dtype=float32)
Parameter dense1_weight (shape=(1, 8), dtype=float32)
Parameter dense1_bias (shape=(1,), dtype=float32)
)
print(*[(name, param.shape) for name, param in net[0].named_parameters()])
print(*[(name, param.shape) for name, param in net.named_parameters()])
('weight', torch.Size([8, 4])) ('bias', torch.Size([8]))
('0.weight', torch.Size([8, 4])) ('0.bias', torch.Size([8])) ('2.weight', torch.Size([1, 8])) ('2.bias', torch.Size([1]))
print(net.layers[1].weights)
print(net.get_weights())
[<tf.Variable 'dense/kernel:0' shape=(4, 4) dtype=float32, numpy=
array([[ 0.31354207, -0.2002306 , 0.04489189, 0.65902776],
[-0.3884784 , -0.41679087, -0.8219113 , -0.44252217],
[-0.6896628 , -0.19932956, 0.6800088 , 0.0731557 ],
[-0.27203923, -0.7259777 , -0.19643682, 0.3815747 ]],
dtype=float32)>, <tf.Variable 'dense/bias:0' shape=(4,) dtype=float32, numpy=array([0., 0., 0., 0.], dtype=float32)>]
[array([[ 0.31354207, -0.2002306 , 0.04489189, 0.65902776],
[-0.3884784 , -0.41679087, -0.8219113 , -0.44252217],
[-0.6896628 , -0.19932956, 0.6800088 , 0.0731557 ],
[-0.27203923, -0.7259777 , -0.19643682, 0.3815747 ]],
dtype=float32), array([0., 0., 0., 0.], dtype=float32), array([[ 0.34046865],
[-0.7770064 ],
[-0.46259564],
[-0.86495763]], dtype=float32), array([0.], dtype=float32)]
Bu bize ağın parametrelerine erişmenin aşağıdaki gibi başka bir yolunu sağlar:
net.collect_params()['dense1_bias'].data()
array([0.])
net.state_dict()['2.bias'].data
tensor([-0.3121])
net.get_weights()[1]
array([0., 0., 0., 0.], dtype=float32)
5.2.1.3. İçiçe Bloklardan Parametreleri Toplama¶
Birden çok bloğu iç içe yerleştirirsek, parametre adlandırma kurallarının nasıl çalıştığını görelim. Bunun için önce blok üreten bir fonksiyon tanımlıyoruz (tabiri caizse bir blok fabrikası) ve sonra bunları daha büyük bloklar içinde birleştiriyoruz.
def block1():
net = nn.Sequential()
net.add(nn.Dense(32, activation='relu'))
net.add(nn.Dense(16, activation='relu'))
return net
def block2():
net = nn.Sequential()
for _ in range(4):
# Burada iç içe konuyor
net.add(block1())
return net
rgnet = nn.Sequential()
rgnet.add(block2())
rgnet.add(nn.Dense(10))
rgnet.initialize()
rgnet(X)
array([[-6.3465846e-09, -1.1096752e-09, 6.4161787e-09, 6.6354140e-09,
-1.1265507e-09, 1.3284951e-10, 9.3619388e-09, 3.2229084e-09,
5.9429879e-09, 8.8181435e-09],
[-8.6219423e-09, -7.5150686e-10, 8.3133251e-09, 8.9321128e-09,
-1.6740003e-09, 3.2405989e-10, 1.2115976e-08, 4.4926449e-09,
8.0741742e-09, 1.2075874e-08]])
def block1():
return nn.Sequential(nn.Linear(4, 8), nn.ReLU(),
nn.Linear(8, 4), nn.ReLU())
def block2():
net = nn.Sequential()
for i in range(4):
# Burada iç içe konuyor
net.add_module(f'block {i}', block1())
return net
rgnet = nn.Sequential(block2(), nn.Linear(4, 1))
rgnet(X)
tensor([[0.3051],
[0.3051]], grad_fn=<AddmmBackward0>)
def block1(name):
return tf.keras.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(4, activation=tf.nn.relu)],
name=name)
def block2():
net = tf.keras.Sequential()
for i in range(4):
# Burada iç içe konuyor
net.add(block1(name=f'block-{i}'))
return net
rgnet = tf.keras.Sequential()
rgnet.add(block2())
rgnet.add(tf.keras.layers.Dense(1))
rgnet(X)
<tf.Tensor: shape=(2, 1), dtype=float32, numpy=
array([[0. ],
[0.01293496]], dtype=float32)>
Ağı tasarladığımıza göre, şimdi nasıl düzenlendiğini görelim.
print(rgnet.collect_params)
print(rgnet.collect_params())
<bound method Block.collect_params of Sequential(
(0): Sequential(
(0): Sequential(
(0): Dense(4 -> 32, Activation(relu))
(1): Dense(32 -> 16, Activation(relu))
)
(1): Sequential(
(0): Dense(16 -> 32, Activation(relu))
(1): Dense(32 -> 16, Activation(relu))
)
(2): Sequential(
(0): Dense(16 -> 32, Activation(relu))
(1): Dense(32 -> 16, Activation(relu))
)
(3): Sequential(
(0): Dense(16 -> 32, Activation(relu))
(1): Dense(32 -> 16, Activation(relu))
)
)
(1): Dense(16 -> 10, linear)
)>
sequential1_ (
Parameter dense2_weight (shape=(32, 4), dtype=float32)
Parameter dense2_bias (shape=(32,), dtype=float32)
Parameter dense3_weight (shape=(16, 32), dtype=float32)
Parameter dense3_bias (shape=(16,), dtype=float32)
Parameter dense4_weight (shape=(32, 16), dtype=float32)
Parameter dense4_bias (shape=(32,), dtype=float32)
Parameter dense5_weight (shape=(16, 32), dtype=float32)
Parameter dense5_bias (shape=(16,), dtype=float32)
Parameter dense6_weight (shape=(32, 16), dtype=float32)
Parameter dense6_bias (shape=(32,), dtype=float32)
Parameter dense7_weight (shape=(16, 32), dtype=float32)
Parameter dense7_bias (shape=(16,), dtype=float32)
Parameter dense8_weight (shape=(32, 16), dtype=float32)
Parameter dense8_bias (shape=(32,), dtype=float32)
Parameter dense9_weight (shape=(16, 32), dtype=float32)
Parameter dense9_bias (shape=(16,), dtype=float32)
Parameter dense10_weight (shape=(10, 16), dtype=float32)
Parameter dense10_bias (shape=(10,), dtype=float32)
)
print(rgnet)
Sequential(
(0): Sequential(
(block 0): Sequential(
(0): Linear(in_features=4, out_features=8, bias=True)
(1): ReLU()
(2): Linear(in_features=8, out_features=4, bias=True)
(3): ReLU()
)
(block 1): Sequential(
(0): Linear(in_features=4, out_features=8, bias=True)
(1): ReLU()
(2): Linear(in_features=8, out_features=4, bias=True)
(3): ReLU()
)
(block 2): Sequential(
(0): Linear(in_features=4, out_features=8, bias=True)
(1): ReLU()
(2): Linear(in_features=8, out_features=4, bias=True)
(3): ReLU()
)
(block 3): Sequential(
(0): Linear(in_features=4, out_features=8, bias=True)
(1): ReLU()
(2): Linear(in_features=8, out_features=4, bias=True)
(3): ReLU()
)
)
(1): Linear(in_features=4, out_features=1, bias=True)
)
print(rgnet.summary())
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
sequential_2 (Sequential) (2, 4) 80
dense_6 (Dense) (2, 1) 5
=================================================================
Total params: 85
Trainable params: 85
Non-trainable params: 0
_________________________________________________________________
None
Katmanlar hiyerarşik olarak iç içe olduğundan, iç içe geçmiş listeler aracılığıyla dizinlenmiş gibi bunlara da erişebiliriz. Örneğin, birinci ana bloğa, içindeki ikinci alt bloğa ve bunun içindeki ilk katmanın ek girdisine aşağıdaki şekilde erişebiliriz.
rgnet[0][1][0].bias.data()
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
rgnet[0][1][0].bias.data
tensor([-0.2715, 0.3849, 0.4326, 0.4178, 0.4517, -0.2524, 0.2472, -0.1414])
rgnet.layers[0].layers[1].layers[1].weights[1]
<tf.Variable 'dense_3/bias:0' shape=(4,) dtype=float32, numpy=array([0., 0., 0., 0.], dtype=float32)>
5.2.2. Parametre İlkleme¶
Artık parametrelere nasıl erişeceğimizi bildiğimize göre, onları nasıl doğru şekilde ilkleteceğimize bakalım. Uygun ilkleme ihtiyacını Section 4.8 içinde tartıştık. Derin öğrenme çerçevesi katmanlarına varsayılan rastgele ilklemeler sağlar. Bununla birlikte, ağırlıklarımızı öteki farklı protokollere göre ilkletmek istiyoruz. Çerçeve, en sık kullanılan protokolleri sağlar ve ayrıca özelleştirilmiş bir ilkletici oluşturmaya izin verir.
Varsayılan olarak, MXNet ağırlık parametrelerini \(U[-0.07, 0.07]\)
tekdüze dağılımdan rasgele çekerek ilkler ve ek girdileri sıfırlar.
MXNet’in init
modülü önceden ayarlı çeşitli ilkleme yöntemleri
sağlar.
Varsayılan olarak PyTorch, girdi ve çıktı boyutuna göre hesaplanan bir
aralıktan çekerek ağırlık ve ek girdi matrislerini tekdüze olarak
başlatır. PyTorch’un nn.init
modülü önceden ayarlı çeşitli ilkleme
yöntemleri sağlar.
Varsayılan olarak Keras, girdi ve çıktı boyutuna göre hesaplanan bir
aralıktan çekerek ağırlık matrislerini tekdüze olarak ilkletir ve ek
girdi parametrelerinin tümü sıfır olarak atanır. TensorFlow, hem kök
modülde hem de keras.initializers
modülünde çeşitli ilkleme
yöntemleri sağlar.
5.2.2.1. Yerleşik İlkletme¶
Yerleşik ilkleticileri çağırarak ilkleyelim. Aşağıdaki kod, tüm ağırlık parametrelerini standart sapması 0.01 olan Gauss rastgele değişkenler olarak ilkletirken ek girdi parametreleri de 0 olarak atanır.
# Burada 'force_reinit', daha önce ilklenmiş olsalar bile parametrelerin
# yeniden ilklenmesini sağlar
net.initialize(init=init.Normal(sigma=0.01), force_reinit=True)
net[0].weight.data()[0]
array([-0.00324057, -0.00895028, -0.00698632, 0.01030831])
def init_normal(m):
if type(m) == nn.Linear:
nn.init.normal_(m.weight, mean=0, std=0.01)
nn.init.zeros_(m.bias)
net.apply(init_normal)
net[0].weight.data[0], net[0].bias.data[0]
(tensor([ 0.0006, -0.0043, 0.0077, -0.0008]), tensor(0.))
net = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(
4, activation=tf.nn.relu,
kernel_initializer=tf.random_normal_initializer(mean=0, stddev=0.01),
bias_initializer=tf.zeros_initializer()),
tf.keras.layers.Dense(1)])
net(X)
net.weights[0], net.weights[1]
(<tf.Variable 'dense_7/kernel:0' shape=(4, 4) dtype=float32, numpy=
array([[ 0.01078499, 0.00606496, -0.02899818, 0.00378611],
[-0.00151843, 0.00543029, 0.02985052, -0.02063808],
[-0.01929664, 0.00598367, -0.00075314, 0.00823843],
[ 0.00188551, -0.01230356, 0.00518165, 0.00810095]],
dtype=float32)>,
<tf.Variable 'dense_7/bias:0' shape=(4,) dtype=float32, numpy=array([0., 0., 0., 0.], dtype=float32)>)
Ayrıca tüm parametreleri belirli bir sabit değere ilkleyebiliriz (örneğin, 1 gibi).
net.initialize(init=init.Constant(1), force_reinit=True)
net[0].weight.data()[0]
array([1., 1., 1., 1.])
def init_constant(m):
if type(m) == nn.Linear:
nn.init.constant_(m.weight, 1)
nn.init.zeros_(m.bias)
net.apply(init_constant)
net[0].weight.data[0], net[0].bias.data[0]
(tensor([1., 1., 1., 1.]), tensor(0.))
net = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(
4, activation=tf.nn.relu,
kernel_initializer=tf.keras.initializers.Constant(1),
bias_initializer=tf.zeros_initializer()),
tf.keras.layers.Dense(1),
])
net(X)
net.weights[0], net.weights[1]
(<tf.Variable 'dense_9/kernel:0' shape=(4, 4) dtype=float32, numpy=
array([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]], dtype=float32)>,
<tf.Variable 'dense_9/bias:0' shape=(4,) dtype=float32, numpy=array([0., 0., 0., 0.], dtype=float32)>)
Ayrıca belirli bloklar için farklı ilkleyiciler uygulayabiliriz. Örneğin, aşağıda ilk katmanı Xavier ilkleyicisi ile ilkliyoruz ve ikinci katmanı sabit 42 değeri ile ilkliyoruz.
net[0].weight.initialize(init=init.Xavier(), force_reinit=True)
net[1].initialize(init=init.Constant(42), force_reinit=True)
print(net[0].weight.data()[0])
print(net[1].weight.data())
[-0.17594433 0.02314097 -0.1992535 0.09509248]
[[42. 42. 42. 42. 42. 42. 42. 42.]]
def xavier(m):
if type(m) == nn.Linear:
nn.init.xavier_uniform_(m.weight)
def init_42(m):
if type(m) == nn.Linear:
nn.init.constant_(m.weight, 42)
net[0].apply(xavier)
net[2].apply(init_42)
print(net[0].weight.data[0])
print(net[2].weight.data)
tensor([-0.0983, 0.5310, -0.2538, 0.6591])
tensor([[42., 42., 42., 42., 42., 42., 42., 42.]])
net = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(
4,
activation=tf.nn.relu,
kernel_initializer=tf.keras.initializers.GlorotUniform()),
tf.keras.layers.Dense(
1, kernel_initializer=tf.keras.initializers.Constant(42)),
])
net(X)
print(net.layers[1].weights[0])
print(net.layers[2].weights[0])
<tf.Variable 'dense_11/kernel:0' shape=(4, 4) dtype=float32, numpy=
array([[-0.41768575, -0.6246867 , -0.05365354, -0.15741253],
[-0.61217654, -0.10486388, 0.5289677 , 0.28025395],
[-0.6302849 , -0.40947932, 0.36533278, -0.60744387],
[-0.32922024, 0.42411977, -0.44172084, 0.8596789 ]],
dtype=float32)>
<tf.Variable 'dense_12/kernel:0' shape=(4, 1) dtype=float32, numpy=
array([[42.],
[42.],
[42.],
[42.]], dtype=float32)>
5.2.2.2. Özelleştirilmiş İlkleme¶
Bazen, ihtiyaç duyduğumuz ilkletme yöntemleri derin öğrenme çerçevesi tarafından sağlanmayabilir. Aşağıdaki örnekte, herhangi bir ağırlık parametresi \(w\) için aşağıdaki garip dağılımı kullanarak bir ilkleyici tanımlıyoruz:
Burada Initializer
sınıfının bir alt sınıfını tanımlıyoruz.
Genellikle, yalnızca bir tensör bağımsız değişkeni (data
) alan ve
ona istenen ilkletilmiş değerleri atayan _init_weight
işlevini
uygulamamız gerekir.
class MyInit(init.Initializer):
def _init_weight(self, name, data):
print('Init', name, data.shape)
data[:] = np.random.uniform(-10, 10, data.shape)
data *= np.abs(data) >= 5
net.initialize(MyInit(), force_reinit=True)
net[0].weight.data()[:2]
Init dense0_weight (8, 4)
Init dense1_weight (1, 8)
array([[ 0. , -0. , -0. , 8.522827 ],
[ 0. , -8.828651 , -0. , -5.6012006]])
Yine, net
’e uygulamak için bir my_init
işlevi uyguluyoruz.
def my_init(m):
if type(m) == nn.Linear:
print("Init", *[(name, param.shape)
for name, param in m.named_parameters()][0])
nn.init.uniform_(m.weight, -10, 10)
m.weight.data *= m.weight.data.abs() >= 5
net.apply(my_init)
net[0].weight[:2]
Init weight torch.Size([8, 4])
Init weight torch.Size([1, 8])
tensor([[ 0.0000, -8.7999, 9.7935, 8.7815],
[-0.0000, 0.0000, -0.0000, 5.8831]], grad_fn=<SliceBackward0>)
Burada Initializer
’ın bir alt sınıfını tanımlıyoruz ve şekil ve veri
türüne göre istenen bir tensörü döndüren __call__
işlevini
uyguluyoruz.
class MyInit(tf.keras.initializers.Initializer):
def __call__(self, shape, dtype=None):
data=tf.random.uniform(shape, -10, 10, dtype=dtype)
factor=(tf.abs(data) >= 5)
factor=tf.cast(factor, tf.float32)
return data * factor
net = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(
4,
activation=tf.nn.relu,
kernel_initializer=MyInit()),
tf.keras.layers.Dense(1),
])
net(X)
print(net.layers[1].weights[0])
<tf.Variable 'dense_13/kernel:0' shape=(4, 4) dtype=float32, numpy=
array([[-0. , 0. , -0. , 7.4787254],
[ 0. , 9.867706 , 0. , -0. ],
[-8.174446 , 7.664215 , -0. , 0. ],
[-5.316007 , -7.503488 , -0. , 0. ]], dtype=float32)>
Her zaman parametreleri doğrudan ayarlama seçeneğimiz olduğunu unutmayın.
net[0].weight.data()[:] += 1
net[0].weight.data()[0, 0] = 42
net[0].weight.data()[0]
array([42. , 1. , 1. , 9.522827])
İleri düzey kullanıcılar için bir hatırlatma: Parametreleri bir
autograd
(otomatik türev) kapsamında ayarlamak istiyorsanız,
otomatik türev alma mekanizmalarının karıştırılmasını önlemek için
set_data
’yı kullanmanız gerekir.
net[0].weight.data[:] += 1
net[0].weight.data[0, 0] = 42
net[0].weight.data[0]
tensor([42.0000, -7.7999, 10.7935, 9.7815])
net.layers[1].weights[0][:].assign(net.layers[1].weights[0] + 1)
net.layers[1].weights[0][0, 0].assign(42)
net.layers[1].weights[0]
<tf.Variable 'dense_13/kernel:0' shape=(4, 4) dtype=float32, numpy=
array([[42. , 1. , 1. , 8.478725],
[ 1. , 10.867706, 1. , 1. ],
[-7.174446, 8.664215, 1. , 1. ],
[-4.316007, -6.503488, 1. , 1. ]], dtype=float32)>
5.2.3. Bağlı Parametreler¶
Genellikle, parametreleri birden çok katmanda paylaşmak isteriz. Bunu biraz daha zekice bir şekilde nasıl yapacağımızı görelim. Aşağıda yoğun (dense) bir katman ayırıyoruz ve ardından onun parametrelerini de özellikle başka bir katmanınkileri ayarlamak için kullanıyoruz.
net = nn.Sequential()
# Parametrelerine atıfta bulunabilmemiz için paylaşılan katmana bir ad
# vermemiz gerekiyor
shared = nn.Dense(8, activation='relu')
net.add(nn.Dense(8, activation='relu'),
shared,
nn.Dense(8, activation='relu', params=shared.params),
nn.Dense(10))
net.initialize()
X = np.random.uniform(size=(2, 20))
net(X)
# Parametrelerin aynı olup olmadığını kontrol edin
print(net[1].weight.data()[0] == net[2].weight.data()[0])
net[1].weight.data()[0, 0] = 100
# Aynı değere sahip olmak yerine aslında aynı nesne olduklarından emin olun
print(net[1].weight.data()[0] == net[2].weight.data()[0])
[ True True True True True True True True]
[ True True True True True True True True]
Bu örnek, ikinci ve üçüncü katmanın parametrelerinin birbirine bağlı olduğunu göstermektedir. Sadece eşit değiller, tamamen aynı tensörle temsil ediliyorlar. Bu yüzden parametrelerden birini değiştirirsek diğeri de değişir. Merak edebilirsiniz, parametreler bağlı olduğunda gradyanlara ne olur? Model parametreleri gradyanlar içerdiğinden, ikinci ve üçüncü gizli katmanların gradyanları geri yayma sırasında birbiriyle toplanır.
# Parametrelerine atıfta bulunabilmemiz için paylaşılan katmana bir ad
# vermemiz gerekiyor
shared = nn.Linear(8, 8)
net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(),
shared, nn.ReLU(),
shared, nn.ReLU(),
nn.Linear(8, 1))
net(X)
# Parametrelerin aynı olup olmadığını kontrol edin
print(net[2].weight.data[0] == net[4].weight.data[0])
net[2].weight.data[0, 0] = 100
# Aynı değere sahip olmak yerine aslında aynı nesne olduklarından emin olun
print(net[2].weight.data[0] == net[4].weight.data[0])
tensor([True, True, True, True, True, True, True, True])
tensor([True, True, True, True, True, True, True, True])
Bu örnek, ikinci ve üçüncü katmanın parametrelerinin birbirine bağlı olduğunu göstermektedir. Sadece eşit değiller, tamamen aynı tensörle temsil ediliyorlar. Bu yüzden parametrelerden birini değiştirirsek diğeri de değişir. Merak edebilirsiniz, parametreler bağlı olduğunda gradyanlara ne olur? Model parametreleri gradyanlar içerdiğinden, ikinci ve üçüncü gizli katmanların gradyanları geri yayma sırasında birbiriyle toplanır.
# tf.keras biraz farklı davranır. Yinelenen katmanı otomatik olarak kaldırır.
shared = tf.keras.layers.Dense(4, activation=tf.nn.relu)
net = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
shared,
shared,
tf.keras.layers.Dense(1),
])
net(X)
# Parametrelerin farklı olup olmadığını kontrol edin
print(len(net.layers) == 3)
True
5.2.4. Özet¶
Model parametrelerine erişmek, ilklemek ve onları bağlamak için birkaç farklı yol var.
Özelleştirilmiş ilkleme kullanabiliriz.
5.2.5. Alıştırmalar¶
Section 5.1 içinde tanımlanan
FancyMLP
modelini kullanınız ve çeşitli katmanların parametrelerine erişiniz.Farklı ilkleyicileri keşfetmek için ilkleme modülü dökümanına bakınız.
Paylaşılan bir parametre katmanı içeren bir MLP oluşturunuz ve onu eğitiniz. Eğitim sürecinde, her katmanın model parametrelerini ve gradyanlarını gözlemleyiniz.
Parametreleri paylaşmak neden iyi bir fikirdir?