Entrada e Saída de Arquivos
===========================
Até agora, discutimos como processar dados e como para construir,
treinar e testar modelos de *Deep Learning*. No entanto, em algum
momento, esperamos ser felizes o suficiente com os modelos aprendidos
que queremos para salvar os resultados para uso posterior em vários
contextos (talvez até mesmo para fazer previsões na implantação). Além
disso, ao executar um longo processo de treinamento, a prática
recomendada é salvar resultados intermediários periodicamente (pontos de
verificação) para garantir que não perdemos vários dias de computação se
tropeçarmos no cabo de alimentação do nosso servidor. Portanto, é hora
de aprender como carregar e armazenar ambos os vetores de peso
individuais e modelos inteiros. Esta seção aborda ambos os problemas.
Carregando e Salvando Tensores
------------------------------
Para tensores individuais, podemos diretamente invocar as funções
``load`` e ``save`` para ler e escrever respectivamente. Ambas as
funções exigem que forneçamos um nome, e ``save`` requer como entrada a
variável a ser salva.
.. raw:: html
.. raw:: html
.. code:: python
from mxnet import np, npx
from mxnet.gluon import nn
npx.set_np()
x = np.arange(4)
npx.save('x-file', x)
.. raw:: html
.. raw:: html
.. code:: python
import torch
from torch import nn
from torch.nn import functional as F
x = torch.arange(4)
torch.save(x, 'x-file')
.. raw:: html
.. raw:: html
.. code:: python
import numpy as np
import tensorflow as tf
x = tf.range(4)
np.save("x-file.npy", x)
.. raw:: html
.. raw:: html
Agora podemos ler os dados do arquivo armazenado de volta na memória.
.. raw:: html
.. raw:: html
.. code:: python
x2 = npx.load('x-file')
x2
.. parsed-literal::
:class: output
[array([0., 1., 2., 3.])]
.. raw:: html
.. raw:: html
.. code:: python
x2 = torch.load("x-file")
x2
.. parsed-literal::
:class: output
tensor([0, 1, 2, 3])
.. raw:: html
.. raw:: html
.. code:: python
x2 = np.load('x-file.npy', allow_pickle=True)
x2
.. parsed-literal::
:class: output
array([0, 1, 2, 3], dtype=int32)
.. raw:: html
.. raw:: html
Podemos armazenar uma lista de tensores e lê-los de volta na memória.
.. raw:: html
.. raw:: html
.. code:: python
y = np.zeros(4)
npx.save('x-files', [x, y])
x2, y2 = npx.load('x-files')
(x2, y2)
.. parsed-literal::
:class: output
(array([0., 1., 2., 3.]), array([0., 0., 0., 0.]))
.. raw:: html
.. raw:: html
.. code:: python
y = torch.zeros(4)
torch.save([x, y],'x-files')
x2, y2 = torch.load('x-files')
(x2, y2)
.. parsed-literal::
:class: output
(tensor([0, 1, 2, 3]), tensor([0., 0., 0., 0.]))
.. raw:: html
.. raw:: html
.. code:: python
y = tf.zeros(4)
np.save('xy-files.npy', [x, y])
x2, y2 = np.load('xy-files.npy', allow_pickle=True)
(x2, y2)
.. parsed-literal::
:class: output
(array([0., 1., 2., 3.]), array([0., 0., 0., 0.]))
.. raw:: html
.. raw:: html
Podemos até escrever e ler um dicionário que mapeia de *strings* a
tensores. Isso é conveniente quando queremos ler ou escrever todos os
pesos em um modelo.
.. raw:: html
.. raw:: html
.. code:: python
mydict = {'x': x, 'y': y}
npx.save('mydict', mydict)
mydict2 = npx.load('mydict')
mydict2
.. parsed-literal::
:class: output
{'x': array([0., 1., 2., 3.]), 'y': array([0., 0., 0., 0.])}
.. raw:: html
.. raw:: html
.. code:: python
mydict = {'x': x, 'y': y}
torch.save(mydict, 'mydict')
mydict2 = torch.load('mydict')
mydict2
.. parsed-literal::
:class: output
{'x': tensor([0, 1, 2, 3]), 'y': tensor([0., 0., 0., 0.])}
.. raw:: html
.. raw:: html
.. code:: python
mydict = {'x': x, 'y': y}
np.save('mydict.npy', mydict)
mydict2 = np.load('mydict.npy', allow_pickle=True)
mydict2
.. parsed-literal::
:class: output
array({'x': , 'y': },
dtype=object)
.. raw:: html
.. raw:: html
Carregando e Salvando Parâmetros de Modelos
-------------------------------------------
Salvar vetores de peso individuais (ou outros tensores) é útil, mas fica
muito tedioso se quisermos salvar (e depois carregar) um modelo inteiro.
Afinal, podemos ter centenas de grupos de parâmetros espalhados por toda
parte. Por esta razão, a estrutura de *Deep Learning* fornece
funcionalidades integradas para carregar e salvar redes inteiras. Um
detalhe importante a notar é que este salva o modelo *parâmetros* e não
o modelo inteiro. Por exemplo, se tivermos um MLP de 3 camadas,
precisamos especificar a arquitetura separadamente. A razão para isso é
que os próprios modelos podem conter código arbitrário, portanto, eles
não podem ser serializados naturalmente. Assim, para restabelecer um
modelo, precisamos para gerar a arquitetura em código e carregue os
parâmetros do disco. Vamos começar com nosso MLP familiar.
.. raw:: html
.. raw:: html
.. code:: python
class MLP(nn.Block):
def __init__(self, **kwargs):
super(MLP, self).__init__(**kwargs)
self.hidden = nn.Dense(256, activation='relu')
self.output = nn.Dense(10)
def forward(self, x):
return self.output(self.hidden(x))
net = MLP()
net.initialize()
X = np.random.uniform(size=(2, 20))
Y = net(X)
.. raw:: html
.. raw:: html
.. code:: python
class MLP(nn.Module):
def __init__(self):
super().__init__()
self.hidden = nn.Linear(20, 256)
self.output = nn.Linear(256, 10)
def forward(self, x):
return self.output(F.relu(self.hidden(x)))
net = MLP()
X = torch.randn(size=(2, 20))
Y = net(X)
.. raw:: html
.. raw:: html
.. code:: python
class MLP(tf.keras.Model):
def __init__(self):
super().__init__()
self.flatten = tf.keras.layers.Flatten()
self.hidden = tf.keras.layers.Dense(units=256, activation=tf.nn.relu)
self.out = tf.keras.layers.Dense(units=10)
def call(self, inputs):
x = self.flatten(inputs)
x = self.hidden(x)
return self.out(x)
net = MLP()
X = tf.random.uniform((2, 20))
Y = net(X)
.. raw:: html
.. raw:: html
A seguir, armazenamos os parâmetros do modelo como um arquivo com o nome
“mlp.params”.
.. raw:: html
.. raw:: html
.. code:: python
net.save_parameters('mlp.params')
.. raw:: html
.. raw:: html
.. code:: python
torch.save(net.state_dict(), 'mlp.params')
.. raw:: html
.. raw:: html
.. code:: python
net.save_weights('mlp.params')
.. raw:: html
.. raw:: html
Para recuperar o modelo, instanciamos um clone do modelo MLP original.
Em vez de inicializar aleatoriamente os parâmetros do modelo, lemos os
parâmetros armazenados no arquivo diretamente.
.. raw:: html
.. raw:: html
.. code:: python
clone = MLP()
clone.load_parameters('mlp.params')
.. raw:: html
.. raw:: html
.. code:: python
clone = MLP()
clone.load_state_dict(torch.load("mlp.params"))
clone.eval()
.. parsed-literal::
:class: output
MLP(
(hidden): Linear(in_features=20, out_features=256, bias=True)
(output): Linear(in_features=256, out_features=10, bias=True)
)
.. raw:: html
.. raw:: html
.. code:: python
clone = MLP()
clone.load_weights("mlp.params")
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Uma vez que ambas as instâncias têm os mesmos parâmetros de modelo, o
resultado computacional da mesma entrada ``X`` deve ser o mesmo.
Deixe-nos verificar isso.
.. raw:: html
.. raw:: html
.. code:: python
Y_clone = clone(X)
Y_clone == Y
.. parsed-literal::
:class: output
array([[ True, True, True, True, True, True, True, True, True,
True],
[ True, True, True, True, True, True, True, True, True,
True]])
.. raw:: html
.. raw:: html
.. code:: python
Y_clone = clone(X)
Y_clone == Y
.. parsed-literal::
:class: output
tensor([[True, True, True, True, True, True, True, True, True, True],
[True, True, True, True, True, True, True, True, True, True]])
.. raw:: html
.. raw:: html
.. code:: python
Y_clone = clone(X)
Y_clone == Y
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Sumário
-------
- As funções ``save`` e ``load`` podem ser usadas para executar E/S de
arquivo para objetos tensores.
- Podemos salvar e carregar todos os conjuntos de parâmetros de uma
rede por meio de um dicionário de parâmetros.
- Salvar a arquitetura deve ser feito em código e não em parâmetros.
Exercícios
----------
1. Mesmo se não houver necessidade de implantar modelos treinados em um
dispositivo diferente, quais são os benefícios práticos de armazenar
parâmetros de modelo?
2. Suponha que desejamos reutilizar apenas partes de uma rede para serem
incorporadas a uma rede de arquitetura diferente. Como você usaria,
digamos, as duas primeiras camadas de uma rede anterior em uma nova
rede?
3. Como você salvaria a arquitetura e os parâmetros da rede? Que
restrições você imporia à arquitetura?
.. raw:: html
.. raw:: html
`Discussão `__
.. raw:: html
.. raw:: html
`Discussão `__
.. raw:: html
.. raw:: html
`Discussão `__
.. raw:: html
.. raw:: html
.. raw:: html