![]() | ![]() | ![]() | ![]() |
Ten samouczek zawiera przykłady użycia danych CSV z TensorFlow.
Istnieją dwie główne części tego:
- Ładowanie danych z dysku
- Wstępne przetwarzanie w formę odpowiednią do treningu.
Ten samouczek skupia się na ładowaniu i podaje kilka szybkich przykładów przetwarzania wstępnego. Samouczek, który koncentruje się na aspekcie przetwarzania wstępnego, znajduje się w przewodniku i samouczku dotyczącym warstw wstępnego przetwarzania .
Ustawiać
import pandas as pd
import numpy as np
# Make numpy values easier to read.
np.set_printoptions(precision=3, suppress=True)
import tensorflow as tf
from tensorflow.keras import layers
from tensorflow.keras.layers.experimental import preprocessing
W danych pamięci
W przypadku każdego małego zestawu danych CSV najprostszym sposobem wytrenowania modelu TensorFlow na nim jest załadowanie go do pamięci jako pandy Dataframe lub tablica NumPy.
Stosunkowo prostym przykładem jest zbiór danych uchowca .
- Zbiór danych jest mały.
- Wszystkie funkcje wejściowe są wartościami zmiennoprzecinkowymi o ograniczonym zakresie.
Oto jak pobrać dane do Pandas DataFrame
:
abalone_train = pd.read_csv(
"https://storage.googleapis.com/download.tensorflow.org/data/abalone_train.csv",
names=["Length", "Diameter", "Height", "Whole weight", "Shucked weight",
"Viscera weight", "Shell weight", "Age"])
abalone_train.head()
Zbiór danych zawiera zestaw pomiarów uchowca , gatunku ślimaka morskiego.
„Abalone shell” (autor: Nicki Dugan Pogue , CC BY-SA 2.0)
Nominalne zadanie dla tego zbioru danych polega na przewidywaniu wieku na podstawie innych pomiarów, więc oddziel funkcje i etykiety do treningu:
abalone_features = abalone_train.copy()
abalone_labels = abalone_features.pop('Age')
W przypadku tego zbioru danych wszystkie funkcje będą traktowane identycznie. Spakuj funkcje do jednej tablicy NumPy .:
abalone_features = np.array(abalone_features)
abalone_features
array([[0.435, 0.335, 0.11 , ..., 0.136, 0.077, 0.097], [0.585, 0.45 , 0.125, ..., 0.354, 0.207, 0.225], [0.655, 0.51 , 0.16 , ..., 0.396, 0.282, 0.37 ], ..., [0.53 , 0.42 , 0.13 , ..., 0.374, 0.167, 0.249], [0.395, 0.315, 0.105, ..., 0.118, 0.091, 0.119], [0.45 , 0.355, 0.12 , ..., 0.115, 0.067, 0.16 ]])
Następnie wykonaj model regresji przewidujący wiek. Ponieważ istnieje tylko jeden tensor wejściowy, wystarczy tutaj model keras.Sequential
Model keras.Sequential
.
abalone_model = tf.keras.Sequential([
layers.Dense(64),
layers.Dense(1)
])
abalone_model.compile(loss = tf.losses.MeanSquaredError(),
optimizer = tf.optimizers.Adam())
Aby wytrenować ten model, przekaż funkcje i etykiety do Model.fit
:
abalone_model.fit(abalone_features, abalone_labels, epochs=10)
Epoch 1/10 104/104 [==============================] - 0s 1ms/step - loss: 60.2584 Epoch 2/10 104/104 [==============================] - 0s 1ms/step - loss: 11.4741 Epoch 3/10 104/104 [==============================] - 0s 1ms/step - loss: 8.5354 Epoch 4/10 104/104 [==============================] - 0s 1ms/step - loss: 8.0559 Epoch 5/10 104/104 [==============================] - 0s 1ms/step - loss: 7.6156 Epoch 6/10 104/104 [==============================] - 0s 1ms/step - loss: 7.2644 Epoch 7/10 104/104 [==============================] - 0s 1ms/step - loss: 6.9853 Epoch 8/10 104/104 [==============================] - 0s 1ms/step - loss: 6.7824 Epoch 9/10 104/104 [==============================] - 0s 1ms/step - loss: 6.6324 Epoch 10/10 104/104 [==============================] - 0s 1ms/step - loss: 6.5348 <tensorflow.python.keras.callbacks.History at 0x7f3f74717080>
Właśnie zobaczyłeś najbardziej podstawowy sposób trenowania modelu przy użyciu danych CSV. Następnie nauczysz się, jak zastosować przetwarzanie wstępne do normalizacji kolumn liczbowych.
Podstawowe przetwarzanie wstępne
Dobrą praktyką jest znormalizowanie danych wejściowych do modelu. Warstwy experimental.preprocessing
przetwarzania wstępnego zapewniają wygodny sposób wbudowania tej normalizacji w model.
Warstwa wstępnie obliczy średnią i wariancję każdej kolumny i użyje ich do normalizacji danych.
Najpierw tworzysz warstwę:
normalize = preprocessing.Normalization()
Następnie użyj metody Normalization.adapt()
, aby dostosować warstwę normalizacji do danych.
normalize.adapt(abalone_features)
Następnie użyj warstwy normalizacji w modelu:
norm_abalone_model = tf.keras.Sequential([
normalize,
layers.Dense(64),
layers.Dense(1)
])
norm_abalone_model.compile(loss = tf.losses.MeanSquaredError(),
optimizer = tf.optimizers.Adam())
norm_abalone_model.fit(abalone_features, abalone_labels, epochs=10)
Epoch 1/10 104/104 [==============================] - 0s 2ms/step - loss: 93.4577 Epoch 2/10 104/104 [==============================] - 0s 1ms/step - loss: 54.7412 Epoch 3/10 104/104 [==============================] - 0s 2ms/step - loss: 16.7861 Epoch 4/10 104/104 [==============================] - 0s 2ms/step - loss: 5.8002 Epoch 5/10 104/104 [==============================] - 0s 1ms/step - loss: 4.9725 Epoch 6/10 104/104 [==============================] - 0s 1ms/step - loss: 4.9332 Epoch 7/10 104/104 [==============================] - 0s 1ms/step - loss: 4.9074 Epoch 8/10 104/104 [==============================] - 0s 1ms/step - loss: 4.9035 Epoch 9/10 104/104 [==============================] - 0s 1ms/step - loss: 4.9028 Epoch 10/10 104/104 [==============================] - 0s 1ms/step - loss: 4.8998 <tensorflow.python.keras.callbacks.History at 0x7f3f7454f4e0>
Mieszane typy danych
Zbiór danych „Titanic” zawiera informacje o pasażerach Titanica. Nominalne zadanie w tym zbiorze danych polega na przewidywaniu, kto przeżył.
Zdjęcie z Wikimedia
Surowe dane można łatwo załadować jako Pandas DataFrame
, ale nie można ich natychmiast użyć jako danych wejściowych do modelu TensorFlow.
titanic = pd.read_csv("https://storage.googleapis.com/tf-datasets/titanic/train.csv")
titanic.head()
titanic_features = titanic.copy()
titanic_labels = titanic_features.pop('survived')
Ze względu na różne typy danych i zakresy nie można po prostu umieścić funkcji w tablicy NumPy i przekazać jej do modelu keras.Sequential
. Każda kolumna wymaga indywidualnego traktowania.
Jedną z opcji jest wstępne przetwarzanie danych w trybie offline (przy użyciu dowolnego narzędzia) w celu konwersji kolumn kategorialnych na kolumny liczbowe, a następnie przekazanie przetworzonych danych wyjściowych do modelu TensorFlow. Wadą tego podejścia jest to, że jeśli zapiszesz i wyeksportujesz model, przetwarzanie wstępne nie zostanie z nim zapisane. Warstwy experimental.preprocessing
przetwarzania wstępnego pozwalają uniknąć tego problemu, ponieważ są częścią modelu.
W tym przykładzie utworzysz model, który implementuje logikę przetwarzania wstępnego przy użyciu funkcjonalnego interfejsu API Keras . Możesz to również zrobić, tworząc podklasy .
Funkcjonalne API działa na „symbolicznych” tensorach. Normalne „chętne” tensory mają wartość. W przeciwieństwie do tych „symbolicznych” tensorów nie. Zamiast tego śledzą, które operacje są na nich wykonywane, i tworzą reprezentację obliczeń, którą można wykonać później. Oto krótki przykład:
# Create a symbolic input
input = tf.keras.Input(shape=(), dtype=tf.float32)
# Do a calculation using is
result = 2*input + 1
# the result doesn't have a value
result
<tf.Tensor 'AddV2:0' shape=(None,) dtype=float32>
calc = tf.keras.Model(inputs=input, outputs=result)
print(calc(1).numpy())
print(calc(2).numpy())
3.0 5.0
Aby zbudować model przetwarzania wstępnego, zacznij od zbudowania zestawu symbolicznych obiektów keras.Input
, dopasowujących nazwy i typy danych kolumn CSV.
inputs = {}
for name, column in titanic_features.items():
dtype = column.dtype
if dtype == object:
dtype = tf.string
else:
dtype = tf.float32
inputs[name] = tf.keras.Input(shape=(1,), name=name, dtype=dtype)
inputs
{'sex': <tf.Tensor 'sex:0' shape=(None, 1) dtype=string>, 'age': <tf.Tensor 'age:0' shape=(None, 1) dtype=float32>, 'n_siblings_spouses': <tf.Tensor 'n_siblings_spouses:0' shape=(None, 1) dtype=float32>, 'parch': <tf.Tensor 'parch:0' shape=(None, 1) dtype=float32>, 'fare': <tf.Tensor 'fare:0' shape=(None, 1) dtype=float32>, 'class': <tf.Tensor 'class:0' shape=(None, 1) dtype=string>, 'deck': <tf.Tensor 'deck:0' shape=(None, 1) dtype=string>, 'embark_town': <tf.Tensor 'embark_town:0' shape=(None, 1) dtype=string>, 'alone': <tf.Tensor 'alone:0' shape=(None, 1) dtype=string>}
Pierwszym krokiem w logice przetwarzania wstępnego jest połączenie razem danych liczbowych i przepuszczenie ich przez warstwę normalizacji:
numeric_inputs = {name:input for name,input in inputs.items()
if input.dtype==tf.float32}
x = layers.Concatenate()(list(numeric_inputs.values()))
norm = preprocessing.Normalization()
norm.adapt(np.array(titanic[numeric_inputs.keys()]))
all_numeric_inputs = norm(x)
all_numeric_inputs
<tf.Tensor 'normalization_1/truediv:0' shape=(None, 4) dtype=float32>
Zbierz wszystkie symboliczne wyniki przetwarzania wstępnego, aby później je połączyć.
preprocessed_inputs = [all_numeric_inputs]
W przypadku wejściowych ciągów znaków użyj funkcji preprocessing.StringLookup
aby odwzorować ciągi na indeksy całkowite w słowniku. Następnie użyj preprocessing.CategoryEncoding
aby przekonwertować indeksy na dane typu float32
odpowiednie dla modelu.
Domyślne ustawienia dla warstwy preprocessing.CategoryEncoding
tworzą jeden gorący wektor dla każdego wejścia. layers.Embedding
również zadziała. Więcej informacji na ten temat zawiera przewodnik i samouczek dotyczący wstępnego przetwarzania warstw .
for name, input in inputs.items():
if input.dtype == tf.float32:
continue
lookup = preprocessing.StringLookup(vocabulary=np.unique(titanic_features[name]))
one_hot = preprocessing.CategoryEncoding(max_tokens=lookup.vocab_size())
x = lookup(input)
x = one_hot(x)
preprocessed_inputs.append(x)
Dzięki kolekcji danych inputs
i processed_inputs
można połączyć wszystkie wstępnie przetworzone dane wejściowe razem i zbudować model, który obsługuje przetwarzanie wstępne:
preprocessed_inputs_cat = layers.Concatenate()(preprocessed_inputs)
titanic_preprocessing = tf.keras.Model(inputs, preprocessed_inputs_cat)
tf.keras.utils.plot_model(model = titanic_preprocessing , rankdir="LR", dpi=72, show_shapes=True)
Ten model
zawiera tylko wstępne przetwarzanie danych wejściowych. Możesz go uruchomić, aby zobaczyć, co robi z Twoimi danymi. Modele Keras nie konwertują automatycznie Pandas DataFrames
ponieważ nie jest jasne, czy należy je przekonwertować na jeden tensor, czy na słownik tensorów. Więc przekonwertuj to na słownik tensorów:
titanic_features_dict = {name: np.array(value)
for name, value in titanic_features.items()}
Wytnij pierwszy przykład szkoleniowy i przekaż go do tego modelu przetwarzania wstępnego, zobaczysz, że funkcje numeryczne i ciągi jedności są połączone razem:
features_dict = {name:values[:1] for name, values in titanic_features_dict.items()}
titanic_preprocessing(features_dict)
<tf.Tensor: shape=(1, 33), dtype=float32, numpy= array([[-0.61 , 0.395, -0.479, -0.497, 0. , 0. , 0. , 1. , 0. , 0. , 0. , 0. , 1. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 1. , 0. , 0. , 0. , 0. , 1. , 0. , 0. , 0. , 1. , 0. ]], dtype=float32)>
Teraz zbuduj model na podstawie tego:
def titanic_model(preprocessing_head, inputs):
body = tf.keras.Sequential([
layers.Dense(64),
layers.Dense(1)
])
preprocessed_inputs = preprocessing_head(inputs)
result = body(preprocessed_inputs)
model = tf.keras.Model(inputs, result)
model.compile(loss=tf.losses.BinaryCrossentropy(from_logits=True),
optimizer=tf.optimizers.Adam())
return model
titanic_model = titanic_model(titanic_preprocessing, inputs)
Podczas trenowania modelu przekaż słownik elementów jako x
, a etykietę jako y
.
titanic_model.fit(x=titanic_features_dict, y=titanic_labels, epochs=10)
Epoch 1/10 20/20 [==============================] - 0s 3ms/step - loss: 0.6595 Epoch 2/10 20/20 [==============================] - 0s 3ms/step - loss: 0.5576 Epoch 3/10 20/20 [==============================] - 0s 3ms/step - loss: 0.5002 Epoch 4/10 20/20 [==============================] - 0s 3ms/step - loss: 0.4719 Epoch 5/10 20/20 [==============================] - 0s 3ms/step - loss: 0.4533 Epoch 6/10 20/20 [==============================] - 0s 3ms/step - loss: 0.4415 Epoch 7/10 20/20 [==============================] - 0s 3ms/step - loss: 0.4346 Epoch 8/10 20/20 [==============================] - 0s 3ms/step - loss: 0.4299 Epoch 9/10 20/20 [==============================] - 0s 3ms/step - loss: 0.4285 Epoch 10/10 20/20 [==============================] - 0s 3ms/step - loss: 0.4261 <tensorflow.python.keras.callbacks.History at 0x7f3f74239b70>
Ponieważ wstępne przetwarzanie jest częścią modelu, możesz zapisać model i załadować go ponownie w innym miejscu i uzyskać identyczne wyniki:
titanic_model.save('test')
reloaded = tf.keras.models.load_model('test')
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Model.state_updates (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version. Instructions for updating: This property should not be used in TensorFlow 2.0, as updates are applied automatically. WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Layer.updates (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version. Instructions for updating: This property should not be used in TensorFlow 2.0, as updates are applied automatically. INFO:tensorflow:Assets written to: test/assets WARNING:tensorflow:5 out of the last 5 calls to <function recreate_function.<locals>.restored_function_body at 0x7f3f60030950> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.
features_dict = {name:values[:1] for name, values in titanic_features_dict.items()}
before = titanic_model(features_dict)
after = reloaded(features_dict)
assert (before-after)<1e-3
print(before)
print(after)
tf.Tensor([[-1.903]], shape=(1, 1), dtype=float32) tf.Tensor([[-1.903]], shape=(1, 1), dtype=float32)
Korzystanie z tf.data
W poprzedniej sekcji podczas uczenia modelu polegałeś na tasowaniu i wsadowaniu danych wbudowanym w model.
Jeśli potrzebujesz większej kontroli nad tf.data
danych wejściowych lub potrzebujesz danych, które nie mieszczą się łatwo w pamięci: użyj tf.data
.
Więcej przykładów można znaleźć w przewodniku tf.data .
Dane w pamięci włączone
Jako pierwszy przykład zastosowania tf.data
do danych CSV rozważ poniższy kod, aby ręcznie tf.data
słownik funkcji z poprzedniej sekcji. Dla każdego indeksu przyjmuje ten indeks dla każdej funkcji:
import itertools
def slices(features):
for i in itertools.count():
# For each feature take index `i`
example = {name:values[i] for name, values in features.items()}
yield example
Uruchom to i wydrukuj pierwszy przykład:
for example in slices(titanic_features_dict):
for name, value in example.items():
print(f"{name:19s}: {value}")
break
sex : male age : 22.0 n_siblings_spouses : 1 parch : 0 fare : 7.25 class : Third deck : unknown embark_town : Southampton alone : n
Najbardziej podstawowymtf.data.Dataset
w programie ładującym dane pamięci jest konstruktor Dataset.from_tensor_slices
. Zwracatf.data.Dataset
który implementuje uogólnioną wersję powyższej funkcji slices
w TensorFlow.
features_ds = tf.data.Dataset.from_tensor_slices(titanic_features_dict)
Możesz iterować potf.data.Dataset
jak każdy innytf.data.Dataset
Python:
for example in features_ds:
for name, value in example.items():
print(f"{name:19s}: {value}")
break
sex : b'male' age : 22.0 n_siblings_spouses : 1 parch : 0 fare : 7.25 class : b'Third' deck : b'unknown' embark_town : b'Southampton' alone : b'n'
Funkcja from_tensor_slices
może obsługiwać dowolną strukturę zagnieżdżonych słowników lub krotek. Poniższy kod tworzy zbiór danych par (features_dict, labels)
:
titanic_ds = tf.data.Dataset.from_tensor_slices((titanic_features_dict, titanic_labels))
Aby wytrenować model przy użyciu tego Dataset
, musisz przynajmniej shuffle
i batch
dane.
titanic_batches = titanic_ds.shuffle(len(titanic_labels)).batch(32)
Zamiast przekazywać features
i labels
do Model.fit
, przekazujesz zbiór danych:
titanic_model.fit(titanic_batches, epochs=5)
Epoch 1/5 20/20 [==============================] - 0s 4ms/step - loss: 0.4233 Epoch 2/5 20/20 [==============================] - 0s 4ms/step - loss: 0.4223 Epoch 3/5 20/20 [==============================] - 0s 4ms/step - loss: 0.4225 Epoch 4/5 20/20 [==============================] - 0s 4ms/step - loss: 0.4213 Epoch 5/5 20/20 [==============================] - 0s 4ms/step - loss: 0.4209 <tensorflow.python.keras.callbacks.History at 0x7f3f680cd9b0>
Z jednego pliku
Do tej pory ten samouczek działał z danymi w pamięci. tf.data
to wysoce skalowalny zestaw narzędzi do tworzenia potoków danych i zapewnia kilka funkcji do obsługi ładowania plików CSV.
titanic_file_path = tf.keras.utils.get_file("train.csv", "https://storage.googleapis.com/tf-datasets/titanic/train.csv")
Downloading data from https://storage.googleapis.com/tf-datasets/titanic/train.csv 32768/30874 [===============================] - 0s 0us/step
Teraz przeczytaj dane CSV z pliku i utwórztf.data.Dataset
.
(Aby uzyskać pełną dokumentację, zobacz tf.data.experimental.make_csv_dataset
)
titanic_csv_ds = tf.data.experimental.make_csv_dataset(
titanic_file_path,
batch_size=5, # Artificially small to make examples easier to show.
label_name='survived',
num_epochs=1,
ignore_errors=True,)
Ta funkcja zawiera wiele wygodnych funkcji, które ułatwiają pracę z danymi. To zawiera:
- Używanie nagłówków kolumn jako kluczy słownika.
- Automatyczne określanie typu każdej kolumny.
for batch, label in titanic_csv_ds.take(1):
for key, value in batch.items():
print(f"{key:20s}: {value}")
print()
print(f"{'label':20s}: {label}")
sex : [b'male' b'male' b'female' b'male' b'female'] age : [28. 11. 29. 28. 34.] n_siblings_spouses : [1 0 1 0 0] parch : [0 0 0 0 0] fare : [ 24.15 18.788 26. 221.779 10.5 ] class : [b'Third' b'Third' b'Second' b'First' b'Second'] deck : [b'unknown' b'unknown' b'unknown' b'C' b'F'] embark_town : [b'Queenstown' b'Cherbourg' b'Southampton' b'Southampton' b'Southampton'] alone : [b'n' b'y' b'n' b'y' b'y'] label : [0 0 1 0 1]
Może również dekompresować dane w locie. Oto plik CSV spakowany gzipem, zawierający zbiór danych o ruchu na drogach międzystanowych dla metra
Zdjęcie z Wikimedia
traffic_volume_csv_gz = tf.keras.utils.get_file(
'Metro_Interstate_Traffic_Volume.csv.gz',
"https://archive.ics.uci.edu/ml/machine-learning-databases/00492/Metro_Interstate_Traffic_Volume.csv.gz",
cache_dir='.', cache_subdir='traffic')
Downloading data from https://archive.ics.uci.edu/ml/machine-learning-databases/00492/Metro_Interstate_Traffic_Volume.csv.gz 409600/405373 [==============================] - 1s 2us/step
Ustaw argument compression_type
na odczyt bezpośrednio z pliku skompresowanego:
traffic_volume_csv_gz_ds = tf.data.experimental.make_csv_dataset(
traffic_volume_csv_gz,
batch_size=256,
label_name='traffic_volume',
num_epochs=1,
compression_type="GZIP")
for batch, label in traffic_volume_csv_gz_ds.take(1):
for key, value in batch.items():
print(f"{key:20s}: {value[:5]}")
print()
print(f"{'label':20s}: {label[:5]}")
holiday : [b'None' b'None' b'None' b'None' b'None'] temp : [280.66 283.22 284.23 280.7 287.79] rain_1h : [0. 0. 0. 0. 0.] snow_1h : [0. 0. 0. 0. 0.] clouds_all : [92 90 90 64 75] weather_main : [b'Mist' b'Drizzle' b'Rain' b'Clouds' b'Clouds'] weather_description : [b'mist' b'light intensity drizzle' b'light rain' b'broken clouds' b'broken clouds'] date_time : [b'2012-10-19 21:00:00' b'2012-10-25 21:00:00' b'2013-05-23 16:00:00' b'2013-11-19 14:00:00' b'2013-05-16 08:00:00'] label : [2942 2587 6305 5242 6404]
Buforowanie
Analiza danych CSV wiąże się z pewnym obciążeniem. W przypadku małych modeli może to stanowić wąskie gardło w treningu.
W zależności od przypadku użycia dobrym pomysłem może być użycie Dataset.cache
lub data.experimental.snapshot
aby dane csv były analizowane tylko w pierwszej epoce.
Główna różnica między metodami cache
i snapshot
polega na tym, że pliki cache
mogą być używane tylko przez proces TensorFlow, który je utworzył, ale pliki snapshot
mogą być odczytywane przez inne procesy.
Na przykład wykonanie iteracji przez traffic_volume_csv_gz_ds
20 razy zajmuje ~ 15 sekund bez buforowania lub ~ 2s z buforowaniem.
%%time
for i, (batch, label) in enumerate(traffic_volume_csv_gz_ds.repeat(20)):
if i % 40 == 0:
print('.', end='')
print()
............................................................................................... CPU times: user 15.4 s, sys: 3.98 s, total: 19.4 s Wall time: 12.4 s
%%time
caching = traffic_volume_csv_gz_ds.cache().shuffle(1000)
for i, (batch, label) in enumerate(caching.shuffle(1000).repeat(20)):
if i % 40 == 0:
print('.', end='')
print()
............................................................................................... CPU times: user 1.48 s, sys: 179 ms, total: 1.65 s Wall time: 1.32 s
%%time
snapshot = tf.data.experimental.snapshot('titanic.tfsnap')
snapshotting = traffic_volume_csv_gz_ds.apply(snapshot).shuffle(1000)
for i, (batch, label) in enumerate(snapshotting.shuffle(1000).repeat(20)):
if i % 40 == 0:
print('.', end='')
print()
............................................................................................... CPU times: user 2.09 s, sys: 449 ms, total: 2.54 s Wall time: 1.64 s
Jeśli ładowanie danych jest spowolnione przez ładowanie plików csv, a cache
i snapshot
są niewystarczające dla Twojego przypadku użycia, rozważ ponowne zakodowanie danych do bardziej uproszczonego formatu.
Wiele plików
Wszystkie dotychczasowe przykłady w tej sekcji można by łatwo wykonać bez tf.data
. Jednym z miejsc, w których tf.data
może naprawdę uprościć tf.data
jest tf.data
ze zbiorami plików.
Na przykład zestaw danych obrazów czcionek znaków jest rozpowszechniany jako zbiór plików csv, po jednym na czcionkę.
Obraz Willi Heidelbach z Pixabay
Pobierz zestaw danych i spójrz na pliki w środku:
fonts_zip = tf.keras.utils.get_file(
'fonts.zip', "https://archive.ics.uci.edu/ml/machine-learning-databases/00417/fonts.zip",
cache_dir='.', cache_subdir='fonts',
extract=True)
Downloading data from https://archive.ics.uci.edu/ml/machine-learning-databases/00417/fonts.zip 160317440/160313983 [==============================] - 8s 0us/step
import pathlib
font_csvs = sorted(str(p) for p in pathlib.Path('fonts').glob("*.csv"))
font_csvs[:10]
['fonts/AGENCY.csv', 'fonts/ARIAL.csv', 'fonts/BAITI.csv', 'fonts/BANKGOTHIC.csv', 'fonts/BASKERVILLE.csv', 'fonts/BAUHAUS.csv', 'fonts/BELL.csv', 'fonts/BERLIN.csv', 'fonts/BERNARD.csv', 'fonts/BITSTREAMVERA.csv']
len(font_csvs)
153
Gdy mamy do czynienia z wieloma plikami można przekazać glob stylu file_pattern
do experimental.make_csv_dataset
funkcji. Kolejność plików jest tasowana w każdej iteracji.
Użyj argumentu num_parallel_reads
aby ustawić, ile plików jest odczytywanych równolegle i przeplatanych razem.
fonts_ds = tf.data.experimental.make_csv_dataset(
file_pattern = "fonts/*.csv",
batch_size=10, num_epochs=1,
num_parallel_reads=20,
shuffle_buffer_size=10000)
Te pliki csv mają obrazy spłaszczone w jednym wierszu. Nazwy kolumn są sformatowane r{row}c{column}
. Oto pierwsza partia:
for features in fonts_ds.take(1):
for i, (name, value) in enumerate(features.items()):
if i>15:
break
print(f"{name:20s}: {value}")
print('...')
print(f"[total: {len(features)} features]")
font : [b'HARRINGTON' b'ISOC' b'GEORGIA' b'CAMBRIA' b'PROXY' b'SNAP' b'COUNTRYBLUEPRINT' b'PROXY' b'VIVALDI' b'GILL'] fontVariant : [b'HARRINGTON' b'ISOCTEUR' b'GEORGIA' b'CAMBRIA' b'PROXY 9' b'SNAP ITC' b'COUNTRYBLUEPRINT' b'PROXY 9' b'VIVALDI' b'GILL SANS ULTRA BOLD CONDENSED'] m_label : [ 94 8800 8539 10659 305 8223 8217 728 170 115] strength : [0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4] italic : [0 0 0 0 1 0 0 1 0 0] orientation : [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] m_top : [49 55 35 46 52 41 38 36 36 46] m_left : [22 33 24 22 27 23 24 40 27 21] originalH : [17 30 48 37 34 59 54 9 25 41] originalW : [30 21 62 39 12 34 25 24 27 26] h : [20 20 20 20 20 20 20 20 20 20] w : [20 20 20 20 20 20 20 20 20 20] r0c0 : [ 1 1 1 1 1 255 255 213 1 1] r0c1 : [ 1 1 3 1 1 255 255 1 1 1] r0c2 : [ 1 1 88 1 1 255 255 1 1 1] r0c3 : [ 1 1 232 1 1 255 255 1 1 1] ... [total: 412 features]
Opcjonalnie: pola pakowania
Prawdopodobnie nie chcesz pracować z każdym pikselem w oddzielnych kolumnach, takich jak ta. Przed próbą użycia tego zestawu danych należy spakować piksele do tensora obrazu.
Oto kod, który analizuje nazwy kolumn, aby utworzyć obrazy dla każdego przykładu:
import re
def make_images(features):
image = [None]*400
new_feats = {}
for name, value in features.items():
match = re.match('r(\d+)c(\d+)', name)
if match:
image[int(match.group(1))*20+int(match.group(2))] = value
else:
new_feats[name] = value
image = tf.stack(image, axis=0)
image = tf.reshape(image, [20, 20, -1])
new_feats['image'] = image
return new_feats
Zastosuj tę funkcję do każdej partii w zestawie danych:
fonts_image_ds = fonts_ds.map(make_images)
for features in fonts_image_ds.take(1):
break
Wykreśl wynikowe obrazy:
from matplotlib import pyplot as plt
plt.figure(figsize=(6,6), dpi=120)
for n in range(9):
plt.subplot(3,3,n+1)
plt.imshow(features['image'][..., n])
plt.title(chr(features['m_label'][n]))
plt.axis('off')
Funkcje niższego poziomu
Jak dotąd ten samouczek skupiał się na narzędziach najwyższego poziomu do odczytywania danych CSV. Istnieją inne dwa interfejsy API, które mogą być pomocne dla zaawansowanych użytkowników, jeśli Twój przypadek użycia nie pasuje do podstawowych wzorców.
-
tf.io.decode_csv
- funkcja do parsowania wierszy tekstu do listy tensorów kolumn CSV. -
tf.data.experimental.CsvDataset
- konstruktor zbioru danych CSV niższego poziomu.
Ta sekcja odtwarza funkcjonalność dostarczaną przez make_csv_dataset
, aby zademonstrować, jak można wykorzystać tę funkcjonalność niższego poziomu.
tf.io.decode_csv
Ta funkcja dekoduje ciąg lub listę ciągów do listy kolumn.
W przeciwieństwie do make_csv_dataset
ta funkcja nie próbuje odgadnąć typów danych kolumn. Typy kolumn określa się, podając listę record_defaults
zawierającą wartość odpowiedniego typu dla każdej kolumny.
Aby odczytać dane Titanica jako ciągi przy użyciu decode_csv
, powiedziałbyś:
text = pathlib.Path(titanic_file_path).read_text()
lines = text.split('\n')[1:-1]
all_strings = [str()]*10
all_strings
['', '', '', '', '', '', '', '', '', '']
features = tf.io.decode_csv(lines, record_defaults=all_strings)
for f in features:
print(f"type: {f.dtype.name}, shape: {f.shape}")
type: string, shape: (627,) type: string, shape: (627,) type: string, shape: (627,) type: string, shape: (627,) type: string, shape: (627,) type: string, shape: (627,) type: string, shape: (627,) type: string, shape: (627,) type: string, shape: (627,) type: string, shape: (627,)
Aby przeanalizować je z ich rzeczywistymi typami, utwórz listę record_defaults
odpowiednich typów:
print(lines[0])
0,male,22.0,1,0,7.25,Third,unknown,Southampton,n
titanic_types = [int(), str(), float(), int(), int(), float(), str(), str(), str(), str()]
titanic_types
[0, '', 0.0, 0, 0, 0.0, '', '', '', '']
features = tf.io.decode_csv(lines, record_defaults=titanic_types)
for f in features:
print(f"type: {f.dtype.name}, shape: {f.shape}")
type: int32, shape: (627,) type: string, shape: (627,) type: float32, shape: (627,) type: int32, shape: (627,) type: int32, shape: (627,) type: float32, shape: (627,) type: string, shape: (627,) type: string, shape: (627,) type: string, shape: (627,) type: string, shape: (627,)
tf.data.experimental.CsvDataset
Klasa tf.data.experimental.CsvDataset
zapewnia minimalny interfejs CSV Dataset
bez wygodnych funkcji make_csv_dataset
: analizowanie nagłówków kolumn, wnioskowanie o typach kolumn, automatyczne tasowanie, przeplatanie plików.
Poniższy konstruktor używa record_defaults
taki sam sposób jak io.parse_csv
:
simple_titanic = tf.data.experimental.CsvDataset(titanic_file_path, record_defaults=titanic_types, header=True)
for example in simple_titanic.take(1):
print([e.numpy() for e in example])
[0, b'male', 22.0, 1, 0, 7.25, b'Third', b'unknown', b'Southampton', b'n']
Powyższy kod jest w zasadzie równoważny z:
def decode_titanic_line(line):
return tf.io.decode_csv(line, titanic_types)
manual_titanic = (
# Load the lines of text
tf.data.TextLineDataset(titanic_file_path)
# Skip the header row.
.skip(1)
# Decode the line.
.map(decode_titanic_line)
)
for example in manual_titanic.take(1):
print([e.numpy() for e in example])
[0, b'male', 22.0, 1, 0, 7.25, b'Third', b'unknown', b'Southampton', b'n']
Wiele plików
Aby przeanalizować zestaw danych czcionek przy użyciu experimental.CsvDataset
, należy najpierw określić typy kolumn dla parametru record_defaults
. Zacznij od sprawdzenia pierwszego wiersza jednego pliku:
font_line = pathlib.Path(font_csvs[0]).read_text().splitlines()[1]
print(font_line)
AGENCY,AGENCY FB,64258,0.400000,0,0.000000,35,21,51,22,20,20,1,1,1,21,101,210,255,255,255,255,255,255,255,255,255,255,255,255,255,255,1,1,1,93,255,255,255,176,146,146,146,146,146,146,146,146,216,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,141,141,141,182,255,255,255,172,141,141,141,115,1,1,1,1,163,255,255,255,255,255,255,255,255,255,255,255,255,255,255,209,1,1,1,1,163,255,255,255,6,6,6,96,255,255,255,74,6,6,6,5,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255,1,1,1,93,255,255,255,70,1,1,1,1,1,1,1,1,163,255,255,255
Tylko dwa pierwsze pola to ciągi, reszta to liczby całkowite lub zmiennoprzecinkowe, a całkowitą liczbę funkcji można uzyskać, licząc przecinki:
num_font_features = font_line.count(',')+1
font_column_types = [str(), str()] + [float()]*(num_font_features-2)
Konstruktor CsvDatasaet
może pobrać listę plików wejściowych, ale czyta je sekwencyjnie. Pierwszy plik na liście plików AGENCY.csv
to AGENCY.csv
:
font_csvs[0]
'fonts/AGENCY.csv'
Więc kiedy przekazujesz listę plików do CsvDataaset
rekordy z AGENCY.csv
są czytane jako pierwsze:
simple_font_ds = tf.data.experimental.CsvDataset(
font_csvs,
record_defaults=font_column_types,
header=True)
for row in simple_font_ds.take(10):
print(row[0].numpy())
b'AGENCY' b'AGENCY' b'AGENCY' b'AGENCY' b'AGENCY' b'AGENCY' b'AGENCY' b'AGENCY' b'AGENCY' b'AGENCY'
Aby przeplatać wiele plików, użyj Dataset.interleave
.
Oto początkowy zbiór danych zawierający nazwy plików CSV:
font_files = tf.data.Dataset.list_files("fonts/*.csv")
Powoduje to tasowanie nazw plików w każdej epoce:
print('Epoch 1:')
for f in list(font_files)[:5]:
print(" ", f.numpy())
print(' ...')
print()
print('Epoch 2:')
for f in list(font_files)[:5]:
print(" ", f.numpy())
print(' ...')
Epoch 1: b'fonts/ELEPHANT.csv' b'fonts/NINA.csv' b'fonts/COPPERPLATE.csv' b'fonts/GOTHICE.csv' b'fonts/SWIS721.csv' ... Epoch 2: b'fonts/PALACE.csv' b'fonts/GABRIOLA.csv' b'fonts/COURIER.csv' b'fonts/CONSTANTIA.csv' b'fonts/QUICKTYPE.csv' ...
Metoda interleave
przyjmuje map_func
która tworzy map_func
Dataset
dla każdego elementu nadrzędnego Dataset
.
Tutaj chcesz utworzyć CsvDataset
z każdego elementu zbioru danych plików:
def make_font_csv_ds(path):
return tf.data.experimental.CsvDataset(
path,
record_defaults=font_column_types,
header=True)
Zestaw Dataset
zwrócony przez przeplot zwraca elementy, cyklicznie po kilku podrzędnych zestawach Dataset
. Zwróć uwagę poniżej, w jaki sposób zestaw danych przechodzi cyklicznie przez cycle_length)=3
trzy pliki czcionek:
font_rows = font_files.interleave(make_font_csv_ds,
cycle_length=3)
fonts_dict = {'font_name':[], 'character':[]}
for row in font_rows.take(10):
fonts_dict['font_name'].append(row[0].numpy().decode())
fonts_dict['character'].append(chr(row[2].numpy()))
pd.DataFrame(fonts_dict)
Występ
Wcześniej zauważono, że io.decode_csv
jest bardziej wydajne, gdy jest uruchamiane na partii ciągów.
Możliwe jest wykorzystanie tego faktu, gdy używasz dużych rozmiarów partii, aby poprawić wydajność ładowania CSV (ale najpierw spróbuj buforować ).
Z wbudowanym programem ładującym 20, 2048 przykładowych partii zajmuje około 17 sekund.
BATCH_SIZE=2048
fonts_ds = tf.data.experimental.make_csv_dataset(
file_pattern = "fonts/*.csv",
batch_size=BATCH_SIZE, num_epochs=1,
num_parallel_reads=100)
%%time
for i,batch in enumerate(fonts_ds.take(20)):
print('.',end='')
print()
.................... CPU times: user 28.9 s, sys: 2.76 s, total: 31.7 s Wall time: 11.7 s
Przekazywanie partii linii tekstu do decode_csv
przebiega szybciej, w około 5 decode_csv
:
fonts_files = tf.data.Dataset.list_files("fonts/*.csv")
fonts_lines = fonts_files.interleave(
lambda fname:tf.data.TextLineDataset(fname).skip(1),
cycle_length=100).batch(BATCH_SIZE)
fonts_fast = fonts_lines.map(lambda x: tf.io.decode_csv(x, record_defaults=font_column_types))
%%time
for i,batch in enumerate(fonts_fast.take(20)):
print('.',end='')
print()
.................... CPU times: user 5.4 s, sys: 0 ns, total: 5.4 s Wall time: 4.84 s
Aby zapoznać się z innym przykładem zwiększania wydajności CSV przy użyciu dużych partii, zobacz samouczek dotyczący nadmiernego dopasowania i niedopasowania .
Takie podejście może działać, ale weź pod uwagę inne opcje, takie jak cache
i snapshot
, lub ponowne kodowanie danych do bardziej usprawnionego formatu.