.. _sec_softmax_scratch: Implementação da Regressão *Softmax* do Zero ============================================ Assim como implementamos a regressão linear do zero, acreditamos que regressão *softmax* é igualmente fundamental e você deve saber os detalhes sangrentos de como implementá-lo sozinho. Vamos trabalhar com o *dataset* Fashion-MNIST, recém-introduzido em :numref:`sec_fashion_mnist`, configurando um iterador de dados com *batch size* 256. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python from IPython import display from mxnet import autograd, gluon, np, npx from d2l import mxnet as d2l npx.set_np() batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size) .. raw:: html
.. raw:: html
.. code:: python import torch from IPython import display from d2l import torch as d2l batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size) .. raw:: html
.. raw:: html
.. code:: python import tensorflow as tf from IPython import display from d2l import tensorflow as d2l batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size) .. raw:: html
.. raw:: html
Inicializando os Parâmetros do Modelo ------------------------------------- Como em nosso exemplo de regressão linear, cada exemplo aqui será representado por um vetor de comprimento fixo. Cada exemplo no conjunto de dados bruto é uma imagem :math:`28 \times 28`. Nesta seção, vamos nivelar cada imagem, tratando-os como vetores de comprimento 784. No futuro, falaremos sobre estratégias mais sofisticadas para explorar a estrutura espacial em imagens, mas, por enquanto, tratamos cada localização de pixel como apenas outro recurso. Lembre-se de que na regressão *softmax*, temos tantas saídas quanto classes. Como nosso conjunto de dados tem 10 classes, nossa rede terá uma dimensão de saída de 10. Consequentemente, nossos pesos constituirão uma matriz :math:`784 \times 10` e os *bias* constituirão um vetor-linha :math:`1 \times 10`. Tal como acontece com a regressão linear, vamos inicializar nossos pesos ``W`` com ruído Gaussiano e nossos *bias* com o valor inicial 0. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python num_inputs = 784 num_outputs = 10 W = np.random.normal(0, 0.01, (num_inputs, num_outputs)) b = np.zeros(num_outputs) W.attach_grad() b.attach_grad() .. raw:: html
.. raw:: html
.. code:: python num_inputs = 784 num_outputs = 10 W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True) b = torch.zeros(num_outputs, requires_grad=True) .. raw:: html
.. raw:: html
.. code:: python num_inputs = 784 num_outputs = 10 W = tf.Variable(tf.random.normal(shape=(num_inputs, num_outputs), mean=0, stddev=0.01)) b = tf.Variable(tf.zeros(num_outputs)) .. raw:: html
.. raw:: html
Definindo a Operação do *Softmax* --------------------------------- Antes de implementar o modelo de regressão do *softmax*, vamos revisar brevemente como o operador de soma funciona ao longo de dimensões específicas em um tensor, conforme discutido em: numref :numref:`subseq_lin-alg-reduction` e :numref:`subseq_lin-alg-non-reduction`. Dada uma matriz ``X``, podemos somar todos os elementos (por padrão) ou apenas sobre elementos no mesmo eixo, ou seja, a mesma coluna (eixo 0) ou a mesma linha (eixo 1). Observe que se ``X`` é um tensor com forma (2, 3) e somamos as colunas, o resultado será um vetor com forma (3,). Ao invocar o operador de soma, podemos especificar para manter o número de eixos no tensor original, em vez de reduzir a dimensão que resumimos. Isso resultará em um tensor bidimensional com forma (1, 3). .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python X = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]) X.sum(0, keepdims=True), X.sum(1, keepdims=True) .. parsed-literal:: :class: output (array([[5., 7., 9.]]), array([[ 6.], [15.]])) .. raw:: html
.. raw:: html
.. code:: python X = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]) X.sum(0, keepdim=True), X.sum(1, keepdim=True) .. parsed-literal:: :class: output (tensor([[5., 7., 9.]]), tensor([[ 6.], [15.]])) .. raw:: html
.. raw:: html
.. code:: python X = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]) tf.reduce_sum(X, 0, keepdims=True), tf.reduce_sum(X, 1, keepdims=True) .. parsed-literal:: :class: output (, ) .. raw:: html
.. raw:: html
Agora estamos prontos para implementar a operação do *softmax* . Lembre-se de que o *softmax* consiste em três etapas: i) exponenciamos cada termo (usando ``exp``); ii) somamos cada linha (temos uma linha por exemplo no lote) para obter a constante de normalização para cada exemplo; iii) dividimos cada linha por sua constante de normalização, garantindo que o resultado seja 1. Antes de olhar para o código, vamos lembrar como isso parece, expresso como uma equação: .. math:: \mathrm{softmax}(\mathbf{X})_{ij} = \frac{\exp(\mathbf{X}_{ij})}{\sum_k \exp(\mathbf{X}_{ik})}. O denominador, ou constante de normalização, às vezes também é chamada de *função de partição* (e seu logaritmo é chamado de função de partição de log). As origens desse nome estão em `física estatística `__ onde uma equação relacionada modela a distribuição sobre um conjunto de partículas. .. raw:: html
.. raw:: html
.. code:: python def softmax(X): X_exp = np.exp(X) partition = X_exp.sum(1, keepdims=True) return X_exp / partition # The broadcasting mechanism is applied here .. raw:: html
.. raw:: html
.. code:: python def softmax(X): X_exp = torch.exp(X) partition = X_exp.sum(1, keepdim=True) return X_exp / partition # The broadcasting mechanism is applied here .. raw:: html
.. raw:: html
.. code:: python def softmax(X): X_exp = tf.exp(X) partition = tf.reduce_sum(X_exp, 1, keepdims=True) return X_exp / partition # The broadcasting mechanism is applied here .. raw:: html
.. raw:: html
Como você pode ver, para qualquer entrada aleatória, transformamos cada elemento em um número não negativo. Além disso, cada linha soma 1, como é necessário para uma probabilidade. .. raw:: html
.. raw:: html
.. code:: python X = np.random.normal(0, 1, (2, 5)) X_prob = softmax(X) X_prob, X_prob.sum(1) .. parsed-literal:: :class: output (array([[0.22376052, 0.06659239, 0.06583703, 0.29964197, 0.3441681 ], [0.63209665, 0.03179282, 0.194987 , 0.09209415, 0.04902935]]), array([1. , 0.99999994])) .. raw:: html
.. raw:: html
.. code:: python X = torch.normal(0, 1, (2, 5)) X_prob = softmax(X) X_prob, X_prob.sum(1) .. parsed-literal:: :class: output (tensor([[0.2947, 0.2476, 0.0139, 0.3211, 0.1228], [0.0861, 0.2708, 0.0988, 0.0651, 0.4792]]), tensor([1.0000, 1.0000])) .. raw:: html
.. raw:: html
.. code:: python X = tf.random.normal((2, 5), 0, 1) X_prob = softmax(X) X_prob, tf.reduce_sum(X_prob, 1) .. parsed-literal:: :class: output (, ) .. raw:: html
.. raw:: html
Observe que embora pareça correto matematicamente, fomos um pouco desleixados em nossa implementação porque falhamos em tomar precauções contra estouro numérico ou estouro negativo devido a elementos grandes ou muito pequenos da matriz. Definindo o Modelo ------------------ Agora que definimos a operação do *softmax*, podemos implementar o modelo de regressão softmax. O código a seguir define como a entrada é mapeada para a saída por meio da rede. Observe que achatamos cada imagem original no lote em um vetor usando a função ``reshape`` antes de passar os dados pelo nosso modelo. .. raw:: html
.. raw:: html
.. code:: python def net(X): return softmax(np.dot(X.reshape((-1, W.shape[0])), W) + b) .. raw:: html
.. raw:: html
.. code:: python def net(X): return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b) .. raw:: html
.. raw:: html
.. code:: python def net(X): return softmax(tf.matmul(tf.reshape(X, (-1, W.shape[0])), W) + b) .. raw:: html
.. raw:: html
Definindo a Função de Perda --------------------------- Em seguida, precisamos implementar a função de perda de entropia cruzada, conforme apresentado em :numref:`sec_softmax`. Esta pode ser a função de perda mais comum em todo o *deep learning* porque, no momento, os problemas de classificação superam em muito os problemas de regressão. Lembre-se de que a entropia cruzada leva a *log-likelihood* negativa da probabilidade prevista atribuída ao rótulo verdadeiro. Em vez de iterar as previsões com um *loop for* Python (que tende a ser ineficiente), podemos escolher todos os elementos por um único operador. Abaixo, nós criamos dados de amostra ``y_hat`` com 2 exemplos de probabilidades previstas em 3 classes e seus rótulos correspondentes ``y``. Com ``y`` sabemos que no primeiro exemplo a primeira classe é a previsão correta e no segundo exemplo, a terceira classe é a verdade fundamental. Usando ``y`` como os índices das probabilidades em\ ``y_hat``, escolhemos a probabilidade da primeira classe no primeiro exemplo e a probabilidade da terceira classe no segundo exemplo. .. raw:: html
.. raw:: html
.. code:: python y = np.array([0, 2]) y_hat = np.array([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]]) y_hat[[0, 1], y] .. parsed-literal:: :class: output array([0.1, 0.5]) .. raw:: html
.. raw:: html
.. code:: python y = torch.tensor([0, 2]) y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]]) y_hat[[0, 1], y] .. parsed-literal:: :class: output tensor([0.1000, 0.5000]) .. raw:: html
.. raw:: html
.. code:: python y_hat = tf.constant([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]]) y = tf.constant([0, 2]) tf.boolean_mask(y_hat, tf.one_hot(y, depth=y_hat.shape[-1])) .. parsed-literal:: :class: output .. raw:: html
.. raw:: html
Agora podemos implementar a função de perda de entropia cruzada de forma eficiente com apenas uma linha de código. .. raw:: html
.. raw:: html
.. code:: python def cross_entropy(y_hat, y): return - np.log(y_hat[range(len(y_hat)), y]) cross_entropy(y_hat, y) .. parsed-literal:: :class: output array([2.3025851, 0.6931472]) .. raw:: html
.. raw:: html
.. code:: python def cross_entropy(y_hat, y): return - torch.log(y_hat[range(len(y_hat)), y]) cross_entropy(y_hat, y) .. parsed-literal:: :class: output tensor([2.3026, 0.6931]) .. raw:: html
.. raw:: html
.. code:: python def cross_entropy(y_hat, y): return -tf.math.log(tf.boolean_mask( y_hat, tf.one_hot(y, depth=y_hat.shape[-1]))) cross_entropy(y_hat, y) .. parsed-literal:: :class: output .. raw:: html
.. raw:: html
Exatidão da Classificação ------------------------- Dada a distribuição de probabilidade prevista ``y_hat``, normalmente escolhemos a classe com a maior probabilidade prevista sempre que a previsão que devemos produzir é difícil. Na verdade, muitos aplicativos exigem que façamos uma escolha. O Gmail deve categorizar um e-mail em “Principal”, “Social”, “Atualizações” ou “Fóruns”. Pode estimar probabilidades internamente, mas no final do dia ele tem que escolher uma das classes. Quando as previsões são consistentes com a classe de *label* ``y``, elas estão corretas. A precisão da classificação é a fração de todas as previsões corretas. Embora possa ser difícil otimizar a precisão diretamente (não é diferenciável), muitas vezes é a medida de desempenho que mais nos preocupa, e quase sempre o relataremos ao treinar classificadores. Para calcular a precisão, fazemos o seguinte. Primeiro, se ``y_hat`` é uma matriz, presumimos que a segunda dimensão armazena pontuações de predição para cada classe. Usamos ``argmax`` para obter a classe prevista pelo índice para a maior entrada em cada linha. Em seguida, comparamos a classe prevista com a verdade fundamental ``y`` elemento a elemento. Uma vez que o operador de igualdade ``==`` é sensível aos tipos de dados, convertemos o tipo de dados de ``y_hat`` para corresponder ao de\ ``y``. O resultado é um tensor contendo entradas de 0 (falso) e 1 (verdadeiro). Tirar a soma resulta no número de previsões corretas. .. raw:: html
.. raw:: html
.. code:: python def accuracy(y_hat, y): #@save """Compute the number of correct predictions.""" if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: y_hat = y_hat.argmax(axis=1) cmp = y_hat.astype(y.dtype) == y return float(cmp.astype(y.dtype).sum()) .. raw:: html
.. raw:: html
.. code:: python def accuracy(y_hat, y): #@save """Compute the number of correct predictions.""" if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: y_hat = y_hat.argmax(axis=1) cmp = y_hat.type(y.dtype) == y return float(cmp.type(y.dtype).sum()) .. raw:: html
.. raw:: html
.. code:: python def accuracy(y_hat, y): #@save """Compute the number of correct predictions.""" if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: y_hat = tf.argmax(y_hat, axis=1) cmp = tf.cast(y_hat, y.dtype) == y return float(tf.reduce_sum(tf.cast(cmp, y.dtype))) .. raw:: html
.. raw:: html
Continuaremos a usar as variáveis ``y_hat`` e\ ``y`` definidas antes como as distribuições de probabilidade e *labels* previstos, respectivamente. Podemos ver que a classe prevista no primeiro exemplo é 2 (o maior elemento da linha é 0,6 com o índice 2), que é inconsistente com o rótulo real, 0. A classe prevista do segundo exemplo é 2 (o maior elemento da linha é 0,5 com o índice de 2), que é consistente com o rótulo real, 2. Portanto, a taxa de precisão da classificação para esses dois exemplos é 0,5. .. raw:: html
.. raw:: html
.. code:: python accuracy(y_hat, y) / len(y) .. parsed-literal:: :class: output 0.5 .. raw:: html
.. raw:: html
.. code:: python accuracy(y_hat, y) / len(y) .. parsed-literal:: :class: output 0.5 .. raw:: html
.. raw:: html
.. code:: python accuracy(y_hat, y) / len(y) .. parsed-literal:: :class: output 0.5 .. raw:: html
.. raw:: html
Da mesma forma, podemos avaliar a precisão da ``rede`` de qualquer modelo em um conjunto de dados que é acessado por meio do iterador de dados ``data_iter``. .. raw:: html
.. raw:: html
.. code:: python def evaluate_accuracy(net, data_iter): #@save """Compute the accuracy for a model on a dataset.""" metric = Accumulator(2) # No. of correct predictions, no. of predictions for X, y in data_iter: metric.add(accuracy(net(X), y), d2l.size(y)) return metric[0] / metric[1] .. raw:: html
.. raw:: html
.. code:: python def evaluate_accuracy(net, data_iter): #@save """Compute the accuracy for a model on a dataset.""" if isinstance(net, torch.nn.Module): net.eval() # Set the model to evaluation mode metric = Accumulator(2) # No. of correct predictions, no. of predictions for X, y in data_iter: metric.add(accuracy(net(X), y), y.numel()) return metric[0] / metric[1] .. raw:: html
.. raw:: html
.. code:: python def evaluate_accuracy(net, data_iter): #@save """Compute the accuracy for a model on a dataset.""" metric = Accumulator(2) # No. of correct predictions, no. of predictions for X, y in data_iter: metric.add(accuracy(net(X), y), d2l.size(y)) return metric[0] / metric[1] .. raw:: html
.. raw:: html
Aqui, ``Accumulator`` é uma classe utilitária para acumular somas sobre múltiplas variáveis. Na função ``evaluate_accuracy`` acima, criamos 2 variáveis na instância ``Accumulator`` para armazenar ambos o número de previsões corretas e o número de previsões, respectivamente. Ambos serão acumulados ao longo do tempo à medida que iteramos no conjunto de dados. .. code:: python class Accumulator: #@save """For accumulating sums over `n` variables.""" def __init__(self, n): self.data = [0.0] * n def add(self, *args): self.data = [a + float(b) for a, b in zip(self.data, args)] def reset(self): self.data = [0.0] * len(self.data) def __getitem__(self, idx): return self.data[idx] PComo inicializamos o modelo ``net`` com pesos aleatórios, a precisão deste modelo deve ser próxima à aleatoriedade, ou seja, 0,1 para 10 classes. .. raw:: html
.. raw:: html
.. code:: python evaluate_accuracy(net, test_iter) .. parsed-literal:: :class: output 0.0811 .. raw:: html
.. raw:: html
.. code:: python evaluate_accuracy(net, test_iter) .. parsed-literal:: :class: output 0.0918 .. raw:: html
.. raw:: html
.. code:: python evaluate_accuracy(net, test_iter) .. parsed-literal:: :class: output 0.0735 .. raw:: html
.. raw:: html
Treinamento ----------- O *loop* de treinamento para regressão *softmax* deve ser extremamente familiar se você ler nossa implementação de regressão linear em :numref:`sec_linear_scratch`. Aqui, nós refatoramos a implementação para torná-la reutilizável. Primeiro, definimos uma função para treinar por uma época. Observe que ``updater`` é uma função geral para atualizar os parâmetros do modelo, que aceita o tamanho do lote como argumento. Pode ser um *wrapper* da função ``d2l.sgd`` ou a função de otimização integrada de uma estrutura. .. raw:: html
.. raw:: html
.. code:: python def train_epoch_ch3(net, train_iter, loss, updater): #@save """Train a model within one epoch (defined in Chapter 3).""" # Sum of training loss, sum of training accuracy, no. of examples metric = Accumulator(3) if isinstance(updater, gluon.Trainer): updater = updater.step for X, y in train_iter: # Compute gradients and update parameters with autograd.record(): y_hat = net(X) l = loss(y_hat, y) l.backward() updater(X.shape[0]) metric.add(float(l.sum()), accuracy(y_hat, y), y.size) # Return training loss and training accuracy return metric[0] / metric[2], metric[1] / metric[2] .. raw:: html
.. raw:: html
.. code:: python def train_epoch_ch3(net, train_iter, loss, updater): #@save """The training loop defined in Chapter 3.""" # Set the model to training mode if isinstance(net, torch.nn.Module): net.train() # Sum of training loss, sum of training accuracy, no. of examples metric = Accumulator(3) for X, y in train_iter: # Compute gradients and update parameters y_hat = net(X) l = loss(y_hat, y) if isinstance(updater, torch.optim.Optimizer): # Using PyTorch in-built optimizer & loss criterion updater.zero_grad() l.backward() updater.step() metric.add(float(l) * len(y), accuracy(y_hat, y), y.size().numel()) else: # Using custom built optimizer & loss criterion l.sum().backward() updater(X.shape[0]) metric.add(float(l.sum()), accuracy(y_hat, y), y.numel()) # Return training loss and training accuracy return metric[0] / metric[2], metric[1] / metric[2] .. raw:: html
.. raw:: html
.. code:: python def train_epoch_ch3(net, train_iter, loss, updater): #@save """The training loop defined in Chapter 3.""" # Sum of training loss, sum of training accuracy, no. of examples metric = Accumulator(3) for X, y in train_iter: # Compute gradients and update parameters with tf.GradientTape() as tape: y_hat = net(X) # Keras implementations for loss takes (labels, predictions) # instead of (predictions, labels) that users might implement # in this book, e.g. `cross_entropy` that we implemented above if isinstance(loss, tf.keras.losses.Loss): l = loss(y, y_hat) else: l = loss(y_hat, y) if isinstance(updater, tf.keras.optimizers.Optimizer): params = net.trainable_variables grads = tape.gradient(l, params) updater.apply_gradients(zip(grads, params)) else: updater(X.shape[0], tape.gradient(l, updater.params)) # Keras loss by default returns the average loss in a batch l_sum = l * float(tf.size(y)) if isinstance( loss, tf.keras.losses.Loss) else tf.reduce_sum(l) metric.add(l_sum, accuracy(y_hat, y), tf.size(y)) # Return training loss and training accuracy return metric[0] / metric[2], metric[1] / metric[2] .. raw:: html
.. raw:: html
Antes de mostrar a implementação da função de treinamento, definimos uma classe de utilitário que plota dados em animação. Novamente, o objetivo é simplificar o código no restante do livro. .. code:: python class Animator: #@save """For plotting data in animation.""" def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None, ylim=None, xscale='linear', yscale='linear', fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1, figsize=(3.5, 2.5)): # Incrementally plot multiple lines if legend is None: legend = [] d2l.use_svg_display() self.fig, self.axes = d2l.plt.subplots(nrows, ncols, figsize=figsize) if nrows * ncols == 1: self.axes = [self.axes, ] # Use a lambda function to capture arguments self.config_axes = lambda: d2l.set_axes( self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend) self.X, self.Y, self.fmts = None, None, fmts def add(self, x, y): # Add multiple data points into the figure if not hasattr(y, "__len__"): y = [y] n = len(y) if not hasattr(x, "__len__"): x = [x] * n if not self.X: self.X = [[] for _ in range(n)] if not self.Y: self.Y = [[] for _ in range(n)] for i, (a, b) in enumerate(zip(x, y)): if a is not None and b is not None: self.X[i].append(a) self.Y[i].append(b) self.axes[0].cla() for x, y, fmt in zip(self.X, self.Y, self.fmts): self.axes[0].plot(x, y, fmt) self.config_axes() display.display(self.fig) display.clear_output(wait=True) A seguinte função de treinamento então treina um modelo ``net`` em um conjunto de dados de treinamento acessado via\ ``train_iter`` para várias épocas, que é especificado por ``num_epochs``. No final de cada época, o modelo é avaliado em um conjunto de dados de teste acessado via ``test_iter``. Vamos aproveitar a classe ``Animator`` para visualizar o progresso do treinamento. .. code:: python def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater): #@save """Train a model (defined in Chapter 3).""" animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9], legend=['train loss', 'train acc', 'test acc']) for epoch in range(num_epochs): train_metrics = train_epoch_ch3(net, train_iter, loss, updater) test_acc = evaluate_accuracy(net, test_iter) animator.add(epoch + 1, train_metrics + (test_acc,)) train_loss, train_acc = train_metrics assert train_loss < 0.5, train_loss assert train_acc <= 1 and train_acc > 0.7, train_acc assert test_acc <= 1 and test_acc > 0.7, test_acc Como uma implementação do zero, nós usamos a descida gradiente estocástica do *minibatch* definido em :numref:`sec_linear_scratch` para otimizar a função de perda do modelo com uma taxa de aprendizado de 0,1. .. raw:: html
.. raw:: html
.. code:: python lr = 0.1 def updater(batch_size): return d2l.sgd([W, b], lr, batch_size) .. raw:: html
.. raw:: html
.. code:: python lr = 0.1 def updater(batch_size): return d2l.sgd([W, b], lr, batch_size) .. raw:: html
.. raw:: html
.. code:: python class Updater(): #@save """For updating parameters using minibatch stochastic gradient descent.""" def __init__(self, params, lr): self.params = params self.lr = lr def __call__(self, batch_size, grads): d2l.sgd(self.params, grads, self.lr, batch_size) updater = Updater([W, b], lr=0.1) .. raw:: html
.. raw:: html
Agora treinamos o modelo com 10 épocas. Observe que tanto o número de épocas (``num_epochs``), e a taxa de aprendizagem (``lr``) são hiperparâmetros ajustáveis. Mudando seus valores, podemos ser capazes de aumentar a precisão da classificação do modelo. .. raw:: html
.. raw:: html
.. code:: python num_epochs = 10 train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater) .. figure:: output_softmax-regression-scratch_a48321_177_0.svg .. raw:: html
.. raw:: html
.. code:: python num_epochs = 10 train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater) .. figure:: output_softmax-regression-scratch_a48321_180_0.svg .. raw:: html
.. raw:: html
.. code:: python num_epochs = 10 train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater) .. figure:: output_softmax-regression-scratch_a48321_183_0.svg .. raw:: html
.. raw:: html
Predição -------- Agora que o treinamento está completo, nosso modelo está pronto para classificar algumas imagens. Dada uma série de imagens, vamos comparar seus *labels* reais (primeira linha de saída de texto) e as previsões do modelo (segunda linha de saída de texto). .. raw:: html
.. raw:: html
.. code:: python def predict_ch3(net, test_iter, n=6): #@save """Predict labels (defined in Chapter 3).""" for X, y in test_iter: break trues = d2l.get_fashion_mnist_labels(y) preds = d2l.get_fashion_mnist_labels(net(X).argmax(axis=1)) titles = [true +'\n' + pred for true, pred in zip(trues, preds)] d2l.show_images( X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n]) predict_ch3(net, test_iter) .. figure:: output_softmax-regression-scratch_a48321_189_0.svg .. raw:: html
.. raw:: html
.. code:: python def predict_ch3(net, test_iter, n=6): #@save """Predict labels (defined in Chapter 3).""" for X, y in test_iter: break trues = d2l.get_fashion_mnist_labels(y) preds = d2l.get_fashion_mnist_labels(net(X).argmax(axis=1)) titles = [true +'\n' + pred for true, pred in zip(trues, preds)] d2l.show_images( X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n]) predict_ch3(net, test_iter) .. figure:: output_softmax-regression-scratch_a48321_192_0.svg .. raw:: html
.. raw:: html
.. code:: python def predict_ch3(net, test_iter, n=6): #@save """Predict labels (defined in Chapter 3).""" for X, y in test_iter: break trues = d2l.get_fashion_mnist_labels(y) preds = d2l.get_fashion_mnist_labels(tf.argmax(net(X), axis=1)) titles = [true +'\n' + pred for true, pred in zip(trues, preds)] d2l.show_images( tf.reshape(X[0:n], (n, 28, 28)), 1, n, titles=titles[0:n]) predict_ch3(net, test_iter) .. figure:: output_softmax-regression-scratch_a48321_195_0.svg .. raw:: html
.. raw:: html
Resumo ------ - Com a regressão *softmax*, podemos treinar modelos para classificação multiclasse. - O loop de treinamento da regressão *softmax* é muito semelhante ao da regressão linear: recuperar e ler dados, definir modelos e funções de perda e treinar modelos usando algoritmos de otimização. Como você descobrirá em breve, os modelos de *deep learning* mais comuns têm procedimentos de treinamento semelhantes. Exercícios ---------- 1. Nesta seção, implementamos diretamente a função *softmax* com base na definição matemática da operação do *softmax*. Que problemas isso pode causar? Dica: tente calcular o tamanho de :math:`\exp(50)`. 2. A função ``cross_entropy`` nesta seção foi implementada de acordo com a definição da função de perda de entropia cruzada. Qual poderia ser o problema com esta implementação? Dica: considere o domínio do logaritmo. 3. Que soluções você pode pensar para resolver os dois problemas acima? 4. É sempre uma boa ideia retornar o *label* mais provável? Por exemplo, você faria isso para diagnóstico médico? 5. Suponha que quiséssemos usar a regressão *softmax* para prever a próxima palavra com base em alguns recursos. Quais são alguns problemas que podem surgir de um vocabulário extenso? .. raw:: html
.. raw:: html
`Discussions `__ .. raw:: html
.. raw:: html
`Discussions `__ .. raw:: html
.. raw:: html
`Discussions `__ .. raw:: html
.. raw:: html
.. raw:: html