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