קלט / פלט של גוגל חוזר 18-20 במאי! שמור מקום ובנה את לוח הזמנים שלך הירשם עכשיו

טען נתוני 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()

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

פגז בטון

"קליפה בטוחה" (מאת ניקי דוגאן פוג , 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 [==============================] - 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>

ראית זה עתה את הדרך הבסיסית ביותר להכשיר מודל באמצעות נתוני 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: 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>

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

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

הטיטאניק

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

ניתן לטעון את הנתונים הגולמיים בקלות כ- 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
<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

כדי לבנות את מודל 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': <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>}

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

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>

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

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)

בעזרת אוסף 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 [==============================] - 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>

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

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)

שימוש ב- 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 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>

מקובץ יחיד

עד כה הדרכה זו עבדה עם נתונים בזיכרון. 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'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]

זה יכול גם לדחוס את הנתונים במהירות. הנה קובץ 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                : [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]

מטמון

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

בהתאם למקרה השימוש שלך, זה יכול להיות רעיון טוב להשתמש ב- Dataset.cache או ב- data.experimental.snapshot כך 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 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

אם טעינת הנתונים שלך מואטת על ידי טעינת קבצי 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'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]

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

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

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

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

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/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'
    ...

שיטת 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 28.9 s, sys: 2.76 s, total: 31.7 s
Wall time: 11.7 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 5.4 s, sys: 0 ns, total: 5.4 s
Wall time: 4.84 s

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

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