Code Text Copy to Drive
Notebook

Gemini
Copyright 2018 The TensorFlow Authors.

Gemini
Show code

Gemini
Show code

Gemini

Regressão básica: previsão da eficiência de combustível


Gemini

Gemini

Em um problema de regressão, o objetivo é prever a saída de um valor contínuo, como um preço ou uma probabilidade. Compare com um problema de classificação, em que o objetivo é selecionar uma classe em uma lista de classes (por exemplo, quando uma imagem contém uma maçã ou uma laranja, reconhecer qual fruta está presente na imagem).

Este tutorial usa o dataset clássico Auto MPG e demonstra como criar modelos para prever a eficiência de combustível dos automóveis do fim da década de 1970 e início da década de 1980. Para fazer isso, você fornecerá aos modelos uma descrição de diversos automóveis desse período. A descrição incluir vários atributos, como cilindros, cilindradas, cavalos e peso.

Este exemplo usa a API do Keras. Para saber mais, confira os tutoriais e guias do Keras.


Gemini
# Use seaborn for pairplot.
!pip install -q seaborn

Gemini
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns

# Make NumPy printouts easier to read.
np.set_printoptions(precision=3, suppress=True)

Gemini
import tensorflow as tf

from tensorflow import keras
from tensorflow.keras import layers

print(tf.__version__)

Gemini

Dataset Auto MPG

O dataset está disponível no Repositório de Aprendizado de Máquina da UCI.


Gemini

Obter os dados

Primeiro, baixe e importe o dataset usando o pandas:


Gemini
url = 'http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data'
column_names = ['MPG''Cylinders''Displacement''Horsepower''Weight',
                'Acceleration''Model Year''Origin']

raw_dataset = pd.read_csv(url, names=column_names,
                          na_values='?', comment='\t',
                          sep=' ', skipinitialspace=True)

Gemini
dataset = raw_dataset.copy()
dataset.tail()

Gemini

Limpar os dados

O dataset contém alguns valores desconhecidos:


Gemini
dataset.isna().sum()

Gemini

Elimine essas linhas para simplificar este tutorial inicial:


Gemini
dataset = dataset.dropna()

Gemini

A coluna "Origin" (origem) armazena uma categoria, não um número. Portanto, a próxima etapa é fazer a codificação one-hot dos valores na coluna com pd.get_dummies.

Observação: você pode configurar tf.keras.Model para fazer esse tipo de transformação, mas isso foge do escopo deste tutorial. Confira exemplos nos tutoriais Classificar dados estruturados usando camadas de pré-processamento do Keras ou Carregar dados em CSV.


Gemini
dataset['Origin'] = dataset['Origin'].map({1'USA'2'Europe'3'Japan'})

Gemini
dataset = pd.get_dummies(dataset, columns=['Origin'], prefix='', prefix_sep='')
dataset.tail()

Gemini

Dividir os dados em datasets de treinamento e teste

Agora, divida o dataset em um conjunto de treinamento e outro de teste. Você usará o conjunto de teste na avaliação final dos modelos.


Gemini
train_dataset = dataset.sample(frac=0.8, random_state=0)
test_dataset = dataset.drop(train_dataset.index)

Gemini

Avaliar os dados

Confira a distribuição conjunta de alguns pares de colunas do conjunto de treinamento.

A linha superior indica que a eficiência de combustível (MPG, na sigla em inglês) é uma função de todos os outros parâmetros. As outras linhas indicam que são funções uma da outra.


Gemini
sns.pairplot(train_dataset[['MPG''Cylinders''Displacement''Weight']], diag_kind='kde')

Gemini

Confira também as estatísticas gerais. Observe como cada característica abrange um intervalo bem diferente:


Gemini
train_dataset.describe().transpose()

Gemini

Separar as características dos rótulos

Separa o valor alvo, o "rótulo", das características. Você treinará o modelo para prever esse rótulo.


Gemini
train_features = train_dataset.copy()
test_features = test_dataset.copy()

train_labels = train_features.pop('MPG')
test_labels = test_features.pop('MPG')

Gemini

Normalização

Na tabela de estatísticas, é fácil ver como os intervalos de cada característica são diferentes:


Gemini
train_dataset.describe().transpose()[['mean''std']]

Gemini

É uma boa prática normalizar os recursos que usem escalas e intervalos diferentes.

Um motivos dessa importância é porque as características são multiplicadas pelos pesos do modelo. Portanto, a escala das saídas e a escala dos gradientes são afetadas pela escala das entradas.

Embora um modelo talvez possa convergir sem a normalização de características, ela deixa o treinamento muito mais estável.

Observação: não há vantagens em normalizar as características one-hot. Isso é feito aqui por questões de simplicidade. Confira mais detalhes de como usar as camadas de pré-processamento no guia Trabalhando com camadas de pré-processamento e no tutorial Classificar dados estruturados usando camadas de pré-processamento do Keras.


Gemini

Camada de normalização

tf.keras.layers.Normalization é uma forma simples e elegante de acrescentar a normalização de características ao seu modelo.

O primeiro passo é criar a camada:


Gemini
normalizer = tf.keras.layers.Normalization(axis=-1)

Gemini

Em seguida, faça a adequação do estado da camada de pré-processamento aos dados chamando Normalization.adapt:


Gemini
normalizer.adapt(np.array(train_features))

Gemini

Calcule a média e a variância., depois armazene-as na camada:


Gemini
print(normalizer.mean.numpy())

Gemini

Quando a camada é chamada, retorna os dados de entrada, com cada característica normalizada de forma independente:


Gemini
first = np.array(train_features[:1])

with np.printoptions(precision=2, suppress=True):
  print('First example:', first)
  print()
  print('Normalized:', normalizer(first).numpy())

Gemini

Regressão linear

Antes de criar um modelo de rede neural profunda, comece com a regressão linear, usando uma ou várias variáveis.


Gemini

Regressão linear com uma variável

Comece com uma regressão linear com uma única variável para prever 'MPG' (milhas por galão) a partir de 'Horsepower' (cavalos).

Geralmente, fazer o treinamento de um modelo tf.keras começa pela definição da arquitetura do modelo. Use um modelo tf.keras.Sequential que representa uma sequência de passos.

Há dois passos no modelo de regressão linear com uma única variável:

  • Normalize as características de entrada 'Horsepower' (cavalos) usando a camada de pré-processamento tf.keras.layers.Normalization.
  • Aplique uma transformação linear ($y = mx+b$) para gerar 1 saída usando uma camada linear (tf.keras.layers.Dense).

O número de entradas pode ser definido pelo argumento input_shape ou automaticamente quando o modelo é executado pela primeira vez.


Gemini

Primeiro, crie uma array NumPy composto pelas características 'Horsepower' (cavalos). Em seguida, instancie tf.keras.layers.Normalization e faça a adequação de seu estado aos dados de horsepower:


Gemini
horsepower = np.array(train_features['Horsepower'])

horsepower_normalizer = layers.Normalization(input_shape=[1,], axis=None)
horsepower_normalizer.adapt(horsepower)

Gemini

Crie o modelo Sequential do Keras:


Gemini
horsepower_model = tf.keras.Sequential([
    horsepower_normalizer,
    layers.Dense(units=1)
])

horsepower_model.summary()

Gemini

Esse modelo preverá 'MPG' a partir de 'Horsepower'.

Execute o modelo não treinado para os primeiros 10 valores de 'Horsepower'. A saída não será boa, mas observe que tem o formato esperado – (10, 1):


Gemini
horsepower_model.predict(horsepower[:10])

Gemini

Após criar o modelo, configure o procedimento de treinamento usando o método Model.compile do Keras. Os argumentos mais importantes a serem compilados são loss (perda) e optimizer (otimizador), já que definem o que será otimizado (mean_absolute_error) e como (usando tf.keras.optimizers.Adam).


Gemini
horsepower_model.compile(
    optimizer=tf.keras.optimizers.Adam(learning_rate=0.1),
    loss='mean_absolute_error')

Gemini

Use Model.fit do Keras para executar o treinamento com 100 épocas:


Gemini
%%time
history = horsepower_model.fit(
    train_features['Horsepower'],
    train_labels,
    epochs=100,
    # Suppress logging.
    verbose=0,
    # Calculate validation results on 20% of the training data.
    validation_split = 0.2)

Gemini

Visualize o progresso de treinamento do modelo usando as estatísticas armazenadas no objeto history (histórico):


Gemini
hist = pd.DataFrame(history.history)
hist['epoch'] = history.epoch
hist.tail()

Gemini
def plot_loss(history):
  plt.plot(history.history['loss'], label='loss')
  plt.plot(history.history['val_loss'], label='val_loss')
  plt.ylim([010])
  plt.xlabel('Epoch')
  plt.ylabel('Error [MPG]')
  plt.legend()
  plt.grid(True)

Gemini
plot_loss(history)

Gemini

Colete os resultados para o conjunto de teste para uso posterior:


Gemini
test_results = {}

test_results['horsepower_model'] = horsepower_model.evaluate(
    test_features['Horsepower'],
    test_labels, verbose=0)

Gemini

Como é uma regressão com uma única variável, é fácil ver as previsões do modelo como uma função da entrada:


Gemini
x = tf.linspace(0.0250251)
y = horsepower_model.predict(x)

Gemini
def plot_horsepower(xy):
  plt.scatter(train_features['Horsepower'], train_labels, label='Data')
  plt.plot(x, y, color='k', label='Predictions')
  plt.xlabel('Horsepower')
  plt.ylabel('MPG')
  plt.legend()

Gemini
plot_horsepower(x, y)

Gemini

Regressão linear com várias entradas


Gemini

Você pode usar uma configuração quase idêntica para fazer previsões com base em diversas entradas. Esse modelo ainda faz a mesma transformação linear $y = mx+b$$y = mx+b$, mas $m$$m$ é uma matriz, e $x$$x$ é um vetor.

Crie novamente um modelo Sequential do Keras com dois passos, sendo que a primeira camada é o normalizer (normalizador), (tf.keras.layers.Normalization(axis=-1)) que você definiu anteriormente e adaptou para todo o dataset:


Gemini
linear_model = tf.keras.Sequential([
    normalizer,
    layers.Dense(units=1)
])

Gemini

Quando você faz uma chamada a Model.predict para um lote de entradas, são produzidas saídasunits=1 para cada exemplo:


Gemini
linear_model.predict(train_features[:10])

Gemini

Quando você faz uma chamada ao modelo, suas matrizes de peso são construídas. Verifique se os pesos de kernel (o $m$$m$ em $y=mx+b$$y=mx+b$) têm um formato igual a (9, 1):


Gemini
linear_model.layers[1].kernel

Gemini

Configure o modelo com Model.compile do Keras e faça o treinamento com Model.fit com 100 épocas:


Gemini
linear_model.compile(
    optimizer=tf.keras.optimizers.Adam(learning_rate=0.1),
    loss='mean_absolute_error')

Gemini
%%time
history = linear_model.fit(
    train_features,
    train_labels,
    epochs=100,
    # Suppress logging.
    verbose=0,
    # Calculate validation results on 20% of the training data.
    validation_split = 0.2)

Gemini

Ao usar todas as entradas nesse modelo de regressão, conseguimos um erro de treinamento e validação bem menor do que horsepower_model, que tinha uma entrada:


Gemini
plot_loss(history)

Gemini

Colete os resultados para o conjunto de teste para uso posterior:


Gemini
test_results['linear_model'] = linear_model.evaluate(
    test_features, test_labels, verbose=0)

Gemini

Regressão com uma rede neural profunda (DNN)


Gemini

Na seção anterior, você implementou dois modelos lineares para entrada única ou diversas entradas.

Agora, você implementará modelos de DNN com uma entrada ou diversas entradas.

O código é basicamente o mesmo, exceto que o modelo é expandido, com a inclusão de algumas camadas não lineares "ocultas". O termo "ocultas" significa apenas que elas não estão ligadas diretamente às entradas ou às saídas.


Gemini

Esses modelos conterão algumas camadas a mais do que o modelo linear:

  • A camada de normalização, da mesma forma que anteriormente (com horsepower_normalizer para um modelo com uma entrada e normalizer para um modelo com várias entradas).
  • Duas camadas Dense não lineares e ocultas, com a função de ativação ReLU (relu) não linear.
  • Uma camada Dense com uma única saída.

Os dois modelos usarão o mesmo procedimento de treinamento, então o método compile é incluído na função build_and_compile_model abaixo.


Gemini
def build_and_compile_model(norm):
  model = keras.Sequential([
      norm,
      layers.Dense(64, activation='relu'),
      layers.Dense(64, activation='relu'),
      layers.Dense(1)
  ])

  model.compile(loss='mean_absolute_error',
                optimizer=tf.keras.optimizers.Adam(0.001))
  return model

Gemini

Regressão usando uma DNN e uma única entrada


Gemini

Crie um modelo de DNN com apenas 'Horsepower' (cavalos) como entrada e horsepower_normalizer (definido anteriormente) como a camada de normalização:


Gemini
dnn_horsepower_model = build_and_compile_model(horsepower_normalizer)

Gemini

Esse modelo tem bem mais parâmetros que podem ser treinados do que os modelos lineares:


Gemini
dnn_horsepower_model.summary()

Gemini

Treine o modelo com Model.fit do Keras:


Gemini
%%time
history = dnn_horsepower_model.fit(
    train_features['Horsepower'],
    train_labels,
    validation_split=0.2,
    verbose=0, epochs=100)

Gemini

Esse modelo tem um desempenho um pouco melhor do que o modelo horsepower_model linear com uma única entrada:


Gemini
plot_loss(history)

Gemini

Se você plotar as previsões como função de 'Horsepower' (cavalos), notará como esse modelo tem vantagens sobre a não linearidade fornecida pelas camadas ocultas:


Gemini
x = tf.linspace(0.0250251)
y = dnn_horsepower_model.predict(x)

Gemini
plot_horsepower(x, y)

Gemini

Colete os resultados para o conjunto de teste para uso posterior:


Gemini
test_results['dnn_horsepower_model'] = dnn_horsepower_model.evaluate(
    test_features['Horsepower'], test_labels,
    verbose=0)

Gemini

Regressão usando uma DNN e várias entradas


Gemini

Repita o processo anterior usando todas as entradas. O desempenho do modelo aumenta um pouco para o dataset de validação.


Gemini
dnn_model = build_and_compile_model(normalizer)
dnn_model.summary()

Gemini
%%time
history = dnn_model.fit(
    train_features,
    train_labels,
    validation_split=0.2,
    verbose=0, epochs=100)

Gemini
plot_loss(history)

Gemini

Colete os resultados para o conjunto de teste:


Gemini
test_results['dnn_model'] = dnn_model.evaluate(test_features, test_labels, verbose=0)

Gemini

Desempenho


Gemini

Como todos os modelos foram treinados, você pode conferir o desempenho de seus conjuntos de teste:


Gemini
pd.DataFrame(test_results, index=['Mean absolute error [MPG]']).T

Gemini

Esses resultados batem com o erro de validação observado durante o treinamento.


Gemini

Fazer previsões

Agora, você pode fazer previsões com dnn_model para o conjunto de testes usando Model.predict do Keras e conferindo a perda:


Gemini
test_predictions = dnn_model.predict(test_features).flatten()

a = plt.axes(aspect='equal')
plt.scatter(test_labels, test_predictions)
plt.xlabel('True Values [MPG]')
plt.ylabel('Predictions [MPG]')
lims = [050]
plt.xlim(lims)
plt.ylim(lims)
_ = plt.plot(lims, lims)


Gemini

Parece que o modelo faz previsões razoavelmente boas.

Agora, confira a distribuição do erro:


Gemini
error = test_predictions - test_labels
plt.hist(error, bins=25)
plt.xlabel('Prediction Error [MPG]')
_ = plt.ylabel('Count')

Gemini

Se você estiver contente com o modelo, salve-o para uso posterior com Model.save:


Gemini
dnn_model.save('dnn_model.keras')

Gemini

Se você recarregar o modelo, ele gerará uma saída idêntica:


Gemini
reloaded = tf.keras.models.load_model('dnn_model.keras')

test_results['reloaded'] = reloaded.evaluate(
    test_features, test_labels, verbose=0)

Gemini
pd.DataFrame(test_results, index=['Mean absolute error [MPG]']).T

Gemini

Conclusão

Este notebook apresentou algumas técnicas para lidar com um problema de regressão. Confira mais algumas dicas que podem ajudar:

  • Erro Quadrático Médio (EQM) (tf.keras.losses.MeanSquaredError) e Erro Absoluto Médio (EMA) (tf.keras.losses.MeanAbsoluteError) são funções de perda comuns usadas para problemas de regressão. O EMA é menos sensível aos pontos fora da curva. Funções de perda diferentes são usadas para problemas de classificação.
  • De maneira similar, as métricas de avaliação usadas para regressão diferem das usadas para classificação.
  • Quando características de dados de entrada numérica têm valores com intervalos diferentes, cada característica deve ter sua escala alterada de forma independente para o mesmo intervalo.
  • O overfitting é um problema comum para modelos de DNN, embora não tenha sido um problema neste tutorial. Consulte mais informações sobre esse tema no tutorial Overfitting e underfitting.