Carregar dados CSV

Ver em TensorFlow.org Executar em Google Colab Ver código fonte no GitHub Baixar notebook

Este tutorial fornece um exemplo de como carregar dados CSV de um arquivo em um tf.data.Dataset.

Os dados usados neste tutorial foram retirados da lista de passageiros do Titanic. O modelo preverá a probabilidade de sobrevivência de um passageiro com base em características como idade, sexo, classe de passagem e se a pessoa estava viajando sozinha.

Setup

try:
  # %tensorflow_version only exists in Colab.
  %tensorflow_version 2.x
except Exception:
  pass

from __future__ import absolute_import, division, print_function, unicode_literals
import functools

import numpy as np
import tensorflow as tf
TRAIN_DATA_URL = "https://storage.googleapis.com/tf-datasets/titanic/train.csv"
TEST_DATA_URL = "https://storage.googleapis.com/tf-datasets/titanic/eval.csv"

train_file_path = tf.keras.utils.get_file("train.csv", TRAIN_DATA_URL)
test_file_path = tf.keras.utils.get_file("eval.csv", TEST_DATA_URL)
Downloading data from https://storage.googleapis.com/tf-datasets/titanic/train.csv
32768/30874 [===============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tf-datasets/titanic/eval.csv
16384/13049 [=====================================] - 0s 0us/step

# Facilitar a leitura de valores numpy.
np.set_printoptions(precision=3, suppress=True)

Carregar dados

Para começar, vejamos a parte superior do arquivo CSV para ver como ele está formatado.

head {train_file_path}
survived,sex,age,n_siblings_spouses,parch,fare,class,deck,embark_town,alone
0,male,22.0,1,0,7.25,Third,unknown,Southampton,n
1,female,38.0,1,0,71.2833,First,C,Cherbourg,n
1,female,26.0,0,0,7.925,Third,unknown,Southampton,y
1,female,35.0,1,0,53.1,First,C,Southampton,n
0,male,28.0,0,0,8.4583,Third,unknown,Queenstown,y
0,male,2.0,3,1,21.075,Third,unknown,Southampton,n
1,female,27.0,0,2,11.1333,Third,unknown,Southampton,n
1,female,14.0,1,0,30.0708,Second,unknown,Cherbourg,n
1,female,4.0,1,1,16.7,Third,G,Southampton,n

Você pode carregar isso usando pandas e passar as matrizes NumPy para o TensorFlow. Se você precisar escalar até um grande conjunto de arquivos ou precisar de um carregador que se integre ao TensorFlow e tf.data, use o tf.data.experimental. função make_csv_dataset:

A única coluna que você precisa identificar explicitamente é aquela com o valor que o modelo pretende prever.

LABEL_COLUMN = 'survived'
LABELS = [0, 1]

Now read the CSV data from the file and create a dataset.

(For the full documentation, see tf.data.experimental.make_csv_dataset)

def get_dataset(file_path, **kwargs):
  dataset = tf.data.experimental.make_csv_dataset(
      file_path,
      batch_size=5, # Artificialmente pequeno para facilitar a exibição de exemplos
      label_name=LABEL_COLUMN,
      na_value="?",
      num_epochs=1,
      ignore_errors=True, 
      **kwargs)
  return dataset

raw_train_data = get_dataset(train_file_path)
raw_test_data = get_dataset(test_file_path)
def show_batch(dataset):
  for batch, label in dataset.take(1):
    for key, value in batch.items():
      print("{:20s}: {}".format(key,value.numpy()))

Cada item do conjunto de dados é um lote, representado como uma tupla de (* muitos exemplos *, * muitos rótulos *). Os dados dos exemplos são organizados em tensores baseados em colunas (em vez de tensores baseados em linhas), cada um com tantos elementos quanto o tamanho do lote (5 neste caso).

Pode ajudar a ver isso por si mesmo.

show_batch(raw_train_data)
sex                 : [b'male' b'male' b'female' b'male' b'female']
age                 : [18. 47. 28. 33. 29.]
n_siblings_spouses  : [0 0 8 1 0]
parch               : [0 0 2 1 4]
fare                : [ 8.3   25.587 69.55  20.525 21.075]
class               : [b'Third' b'First' b'Third' b'Third' b'Third']
deck                : [b'unknown' b'E' b'unknown' b'unknown' b'unknown']
embark_town         : [b'Southampton' b'Southampton' b'Southampton' b'Southampton'
 b'Southampton']
alone               : [b'y' b'y' b'n' b'n' b'n']

Como você pode ver, as colunas no CSV são nomeadas. O construtor do conjunto de dados selecionará esses nomes automaticamente. Se o arquivo com o qual você está trabalhando não contém os nomes das colunas na primeira linha, passe-os em uma lista de strings para o argumento column_names na função make_csv_dataset.

CSV_COLUMNS = ['survived', 'sex', 'age', 'n_siblings_spouses', 'parch', 'fare', 'class', 'deck', 'embark_town', 'alone']

temp_dataset = get_dataset(train_file_path, column_names=CSV_COLUMNS)

show_batch(temp_dataset)
sex                 : [b'female' b'male' b'male' b'male' b'female']
age                 : [44.  28.  32.5 21.  33. ]
n_siblings_spouses  : [0 0 1 0 0]
parch               : [0 0 0 0 2]
fare                : [27.721  8.05  30.071  7.733 26.   ]
class               : [b'First' b'Third' b'Second' b'Third' b'Second']
deck                : [b'B' b'unknown' b'unknown' b'unknown' b'unknown']
embark_town         : [b'Cherbourg' b'Southampton' b'Cherbourg' b'Queenstown' b'Southampton']
alone               : [b'y' b'y' b'n' b'y' b'n']

Este exemplo vai usar todas as colunas disponíveis. Se você precisar omitir algumas colunas do conjunto de dados, crie uma lista apenas das colunas que planeja usar e passe-a para o argumento (opcional) select_columns do construtor.

SELECT_COLUMNS = ['survived', 'age', 'n_siblings_spouses', 'class', 'deck', 'alone']

temp_dataset = get_dataset(train_file_path, select_columns=SELECT_COLUMNS)

show_batch(temp_dataset)
age                 : [28. 23. 44. 23. 28.]
n_siblings_spouses  : [0 0 1 3 0]
class               : [b'Third' b'Third' b'Second' b'First' b'Third']
deck                : [b'unknown' b'unknown' b'unknown' b'C' b'unknown']
alone               : [b'y' b'y' b'n' b'n' b'y']

Pré-processamento dos Dados

Um arquivo CSV pode conter uma variedade de tipos de dados. Normalmente, você deseja converter desses tipos mistos em um vetor de comprimento fixo antes de alimentar os dados em seu modelo.

O TensorFlow possui um sistema interno para descrever conversões de entrada comuns: tf.feature_column, consulte este tutorial para detalhes.

Você pode pré-processar seus dados usando qualquer ferramenta que desejar (como nltk ou sklearn) e apenas passar a saída processada para o TensorFlow.

A principal vantagem de fazer o pré-processamento dentro do seu modelo é que, quando você exporta o modelo, ele inclui o pré-processamento. Dessa forma, você pode passar os dados brutos diretamente para o seu modelo.

Dados contínuos

Se seus dados já estiverem em um formato numérico apropriado, você poderá compactá-los em um vetor antes de transmiti-los ao modelo:

SELECT_COLUMNS = ['survived', 'age', 'n_siblings_spouses', 'parch', 'fare']
DEFAULTS = [0, 0.0, 0.0, 0.0, 0.0]
temp_dataset = get_dataset(train_file_path, 
                           select_columns=SELECT_COLUMNS,
                           column_defaults = DEFAULTS)

show_batch(temp_dataset)
age                 : [ 4. 28. 40. 25. 35.]
n_siblings_spouses  : [1. 0. 1. 0. 0.]
parch               : [1. 0. 1. 0. 0.]
fare                : [ 16.7     7.55  134.5     0.    512.329]

example_batch, labels_batch = next(iter(temp_dataset)) 

Aqui está uma função simples que agrupará todas as colunas:

def pack(features, label):
  return tf.stack(list(features.values()), axis=-1), label

Aplique isso a cada elemento do conjunto de dados:

packed_dataset = temp_dataset.map(pack)

for features, labels in packed_dataset.take(1):
  print(features.numpy())
  print()
  print(labels.numpy())
[[22.    0.    0.    7.25]
 [24.    2.    3.   18.75]
 [28.    0.    0.   13.  ]
 [24.    2.    0.   24.15]
 [46.    0.    0.   79.2 ]]

[0 1 1 0 0]

Se você tiver tipos de dados mistos, poderá separar esses campos numéricos simples. A API tf.feature_column pode lidar com eles, mas isso gera alguma sobrecarga e deve ser evitado, a menos que seja realmente necessário. Volte para o conjunto de dados misto:

show_batch(raw_train_data)
sex                 : [b'female' b'male' b'male' b'male' b'female']
age                 : [22. 45. 28. 29. 14.]
n_siblings_spouses  : [0 0 8 0 0]
parch               : [0 0 2 0 0]
fare                : [10.517  8.05  69.55  30.     7.854]
class               : [b'Third' b'Third' b'Third' b'First' b'Third']
deck                : [b'unknown' b'unknown' b'unknown' b'D' b'unknown']
embark_town         : [b'Southampton' b'Southampton' b'Southampton' b'Southampton'
 b'Southampton']
alone               : [b'y' b'y' b'n' b'y' b'y']

example_batch, labels_batch = next(iter(temp_dataset)) 

Portanto, defina um pré-processador mais geral que selecione uma lista de recursos numéricos e os agrupe em uma única coluna:

class PackNumericFeatures(object):
  def __init__(self, names):
    self.names = names

  def __call__(self, features, labels):
    numeric_features = [features.pop(name) for name in self.names]
    numeric_features = [tf.cast(feat, tf.float32) for feat in numeric_features]
    numeric_features = tf.stack(numeric_features, axis=-1)
    features['numeric'] = numeric_features

    return features, labels
NUMERIC_FEATURES = ['age','n_siblings_spouses','parch', 'fare']

packed_train_data = raw_train_data.map(
    PackNumericFeatures(NUMERIC_FEATURES))

packed_test_data = raw_test_data.map(
    PackNumericFeatures(NUMERIC_FEATURES))
show_batch(packed_train_data)
sex                 : [b'female' b'female' b'female' b'male' b'male']
class               : [b'Third' b'Second' b'First' b'Third' b'Third']
deck                : [b'unknown' b'unknown' b'unknown' b'unknown' b'unknown']
embark_town         : [b'Southampton' b'Southampton' b'Cherbourg' b'Southampton' b'Cherbourg']
alone               : [b'n' b'n' b'y' b'y' b'y']
numeric             : [[ 36.      1.      0.     17.4  ]
 [ 29.      1.      0.     26.   ]
 [ 30.      0.      0.    106.425]
 [ 55.5     0.      0.      8.05 ]
 [ 28.      0.      0.      7.225]]

example_batch, labels_batch = next(iter(packed_train_data)) 

Normalização dos dados

Dados contínuos sempre devem ser normalizados.

import pandas as pd
desc = pd.read_csv(train_file_path)[NUMERIC_FEATURES].describe()
desc
MEAN = np.array(desc.T['mean'])
STD = np.array(desc.T['std'])
def normalize_numeric_data(data, mean, std):
  # Center the data
  return (data-mean)/std

Agora crie uma coluna numérica. A API tf.feature_columns.numeric_column aceita um argumento normalizer_fn, que será executado em cada lote.

Ligue o MEAN e oSTD ao normalizador fn usando functools.partial

# Veja o que você acabou de criar.
normalizer = functools.partial(normalize_numeric_data, mean=MEAN, std=STD)

numeric_column = tf.feature_column.numeric_column('numeric', normalizer_fn=normalizer, shape=[len(NUMERIC_FEATURES)])
numeric_columns = [numeric_column]
numeric_column
NumericColumn(key='numeric', shape=(4,), default_value=None, dtype=tf.float32, normalizer_fn=functools.partial(<function normalize_numeric_data at 0x7f20001947b8>, mean=array([29.631,  0.545,  0.38 , 34.385]), std=array([12.512,  1.151,  0.793, 54.598])))

Ao treinar o modelo, inclua esta coluna de característica para selecionar e centralizar este bloco de dados numéricos:

example_batch['numeric']
<tf.Tensor: shape=(5, 4), dtype=float32, numpy=
array([[17.   ,  0.   ,  0.   ,  7.125],
       [ 3.   ,  1.   ,  1.   , 26.   ],
       [28.   ,  0.   ,  0.   ,  7.896],
       [25.   ,  0.   ,  0.   ,  7.05 ],
       [42.   ,  1.   ,  0.   , 52.   ]], dtype=float32)>
numeric_layer = tf.keras.layers.DenseFeatures(numeric_columns)
numeric_layer(example_batch).numpy()
array([[-1.01 , -0.474, -0.479, -0.499],
       [-2.128,  0.395,  0.782, -0.154],
       [-0.13 , -0.474, -0.479, -0.485],
       [-0.37 , -0.474, -0.479, -0.501],
       [ 0.989,  0.395, -0.479,  0.323]], dtype=float32)

A normalização baseada em média usada aqui requer conhecer os meios de cada coluna antes do tempo.

Dados categóricos

Algumas das colunas nos dados CSV são colunas categóricas. Ou seja, o conteúdo deve ser um dentre um conjunto limitado de opções.

Use a API tf.feature_column para criar uma coleção com uma tf.feature_column.indicator_column para cada coluna categórica.

CATEGORIES = {
    'sex': ['male', 'female'],
    'class' : ['First', 'Second', 'Third'],
    'deck' : ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'],
    'embark_town' : ['Cherbourg', 'Southhampton', 'Queenstown'],
    'alone' : ['y', 'n']
}

categorical_columns = []
for feature, vocab in CATEGORIES.items():
  cat_col = tf.feature_column.categorical_column_with_vocabulary_list(
        key=feature, vocabulary_list=vocab)
  categorical_columns.append(tf.feature_column.indicator_column(cat_col))
# Veja o que você acabou de criar.
categorical_columns
[IndicatorColumn(categorical_column=VocabularyListCategoricalColumn(key='sex', vocabulary_list=('male', 'female'), dtype=tf.string, default_value=-1, num_oov_buckets=0)),
 IndicatorColumn(categorical_column=VocabularyListCategoricalColumn(key='class', vocabulary_list=('First', 'Second', 'Third'), dtype=tf.string, default_value=-1, num_oov_buckets=0)),
 IndicatorColumn(categorical_column=VocabularyListCategoricalColumn(key='deck', vocabulary_list=('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'), dtype=tf.string, default_value=-1, num_oov_buckets=0)),
 IndicatorColumn(categorical_column=VocabularyListCategoricalColumn(key='embark_town', vocabulary_list=('Cherbourg', 'Southhampton', 'Queenstown'), dtype=tf.string, default_value=-1, num_oov_buckets=0)),
 IndicatorColumn(categorical_column=VocabularyListCategoricalColumn(key='alone', vocabulary_list=('y', 'n'), dtype=tf.string, default_value=-1, num_oov_buckets=0))]
categorical_layer = tf.keras.layers.DenseFeatures(categorical_columns)
print(categorical_layer(example_batch).numpy()[0])
[1. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]

Isso fará parte de uma entrada de processamento de dados posteriormente, quando você construir o modelo.

Camada combinada de pré-processamento

Adicione as duas coleções de colunas de recursos e passe-as para um tf.keras.layers.DenseFeatures para criar uma camada de entrada que extrairá e pré-processará os dois tipos de entrada:

preprocessing_layer = tf.keras.layers.DenseFeatures(categorical_columns+numeric_columns)
print(preprocessing_layer(example_batch).numpy()[0])
[ 1.     0.     0.     0.     1.     0.     0.     0.     0.     0.

  0.     0.     0.     0.     0.     0.     0.     0.    -1.01  -0.474
 -0.479 -0.499  1.     0.   ]

Construir o modelo

Crie um tf.keras.Sequential, começando com o preprocessing_layer.

model = tf.keras.Sequential([
  preprocessing_layer,
  tf.keras.layers.Dense(128, activation='relu'),
  tf.keras.layers.Dense(128, activation='relu'),
  tf.keras.layers.Dense(1),
])

model.compile(
    loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
    optimizer='adam',
    metrics=['accuracy'])

Treinar, avaliar, e prever

Agora o modelo pode ser instanciado e treinado.

train_data = packed_train_data.shuffle(500)
test_data = packed_test_data
model.fit(train_data, epochs=20)
Epoch 1/20
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'collections.OrderedDict'> input: OrderedDict([('sex', <tf.Tensor 'ExpandDims_4:0' shape=(None, 1) dtype=string>), ('class', <tf.Tensor 'ExpandDims_1:0' shape=(None, 1) dtype=string>), ('deck', <tf.Tensor 'ExpandDims_2:0' shape=(None, 1) dtype=string>), ('embark_town', <tf.Tensor 'ExpandDims_3:0' shape=(None, 1) dtype=string>), ('alone', <tf.Tensor 'ExpandDims:0' shape=(None, 1) dtype=string>), ('numeric', <tf.Tensor 'IteratorGetNext:4' shape=(None, 4) dtype=float32>)])
Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'collections.OrderedDict'> input: OrderedDict([('sex', <tf.Tensor 'ExpandDims_4:0' shape=(None, 1) dtype=string>), ('class', <tf.Tensor 'ExpandDims_1:0' shape=(None, 1) dtype=string>), ('deck', <tf.Tensor 'ExpandDims_2:0' shape=(None, 1) dtype=string>), ('embark_town', <tf.Tensor 'ExpandDims_3:0' shape=(None, 1) dtype=string>), ('alone', <tf.Tensor 'ExpandDims:0' shape=(None, 1) dtype=string>), ('numeric', <tf.Tensor 'IteratorGetNext:4' shape=(None, 4) dtype=float32>)])
Consider rewriting this model with the Functional API.
126/126 [==============================] - 0s 3ms/step - loss: 0.5113 - accuracy: 0.7400
Epoch 2/20
126/126 [==============================] - 0s 3ms/step - loss: 0.4204 - accuracy: 0.8102
Epoch 3/20
126/126 [==============================] - 0s 3ms/step - loss: 0.4088 - accuracy: 0.8246
Epoch 4/20
126/126 [==============================] - 0s 3ms/step - loss: 0.3902 - accuracy: 0.8309
Epoch 5/20
126/126 [==============================] - 0s 3ms/step - loss: 0.3828 - accuracy: 0.8357
Epoch 6/20
126/126 [==============================] - 0s 3ms/step - loss: 0.3705 - accuracy: 0.8405
Epoch 7/20
126/126 [==============================] - 0s 3ms/step - loss: 0.3695 - accuracy: 0.8517
Epoch 8/20
126/126 [==============================] - 0s 3ms/step - loss: 0.3575 - accuracy: 0.8437
Epoch 9/20
126/126 [==============================] - 0s 3ms/step - loss: 0.3656 - accuracy: 0.8437
Epoch 10/20
126/126 [==============================] - 0s 3ms/step - loss: 0.3425 - accuracy: 0.8517
Epoch 11/20
126/126 [==============================] - 0s 3ms/step - loss: 0.3444 - accuracy: 0.8501
Epoch 12/20
126/126 [==============================] - 0s 3ms/step - loss: 0.3461 - accuracy: 0.8517
Epoch 13/20
126/126 [==============================] - 0s 3ms/step - loss: 0.3269 - accuracy: 0.8581
Epoch 14/20
126/126 [==============================] - 0s 3ms/step - loss: 0.3354 - accuracy: 0.8501
Epoch 15/20
126/126 [==============================] - 0s 3ms/step - loss: 0.3225 - accuracy: 0.8660
Epoch 16/20
126/126 [==============================] - 0s 3ms/step - loss: 0.3213 - accuracy: 0.8533
Epoch 17/20
126/126 [==============================] - 0s 3ms/step - loss: 0.3193 - accuracy: 0.8628
Epoch 18/20
126/126 [==============================] - 0s 3ms/step - loss: 0.3155 - accuracy: 0.8612
Epoch 19/20
126/126 [==============================] - 0s 3ms/step - loss: 0.3075 - accuracy: 0.8676
Epoch 20/20
126/126 [==============================] - 0s 3ms/step - loss: 0.3061 - accuracy: 0.8596

<tensorflow.python.keras.callbacks.History at 0x7f20001bdef0>

Depois que o modelo é treinado, você pode verificar sua acurácia no conjunto test_data.

test_loss, test_accuracy = model.evaluate(test_data)

print('\n\nTest Loss {}, Test Accuracy {}'.format(test_loss, test_accuracy))
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'collections.OrderedDict'> input: OrderedDict([('sex', <tf.Tensor 'ExpandDims_4:0' shape=(None, 1) dtype=string>), ('class', <tf.Tensor 'ExpandDims_1:0' shape=(None, 1) dtype=string>), ('deck', <tf.Tensor 'ExpandDims_2:0' shape=(None, 1) dtype=string>), ('embark_town', <tf.Tensor 'ExpandDims_3:0' shape=(None, 1) dtype=string>), ('alone', <tf.Tensor 'ExpandDims:0' shape=(None, 1) dtype=string>), ('numeric', <tf.Tensor 'IteratorGetNext:4' shape=(None, 4) dtype=float32>)])
Consider rewriting this model with the Functional API.
53/53 [==============================] - 0s 3ms/step - loss: 0.4557 - accuracy: 0.8447


Test Loss 0.45572778582572937, Test Accuracy 0.8446969985961914

Use tf.keras.Model.predict para inferir rótulos em um lote ou em um conjunto de dados de lotes.

predictions = model.predict(test_data)

# Mostrar alguns resultados
for prediction, survived in zip(predictions[:10], list(test_data)[0][1][:10]):
  print("Predicted survival: {:.2%}".format(prediction[0]),
        " | Actual outcome: ",
        ("SURVIVED" if bool(survived) else "DIED"))

WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'collections.OrderedDict'> input: OrderedDict([('sex', <tf.Tensor 'ExpandDims_4:0' shape=(None, 1) dtype=string>), ('class', <tf.Tensor 'ExpandDims_1:0' shape=(None, 1) dtype=string>), ('deck', <tf.Tensor 'ExpandDims_2:0' shape=(None, 1) dtype=string>), ('embark_town', <tf.Tensor 'ExpandDims_3:0' shape=(None, 1) dtype=string>), ('alone', <tf.Tensor 'ExpandDims:0' shape=(None, 1) dtype=string>), ('numeric', <tf.Tensor 'IteratorGetNext:4' shape=(None, 4) dtype=float32>)])
Consider rewriting this model with the Functional API.
Predicted survival: 598.16%  | Actual outcome:  SURVIVED
Predicted survival: 223.96%  | Actual outcome:  SURVIVED
Predicted survival: 295.53%  | Actual outcome:  DIED
Predicted survival: 493.27%  | Actual outcome:  DIED
Predicted survival: -175.62%  | Actual outcome:  SURVIVED