6.4 Atidėtasis inicializavimas
Iki šiol gali atrodyti, kad išsisukome nerūpestingai kurdami savo tinklus. Konkrečiai, darėme šiuos neintuityvius dalykus, kurie gali neatrodyti tokie, kad turėtų veikti:
- Apibrėžėme tinklų architektūras nenurodydami įvesties dimensijos.
- Pridėjome sluoksnius nenurodydami ankstesnio sluoksnio išvesties dimensijos.
- Net „inicializavome“ šiuos parametrus prieš pateikdami pakankamai informacijos, kad būtų galima nustatyti, kiek parametrų turėtų turėti mūsų modeliai.
Galbūt nustebsite, kad mūsų kodas apskritai veikia. Juk giliojo mokymosi karkasas jokiu būdu negalėtų žinoti, kokia bus tinklo įvesties dimensija. Gudrybė čia ta, kad karkasas atideda inicializavimą, laukdamas, kol pirmą kartą perduosime duomenis per modelį, kad lėkdamas išvestų kiekvieno sluoksnio dydžius.
Vėliau, dirbant su konvoliuciniais neuroniniais tinklais, ši technika taps dar patogesnė, nes įvesties dimensija (pvz., vaizdo skyra) darys įtaką kiekvieno paskesnio sluoksnio dimensijai. Todėl galimybė nustatyti parametrus nereikalaujant žinoti, rašant kodą, dimensijos reikšmės, gali labai supaprastinti mūsų modelių nurodymo ir vėlesnio keitimo užduotį. Toliau gilinamės į inicializavimo mechaniką.
from mxnet import np, npx
from mxnet.gluon import nn
npx.set_np()
from d2l import torch as d2l
import torch
from torch import nn
import tensorflow as tf
from d2l import jax as d2l
from flax import linen as nn
import jax
from jax import numpy as jnp
Pradžioje sukurkime MLP egzempliorių.
net = nn.Sequential()
net.add(nn.Dense(256, activation='relu'))
net.add(nn.Dense(10))
net = nn.Sequential(nn.LazyLinear(256), nn.ReLU(), nn.LazyLinear(10))
net = tf.keras.models.Sequential([
tf.keras.layers.Dense(256, activation=tf.nn.relu),
tf.keras.layers.Dense(10),
])
net = nn.Sequential([nn.Dense(256), nn.relu, nn.Dense(10)])
Šiuo metu tinklas jokiu būdu negali žinoti įvesties sluoksnio svorių dimensijų, nes įvesties dimensija lieka nežinoma.
Todėl karkasas dar neinicializavo jokių parametrų. Tai patvirtiname žemiau bandydami pasiekti parametrus.
Kaip minėta 6.2.1 skyrelis, parametrai ir tinklo apibrėžimas yra atskirti
Jax ir Flax, ir naudotojas tvarko abu rankiniu būdu. Flax modeliai yra be būsenos,
todėl atributo parameters nėra.
print(net.collect_params)
print(net.collect_params())
net[0].weight
[net.layers[i].get_weights() for i in range(len(net.layers))]
Atkreipkite dėmesį, kad nors parametrų objektai egzistuoja,
kiekvieno sluoksnio įvesties dimensija nurodyta kaip -1.
MXNet naudoja ypatingą reikšmę -1 žymėti,
kad parametro dimensija lieka nežinoma.
Šiuo metu bandymai pasiekti net[0].weight.data()
sukeltų vykdymo klaidą, nurodančią, kad tinklas
turi būti inicializuotas prieš pasiekiant parametrus.
Dabar pažiūrėkime, kas nutinka, kai bandome inicializuoti
parametrus metodu initialize.
Atkreipkite dėmesį, kad kiekvieno sluoksnio objektai egzistuoja, bet svoriai yra tušti.
Naudojant net.get_weights() būtų išmesta klaida, nes svoriai
dar neinicializuoti.
net.initialize()
net.collect_params()
Kaip matome, nieko nepasikeitė. Kai įvesties dimensijos nežinomos, inicializavimo kvietimai iš tikrųjų neinicializuoja parametrų. Vietoj to šis kvietimas MXNet užregistruoja, kad norime (ir pasirinktinai pagal kokį skirstinį) inicializuoti parametrus.
Toliau perduokime duomenis per tinklą, kad karkasas pagaliau inicializuotų parametrus.
X = np.random.uniform(size=(2, 20))
net(X)
net.collect_params()
X = torch.rand(2, 20)
net(X)
net[0].weight.shape
X = tf.random.uniform((2, 20))
net(X)
[w.shape for w in net.get_weights()]
params = net.init(d2l.get_key(), jnp.zeros((2, 20)))
jax.tree_util.tree_map(lambda x: x.shape, params).tree_flatten_with_keys()
Vos tik sužinome įvesties dimensiją, 20, karkasas gali nustatyti pirmojo sluoksnio svorių matricos formą, įstatydamas reikšmę 20. Atpažinęs pirmojo sluoksnio formą, karkasas pereina prie antrojo sluoksnio ir taip toliau per skaičiavimo grafą, kol bus žinomos visos formos. Atkreipkite dėmesį, kad šiuo atveju atidėtojo inicializavimo reikia tik pirmajam sluoksniui, bet karkasas inicializuoja nuosekliai. Kai tik žinomos visų parametrų formos, karkasas pagaliau gali inicializuoti parametrus.
Šis metodas perduoda fiktyvias įvestis per tinklą bandomajam paleidimui, kad išvestų visų parametrų formas ir vėliau inicializuotų parametrus. Jis bus naudojamas vėliau, kai numatytieji atsitiktiniai inicializavimai nepageidaujami.
Parametrų inicializavimas Flax visada atliekamas rankiniu būdu ir jį tvarko naudotojas. Šis metodas kaip argumentus priima fiktyvią įvestį ir raktų žodyną. Šiame raktų žodyne yra ATS (atsitiktinių skaičių generatoriai) modelio parametrams inicializuoti ir dropout ATS dropout kaukei generuoti modeliuose su dropout sluoksniais. Daugiau apie dropout bus aptarta vėliau 5.6 skyrelis. Galiausiai metodas inicializuoja modelį, grąžindamas parametrus. Jį naudojome „po gaubtu“ ir ankstesniuose skyreliuose.
@d2l.add_to_class(d2l.Module) #@save
def apply_init(self, inputs, init=None):
self.forward(*inputs)
if init is not None:
self.net.apply(init)
@d2l.add_to_class(d2l.Module) #@save
def apply_init(self, dummy_input, key):
params = self.init(key, *dummy_input) # dummy_input tuple unpacked
return params
6.4.1 Santrauka
Atidėtasis inicializavimas gali būti patogus, leisdamas karkasui automatiškai išvesti parametrų formas, todėl lengva keisti architektūras ir pašalinamas vienas dažnas klaidų šaltinis. Galime perduoti duomenis per modelį, kad karkasas pagaliau inicializuotų parametrus.
6.4.2 Pratimai
- Kas nutinka, jei nurodote įvesties dimensijas pirmajam sluoksniui, bet ne paskesniems? Ar inicializavimas įvyksta iš karto?
- Kas nutinka, jei nurodote nesutampančias dimensijas?
- Ką turėtumėte daryti, jei turite kintamos dimensijos įvestį? Užuomina: pažiūrėkite į parametrų susiejimą.
