Gerenciamento de Parâmetros
===========================
Depois de escolher uma arquitetura e definir nossos hiperparâmetros,
passamos para o ciclo de treinamento, onde nosso objetivo é encontrar
valores de parâmetro que minimizam nossa função de perda. Após o
treinamento, precisaremos desses parâmetros para fazer previsões
futuras. Além disso, às vezes desejamos para extrair os parâmetros seja
para reutilizá-los em algum outro contexto, para salvar nosso modelo em
disco para que pode ser executado em outro *software*, ou para exame na
esperança de ganhar compreensão científica.
Na maioria das vezes, seremos capazes de ignorar os detalhes essenciais
de como os parâmetros são declarados e manipulado, contando com
estruturas de *Deep Learning* para fazer o trabalho pesado. No entanto,
quando nos afastamos de arquiteturas empilhadas com camadas padrão, às
vezes precisaremos declarar e manipular parâmetros. Nesta seção,
cobrimos o seguinte:
- Parâmetros de acesso para depuração, diagnóstico e visualizações.
- Inicialização de parâmetros.
- Parâmetros de compartilhamento em diferentes componentes do modelo.
Começamos nos concentrando em um MLP com uma camada oculta.
.. raw:: html
.. raw:: html
.. code:: python
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() # Use o método de inicialização padrão
X = np.random.uniform(size=(2, 4))
net(X) # Forward computation
.. parsed-literal::
:class: output
array([[0.0054572 ],
[0.00488594]])
.. raw:: html
.. raw:: html
.. code:: python
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)
.. parsed-literal::
:class: output
tensor([[0.2046],
[0.4232]], grad_fn=
)
.. raw:: html
.. raw:: html
.. code:: python
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)
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Acesso a Parâmetros
-------------------
Vamos começar explicando como acessar os parâmetros dos modelos que você
já conhece. Quando um modelo é definido por meio da classe
``Sequential``, podemos primeiro acessar qualquer camada indexando no
modelo como se fosse uma lista. Os parâmetros de cada camada são
convenientemente localizado em seu atributo. Podemos inspecionar os
parâmetros da segunda camada totalmente conectada da seguinte maneira.
.. raw:: html
.. raw:: html
.. code:: python
print(net[1].params)
.. parsed-literal::
:class: output
dense1_ (
Parameter dense1_weight (shape=(1, 8), dtype=float32)
Parameter dense1_bias (shape=(1,), dtype=float32)
)
.. raw:: html
.. raw:: html
.. code:: python
print(net[2].state_dict())
.. parsed-literal::
:class: output
OrderedDict([('weight', tensor([[ 0.2166, -0.3507, -0.1181, 0.2769, -0.1432, 0.2410, 0.0740, 0.2221]])), ('bias', tensor([0.3118]))])
.. raw:: html
.. raw:: html
.. code:: python
print(net.layers[2].weights)
.. parsed-literal::
:class: output
[, ]
.. raw:: html
.. raw:: html
A saída nos diz algumas coisas importantes. Primeiro, esta camada
totalmente conectada contém dois parâmetros, correspondendo aos pesos e
vieses, respectivamente. Ambos são armazenados como *floats* de precisão
simples (float32). Observe que os nomes dos parâmetros nos permitem
identificar de forma única parâmetros de cada camada, mesmo em uma rede
contendo centenas de camadas.
Parâmetros Direcionados
~~~~~~~~~~~~~~~~~~~~~~~
Observe que cada parâmetro é representado como uma instância da classe
de parâmetro. Para fazer algo útil com os parâmetros, primeiro
precisamos acessar os valores numéricos subjacentes. Existem várias
maneiras de fazer isso. Alguns são mais simples, enquanto outros são
mais gerais. O código a seguir extrai o viés da segunda camada de rede
neural, que retorna uma instância de classe de parâmetro, e acessa
posteriormente o valor desse parâmetro.
.. raw:: html
.. raw:: html
.. code:: python
print(type(net[1].bias))
print(net[1].bias)
print(net[1].bias.data())
.. parsed-literal::
:class: output
Parameter dense1_bias (shape=(1,), dtype=float32)
[0.]
Os parâmetros são objetos complexos, contendo valores, gradientes, e
informações adicionais. É por isso que precisamos solicitar o valor
explicitamente.
Além do valor, cada parâmetro também nos permite acessar o gradiente.
Como ainda não invocamos a *backpropagation* para esta rede, ela está em
seu estado inicial.
.. code:: python
net[1].weight.grad()
.. parsed-literal::
:class: output
array([[0., 0., 0., 0., 0., 0., 0., 0.]])
.. raw:: html
.. raw:: html
.. code:: python
print(type(net[2].bias))
print(net[2].bias)
print(net[2].bias.data)
.. parsed-literal::
:class: output
Parameter containing:
tensor([0.3118], requires_grad=True)
tensor([0.3118])
Os parâmetros são objetos complexos, contendo valores, gradientes, e
informações adicionais. É por isso que precisamos solicitar o valor
explicitamente.
Além do valor, cada parâmetro também nos permite acessar o gradiente.
Como ainda não invocamos a *backpropagation* para esta rede, ela está em
seu estado inicial.
.. code:: python
net[2].weight.grad == None
.. parsed-literal::
:class: output
True
.. raw:: html
.. raw:: html
.. code:: python
print(type(net.layers[2].weights[1]))
print(net.layers[2].weights[1])
print(tf.convert_to_tensor(net.layers[2].weights[1]))
.. parsed-literal::
:class: output
tf.Tensor([0.], shape=(1,), dtype=float32)
.. raw:: html
.. raw:: html
Todos os Parâmetros de Uma Vez
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Quando precisamos realizar operações em todos os parâmetros, acessá-los
um por um pode se tornar tedioso. A situação pode ficar especialmente
complicada quando trabalhamos com blocos mais complexos (por exemplo,
blocos aninhados), uma vez que precisaríamos voltar recursivamente
através de toda a árvore para extrair parâmetros de cada sub-bloco.
Abaixo, demonstramos como acessar os parâmetros da primeira camada
totalmente conectada versus acessar todas as camadas.
.. raw:: html
.. raw:: html
.. code:: python
print(net[0].collect_params())
print(net.collect_params())
.. parsed-literal::
:class: output
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)
)
.. raw:: html
.. raw:: html
.. code:: python
print(*[(name, param.shape) for name, param in net[0].named_parameters()])
print(*[(name, param.shape) for name, param in net.named_parameters()])
.. parsed-literal::
:class: output
('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]))
.. raw:: html
.. raw:: html
.. code:: python
print(net.layers[1].weights)
print(net.get_weights())
.. parsed-literal::
:class: output
[, ]
[array([[-0.00696713, 0.06294048, -0.34609908, 0.28365868],
[ 0.05357468, -0.3715149 , -0.21233797, 0.5667568 ],
[ 0.75887805, -0.24776524, 0.58249944, 0.15801603],
[-0.309555 , -0.46662605, 0.3352285 , -0.7561607 ]],
dtype=float32), array([0., 0., 0., 0.], dtype=float32), array([[-0.8849803 ],
[ 0.28838134],
[ 0.8427515 ],
[ 0.29679787]], dtype=float32), array([0.], dtype=float32)]
.. raw:: html
.. raw:: html
Isso nos fornece outra maneira de acessar os parâmetros da rede como
segue.
.. raw:: html
.. raw:: html
.. code:: python
net.collect_params()['dense1_bias'].data()
.. parsed-literal::
:class: output
array([0.])
.. raw:: html
.. raw:: html
.. code:: python
net.state_dict()['2.bias'].data
.. parsed-literal::
:class: output
tensor([0.3118])
.. raw:: html
.. raw:: html
.. code:: python
net.get_weights()[1]
.. parsed-literal::
:class: output
array([0., 0., 0., 0.], dtype=float32)
.. raw:: html
.. raw:: html
Coletando Parâmetros de Blocos Aninhados
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Vamos ver como funcionam as convenções de nomenclatura de parâmetros se
aninharmos vários blocos uns dentro dos outros. Para isso, primeiro
definimos uma função que produz blocos (uma fábrica de blocos, por assim
dizer) e então combine-os dentro de blocos ainda maiores.
.. raw:: html
.. raw:: html
.. code:: python
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):
# Nested here
net.add(block1())
return net
rgnet = nn.Sequential()
rgnet.add(block2())
rgnet.add(nn.Dense(10))
rgnet.initialize()
rgnet(X)
.. parsed-literal::
:class: output
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]])
.. raw:: html
.. raw:: html
.. code:: python
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):
# Nested here
net.add_module(f'block {i}', block1())
return net
rgnet = nn.Sequential(block2(), nn.Linear(4, 1))
rgnet(X)
.. parsed-literal::
:class: output
tensor([[0.1150],
[0.1150]], grad_fn=
)
.. raw:: html
.. raw:: html
.. code:: python
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):
# Nested here
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)
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Agora que projetamos a rede, vamos ver como está organizado.
.. raw:: html
.. raw:: html
.. code:: python
print(rgnet.collect_params)
print(rgnet.collect_params())
.. parsed-literal::
:class: output
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)
)
.. raw:: html
.. raw:: html
.. code:: python
print(rgnet)
.. parsed-literal::
:class: output
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)
)
.. raw:: html
.. raw:: html
.. code:: python
print(rgnet.summary())
.. parsed-literal::
:class: output
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
.. raw:: html
.. raw:: html
Uma vez que as camadas são aninhadas hierarquicamente, também podemos
acessá-los como se indexação por meio de listas aninhadas. Por exemplo,
podemos acessar o primeiro bloco principal, dentro dele o segundo
sub-bloco, e dentro disso o viés da primeira camada, com o seguinte.
.. raw:: html
.. raw:: html
.. code:: python
rgnet[0][1][0].bias.data()
.. parsed-literal::
:class: output
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.])
.. raw:: html
.. raw:: html
.. code:: python
rgnet[0][1][0].bias.data
.. parsed-literal::
:class: output
tensor([ 0.3746, 0.1523, -0.2427, -0.0837, -0.3223, -0.3808, -0.1753, 0.4000])
.. raw:: html
.. raw:: html
.. code:: python
rgnet.layers[0].layers[1].layers[1].weights[1]
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Inicialização de Parâmetros
---------------------------
Agora que sabemos como acessar os parâmetros, vamos ver como
inicializá-los corretamente. Discutimos a necessidade de inicialização
adequada em :numref:`sec_numerical_stability`. A estrutura de *Deep
Learning* fornece inicializações aleatórias padrão para suas camadas. No
entanto, muitas vezes queremos inicializar nossos pesos de acordo com
vários outros protocolos. A estrutura fornece mais comumente protocolos
usados e também permite criar um inicializador personalizado.
.. raw:: html
.. raw:: html
Por padrão, MXNet inicializa os parâmetros de peso ao desenhar
aleatoriamente de uma distribuição uniforme :math:`U(-0.07, 0.07)`,
limpar os parâmetros de polarização para zero. O módulo ``init`` do
MXNet oferece uma variedade de métodos de inicialização predefinidos.
.. raw:: html
.. raw:: html
Por padrão, o PyTorch inicializa matrizes de ponderação e polarização
uniformemente extraindo de um intervalo que é calculado de acordo com a
dimensão de entrada e saída. O módulo ``nn.init`` do PyTorch oferece uma
variedade de métodos de inicialização predefinidos.
.. raw:: html
.. raw:: html
Por padrão, Keras inicializa matrizes de ponderação uniformemente,
tirando de um intervalo que é calculado de acordo com a dimensão de
entrada e saída, e os parâmetros de polarização são todos definidos como
zero. O TensorFlow oferece uma variedade de métodos de inicialização no
módulo raiz e no módulo ``keras.initializers``.
.. raw:: html
.. raw:: html
Inicialização *Built-in*
~~~~~~~~~~~~~~~~~~~~~~~~
Vamos começar chamando inicializadores integrados. O código abaixo
inicializa todos os parâmetros de peso como variáveis aleatórias
gaussianas com desvio padrão de 0,01, enquanto os parâmetros de
polarização são zerados.
.. raw:: html
.. raw:: html
.. code:: python
# Aqui `force_reinit` garante que os parâmetros são inciados mesmo se
# eles já foram iniciados anteriormente
net.initialize(init=init.Normal(sigma=0.01), force_reinit=True)
net[0].weight.data()[0]
.. parsed-literal::
:class: output
array([-0.00324057, -0.00895028, -0.00698632, 0.01030831])
.. raw:: html
.. raw:: html
.. code:: python
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]
.. parsed-literal::
:class: output
(tensor([ 0.0045, -0.0082, -0.0063, 0.0008]), tensor(0.))
.. raw:: html
.. raw:: html
.. code:: python
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]
.. parsed-literal::
:class: output
(,
)
.. raw:: html
.. raw:: html
Também podemos inicializar todos os parâmetros a um determinado valor
constante (digamos, 1).
.. raw:: html
.. raw:: html
.. code:: python
net.initialize(init=init.Constant(1), force_reinit=True)
net[0].weight.data()[0]
.. parsed-literal::
:class: output
array([1., 1., 1., 1.])
.. raw:: html
.. raw:: html
.. code:: python
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]
.. parsed-literal::
:class: output
(tensor([1., 1., 1., 1.]), tensor(0.))
.. raw:: html
.. raw:: html
.. code:: python
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]
.. parsed-literal::
:class: output
(,
)
.. raw:: html
.. raw:: html
Também podemos aplicar inicializadores diferentes para certos blocos.
Por exemplo, abaixo inicializamos a primeira camada com o inicializador
Xavier e inicializar a segunda camada para um valor constante de 42.
.. raw:: html
.. raw:: html
.. code:: python
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())
.. parsed-literal::
:class: output
[-0.17594433 0.02314097 -0.1992535 0.09509248]
[[42. 42. 42. 42. 42. 42. 42. 42.]]
.. raw:: html
.. raw:: html
.. code:: python
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)
.. parsed-literal::
:class: output
tensor([ 0.5433, -0.5930, -0.6287, -0.6148])
tensor([[42., 42., 42., 42., 42., 42., 42., 42.]])
.. raw:: html
.. raw:: html
.. code:: python
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(1)),
])
net(X)
print(net.layers[1].weights[0])
print(net.layers[2].weights[0])
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Inicialização Customizada
~~~~~~~~~~~~~~~~~~~~~~~~~
Às vezes, os métodos de inicialização de que precisamos não são
fornecidos pela estrutura de *Deep Learning*. No exemplo abaixo,
definimos um inicializador para qualquer parâmetro de peso :math:`w`
usando a seguinte distribuição estranha:
.. math::
\begin{aligned}
w \sim \begin{cases}
U(5, 10) & \text{ with probability } \frac{1}{4} \\
0 & \text{ with probability } \frac{1}{2} \\
U(-10, -5) & \text{ with probability } \frac{1}{4}
\end{cases}
\end{aligned}
.. raw:: html
.. raw:: html
Aqui definimos uma subclasse da classe ``Initializer``. Normalmente, só
precisamos implementar a função ``_init_weight`` que leva um argumento
tensor (``data``) e atribui a ele os valores inicializados desejados.
.. code:: python
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]
.. parsed-literal::
:class: output
Init dense0_weight (8, 4)
Init dense1_weight (1, 8)
.. parsed-literal::
:class: output
array([[ 0. , -0. , -0. , 8.522827 ],
[ 0. , -8.828651 , -0. , -5.6012006]])
.. raw:: html
.. raw:: html
Novamente, implementamos uma função ``my_init`` para aplicar a\ ``net``.
.. code:: python
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]
.. parsed-literal::
:class: output
Init weight torch.Size([8, 4])
Init weight torch.Size([1, 8])
.. parsed-literal::
:class: output
tensor([[ 6.2500, 9.0335, 6.4244, -6.6561],
[-5.9088, 9.8715, 7.8404, 0.0000]], grad_fn=)
.. raw:: html
.. raw:: html
Aqui nós definimos uma subclasse de ``Initializer`` e implementamos
o\ ``__call__`` função que retorna um tensor desejado de acordo com a
forma e o tipo de dados.
.. code:: python
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])
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Observe que sempre temos a opção de definir parâmetros diretamente.
.. raw:: html
.. raw:: html
.. code:: python
net[0].weight.data()[:] += 1
net[0].weight.data()[0, 0] = 42
net[0].weight.data()[0]
.. parsed-literal::
:class: output
array([42. , 1. , 1. , 9.522827])
Uma observação para usuários avançados: se você quiser ajustar os
parâmetros dentro de um escopo ``autograd``, você precisa usar
``set_data`` para evitar confundir a mecânica de diferenciação
automática.
.. raw:: html
.. raw:: html
.. code:: python
net[0].weight.data[:] += 1
net[0].weight.data[0, 0] = 42
net[0].weight.data[0]
.. parsed-literal::
:class: output
tensor([42.0000, 10.0335, 7.4244, -5.6561])
.. raw:: html
.. raw:: html
.. code:: python
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]
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Parâmetros *Tied*
-----------------
Frequentemente, queremos compartilhar parâmetros em várias camadas.
Vamos ver como fazer isso com elegância. A seguir, alocamos uma camada
densa e usar seus parâmetros especificamente para definir os de outra
camada.
.. raw:: html
.. raw:: html
.. code:: python
net = nn.Sequential()
# Precisamos dar as camadas compartilhadas um nome
# para que possamos referenciar seus parâmetros
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)
# Checar se são os mesmos parâmetros
print(net[1].weight.data()[0] == net[2].weight.data()[0])
net[1].weight.data()[0, 0] = 100
# Garantindo que são o mesmo objeto ao invés de ter
# apenas o mesmo valor
print(net[1].weight.data()[0] == net[2].weight.data()[0])
.. parsed-literal::
:class: output
[ True True True True True True True True]
[ True True True True True True True True]
Este exemplo mostra que os parâmetros da segunda e terceira camadas são
amarrados. Eles não são apenas iguais, eles são representado pelo mesmo
tensor exato. Assim, se mudarmos um dos parâmetros, o outro também muda.
Você pode se perguntar, quando os parâmetros são amarrados o que
acontece com os gradientes? Uma vez que os parâmetros do modelo contêm
gradientes, os gradientes da segunda camada oculta e a terceira camada
oculta são adicionadas juntas durante a retropropagação.
.. raw:: html
.. raw:: html
.. code:: python
# Precisamos dar as camadas compartilhadas um nome
# para que possamos referenciar seus parâmetros
#
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)
# Checar se são os mesmos parâmetros
print(net[2].weight.data[0] == net[4].weight.data[0])
net[2].weight.data[0, 0] = 100
# Garantindo que são o mesmo objeto ao invés de ter
# apenas o mesmo valor
print(net[2].weight.data[0] == net[4].weight.data[0])
.. parsed-literal::
:class: output
tensor([True, True, True, True, True, True, True, True])
tensor([True, True, True, True, True, True, True, True])
Este exemplo mostra que os parâmetros da segunda e terceira camadas são
amarrados. Eles não são apenas iguais, eles são representado pelo mesmo
tensor exato. Assim, se mudarmos um dos parâmetros, o outro também muda.
Você pode se perguntar, quando os parâmetros são amarrados o que
acontece com os gradientes? Uma vez que os parâmetros do modelo contêm
gradientes, os gradientes da segunda camada oculta e a terceira camada
oculta são adicionadas juntas durante a retropropagação.
.. raw:: html
.. raw:: html
.. code:: python
# tf.keras behaves a bit differently. It removes the duplicate layer
# automatically
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)
# Checando se os parâmetros são diferentes
print(len(net.layers) == 3)
.. parsed-literal::
:class: output
True
.. raw:: html
.. raw:: html
Sumário
-------
- Temos várias maneiras de acessar, inicializar e vincular os
parâmetros do modelo.
- Podemos usar inicialização personalizada.
Exercícios
----------
1. Use o modelo ``FancyMLP`` definido em
:numref:`sec_model_construction` e acesse os parâmetros das várias
camadas.
2. Observe o documento do módulo de inicialização para explorar
diferentes inicializadores.
3. Construa um MLP contendo uma camada de parâmetros compartilhados e
treine-o. Durante o processo de treinamento, observe os parâmetros do
modelo e gradientes de cada camada.
4. Por que compartilhar parâmetros é uma boa ideia?
.. raw:: html
.. raw:: html
`Discussão `__
.. raw:: html
.. raw:: html
`Discussão `__
.. raw:: html
.. raw:: html
`Discussão `__
.. raw:: html
.. raw:: html
.. raw:: html