.. _sec_linear_scratch:
Linear Regression Implementation from Scratch
=============================================
Agora que você entende as principais ideias por trás da regressão
linear, podemos começar a trabalhar por meio de uma implementação
prática no código. Nesta seção, vamos implementar todo o método do zero,
incluindo o pipeline de dados, o modelo, a função de perda e o
otimizador de descida gradiente estocástico do minibatch. Embora as
estruturas modernas de *deep learning* possam automatizar quase todo
esse trabalho, implementar coisas do zero é a única maneira para ter
certeza de que você realmente sabe o que está fazendo. Além disso,
quando chega a hora de personalizar modelos, definindo nossas próprias
camadas ou funções de perda, entender como as coisas funcionam nos
bastidores será útil. Nesta seção, contaremos apenas com tensores e
diferenciação automática. Posteriormente, apresentaremos uma
implementação mais concisa, aproveitando sinos e assobios de
*frameworks* de *deep learning*.
.. raw:: html
.. raw:: html
.. code:: python
%matplotlib inline
import random
from mxnet import autograd, np, npx
from d2l import mxnet as d2l
npx.set_np()
.. raw:: html
.. raw:: html
.. code:: python
%matplotlib inline
import random
import torch
from d2l import torch as d2l
.. raw:: html
.. raw:: html
.. code:: python
%matplotlib inline
import random
import tensorflow as tf
from d2l import tensorflow as d2l
.. raw:: html
.. raw:: html
Gerando o Dataset
-----------------
Para manter as coisas simples, iremos construir um conjunto de dados
artificial de acordo com um modelo linear com ruído aditivo. Nossa
tarefa será recuperar os parâmetros deste modelo usando o conjunto
finito de exemplos contidos em nosso conjunto de dados. Manteremos os
dados em baixa dimensão para que possamos visualizá-los facilmente. No
seguinte *snippet* de código, geramos um conjunto de dados contendo 1000
exemplos, cada um consistindo em 2 *features* amostrado a partir de uma
distribuição normal padrão. Assim, nosso conjunto de dados sintético
será uma matriz :math:`\mathbf{X}\in\mathbb{R}^{1000\times 2}`.
Os verdadeiros parâmetros que geram nosso conjunto de dados serão
:math:`\mathbf{w} = [2, -3,4]^\top` e :math:`b = 4,2`, e nossos rótulos
sintéticos serão atribuídos de acordo ao seguinte modelo linear com o
termo de ruído :math:`\epsilon`:
.. math:: \mathbf{y}= \mathbf{X} \mathbf{w} + b + \mathbf\epsilon.
Você pode pensar em :math:`\epsilon` como um potencial de captura erros
de medição nos recursos e rótulos. Vamos assumir que as premissas padrão
são válidas e, portanto, que :math:`\epsilon` obedece a uma distribuição
normal com média 0. Para tornar nosso problema mais fácil, definiremos
seu desvio padrão em 0,01. O código a seguir gera nosso conjunto de
dados sintético.
.. raw:: html
.. raw:: html
.. code:: python
def synthetic_data(w, b, num_examples): #@save
"""Generate y = Xw + b + noise."""
X = np.random.normal(0, 1, (num_examples, len(w)))
y = np.dot(X, w) + b
y += np.random.normal(0, 0.01, y.shape)
return X, y.reshape((-1, 1))
true_w = np.array([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
.. raw:: html
.. raw:: html
.. code:: python
def synthetic_data(w, b, num_examples): #@save
"""Generate y = Xw + b + noise."""
X = torch.normal(0, 1, (num_examples, len(w)))
y = torch.matmul(X, w) + b
y += torch.normal(0, 0.01, y.shape)
return X, y.reshape((-1, 1))
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
.. raw:: html
.. raw:: html
.. code:: python
def synthetic_data(w, b, num_examples): #@save
"""Generate y = Xw + b + noise."""
X = tf.zeros((num_examples, w.shape[0]))
X += tf.random.normal(shape=X.shape)
y = tf.matmul(X, tf.reshape(w, (-1, 1))) + b
y += tf.random.normal(shape=y.shape, stddev=0.01)
y = tf.reshape(y, (-1, 1))
return X, y
true_w = tf.constant([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
.. raw:: html
.. raw:: html
Observe que cada linha em ``features`` consiste em um exemplo de dados
bidimensionais e que cada linha em ``labels`` consiste em um valor de
rótulo unidimensional (um escalar).
.. raw:: html
.. raw:: html
.. code:: python
print('features:', features[0],'\nlabel:', labels[0])
.. parsed-literal::
:class: output
features: [2.2122064 1.1630787]
label: [4.662078]
.. raw:: html
.. raw:: html
.. code:: python
print('features:', features[0],'\nlabel:', labels[0])
.. parsed-literal::
:class: output
features: tensor([-0.9563, 1.2447])
label: tensor([-1.9621])
.. raw:: html
.. raw:: html
.. code:: python
print('features:', features[0],'\nlabel:', labels[0])
.. parsed-literal::
:class: output
features: tf.Tensor([1.1301318 0.14684609], shape=(2,), dtype=float32)
label: tf.Tensor([5.960015], shape=(1,), dtype=float32)
.. raw:: html
.. raw:: html
Ao gerar um gráfico de dispersão usando o segundo recurso
``features [:, 1]`` e ``labels``, podemos observar claramente a
correlação linear entre os dois.
.. raw:: html
.. raw:: html
.. code:: python
d2l.set_figsize()
# The semicolon is for displaying the plot only
d2l.plt.scatter(features[:, (1)].asnumpy(), labels.asnumpy(), 1);
.. figure:: output_linear-regression-scratch_58de05_39_0.svg
.. raw:: html
.. raw:: html
.. code:: python
d2l.set_figsize()
# The semicolon is for displaying the plot only
d2l.plt.scatter(features[:, (1)].detach().numpy(), labels.detach().numpy(), 1);
.. figure:: output_linear-regression-scratch_58de05_42_0.svg
.. raw:: html
.. raw:: html
.. code:: python
d2l.set_figsize()
# The semicolon is for displaying the plot only
d2l.plt.scatter(features[:, (1)].numpy(), labels.numpy(), 1);
.. figure:: output_linear-regression-scratch_58de05_45_0.svg
.. raw:: html
.. raw:: html
Lendo o *Dataset*
-----------------
Lembre-se de que os modelos de treinamento consistem em fazer várias
passagens sobre o *dataset*, pegando um *minibatch* de exemplos por vez,
e usando-los para atualizar nosso modelo. Uma vez que este processo é
tão fundamental para treinar algoritmos de a\ *machine learning*, vale a
pena definir uma função de utilidade para embaralhar o conjunto de dados
e acessá-lo em *minibatches*.
No código a seguir, nós definimos a função ``data_iter`` para demonstrar
uma possível implementação dessa funcionalidade. A função leva um
tamanho de amostra, uma matriz de *features*, e um vetor de *labels*,
produzindo *minibatches* do tamanho ``batch_size``. Cada *minibatch*
consiste em uma tupla de *features* e *labels*.
.. raw:: html
.. raw:: html
.. code:: python
def data_iter(batch_size, features, labels):
num_examples = len(features)
indices = list(range(num_examples))
# The examples are read at random, in no particular order
random.shuffle(indices)
for i in range(0, num_examples, batch_size):
batch_indices = np.array(
indices[i: min(i + batch_size, num_examples)])
yield features[batch_indices], labels[batch_indices]
.. raw:: html
.. raw:: html
.. code:: python
def data_iter(batch_size, features, labels):
num_examples = len(features)
indices = list(range(num_examples))
# The examples are read at random, in no particular order
random.shuffle(indices)
for i in range(0, num_examples, batch_size):
batch_indices = torch.tensor(
indices[i: min(i + batch_size, num_examples)])
yield features[batch_indices], labels[batch_indices]
.. raw:: html
.. raw:: html
.. code:: python
def data_iter(batch_size, features, labels):
num_examples = len(features)
indices = list(range(num_examples))
# The examples are read at random, in no particular order
random.shuffle(indices)
for i in range(0, num_examples, batch_size):
j = tf.constant(indices[i: min(i + batch_size, num_examples)])
yield tf.gather(features, j), tf.gather(labels, j)
.. raw:: html
.. raw:: html
Em geral, queremos usar *minibatches* de tamanhos razoáveis para
aproveitar as vantagens do hardware da GPU, que se destaca em operações
de paralelização. Porque cada exemplo pode ser alimentado por meio de
nossos modelos em paralelo e o gradiente da função de perda para cada
exemplo também pode ser tomado em paralelo, GPUs nos permitem processar
centenas de exemplos em pouco mais tempo do que pode demorar para
processar apenas um único exemplo.
Para construir alguma intuição, vamos ler e imprimir o primeiro pequeno
lote de exemplos de dados. A forma dos recursos em cada *minibatch* nos
diz o tamanho do *minibatch* e o número de recursos de entrada. Da mesma
forma, nosso *minibatch* de rótulos terá uma forma dada por
``batch_size``.
.. raw:: html
.. raw:: html
.. code:: python
batch_size = 10
for X, y in data_iter(batch_size, features, labels):
print(X, '\n', y)
break
.. parsed-literal::
:class: output
[[ 1.0702589 0.22332872]
[ 0.576972 0.8087885 ]
[ 0.10064353 0.64281934]
[ 1.2020247 -2.1224012 ]
[-1.0674467 -0.34258512]
[ 0.20714448 1.0840217 ]
[ 0.90336937 -0.38090217]
[ 0.8332984 1.8107505 ]
[ 1.8917114 -1.1688148 ]
[ 0.59860843 -3.0636313 ]]
[[ 5.5869737 ]
[ 2.5922568 ]
[ 2.215121 ]
[13.813773 ]
[ 3.2241936 ]
[ 0.91169757]
[ 7.301087 ]
[-0.2817729 ]
[11.958657 ]
[15.81957 ]]
.. raw:: html
.. raw:: html
.. code:: python
batch_size = 10
for X, y in data_iter(batch_size, features, labels):
print(X, '\n', y)
break
.. parsed-literal::
:class: output
tensor([[-0.0649, 0.4390],
[-0.2518, -0.4019],
[-0.1489, 0.2960],
[ 1.6701, -0.8914],
[ 0.6946, 0.2719],
[-1.4623, 0.5890],
[ 0.1270, 0.7019],
[-1.2410, 0.1549],
[-0.3620, -0.1373],
[-0.2483, -1.6446]])
tensor([[ 2.5737],
[ 5.0419],
[ 2.8981],
[10.5841],
[ 4.6693],
[-0.7264],
[ 2.0609],
[ 1.1768],
[ 3.9539],
[ 9.2774]])
.. raw:: html
.. raw:: html
.. code:: python
batch_size = 10
for X, y in data_iter(batch_size, features, labels):
print(X, '\n', y)
break
.. parsed-literal::
:class: output
tf.Tensor(
[[-0.5730527 0.7819388 ]
[-1.4290377 0.501779 ]
[-1.0716456 -0.3164782 ]
[-0.7258405 0.5719521 ]
[ 1.7161779 -0.6173083 ]
[-1.5024987 1.2860105 ]
[ 0.5211034 -1.16397 ]
[ 0.5002108 -0.70382583]
[ 0.2605837 1.0829461 ]
[ 0.4482001 -0.56022495]], shape=(10, 2), dtype=float32)
tf.Tensor(
[[ 0.38191256]
[-0.36548588]
[ 3.1524463 ]
[ 0.80933195]
[ 9.73811 ]
[-3.1788385 ]
[ 9.195568 ]
[ 7.5800247 ]
[ 1.0323184 ]
[ 7.0033584 ]], shape=(10, 1), dtype=float32)
.. raw:: html
.. raw:: html
Conforme executamos a iteração, obtemos *minibatches* distintos
sucessivamente até que todo o conjunto de dados se esgote (tente isto).
Embora a iteração implementada acima seja boa para fins didáticos, é
ineficiente de maneiras que podem nos colocar em apuros em problemas
reais. Por exemplo, requer que carreguemos todos os dados na memória e
que realizamos muitos acessos aleatórios à memória. Os iteradores
integrados implementados em uma estrutura de *deep learning* são
consideravelmente mais eficientes e podem lidar com dados armazenados em
arquivos e dados alimentados por meio de fluxos de dados.
Initializing Model Parameters
-----------------------------
Antes de começarmos a otimizar os parâmetros do nosso modelo por
gradiente descendente estocástico de *minibatch*, precisamos ter alguns
parâmetros em primeiro lugar. No código a seguir, inicializamos os pesos
por amostragem números aleatórios de uma distribuição normal com média 0
e um desvio padrão de 0,01, e definindo a tendência para 0.
.. raw:: html
.. raw:: html
.. code:: python
w = np.random.normal(0, 0.01, (2, 1))
b = np.zeros(1)
w.attach_grad()
b.attach_grad()
.. raw:: html
.. raw:: html
.. code:: python
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
.. raw:: html
.. raw:: html
.. code:: python
w = tf.Variable(tf.random.normal(shape=(2, 1), mean=0, stddev=0.01),
trainable=True)
b = tf.Variable(tf.zeros(1), trainable=True)
.. raw:: html
.. raw:: html
Depois de inicializar nossos parâmetros, nossa próxima tarefa é
atualizá-los até eles se ajustam aos nossos dados suficientemente bem.
Cada atualização requer a obtenção do gradiente da nossa função de perda
no que diz respeito aos parâmetros. Dado este gradiente, podemos
atualizar cada parâmetro na direção que pode reduzir a perda.
Uma vez que ninguém quer calcular gradientes explicitamente (isso é
entediante e sujeito a erros), usamos diferenciação automática, conforme
apresentado em :numref:`sec_autograd`, para calcular o gradiente.
Definindo o Modelo
------------------
Em seguida, devemos definir nosso modelo, relacionando suas entradas e
parâmetros com suas saídas. Lembre-se de que, para calcular a saída do
modelo linear, simplesmente pegamos o produto escalar vetor-matriz dos
recursos de entrada :math:`\mathbf{X}` e os pesos do modelo
:math:`\mathbf{w}`, e adicione o *offset* :math:`b` a cada exemplo.
Observe que abaixo de :math:`\mathbf{Xw}` está um vetor e :math:`b` é um
escalar. Lembre-se do mecanismo de transmissão conforme descrito em
:numref:`subsec_broadcasting`. Quando adicionamos um vetor e um
escalar, o escalar é adicionado a cada componente do vetor.
.. raw:: html
.. raw:: html
.. code:: python
def linreg(X, w, b): #@save
"""The linear regression model."""
return np.dot(X, w) + b
.. raw:: html
.. raw:: html
.. code:: python
def linreg(X, w, b): #@save
"""The linear regression model."""
return torch.matmul(X, w) + b
.. raw:: html
.. raw:: html
.. code:: python
def linreg(X, w, b): #@save
"""The linear regression model."""
return tf.matmul(X, w) + b
.. raw:: html
.. raw:: html
Definindo a Função de Perda
---------------------------
Uma vez que atualizar nosso modelo requer tomar o gradiente de nossa
função de perda, devemos definir a função de perda primeiro. Aqui vamos
usar a função de perda quadrada conforme descrito em
:numref:`sec_linear_regression`. Na implementação, precisamos
transformar o valor verdadeiro ``y`` na forma do valor previsto
``y_hat``. O resultado retornado pela seguinte função também terá a
mesma forma de ``y_hat``.
.. raw:: html
.. raw:: html
.. code:: python
def squared_loss(y_hat, y): #@save
"""Squared loss."""
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
.. raw:: html
.. raw:: html
.. code:: python
def squared_loss(y_hat, y): #@save
"""Squared loss."""
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
.. raw:: html
.. raw:: html
.. code:: python
def squared_loss(y_hat, y): #@save
"""Squared loss."""
return (y_hat - tf.reshape(y, y_hat.shape)) ** 2 / 2
.. raw:: html
.. raw:: html
Definindo o Algoritmo de Otimização
-----------------------------------
Como discutimos em :numref:`sec_linear_regression`, a regressão linear
tem uma solução de forma fechada. No entanto, este não é um livro sobre
regressão linear: é um livro sobre *deep learning*. Uma vez que nenhum
dos outros modelos que este livro apresenta pode ser resolvido
analiticamente, aproveitaremos esta oportunidade para apresentar seu
primeiro exemplo de trabalho de gradiente descendente estocástico de
*minibatch*.
Em cada etapa, usando um *minibatch* retirado aleatoriamente de nosso
conjunto de dados, vamos estimar o gradiente da perda em relação aos
nossos parâmetros. A seguir, vamos atualizar nossos parâmetros na
direção que pode reduzir a perda. O código a seguir aplica a atualização
da descida gradiente estocástica do *minibatch*, dado um conjunto de
parâmetros, uma taxa de aprendizagem e um tamanho de *batch*. O tamanho
da etapa de atualização é determinado pela taxa de aprendizagem ``lr``.
Como nossa perda é calculada como a soma do *minibatch* de exemplos,
normalizamos o tamanho do nosso passo pelo tamanho do *batch*
(``batch_size``), de modo que a magnitude de um tamanho de passo típico
não depende muito de nossa escolha do tamanho do lote.
.. raw:: html
.. raw:: html
.. code:: python
def sgd(params, lr, batch_size): #@save
"""Minibatch stochastic gradient descent."""
for param in params:
param[:] = param - lr * param.grad / batch_size
.. raw:: html
.. raw:: html
.. code:: python
def sgd(params, lr, batch_size): #@save
"""Minibatch stochastic gradient descent."""
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
.. raw:: html
.. raw:: html
.. code:: python
def sgd(params, grads, lr, batch_size): #@save
"""Minibatch stochastic gradient descent."""
for param, grad in zip(params, grads):
param.assign_sub(lr*grad/batch_size)
.. raw:: html
.. raw:: html
Treinamento
-----------
Agora que temos todas as peças no lugar, estamos prontos para
implementar o *loop* de treinamento principal. É crucial que você
entenda este código porque você verá loops de treinamento quase
idênticos repetidamente ao longo de sua carreira de *deep learning*.
Em cada iteração, pegaremos um *minibatch* de exemplos de treinamento, e
os passamos por nosso modelo para obter um conjunto de previsões. Depois
de calcular a perda, iniciamos a passagem para trás pela rede,
armazenando os gradientes em relação a cada parâmetro. Finalmente,
chamaremos o algoritmo de otimização de ``sgd`` para atualizar os
parâmetros do modelo.
Em resumo, vamos executar o seguinte loop:
- Inicializar parâmetros :math:`(\mathbf{w}, b)`
- Repetir até terminar
- Computar gradiente
:math:`\mathbf{g} \leftarrow \partial_{(\mathbf{w},b)} \frac{1}{|\mathcal{B}|} \sum_{i \in \mathcal{B}} l(\mathbf{x}^{(i)}, y^{(i)}, \mathbf{w}, b)`
- Atualizar parâmetros
:math:`(\mathbf{w}, b) \leftarrow (\mathbf{w}, b) - \eta \mathbf{g}`
Em cada *época*, iremos iterar por todo o conjunto de dados (usando a
função ``data_iter``) uma vez passando por todos os exemplos no conjunto
de dados de treinamento (assumindo que o número de exemplos seja
divisível pelo tamanho do lote). O número de épocas ``num_epochs`` e a
taxa de aprendizagem\ ``lr`` são hiperparâmetros, que definimos aqui
como 3 e 0,03, respectivamente. Infelizmente, definir hiperparâmetros é
complicado e requer alguns ajustes por tentativa e erro. Excluímos esses
detalhes por enquanto, mas os revisamos mais tarde em
:numref:`chap_optimization`.
.. raw:: html
.. raw:: html
.. code:: python
lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss
for epoch in range(num_epochs):
for X, y in data_iter(batch_size, features, labels):
with autograd.record():
l = loss(net(X, w, b), y) # Minibatch loss in `X` and `y`
# Because `l` has a shape (`batch_size`, 1) and is not a scalar
# variable, the elements in `l` are added together to obtain a new
# variable, on which gradients with respect to [`w`, `b`] are computed
l.backward()
sgd([w, b], lr, batch_size) # Update parameters using their gradient
train_l = loss(net(features, w, b), labels)
print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')
.. parsed-literal::
:class: output
[03:59:56] src/base.cc:49: GPU context requested, but no GPUs found.
epoch 1, loss 0.025171
epoch 2, loss 0.000088
epoch 3, loss 0.000051
.. raw:: html
.. raw:: html
.. code:: python
lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss
for epoch in range(num_epochs):
for X, y in data_iter(batch_size, features, labels):
l = loss(net(X, w, b), y) # Minibatch loss in `X` and `y`
# Compute gradient on `l` with respect to [`w`, `b`]
l.sum().backward()
sgd([w, b], lr, batch_size) # Update parameters using their gradient
with torch.no_grad():
train_l = loss(net(features, w, b), labels)
print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')
.. parsed-literal::
:class: output
epoch 1, loss 0.044391
epoch 2, loss 0.000181
epoch 3, loss 0.000046
.. raw:: html
.. raw:: html
.. code:: python
lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss
for epoch in range(num_epochs):
for X, y in data_iter(batch_size, features, labels):
with tf.GradientTape() as g:
l = loss(net(X, w, b), y) # Minibatch loss in `X` and `y`
# Compute gradient on l with respect to [`w`, `b`]
dw, db = g.gradient(l, [w, b])
# Update parameters using their gradient
sgd([w, b], [dw, db], lr, batch_size)
train_l = loss(net(features, w, b), labels)
print(f'epoch {epoch + 1}, loss {float(tf.reduce_mean(train_l)):f}')
.. parsed-literal::
:class: output
epoch 1, loss 0.034802
epoch 2, loss 0.000124
epoch 3, loss 0.000052
.. raw:: html
.. raw:: html
Neste caso, porque nós mesmos sintetizamos o conjunto de dados, sabemos
exatamente quais são os verdadeiros parâmetros. Assim, podemos avaliar
nosso sucesso no treinamento comparando os parâmetros verdadeiros com
aqueles que aprendemos através de nosso ciclo de treinamento. Na
verdade, eles acabam sendo muito próximos um do outro.
.. raw:: html
.. raw:: html
.. code:: python
print(f'error in estimating w: {true_w - w.reshape(true_w.shape)}')
print(f'error in estimating b: {true_b - b}')
.. parsed-literal::
:class: output
error in estimating w: [ 0.00044966 -0.00043631]
error in estimating b: [0.00071049]
.. raw:: html
.. raw:: html
.. code:: python
print(f'error in estimating w: {true_w - w.reshape(true_w.shape)}')
print(f'error in estimating b: {true_b - b}')
.. parsed-literal::
:class: output
error in estimating w: tensor([ 0.0006, -0.0003], grad_fn=)
error in estimating b: tensor([0.0008], grad_fn=)
.. raw:: html
.. raw:: html
.. code:: python
print(f'error in estimating w: {true_w - tf.reshape(w, true_w.shape)}')
print(f'error in estimating b: {true_b - b}')
.. parsed-literal::
:class: output
error in estimating w: [ 0.00061536 -0.00040913]
error in estimating b: [-0.00094461]
.. raw:: html
.. raw:: html
Observe que não devemos tomar isso como garantido que somos capazes de
recuperar os parâmetros perfeitamente. No entanto, no *machine
learning*, normalmente estamos menos preocupados com a recuperação de
verdadeiros parâmetros subjacentes, e mais preocupados com parâmetros
que levam a previsões altamente precisas. Felizmente, mesmo em problemas
de otimização difíceis, o gradiente descendente estocástico pode muitas
vezes encontrar soluções notavelmente boas, devido em parte ao fato de
que, para redes profundas, existem muitas configurações dos parâmetros
que levam a uma previsão altamente precisa.
Resumo
------
- Vimos como uma rede profunda pode ser implementada e otimizada do
zero, usando apenas tensores e diferenciação automática, sem a
necessidade de definir camadas ou otimizadores sofisticados.
- Esta seção apenas arranha a superfície do que é possível. Nas seções
a seguir, descreveremos modelos adicionais com base nos conceitos que
acabamos de apresentar e aprenderemos como implementá-los de forma
mais concisa.
Exercícios
----------
1. O que aconteceria se inicializássemos os pesos para zero. O algoritmo
ainda funcionaria?
2. Suponha que você seja `Georg Simon
Ohm `__ tentando inventar um
modelo entre tensão e corrente. Você poderia usar a diferenciação
automática para aprender os parâmetros do seu modelo?
3. Você pode usar a `Lei de
Planck `__ para
determinar a temperatura de um objeto usando densidade de energia
espectral?
4. Quais são os problemas que você pode encontrar se quiser calcular as
derivadas secundárias? Como você os consertaria?
5. Por que a função ``reshape`` é necessária na
função\ ``squared_loss``?
6. Experimente usar diferentes taxas de aprendizagem para descobrir a
rapidez com que o valor da função de perda diminui.
7. Se o número de exemplos não pode ser dividido pelo tamanho do lote, o
que acontece com o comportamento da função ``data_iter``?
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
.. raw:: html