צפה בהערות מרכזיות, הפעלות מוצר, סדנאות ועוד מ- Google I / O ראה רשימת השמעה

טען נתוני CSV

צפה ב- TensorFlow.org הפעל בגוגל קולאב צפה במקור ב- GitHubהורד מחברת

מדריך זה מספק דוגמאות לשימוש בנתוני CSV עם TensorFlow.

ישנם שני חלקים עיקריים לכך:

  1. טוען את הנתונים מהדיסק
  2. עיבוד מראש לצורה המתאימה להכשרה.

מדריך זה מתמקד בטעינה ונותן כמה דוגמאות מהירות לעיבוד מקדים. לקבלת הדרכה המתמקדת בפן העיבוד המקדים לראות את מדריך שכבות עיבוד המקדים ו הדרכה .

להכין

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

בנתוני זיכרון

עבור כל מערך CSV קטן הדרך הפשוטה ביותר להכשיר מודל TensorFlow עליו היא לטעון אותו לזיכרון כ- Dataframe של פנדה או כמערך NumPy.

דוגמה פשוטה יחסית היא מערך הנתונים של abalone .

  • מערך הנתונים קטן.
  • כל תכונות הקלט כולן ערכי נקודה צפה לטווח מוגבל.

כך תוכל להוריד את הנתונים ל- 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()

מערך הנתונים מכיל קבוצה של מדידות של אבלון , סוג של חלזון ים.

פגז בטון

"מעטפת Abalone" (מאת ניקי דוגאן פוג , CC BY-SA 2.0)

המשימה הנומינלית עבור מערך נתונים זה היא לחזות את הגיל ממדידות אחרות, לכן הפרד בין התכונות והתוויות לאימון:

abalone_features = abalone_train.copy()
abalone_labels = abalone_features.pop('Age')

עבור מערך נתונים זה תתייחס לכל התכונות באופן זהה. ארוז את התכונות במערך 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 ]])

לאחר מכן הפוך מודל רגרסיה לחזות את הגיל. מכיוון שיש רק טנסור קלט יחיד, מספיק כאן מודל keras.Sequential .

abalone_model = tf.keras.Sequential([
  layers.Dense(64),
  layers.Dense(1)
])

abalone_model.compile(loss = tf.losses.MeanSquaredError(),
                      optimizer = tf.optimizers.Adam())

כדי להכשיר את המודל הזה, העבירו את התכונות והתוויות ל- Model.fit :

abalone_model.fit(abalone_features, abalone_labels, epochs=10)
Epoch 1/10
104/104 [==============================] - 1s 2ms/step - loss: 61.2118
Epoch 2/10
104/104 [==============================] - 0s 2ms/step - loss: 11.9831
Epoch 3/10
104/104 [==============================] - 0s 2ms/step - loss: 8.7308
Epoch 4/10
104/104 [==============================] - 0s 1ms/step - loss: 8.1889
Epoch 5/10
104/104 [==============================] - 0s 2ms/step - loss: 7.6973
Epoch 6/10
104/104 [==============================] - 0s 2ms/step - loss: 7.2868
Epoch 7/10
104/104 [==============================] - 0s 2ms/step - loss: 6.9842
Epoch 8/10
104/104 [==============================] - 0s 1ms/step - loss: 6.7556
Epoch 9/10
104/104 [==============================] - 0s 1ms/step - loss: 6.6239
Epoch 10/10
104/104 [==============================] - 0s 2ms/step - loss: 6.5126
<tensorflow.python.keras.callbacks.History at 0x7f4bf8402410>

ראית זה עתה את הדרך הבסיסית ביותר להכשיר מודל באמצעות נתוני CSV. בשלב הבא תלמד כיצד להחיל עיבוד מקדים לנורמליזציה של עמודות מספריות.

עיבוד מקדים בסיסי

זה נוהג טוב לנרמל את התשומות למודל שלך. השכבות experimental.preprocessing מקדים מספקות דרך נוחה לבנות נורמליזציה זו במודל שלך.

השכבה תחשב מראש את הממוצע והשונות של כל עמודה, ותשתמש בהן כדי לנרמל את הנתונים.

ראשית אתה יוצר את השכבה:

normalize = preprocessing.Normalization()

לאחר מכן אתה משתמש בשיטת Normalization.adapt() כדי להתאים את שכבת הנורמליזציה לנתונים שלך.

normalize.adapt(abalone_features)

ואז השתמש בשכבת הנורמליזציה במודל שלך:

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: 92.2295
Epoch 2/10
104/104 [==============================] - 0s 2ms/step - loss: 52.6606
Epoch 3/10
104/104 [==============================] - 0s 2ms/step - loss: 15.8834
Epoch 4/10
104/104 [==============================] - 0s 2ms/step - loss: 5.6510
Epoch 5/10
104/104 [==============================] - 0s 2ms/step - loss: 5.0132
Epoch 6/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9937
Epoch 7/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9871
Epoch 8/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9607
Epoch 9/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9417
Epoch 10/10
104/104 [==============================] - 0s 2ms/step - loss: 4.9781
<tensorflow.python.keras.callbacks.History at 0x7f4bf8188490>

סוגי נתונים מעורבים

מערך הנתונים "טיטאניק" מכיל מידע על הנוסעים בטיטאניק. המשימה הנומינלית במערך נתונים זה היא לחזות מי שרד.

הטיטאניק

תמונה מוויקימדיה

ניתן לטעון את הנתונים הגולמיים בקלות כ- Pandas DataFrame , אך אינם DataFrame לשימוש מיידית כקלט למודל 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')

בגלל סוגי הנתונים והטווחים השונים אתה לא יכול פשוט לערום את התכונות למערך keras.Sequential ולהעביר אותו למודל keras.Sequential . יש לטפל בכל עמודה בנפרד.

כאפשרות אחת, תוכל לעבד מראש את הנתונים שלך במצב לא מקוון (באמצעות כל כלי שתרצה) להמיר עמודות קטגוריות לעמודות מספריות, ואז להעביר את הפלט המעובד למודל TensorFlow שלך. החיסרון בגישה זו הוא שאם אתה שומר ומייצא את המודל שלך העיבוד המקדים לא נשמר באמצעותו. השכבות experimental.preprocessing נמנעות מבעיה זו מכיוון שהן חלק מהמודל.

בדוגמה זו תבנה מודל המיישם את לוגיקת העיבוד המוקדם באמצעות ממשק ה- API הפונקציונלי של Keras . אתה יכול גם לעשות זאת על ידי סיווג משנה .

ה- API הפונקציונלי פועל על טנזורים "סימבוליים". לטנדרים "להוטים" רגילים יש ערך. לעומת זאת טנזורים "סמליים" אלה אינם. במקום זאת הם עוקבים אחר הפעולות המופעלות עליהם, ובונים ייצוג של החישוב שתוכלו להריץ אחר כך. הנה דוגמה מהירה:

# 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
<KerasTensor: shape=(None,) dtype=float32 (created by layer 'tf.__operators__.add')>
calc = tf.keras.Model(inputs=input, outputs=result)
print(calc(1).numpy())
print(calc(2).numpy())
3.0
5.0

כדי לבנות את מודל keras.Input המוקדם, התחל בבניית קבוצה של keras.Input סמליות. keras.Input אובייקטים המתאימים לשמות ולסוגי הנתונים של עמודות ה- 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': <KerasTensor: shape=(None, 1) dtype=string (created by layer 'sex')>,
 'age': <KerasTensor: shape=(None, 1) dtype=float32 (created by layer 'age')>,
 'n_siblings_spouses': <KerasTensor: shape=(None, 1) dtype=float32 (created by layer 'n_siblings_spouses')>,
 'parch': <KerasTensor: shape=(None, 1) dtype=float32 (created by layer 'parch')>,
 'fare': <KerasTensor: shape=(None, 1) dtype=float32 (created by layer 'fare')>,
 'class': <KerasTensor: shape=(None, 1) dtype=string (created by layer 'class')>,
 'deck': <KerasTensor: shape=(None, 1) dtype=string (created by layer 'deck')>,
 'embark_town': <KerasTensor: shape=(None, 1) dtype=string (created by layer 'embark_town')>,
 'alone': <KerasTensor: shape=(None, 1) dtype=string (created by layer 'alone')>}

השלב הראשון בלוגיקה שלך לעיבוד מקדים הוא לשרשר את הקלטים המספריים יחד ולהריץ אותם דרך שכבת נורמליזציה:

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
<KerasTensor: shape=(None, 4) dtype=float32 (created by layer 'normalization_1')>

אסוף את כל תוצאות העיבוד המקדים הסמלי, כדי לשרשר אותן בהמשך.

preprocessed_inputs = [all_numeric_inputs]

עבור תשומות המחרוזות השתמש בפונקציה preprocessing.StringLookup כדי למפות ממחרוזות למדדי מספרים שלמים באוצר מילים. בשלב הבא, השתמשו preprocessing.CategoryEncoding float32 כדי להמיר את האינדקסים לנתוני float32 המתאימים למודל.

הגדרות ברירת המחדל לשכבת preprocessing.CategoryEncoding יוצרות וקטור חם אחד לכל קלט. layers.Embedding . layers.Embedding תעבוד גם כן. ראה מדריך שכבות העיבוד המקדים ו הדרכה עבור יותר על הנושא הזה.

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)
WARNING:tensorflow:vocab_size is deprecated, please use vocabulary_size.
WARNING:tensorflow:max_tokens is deprecated, please use num_tokens instead.
WARNING:tensorflow:vocab_size is deprecated, please use vocabulary_size.
WARNING:tensorflow:max_tokens is deprecated, please use num_tokens instead.
WARNING:tensorflow:vocab_size is deprecated, please use vocabulary_size.
WARNING:tensorflow:max_tokens is deprecated, please use num_tokens instead.
WARNING:tensorflow:vocab_size is deprecated, please use vocabulary_size.
WARNING:tensorflow:max_tokens is deprecated, please use num_tokens instead.
WARNING:tensorflow:vocab_size is deprecated, please use vocabulary_size.
WARNING:tensorflow:max_tokens is deprecated, please use num_tokens instead.

בעזרת אוסף inputs processed_inputs , תוכלו לשרשר את כל inputs processed_inputs מראש יחד, ולבנות מודל המטפל בעיבוד המקדים:

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)

png

model זה מכיל רק את עיבוד הקלט מראש. אתה יכול להריץ אותו כדי לראות מה זה עושה לנתונים שלך. מודלים של Keras אינם ממירים אוטומטית את Pandas DataFrames מכיוון שלא ברור אם יש להמיר אותו לטנזור אחד או למילון של טנזורים. אז המירו אותו למילון של טנזורים:

titanic_features_dict = {name: np.array(value) 
                         for name, value in titanic_features.items()}

פרוס את דוגמת האימון הראשונה והעביר אותה למודל זה של עיבוד מקדים, אתה רואה את המאפיינים המספריים ואת מחרוזת ה- hot-hot כולם משורשרים יחד:

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)>

כעת בנה את המודל נוסף על כך:

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)

כאשר אתה מאמן את המודל, העבר את מילון התכונות כ- x , ואת התווית כ- y .

titanic_model.fit(x=titanic_features_dict, y=titanic_labels, epochs=10)
Epoch 1/10
20/20 [==============================] - 1s 4ms/step - loss: 0.6321
Epoch 2/10
20/20 [==============================] - 0s 4ms/step - loss: 0.5221
Epoch 3/10
20/20 [==============================] - 0s 4ms/step - loss: 0.4835
Epoch 4/10
20/20 [==============================] - 0s 4ms/step - loss: 0.4595
Epoch 5/10
20/20 [==============================] - 0s 4ms/step - loss: 0.4463
Epoch 6/10
20/20 [==============================] - 0s 4ms/step - loss: 0.4381
Epoch 7/10
20/20 [==============================] - 0s 4ms/step - loss: 0.4299
Epoch 8/10
20/20 [==============================] - 0s 4ms/step - loss: 0.4272
Epoch 9/10
20/20 [==============================] - 0s 4ms/step - loss: 0.4238
Epoch 10/10
20/20 [==============================] - 0s 4ms/step - loss: 0.4236
<tensorflow.python.keras.callbacks.History at 0x7f4c97f3a0d0>

מכיוון שעיבוד המקדים הוא חלק מהמודל, אתה יכול לשמור את המודל וטען אותו מחדש במקום אחר ולקבל תוצאות זהות:

titanic_model.save('test')
reloaded = tf.keras.models.load_model('test')
INFO:tensorflow:Assets written to: test/assets
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.878]], shape=(1, 1), dtype=float32)
tf.Tensor([[-1.878]], shape=(1, 1), dtype=float32)

שימוש ב- tf.data

בחלק הקודם הסתמכת על הנתונים המובנים של המודל בדשדוש ואצווה בזמן אימון המודל.

אם אתה זקוק לשליטה רבה יותר בצינור נתוני הקלט או אם עליך להשתמש בנתונים שלא נכנסים בקלות לזיכרון: השתמש ב- tf.data

לדוגמאות נוספות עיין במדריך tf.data .

בנתוני זיכרון

כדוגמה ראשונה להחלת tf.data על נתוני CSV שקול את הקוד הבא כדי לפרוס ידנית את מילון התכונות tf.data הקודם. עבור כל אינדקס נדרש אינדקס זה לכל תכונה:

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

הפעל זאת והדפס את הדוגמה הראשונה:

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

ה-tf.data.Dataset הבסיסי ביותרtf.data.Dataset זיכרון הוא Dataset.from_tensor_slices . זה מחזירtf.data.Dataset המיישם גרסה כללית של פונקציית slices הנ"ל, ב- TensorFlow.

features_ds = tf.data.Dataset.from_tensor_slices(titanic_features_dict)

אתה יכול לחזור עלtf.data.Dataset כמו כל פיתון אחר שאפשר לחזור עליו:

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'

הפונקציה from_tensor_slices יכולה להתמודד עם כל מבנה של מילונים או from_tensor_slices מקוננות. הקוד הבא יוצר מערך נתונים של (features_dict, labels) זוגות:

titanic_ds = tf.data.Dataset.from_tensor_slices((titanic_features_dict, titanic_labels))

כדי לאמן מודל זה באמצעות Dataset , עליך לפחות shuffle ו batch הנתונים.

titanic_batches = titanic_ds.shuffle(len(titanic_labels)).batch(32)

במקום להעביר features labels ל- Model.fit , אתה מעביר את מערך הנתונים:

titanic_model.fit(titanic_batches, epochs=5)
Epoch 1/5
20/20 [==============================] - 0s 5ms/step - loss: 0.4225
Epoch 2/5
20/20 [==============================] - 0s 5ms/step - loss: 0.4214
Epoch 3/5
20/20 [==============================] - 0s 5ms/step - loss: 0.4203
Epoch 4/5
20/20 [==============================] - 0s 5ms/step - loss: 0.4203
Epoch 5/5
20/20 [==============================] - 0s 5ms/step - loss: 0.4199
<tensorflow.python.keras.callbacks.History at 0x7f4c968ccf90>

מקובץ יחיד

עד כה הדרכה זו עבדה עם נתונים בזיכרון. tf.data הוא ערכת כלים ניתנת להרחבה לבניית צינורות נתונים, ומספקת כמה פונקציות להתמודדות עם טעינת קבצי 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

כעת קרא את נתוני ה- CSV מהקובץ וצורtf.data.Dataset .

(לקבלת התיעוד המלא, ראה 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,)

פונקציה זו כוללת תכונות נוחות רבות כך שקל לעבוד איתם את הנתונים. זה כולל:

  • שימוש בכותרות העמודות כמפתחות מילון.
  • קביעה אוטומטית של סוג כל עמודה.
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'male' b'male' b'male']
age                 : [28. 44. 40. 16. 25.]
n_siblings_spouses  : [8 0 0 0 1]
parch               : [2 0 0 0 0]
fare                : [69.55   7.925  0.    10.5   91.079]
class               : [b'Third' b'Third' b'First' b'Second' b'First']
deck                : [b'unknown' b'unknown' b'B' b'unknown' b'B']
embark_town         : [b'Southampton' b'Southampton' b'Southampton' b'Southampton' b'Cherbourg']
alone               : [b'n' b'y' b'y' b'y' b'n']

label               : [0 1 0 0 1]

זה יכול גם לדחוס את הנתונים במהירות. הנה קובץ CSV gzipped המכיל את מערך התעבורה בין מדינות המטרו

פקק תנועה.

תמונה מוויקימדיה

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

הגדר את הארגומנט compression_type לקרוא ישירות מהקובץ הדחוס:

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                : [291.08 273.08 296.58 301.02 288.18]
rain_1h             : [0.   0.   0.25 0.   0.  ]
snow_1h             : [0. 0. 0. 0. 0.]
clouds_all          : [ 1 90  0  0  0]
weather_main        : [b'Clear' b'Clouds' b'Rain' b'Clear' b'Clear']
weather_description : [b'sky is clear' b'overcast clouds' b'light rain' b'Sky is Clear'
 b'Sky is Clear']
date_time           : [b'2013-05-17 01:00:00' b'2013-04-11 08:00:00' b'2013-06-12 19:00:00'
 b'2013-08-26 23:00:00' b'2013-07-25 05:00:00']

label               : [ 475 3069 3342 1133 2903]

מטמון

יש תקורה לניתוח נתוני ה- csv. עבור דגמים קטנים זה יכול להיות צוואר הבקבוק באימונים.

בהתאם למקרה השימוש שלך, זה יכול להיות רעיון טוב להשתמש ב- Dataset.cache או ב- data.experimental.snapshot כך שנתוני csv ינותחו רק בעידן הראשון.

ההבדל העיקרי בין שיטת cache לבין snapshot הוא שקובצי cache יכולים לשמש רק על ידי התהליך TensorFlow שיצר אותם, אך ניתן לקרוא קבצי snapshot ידי תהליכים אחרים.

לדוגמה, איטרציה על traffic_volume_csv_gz_ds 20 פעמים, אורכת ~ 15 שניות ללא שמירה במטמון, או ~ 2 שניות במטמון.

%%time
for i, (batch, label) in enumerate(traffic_volume_csv_gz_ds.repeat(20)):
  if i % 40 == 0:
    print('.', end='')
print()
...............................................................................................
CPU times: user 16.8 s, sys: 4.11 s, total: 20.9 s
Wall time: 12.9 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.55 s, sys: 198 ms, total: 1.75 s
Wall time: 1.37 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.31 s, sys: 396 ms, total: 2.7 s
Wall time: 1.64 s

אם טעינת הנתונים שלך מואטת על ידי טעינת קבצי csv, cache snapshot אינם מספיקים למקרה השימוש שלך, שקול קידוד מחדש של הנתונים שלך לפורמט יעיל יותר.

מספר קבצים

כל הדוגמאות עד כה בסעיף זה יכולות להיעשות בקלות ללא tf.data . מקום אחד בו tf.data באמת יכולים לפשט את הדברים הוא כאשר מתעסקים באוספי קבצים.

לדוגמא, מערך התמונות של גופני תווים מופץ כאוסף של קבצי csv, אחד לכל גופן.

גופנים

תמונה של וילי היידלבאך מ- Pixabay

הורד את מערך הנתונים, והסתכל על הקבצים שבפנים:

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

כאשר אתה מתמודד עם חבורה של קבצים תוכל להעביר קובץ_דפוס בסגנון גלוב לפונקציה experimental.make_csv_dataset . file_pattern . סדר הקבצים מדשדש בכל איטרציה.

השתמש בארגומנט num_parallel_reads כדי להגדיר כמה קבצים נקראים במקביל ושזורים זה בזה.

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)

בקבצי ה- csv הללו התמונות נשטחו לשורה אחת. שמות העמודות מעוצבים r{row}c{column} . הנה המנה הראשונה:

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'CANDARA' b'SWIS721' b'BROADWAY' b'MONOTYPE' b'VIVALDI' b'SWIS721'
 b'MINGLIU' b'SNAP' b'BROADWAY' b'OCRA']
fontVariant         : [b'CANDARA' b'SWIS721 LTEX BT' b'BROADWAY' b'MONOTYPE CORSIVA' b'VIVALDI'
 b'SWIS721 LTEX BT' b'MINGLIU_HKSCS-EXTB' b'SNAP ITC' b'BROADWAY'
 b'scanned']
m_label             : [7901  172  163 1056  214  229  215   48  123   50]
strength            : [0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4]
italic              : [0 1 1 0 1 1 1 0 1 0]
orientation         : [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
m_top               : [37 56 35 42 25 31 42 40 35  0]
m_left              : [23 36 22 25 26 27 22 22 34  0]
originalH           : [48 16 51 40 56 54 35 49 54 32]
originalW           : [39 41 49 40 57 40 40 53 36 22]
h                   : [20 20 20 20 20 20 20 20 20 20]
w                   : [20 20 20 20 20 20 20 20 20 20]
r0c0                : [  1 131   1   1   1   1   1   1   1  24]
r0c1                : [  1 255   1   1   1   1   1   1   1 227]
r0c2                : [  1 255   1   1   1   1   1   1   1  96]
r0c3                : [142 255   1   1   1   1   1   1   1 154]
...
[total: 412 features]

אופציונלי: שדות אריזה

אתה בטח לא רוצה לעבוד עם כל פיקסל בעמודות נפרדות כאלה. לפני שתנסה להשתמש במערך נתונים הקפד לארוז את הפיקסלים לטנזור תמונה.

הנה קוד המנתח את שמות העמודות לבניית תמונות לכל דוגמה:

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

החל פונקציה זו על כל אצווה במערך הנתונים:

fonts_image_ds = fonts_ds.map(make_images)

for features in fonts_image_ds.take(1):
  break

התווה את התמונות שהתקבלו:

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')
/home/kbuilder/.local/lib/python3.7/site-packages/matplotlib/backends/backend_agg.py:240: RuntimeWarning: Glyph 3926 missing from current font.
  font.set_text(s, 0.0, flags=flags)
/home/kbuilder/.local/lib/python3.7/site-packages/matplotlib/backends/backend_agg.py:203: RuntimeWarning: Glyph 3926 missing from current font.
  font.set_text(s, 0, flags=flags)

png

פונקציות ברמה נמוכה יותר

עד כה הדרכה זו התמקדה בכלי עזר ברמה הגבוהה ביותר לקריאת נתוני CSV. ישנם שני ממשקי API נוספים שעשויים להועיל למשתמשים מתקדמים אם מקרה השימוש שלך אינו מתאים לדפוסים הבסיסיים.

סעיף זה משחזר פונקציונליות המסופקת על ידי make_csv_dataset , כדי להדגים כיצד ניתן להשתמש בפונקציונליות זו ברמה נמוכה יותר.

tf.io.decode_csv

פונקציה זו מפענחת מחרוזת, או רשימת מחרוזות לרשימת עמודות.

שלא כמו make_csv_dataset פונקציה זו אינה מנסה לנחש סוגי נתונים בעמודה. אתה מציין את סוגי העמודות על ידי מתן רשימה של record_defaults המכילות ערך מהסוג הנכון, עבור כל עמודה.

כדי לקרוא את נתוני הטיטאניק כמחרוזות באמצעות decode_csv היית אומר:

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,)

כדי לנתח אותם עם הסוגים האמיתיים שלהם, צור רשימה של record_defaults מהסוגים המתאימים:

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

המחלקה tf.data.experimental.CsvDataset מספקת ממשק קבוצות Dataset מינימלי של CSV ללא תכונות הנוחות של פונקציית make_csv_dataset : ניתוח כותרת עמודות, הסקת סוג עמודות, דשדוש אוטומטי, שיבוץ קבצים.

בונה זה עוקב אחר שימוש ב- record_defaults באותו אופן כמו 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']

הקוד לעיל שווה בעצם ל:

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']

מספר קבצים

כדי לנתח את מערך הגופנים באמצעות experimental.CsvDataset , ראשית עליך לקבוע את סוגי העמודות עבור record_defaults . התחל בבדיקת השורה הראשונה של קובץ אחד:

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

רק שני השדות הראשונים הם מחרוזות, השאר הם צבעים או צף, ואתה יכול לקבל את המספר הכולל של תכונות על ידי ספירת הפסיקים:

num_font_features = font_line.count(',')+1
font_column_types = [str(), str()] + [float()]*(num_font_features-2)

CsvDatasaet של CsvDatasaet יכול לקחת רשימה של קבצי קלט, אך קורא אותם ברצף. הקובץ הראשון ברשימת ה- CSV הוא AGENCY.csv :

font_csvs[0]
'fonts/AGENCY.csv'

לכן, כאשר אתה עובר לעבור לרשימת קבצי CsvDataaset רשום מן AGENCY.csv נקראים ראשון:

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'

כדי לשלב מספר קבצים, השתמש ב- Dataset.interleave .

הנה מערך נתונים ראשוני המכיל את שמות קבצי ה- csv:

font_files = tf.data.Dataset.list_files("fonts/*.csv")

זה מטלטל את שמות הקבצים בכל תקופה:

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/MAIANDRA.csv'
     b'fonts/MODERN.csv'
     b'fonts/VIN.csv'
     b'fonts/CONSTANTIA.csv'
     b'fonts/PROXY.csv'
    ...

Epoch 2:
     b'fonts/ROMAN.csv'
     b'fonts/TAHOMA.csv'
     b'fonts/BRITANNIC.csv'
     b'fonts/OCRA.csv'
     b'fonts/TW.csv'
    ...

שיטת interleave לוקחת map_func שיוצר ילד- Dataset עבור כל רכיב בהורה- Dataset .

כאן, אתה רוצה ליצור CsvDataset מכל רכיב במערך הקבצים:

def make_font_csv_ds(path):
  return tf.data.experimental.CsvDataset(
    path, 
    record_defaults=font_column_types, 
    header=True)

Dataset הנתונים המוחזר על ידי אינטרלייב מחזיר אלמנטים על ידי רכיבה על פני מספר מערכי Dataset של הילד. שים לב, למטה, כיצד מערך הנתונים מחזור על פני cycle_length)=3 שלושה קבצי גופן:

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)

ביצועים

מוקדם יותר צוין כי io.decode_csv יעיל יותר כאשר הוא מופעל על מנת מחרוזות.

ניתן לנצל עובדה זו, כאשר משתמשים בגדלי אצווה גדולים, כדי לשפר את ביצועי הטעינה של CSV (אך נסה תחילה למטמון ).

עם המטען המובנה 20, קבוצות לדוגמה של 2048 נמשכות בערך 17.

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 27.5 s, sys: 1.53 s, total: 29 s
Wall time: 11.4 s

העברת קבוצות של שורות טקסט ל- decode_csv פועלת מהר יותר, בערך 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 9.23 s, sys: 0 ns, total: 9.23 s
Wall time: 1.52 s

לקבלת דוגמה נוספת להגדלת ביצועי csv באמצעות קבוצות גדולות, עיין במדריך overfit ו- underfit .

גישה מסוג זה עשויה לעבוד, אך שקול אפשרויות אחרות כמו cache ותמונת snapshot , או קידוד מחדש של הנתונים שלך לפורמט יעיל יותר.