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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. raw:: html
`Discussão `__ .. raw:: html
.. raw:: html
`Discussão `__ .. raw:: html
.. raw:: html
`Discussão `__ .. raw:: html
.. raw:: html
.. raw:: html