Camadas Personalizadas
======================
Um fator por trás do sucesso do *Deep Learning* é a disponibilidade de
uma ampla gama de camadas que pode ser composto de maneiras criativas
projetar arquiteturas adequadas para uma ampla variedade de tarefas. Por
exemplo, os pesquisadores inventaram camadas especificamente para lidar
com imagens, texto, loop sobre dados sequenciais, e realizando
programação dinâmica. Mais cedo ou mais tarde, você encontrará ou
inventará uma camada que ainda não existe na estrutura de *Deep
Learning*. Nesses casos, você deve construir uma camada personalizada.
Nesta seção, mostramos como.
Camadas Sem Parâmetros
----------------------
Para começar, construímos uma camada personalizada que não possui
parâmetros próprios. Isso deve parecer familiar, se você se lembra de
nosso introdução ao bloco em :numref:`sec_model_construction`. A
seguinte classe ``CenteredLayer`` simplesmente subtrai a média de sua
entrada. Para construí-lo, simplesmente precisamos herdar da classe da
camada base e implementar a função de propagação direta.
.. raw:: html
.. raw:: html
.. code:: python
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()
.. raw:: html
.. raw:: html
.. code:: python
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()
.. raw:: html
.. raw:: html
.. code:: python
import tensorflow as tf
class CenteredLayer(tf.keras.Model):
def __init__(self):
super().__init__()
def call(self, inputs):
return inputs - tf.reduce_mean(inputs)
.. raw:: html
.. raw:: html
Vamos verificar se nossa camada funciona conforme o esperado,
alimentando alguns dados por meio dela.
.. raw:: html
.. raw:: html
.. code:: python
layer = CenteredLayer()
layer(np.array([1, 2, 3, 4, 5]))
.. parsed-literal::
:class: output
array([-2., -1., 0., 1., 2.])
.. raw:: html
.. raw:: html
.. code:: python
layer = CenteredLayer()
layer(torch.FloatTensor([1, 2, 3, 4, 5]))
.. parsed-literal::
:class: output
tensor([-2., -1., 0., 1., 2.])
.. raw:: html
.. raw:: html
.. code:: python
layer = CenteredLayer()
layer(tf.constant([1, 2, 3, 4, 5]))
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Agora podemos incorporar nossa camada como um componente na construção
de modelos mais complexos.
.. raw:: html
.. raw:: html
.. code:: python
net = nn.Sequential()
net.add(nn.Dense(128), CenteredLayer())
net.initialize()
.. raw:: html
.. raw:: html
.. code:: python
net = nn.Sequential(nn.Linear(8, 128), CenteredLayer())
.. raw:: html
.. raw:: html
.. code:: python
net = tf.keras.Sequential([tf.keras.layers.Dense(128), CenteredLayer()])
.. raw:: html
.. raw:: html
Como uma verificação extra de sanidade, podemos enviar dados aleatórios
através da rede e verificar se a média é de fato 0. Porque estamos
lidando com números de ponto flutuante, ainda podemos ver um número
muito pequeno diferente de zero devido à quantização.
.. raw:: html
.. raw:: html
.. code:: python
Y = net(np.random.uniform(size=(4, 8)))
Y.mean()
.. parsed-literal::
:class: output
array(3.783498e-10)
.. raw:: html
.. raw:: html
.. code:: python
Y = net(torch.rand(4, 8))
Y.mean()
.. parsed-literal::
:class: output
tensor(-4.1910e-09, grad_fn=)
.. raw:: html
.. raw:: html
.. code:: python
Y = net(tf.random.uniform((4, 8)))
tf.reduce_mean(Y)
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Camadas com Parâmetros
----------------------
Agora que sabemos como definir camadas simples, vamos prosseguir para a
definição de camadas com parâmetros que pode ser ajustado por meio de
treinamento. Podemos usar funções integradas para criar parâmetros, que
fornecem algumas funcionalidades básicas de manutenção. Em particular,
eles governam o acesso, inicialização, compartilhar, salvar e carregar
parâmetros do modelo. Dessa forma, entre outros benefícios, não
precisaremos escrever rotinas de serialização personalizadas para cada
camada personalizada.
Agora, vamos implementar nossa própria versão da camada totalmente
conectada. Lembre-se de que esta camada requer dois parâmetros, um para
representar o peso e outro para o viés. Nesta implementação, preparamos
a ativação do ReLU como padrão. Esta camada requer a entrada de
argumentos: ``in_units`` e ``units``, que denotam o número de entradas e
saídas, respectivamente.
.. raw:: html
.. raw:: html
.. code:: python
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)
.. raw:: html
.. raw:: html
.. code:: python
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)
.. raw:: html
.. raw:: html
.. code:: python
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)
.. raw:: html
.. raw:: html
Em seguida, instanciamos a classe ``MyDense`` e acessar seus parâmetros
de modelo.
.. raw:: html
.. raw:: html
.. code:: python
dense = MyDense(units=3, in_units=5)
dense.params
.. parsed-literal::
:class: output
mydense0_ (
Parameter mydense0_weight (shape=(5, 3), dtype=)
Parameter mydense0_bias (shape=(3,), dtype=)
)
.. raw:: html
.. raw:: html
.. code:: python
dense = MyLinear(5, 3)
dense.weight
.. parsed-literal::
:class: output
Parameter containing:
tensor([[-0.4359, -0.8619, -0.4170],
[-0.3191, 0.3732, 0.7508],
[-0.1494, -0.8253, -0.7071],
[ 1.0940, -0.4332, -2.2712],
[ 1.9598, -0.2122, 1.8529]], requires_grad=True)
.. raw:: html
.. raw:: html
.. code:: python
dense = MyDense(3)
dense(tf.random.uniform((2, 5)))
dense.get_weights()
.. parsed-literal::
:class: output
[array([[-0.03506669, 0.01943431, -0.00358615],
[ 0.02064196, -0.07825888, -0.00893933],
[ 0.00577814, 0.0454763 , 0.02938056],
[-0.03217198, -0.05240436, -0.00833275],
[ 0.05816463, 0.02635984, -0.05368067]], dtype=float32),
array([0., 0., 0.], dtype=float32)]
.. raw:: html
.. raw:: html
Podemos realizar cálculos de propagação direta usando camadas
personalizadas.
.. raw:: html
.. raw:: html
.. code:: python
dense.initialize()
dense(np.random.uniform(size=(2, 5)))
.. parsed-literal::
:class: output
array([[0. , 0.01633355, 0. ],
[0. , 0.01581812, 0. ]])
.. raw:: html
.. raw:: html
.. code:: python
dense(torch.rand(2, 5))
.. parsed-literal::
:class: output
tensor([[1.1498, 0.0000, 0.0000],
[1.9590, 0.0000, 0.0000]])
.. raw:: html
.. raw:: html
.. code:: python
dense(tf.random.uniform((2, 5)))
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Também podemos construir modelos usando camadas personalizadas. Assim
que tivermos isso, podemos usá-lo como a camada totalmente conectada
integrada.
.. raw:: html
.. raw:: html
.. code:: python
net = nn.Sequential()
net.add(MyDense(8, in_units=64),
MyDense(1, in_units=8))
net.initialize()
net(np.random.uniform(size=(2, 64)))
.. parsed-literal::
:class: output
array([[0.06508517],
[0.0615553 ]])
.. raw:: html
.. raw:: html
.. code:: python
net = nn.Sequential(MyLinear(64, 8), MyLinear(8, 1))
net(torch.rand(2, 64))
.. parsed-literal::
:class: output
tensor([[0.],
[0.]])
.. raw:: html
.. raw:: html
.. code:: python
net = tf.keras.models.Sequential([MyDense(8), MyDense(1)])
net(tf.random.uniform((2, 64)))
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Sumário
-------
- Podemos projetar camadas personalizadas por meio da classe de camada
básica. Isso nos permite definir novas camadas flexíveis que se
comportam de maneira diferente de quaisquer camadas existentes na
biblioteca.
- Uma vez definidas, as camadas personalizadas podem ser chamadas em
contextos e arquiteturas arbitrários.
- As camadas podem ter parâmetros locais, que podem ser criados por
meio de funções integradas.
Exercícios
----------
3. Projete uma camada que recebe uma entrada e calcula uma redução de
tensor, ou seja, ele retorna
:math:`y_k = \sum_{i, j} W_{ijk} x_i x_j`.
4. Projete uma camada que retorne a metade anterior dos coeficientes de
Fourier dos dados.
.. raw:: html
.. raw:: html
`Discussão `__
.. raw:: html
.. raw:: html
`Discussão `__
.. raw:: html
.. raw:: html
`Discussão `__
.. raw:: html
.. raw:: html
.. raw:: html