דף זה תורגם על ידי Cloud Translation API.
Switch to English

tf.data: בניית צינורות קלט TensorFlow

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

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

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

יש שתי דרכים ברורות ליצור מערך נתונים:

  • מקור נתונים בונה Dataset נתונים מנתונים המאוחסנים בזיכרון או בקובץ אחד או יותר.

  • טרנספורמציית נתונים בונה מערך נתונים מאובייקטים tf.data.Dataset או יותר.

 import tensorflow as tf
 
 import pathlib
import os
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

np.set_printoptions(precision=4)
 

מכניקה בסיסית

כדי ליצור צינור קלט, עליך להתחיל עם מקור נתונים. לדוגמה, כדי לבנות Dataset נתונים מנתונים בזיכרון, אתה יכול להשתמש tf.data.Dataset.from_tensors() או tf.data.Dataset.from_tensor_slices() . לחלופין, אם נתוני הקלט שלך מאוחסנים בקובץ בפורמט TFRecord המומלץ, באפשרותך להשתמש tf.data.TFRecordDataset() .

ברגע שיש לך אובייקט ערכת Dataset , אתה יכול להפוך אותו tf.data.Dataset Dataset חדש באמצעות שיטת שרשור קוראת לאובייקט tf.data.Dataset . לדוגמה, באפשרותך להחיל טרנספורמציות לכל אלמנט כגון Dataset.map() , וטרנספורמציות מרובות אלמנטים כגון Dataset.batch() . עיין בתיעוד עבור tf.data.Dataset לרשימה מלאה של טרנספורמציות.

האובייקט Dataset הנתונים הוא ניתן לאפשר פיתון. זה מאפשר לצרוך את האלמנטים שלו באמצעות לולאה for:

 dataset = tf.data.Dataset.from_tensor_slices([8, 3, 0, 8, 2, 1])
dataset
 
<TensorSliceDataset shapes: (), types: tf.int32>
 for elem in dataset:
  print(elem.numpy())
 
8
3
0
8
2
1

או על ידי יצירת מפורשות של איטרטור פייתון באמצעות iter וצריכת האלמנטים שלו באמצעות next :

 it = iter(dataset)

print(next(it).numpy())
 
8

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

 print(dataset.reduce(0, lambda state, value: state + value).numpy())
 
22

מבנה מערך הנתונים

מערך נתונים מכיל אלמנטים שלכל אחד מהם אותו מבנה (מקונן) והרכיבים הבודדים במבנה יכולים להיות מכל סוג שניתן לייצג על ידי tf.TypeSpec , כולל tf.Tensor , tf.sparse.SparseTensor , tf.RaggedTensor , tf.TensorArray , או tf.data.Dataset .

המאפיין Dataset.element_spec מאפשר לך לבדוק את סוג כל רכיב רכיב. המאפיין מחזיר מבנה מקונן של אובייקטים tf.TypeSpec , שמתאים למבנה האלמנט, שיכול להיות רכיב יחיד, כותרת של רכיבים, או כותרת של רכיבים מקוננים. לדוגמה:

 dataset1 = tf.data.Dataset.from_tensor_slices(tf.random.uniform([4, 10]))

dataset1.element_spec
 
TensorSpec(shape=(10,), dtype=tf.float32, name=None)
 dataset2 = tf.data.Dataset.from_tensor_slices(
   (tf.random.uniform([4]),
    tf.random.uniform([4, 100], maxval=100, dtype=tf.int32)))

dataset2.element_spec
 
(TensorSpec(shape=(), dtype=tf.float32, name=None),
 TensorSpec(shape=(100,), dtype=tf.int32, name=None))
 dataset3 = tf.data.Dataset.zip((dataset1, dataset2))

dataset3.element_spec
 
(TensorSpec(shape=(10,), dtype=tf.float32, name=None),
 (TensorSpec(shape=(), dtype=tf.float32, name=None),
  TensorSpec(shape=(100,), dtype=tf.int32, name=None)))
 # Dataset containing a sparse tensor.
dataset4 = tf.data.Dataset.from_tensors(tf.SparseTensor(indices=[[0, 0], [1, 2]], values=[1, 2], dense_shape=[3, 4]))

dataset4.element_spec
 
SparseTensorSpec(TensorShape([3, 4]), tf.int32)
 # Use value_type to see the type of value represented by the element spec
dataset4.element_spec.value_type
 
tensorflow.python.framework.sparse_tensor.SparseTensor

טרנספורמציות Dataset הנתונים תומכות במערכי נתונים של כל מבנה. בעת שימוש Dataset.map() ו- Dataset.filter() החלות על כל אלמנט פונקציה, מבנה האלמנט קובע את טיעוני הפונקציה:

 dataset1 = tf.data.Dataset.from_tensor_slices(
    tf.random.uniform([4, 10], minval=1, maxval=10, dtype=tf.int32))

dataset1
 
<TensorSliceDataset shapes: (10,), types: tf.int32>
 for z in dataset1:
  print(z.numpy())
 
[8 1 2 6 1 7 2 6 1 3]
[6 5 6 5 3 5 2 5 3 6]
[5 8 4 8 3 1 4 6 4 8]
[2 4 5 8 3 5 7 9 4 2]

 dataset2 = tf.data.Dataset.from_tensor_slices(
   (tf.random.uniform([4]),
    tf.random.uniform([4, 100], maxval=100, dtype=tf.int32)))

dataset2
 
<TensorSliceDataset shapes: ((), (100,)), types: (tf.float32, tf.int32)>
 dataset3 = tf.data.Dataset.zip((dataset1, dataset2))

dataset3
 
<ZipDataset shapes: ((10,), ((), (100,))), types: (tf.int32, (tf.float32, tf.int32))>
 for a, (b,c) in dataset3:
  print('shapes: {a.shape}, {b.shape}, {c.shape}'.format(a=a, b=b, c=c))
 
shapes: (10,), (), (100,)
shapes: (10,), (), (100,)
shapes: (10,), (), (100,)
shapes: (10,), (), (100,)

קריאת נתוני קלט

צורכים מערכי NumPy

ראה טעינת מערכי NumPy לקבלת דוגמאות נוספות.

אם כל ההתקפים נתוני הקלט שלך בזיכרון, הדרך הפשוטה ביותר ליצור Dataset מהם היא להמיר אותם tf.Tensor חפצים ולהשתמש Dataset.from_tensor_slices() .

 train, test = tf.keras.datasets.fashion_mnist.load_data()
 
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz
32768/29515 [=================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
26427392/26421880 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
8192/5148 [===============================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
4423680/4422102 [==============================] - 0s 0us/step

 images, labels = train
images = images/255

dataset = tf.data.Dataset.from_tensor_slices((images, labels))
dataset
 
<TensorSliceDataset shapes: ((28, 28), ()), types: (tf.float64, tf.uint8)>

צורכים גנרטורים מפיתון

מקור נתונים נפוץ נוסף שניתן בקלות tf.data.Dataset כ tf.data.Dataset הוא מחולל הפיתון.

 def count(stop):
  i = 0
  while i<stop:
    yield i
    i += 1
 
 for n in count(5):
  print(n)
 
0
1
2
3
4

Dataset.from_generator ממיר את גנרטור הפיתון למכשיר tf.data.Dataset .

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

הארגומנט output_types נדרש מכיוון tf.data בונה פנימית tf.Graph , וקצוות הגרפים דורשים סוג tf.dtype .

 ds_counter = tf.data.Dataset.from_generator(count, args=[25], output_types=tf.int32, output_shapes = (), )
 
 for count_batch in ds_counter.repeat().batch(10).take(10):
  print(count_batch.numpy())
 
[0 1 2 3 4 5 6 7 8 9]
[10 11 12 13 14 15 16 17 18 19]
[20 21 22 23 24  0  1  2  3  4]
[ 5  6  7  8  9 10 11 12 13 14]
[15 16 17 18 19 20 21 22 23 24]
[0 1 2 3 4 5 6 7 8 9]
[10 11 12 13 14 15 16 17 18 19]
[20 21 22 23 24  0  1  2  3  4]
[ 5  6  7  8  9 10 11 12 13 14]
[15 16 17 18 19 20 21 22 23 24]

טיעון output_shapes אינו נדרש אך מומלץ מאוד כיוון שפעולות רבות של זרימת tensor אינן תומכות בטנסורים בדרגה לא ידועה. אם אורך ציר מסוים אינו ידוע או משתנה, קבעו אותו כ- None output_shapes .

חשוב גם לשים לב כי output_shapes output_types output_shapes output_types פועלים לפי אותם כללי קינון כמו שיטות נתונים אחרות.

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

 def gen_series():
  i = 0
  while True:
    size = np.random.randint(0, 10)
    yield i, np.random.normal(size=(size,))
    i += 1
 
 for i, series in gen_series():
  print(i, ":", str(series))
  if i > 5:
    break
 
0 : [-1.978  -1.0531  0.1959 -2.1618]
1 : [ 1.9185 -0.1874  0.5084]
2 : [0.1441 0.3987 0.7737 0.9266 1.5057 0.9151]
3 : [ 0.681  -0.6155 -0.1231 -0.2429  0.6892  1.2571 -1.7588 -1.6575 -0.5375]
4 : [-0.5567  1.5298  0.7242  0.2213]
5 : [ 1.5572 -0.6856]
6 : [-1.0965 -0.336   1.2405  0.6006]

הפלט הראשון הוא int32 והשני הוא float32 .

הפריט הראשון הוא סולם, צורה () , והשני הוא וקטור באורך לא ידוע, צורה (None,)

 ds_series = tf.data.Dataset.from_generator(
    gen_series, 
    output_types=(tf.int32, tf.float32), 
    output_shapes=((), (None,)))

ds_series
 
<FlatMapDataset shapes: ((), (None,)), types: (tf.int32, tf.float32)>

כעת ניתן להשתמש בו כמו tf.data.Dataset רגיל. שים לב כי בעת אצווה של מערך נתונים עם צורה משתנה, עליך להשתמש ב Dataset.padded_batch .

 ds_series_batch = ds_series.shuffle(20).padded_batch(10)

ids, sequence_batch = next(iter(ds_series_batch))
print(ids.numpy())
print()
print(sequence_batch.numpy())
 
[ 2 10 18  3  6 15 25 23  0  4]

[[ 1.2665 -0.6274  0.4076  1.0146  0.      0.      0.      0.    ]
 [ 0.8091 -0.0683 -0.1464  0.2734  0.7461 -0.1009  0.      0.    ]
 [-0.9381  1.5075  0.      0.      0.      0.      0.      0.    ]
 [ 1.5705  0.4438  0.      0.      0.      0.      0.      0.    ]
 [-0.4692 -1.8328 -2.2838  0.7418  0.0172 -0.3547 -1.4502 -1.2786]
 [-1.574   0.      0.      0.      0.      0.      0.      0.    ]
 [-0.9274  1.4758  0.      0.      0.      0.      0.      0.    ]
 [-0.5043  0.7066  0.9599 -1.2986  0.      0.      0.      0.    ]
 [ 0.      0.      0.      0.      0.      0.      0.      0.    ]
 [-0.4893 -0.6937  0.      0.      0.      0.      0.      0.    ]]

לדוגמה ריאלית יותר, נסה לעטוף preprocessing.image.ImageDataGenerator כ- tf.data.Dataset .

הורד תחילה את הנתונים:

 flowers = tf.keras.utils.get_file(
    'flower_photos',
    'https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',
    untar=True)
 
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz
228818944/228813984 [==============================] - 2s 0us/step

צור את התמונה. image.ImageDataGenerator

 img_gen = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255, rotation_range=20)
 
 images, labels = next(img_gen.flow_from_directory(flowers))
 
Found 3670 images belonging to 5 classes.

 print(images.dtype, images.shape)
print(labels.dtype, labels.shape)
 
float32 (32, 256, 256, 3)
float32 (32, 5)

 ds = tf.data.Dataset.from_generator(
    img_gen.flow_from_directory, args=[flowers], 
    output_types=(tf.float32, tf.float32), 
    output_shapes=([32,256,256,3], [32,5])
)

ds
 
<FlatMapDataset shapes: ((32, 256, 256, 3), (32, 5)), types: (tf.float32, tf.float32)>

צורכים נתונים של TFRecord

ראה טעינת רשמי TFR לקבלת דוגמה מקצה לקצה.

ממשק ה- API של tf.data תומך במגוון פורמטים של קבצים כך שתוכל לעבד מערכי נתונים גדולים שאינם מתאימים לזיכרון. לדוגמא, תבנית הקובץ TFRecord היא פורמט בינארי פשוט מכוון רשומות שמשמש יישומי TensorFlow רבים לנתוני הדרכה. הכיתה tf.data.TFRecordDataset מאפשרת לך להזרים את התוכן של קובץ TFRecord אחד או יותר כחלק מצינור קלט.

להלן דוגמא המשתמשת בקובץ הבדיקה משלטים של שם הרחוב הצרפתי (FSNS).

 # Creates a dataset that reads all of the examples from two files.
fsns_test_file = tf.keras.utils.get_file("fsns.tfrec", "https://storage.googleapis.com/download.tensorflow.org/data/fsns-20160927/testdata/fsns-00000-of-00001")
 
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/fsns-20160927/testdata/fsns-00000-of-00001
7905280/7904079 [==============================] - 0s 0us/step

טיעון filenames TFRecordDataset יכול להיות מחרוזת, רשימת מחרוזות או tf.Tensor של מחרוזות. לכן אם יש לך שתי קבוצות של קבצים למטרות אימון ותיקוף, אתה יכול ליצור שיטת מפעל המייצרת את מערך הנתונים, תוך שמות קבצים כארגומנט קלט:

 dataset = tf.data.TFRecordDataset(filenames = [fsns_test_file])
dataset
 
<TFRecordDatasetV2 shapes: (), types: tf.string>

פרויקטים רבים של TensorFlow משתמשים ברשומות tf.train.Example בהמשכים tf.train.Example TFRecord שלהם. אלה צריכים להיות מפוענחים לפני שניתן יהיה לבדוק אותם:

 raw_example = next(iter(dataset))
parsed = tf.train.Example.FromString(raw_example.numpy())

parsed.features.feature['image/text']
 
bytes_list {
  value: "Rue Perreyon"
}

צורכים נתוני טקסט

ראה טעינת טקסט לקבלת דוגמה מקצה לקצה.

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

 directory_url = 'https://storage.googleapis.com/download.tensorflow.org/data/illiad/'
file_names = ['cowper.txt', 'derby.txt', 'butler.txt']

file_paths = [
    tf.keras.utils.get_file(file_name, directory_url + file_name)
    for file_name in file_names
]
 
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/cowper.txt
819200/815980 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/derby.txt
811008/809730 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/butler.txt
811008/807992 [==============================] - 0s 0us/step

 dataset = tf.data.TextLineDataset(file_paths)
 

להלן השורות הראשונות של הקובץ הראשון:

 for line in dataset.take(5):
  print(line.numpy())
 
b"\xef\xbb\xbfAchilles sing, O Goddess! Peleus' son;"
b'His wrath pernicious, who ten thousand woes'
b"Caused to Achaia's host, sent many a soul"
b'Illustrious into Ades premature,'
b'And Heroes gave (so stood the will of Jove)'

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

 files_ds = tf.data.Dataset.from_tensor_slices(file_paths)
lines_ds = files_ds.interleave(tf.data.TextLineDataset, cycle_length=3)

for i, line in enumerate(lines_ds.take(9)):
  if i % 3 == 0:
    print()
  print(line.numpy())
 

b"\xef\xbb\xbfAchilles sing, O Goddess! Peleus' son;"
b"\xef\xbb\xbfOf Peleus' son, Achilles, sing, O Muse,"
b'\xef\xbb\xbfSing, O goddess, the anger of Achilles son of Peleus, that brought'

b'His wrath pernicious, who ten thousand woes'
b'The vengeance, deep and deadly; whence to Greece'
b'countless ills upon the Achaeans. Many a brave soul did it send'

b"Caused to Achaia's host, sent many a soul"
b'Unnumbered ills arose; which many a soul'
b'hurrying down to Hades, and many a hero did it yield a prey to dogs and'

כברירת מחדל, TextLineDataset מניב כל שורה בכל קובץ, מה שאולי לא רצוי, למשל, אם הקובץ מתחיל בשורת כותרת, או מכיל הערות. ניתן להסיר שורות אלה באמצעות Dataset.skip() או Dataset.filter() . כאן אתה מדלג על השורה הראשונה ואז מסנן כדי למצוא רק ניצולים.

 titanic_file = tf.keras.utils.get_file("train.csv", "https://storage.googleapis.com/tf-datasets/titanic/train.csv")
titanic_lines = tf.data.TextLineDataset(titanic_file)
 
Downloading data from https://storage.googleapis.com/tf-datasets/titanic/train.csv
32768/30874 [===============================] - 0s 0us/step

 for line in titanic_lines.take(10):
  print(line.numpy())
 
b'survived,sex,age,n_siblings_spouses,parch,fare,class,deck,embark_town,alone'
b'0,male,22.0,1,0,7.25,Third,unknown,Southampton,n'
b'1,female,38.0,1,0,71.2833,First,C,Cherbourg,n'
b'1,female,26.0,0,0,7.925,Third,unknown,Southampton,y'
b'1,female,35.0,1,0,53.1,First,C,Southampton,n'
b'0,male,28.0,0,0,8.4583,Third,unknown,Queenstown,y'
b'0,male,2.0,3,1,21.075,Third,unknown,Southampton,n'
b'1,female,27.0,0,2,11.1333,Third,unknown,Southampton,n'
b'1,female,14.0,1,0,30.0708,Second,unknown,Cherbourg,n'
b'1,female,4.0,1,1,16.7,Third,G,Southampton,n'

 def survived(line):
  return tf.not_equal(tf.strings.substr(line, 0, 1), "0")

survivors = titanic_lines.skip(1).filter(survived)
 
 for line in survivors.take(10):
  print(line.numpy())
 
b'1,female,38.0,1,0,71.2833,First,C,Cherbourg,n'
b'1,female,26.0,0,0,7.925,Third,unknown,Southampton,y'
b'1,female,35.0,1,0,53.1,First,C,Southampton,n'
b'1,female,27.0,0,2,11.1333,Third,unknown,Southampton,n'
b'1,female,14.0,1,0,30.0708,Second,unknown,Cherbourg,n'
b'1,female,4.0,1,1,16.7,Third,G,Southampton,n'
b'1,male,28.0,0,0,13.0,Second,unknown,Southampton,y'
b'1,female,28.0,0,0,7.225,Third,unknown,Cherbourg,y'
b'1,male,28.0,0,0,35.5,First,A,Southampton,y'
b'1,female,38.0,1,5,31.3875,Third,unknown,Southampton,n'

צורכים נתוני CSV

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

פורמט קובץ ה- CSV הוא פורמט פופולרי לאחסון נתונים טבלאים בטקסט רגיל.

לדוגמה:

 titanic_file = tf.keras.utils.get_file("train.csv", "https://storage.googleapis.com/tf-datasets/titanic/train.csv")
 
 df = pd.read_csv(titanic_file, index_col=None)
df.head()
 

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

 titanic_slices = tf.data.Dataset.from_tensor_slices(dict(df))

for feature_batch in titanic_slices.take(1):
  for key, value in feature_batch.items():
    print("  {!r:20s}: {}".format(key, value))
 
  'survived'          : 0
  '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'

גישה מדרגית יותר היא לטעון מהדיסק לפי הצורך.

מודול tf.data מספק שיטות לחילוץ רשומות מקובץ CSV אחד או יותר tf.data ל- RFC 4180 .

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

 titanic_batches = tf.data.experimental.make_csv_dataset(
    titanic_file, batch_size=4,
    label_name="survived")
 
 for feature_batch, label_batch in titanic_batches.take(1):
  print("'survived': {}".format(label_batch))
  print("features:")
  for key, value in feature_batch.items():
    print("  {!r:20s}: {}".format(key, value))
 
'survived': [0 1 0 0]
features:
  'sex'               : [b'male' b'female' b'male' b'male']
  'age'               : [28. 42. 43. 21.]
  'n_siblings_spouses': [0 0 0 0]
  'parch'             : [0 0 0 0]
  'fare'              : [47.1    13.      8.05    8.6625]
  'class'             : [b'First' b'Second' b'Third' b'Third']
  'deck'              : [b'unknown' b'unknown' b'unknown' b'unknown']
  'embark_town'       : [b'Southampton' b'Southampton' b'Southampton' b'Southampton']
  'alone'             : [b'y' b'y' b'y' b'y']

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

 titanic_batches = tf.data.experimental.make_csv_dataset(
    titanic_file, batch_size=4,
    label_name="survived", select_columns=['class', 'fare', 'survived'])
 
 for feature_batch, label_batch in titanic_batches.take(1):
  print("'survived': {}".format(label_batch))
  for key, value in feature_batch.items():
    print("  {!r:20s}: {}".format(key, value))
 
'survived': [1 0 1 1]
  'fare'              : [24.15    0.     13.8583 53.1   ]
  'class'             : [b'Third' b'Second' b'Second' b'First']

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

 titanic_types  = [tf.int32, tf.string, tf.float32, tf.int32, tf.int32, tf.float32, tf.string, tf.string, tf.string, tf.string] 
dataset = tf.data.experimental.CsvDataset(titanic_file, titanic_types , header=True)

for line in dataset.take(10):
  print([item.numpy() for item in line])
 
[0, b'male', 22.0, 1, 0, 7.25, b'Third', b'unknown', b'Southampton', b'n']
[1, b'female', 38.0, 1, 0, 71.2833, b'First', b'C', b'Cherbourg', b'n']
[1, b'female', 26.0, 0, 0, 7.925, b'Third', b'unknown', b'Southampton', b'y']
[1, b'female', 35.0, 1, 0, 53.1, b'First', b'C', b'Southampton', b'n']
[0, b'male', 28.0, 0, 0, 8.4583, b'Third', b'unknown', b'Queenstown', b'y']
[0, b'male', 2.0, 3, 1, 21.075, b'Third', b'unknown', b'Southampton', b'n']
[1, b'female', 27.0, 0, 2, 11.1333, b'Third', b'unknown', b'Southampton', b'n']
[1, b'female', 14.0, 1, 0, 30.0708, b'Second', b'unknown', b'Cherbourg', b'n']
[1, b'female', 4.0, 1, 1, 16.7, b'Third', b'G', b'Southampton', b'n']
[0, b'male', 20.0, 0, 0, 8.05, b'Third', b'unknown', b'Southampton', b'y']

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

 %%writefile missing.csv
1,2,3,4
,2,3,4
1,,3,4
1,2,,4
1,2,3,
,,,
 
Writing missing.csv

 # Creates a dataset that reads all of the records from two CSV files, each with
# four float columns which may have missing values.

record_defaults = [999,999,999,999]
dataset = tf.data.experimental.CsvDataset("missing.csv", record_defaults)
dataset = dataset.map(lambda *items: tf.stack(items))
dataset
 
<MapDataset shapes: (4,), types: tf.int32>
 for line in dataset:
  print(line.numpy())
 
[1 2 3 4]
[999   2   3   4]
[  1 999   3   4]
[  1   2 999   4]
[  1   2   3 999]
[999 999 999 999]

כברירת מחדל, CsvDataset מניב כל עמודה בכל שורה בקובץ, מה שאולי לא רצוי, למשל אם הקובץ מתחיל בשורת כותרות שיש להתעלם ממנה, או אם אין צורך בעמודות מסוימות בקלט. ניתן להסיר שורות ושדות אלה באמצעות ארגומנטים header ו- select_cols בהתאמה.

 # Creates a dataset that reads all of the records from two CSV files with
# headers, extracting float data from columns 2 and 4.
record_defaults = [999, 999] # Only provide defaults for the selected columns
dataset = tf.data.experimental.CsvDataset("missing.csv", record_defaults, select_cols=[1, 3])
dataset = dataset.map(lambda *items: tf.stack(items))
dataset
 
<MapDataset shapes: (2,), types: tf.int32>
 for line in dataset:
  print(line.numpy())
 
[2 4]
[2 4]
[999   4]
[2 4]
[  2 999]
[999 999]

צורכת קבוצות קבצים

ישנם ערכות נתונים רבות המופצות כסט קבצים, כאשר כל קובץ הוא דוגמה.

 flowers_root = tf.keras.utils.get_file(
    'flower_photos',
    'https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',
    untar=True)
flowers_root = pathlib.Path(flowers_root)

 

ספריית השורש מכילה ספרייה לכל מחלקה:

 for item in flowers_root.glob("*"):
  print(item.name)
 
sunflowers
daisy
LICENSE.txt
roses
tulips
dandelion

הקבצים בכל ספריית כיתות הם דוגמאות:

 list_ds = tf.data.Dataset.list_files(str(flowers_root/'*/*'))

for f in list_ds.take(5):
  print(f.numpy())
 
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/7243478942_30bf542a2d_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/tulips/4525067924_177ea3bfb4.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/tulips/7002703410_3e97b29da5_n.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/daisy/6299910262_336309ffa5_n.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/sunflowers/6140661443_bb48344226.jpg'

קרא את הנתונים באמצעות הפונקציה tf.io.read_file את התווית מהנתיב, חוזר (image, label) זוגות:

 def process_path(file_path):
  label = tf.strings.split(file_path, os.sep)[-2]
  return tf.io.read_file(file_path), label

labeled_ds = list_ds.map(process_path)
 
 for image_raw, label_text in labeled_ds.take(1):
  print(repr(image_raw.numpy()[:100]))
  print()
  print(label_text.numpy())
 
b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x01\x00H\x00H\x00\x00\xff\xe2\x0cXICC_PROFILE\x00\x01\x01\x00\x00\x0cHLino\x02\x10\x00\x00mntrRGB XYZ \x07\xce\x00\x02\x00\t\x00\x06\x001\x00\x00acspMSFT\x00\x00\x00\x00IEC sRGB\x00\x00\x00\x00\x00\x00'

b'tulips'

אצווה של רכיבי נתונים

אצווה פשוטה

הצורה הפשוטה ביותר לאחסון ערימות n אלמנטים רצופים של מערך נתונים לאלמנט יחיד. טרנספורמציית Dataset.batch() עושה בדיוק את זה, עם אותם אילוצים כמו מפעיל tf.stack() , המופעל על כל רכיב של האלמנטים: כלומר, עבור כל רכיב i , כל האלמנטים חייבים להיות עם טנסור באותה צורה בדיוק.

 inc_dataset = tf.data.Dataset.range(100)
dec_dataset = tf.data.Dataset.range(0, -100, -1)
dataset = tf.data.Dataset.zip((inc_dataset, dec_dataset))
batched_dataset = dataset.batch(4)

for batch in batched_dataset.take(4):
  print([arr.numpy() for arr in batch])
 
[array([0, 1, 2, 3]), array([ 0, -1, -2, -3])]
[array([4, 5, 6, 7]), array([-4, -5, -6, -7])]
[array([ 8,  9, 10, 11]), array([ -8,  -9, -10, -11])]
[array([12, 13, 14, 15]), array([-12, -13, -14, -15])]

בעוד tf.data מנסה להפיץ מידע על הצורה, הגדרות ברירת המחדל של Dataset.batch גורמות לגודל אצווה לא ידוע מכיוון שייתכן שה אצווה האחרונה אינה מלאה. שים לב ל- None s בצורה:

 batched_dataset
 
<BatchDataset shapes: ((None,), (None,)), types: (tf.int64, tf.int64)>

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

 batched_dataset = dataset.batch(7, drop_remainder=True)
batched_dataset
 
<BatchDataset shapes: ((7,), (7,)), types: (tf.int64, tf.int64)>

אצווה טנסורים עם ריפוד

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

 dataset = tf.data.Dataset.range(100)
dataset = dataset.map(lambda x: tf.fill([tf.cast(x, tf.int32)], x))
dataset = dataset.padded_batch(4, padded_shapes=(None,))

for batch in dataset.take(2):
  print(batch.numpy())
  print()

 
[[0 0 0]
 [1 0 0]
 [2 2 0]
 [3 3 3]]

[[4 4 4 4 0 0 0]
 [5 5 5 5 5 0 0]
 [6 6 6 6 6 6 0]
 [7 7 7 7 7 7 7]]


טרנספורמציית Dataset.padded_batch מאפשרת לך להגדיר ריפוד שונה עבור כל ממד של כל רכיב, והיא עשויה להיות באורך משתנה (מסומן על ידי None בדוגמה שלמעלה) או באורך קבוע. ניתן גם לעקוף את ערך הריפוד, שברירת המחדל הוא 0.

תזרימי עבודה

עיבוד של תקופות מרובות

ממשק ה- API של tf.data מציע שתי דרכים עיקריות לעבד תקופות מרובות של אותם נתונים.

הדרך הפשוטה ביותר לאיטרציה על בסיס נתונים בערך בתקופות מרובות היא להשתמש Dataset.repeat() . ראשית, צור מערך נתונים של נתונים טיטניים:

 titanic_file = tf.keras.utils.get_file("train.csv", "https://storage.googleapis.com/tf-datasets/titanic/train.csv")
titanic_lines = tf.data.TextLineDataset(titanic_file)
 
 def plot_batch_sizes(ds):
  batch_sizes = [batch.shape[0] for batch in ds]
  plt.bar(range(len(batch_sizes)), batch_sizes)
  plt.xlabel('Batch number')
  plt.ylabel('Batch size')
 

החלת טרנספורמציית Dataset.repeat() ללא ארגומנטים תחזור על הקלט ללא הגבלת זמן.

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

 titanic_batches = titanic_lines.repeat(3).batch(128)
plot_batch_sizes(titanic_batches)
 

png

אם אתה זקוק להפרדת תקופה ברורה, שים את Dataset.batch לפני החזרה:

 titanic_batches = titanic_lines.batch(128).repeat(3)

plot_batch_sizes(titanic_batches)
 

png

אם ברצונך לבצע חישוב מותאם אישית (למשל לאסוף נתונים סטטיסטיים) בסוף כל תקופה, הכי פשוט להפעיל מחדש את גרסת ה- dataset על כל תקופה:

 epochs = 3
dataset = titanic_lines.batch(128)

for epoch in range(epochs):
  for batch in dataset:
    print(batch.shape)
  print("End of epoch: ", epoch)
 
(128,)
(128,)
(128,)
(128,)
(116,)
End of epoch:  0
(128,)
(128,)
(128,)
(128,)
(116,)
End of epoch:  1
(128,)
(128,)
(128,)
(128,)
(116,)
End of epoch:  2

ערבוב אקראי של נתוני קלט

טרנספורמציית Dataset.shuffle() שומרת על מאגר בגודל קבוע ובוחר את האלמנט הבא באופן אחיד באקראי מאותו מאגר.

הוסף אינדקס למערך הנתונים כך שתוכל לראות את האפקט:

 lines = tf.data.TextLineDataset(titanic_file)
counter = tf.data.experimental.Counter()

dataset = tf.data.Dataset.zip((counter, lines))
dataset = dataset.shuffle(buffer_size=100)
dataset = dataset.batch(20)
dataset
 
<BatchDataset shapes: ((None,), (None,)), types: (tf.int64, tf.string)>

מכיוון buffer_size של buffer_size 100, וגודל האצווה הוא 20, האצווה הראשונה אינה מכילה אלמנטים עם אינדקס מעל 120.

 n,line_batch = next(iter(dataset))
print(n.numpy())
 
[ 63  48 101  12 103  52   6  39   4   9  93  91   5  86  79  64  95  33
 102  50]

בדומה Dataset.batch - Dataset.batch את ההזמנה ביחס לענייני Dataset.repeat .

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

 dataset = tf.data.Dataset.zip((counter, lines))
shuffled = dataset.shuffle(buffer_size=100).batch(10).repeat(2)

print("Here are the item ID's near the epoch boundary:\n")
for n, line_batch in shuffled.skip(60).take(5):
  print(n.numpy())
 
Here are the item ID's near the epoch boundary:

[613 609 624 553 608 583 493 617 611 610]
[217 508 579 601 319 616 606 549 618 623]
[416 567 404 622 283 458 503 602]
[ 87  68  56  16   6  62   1  89  58 106]
[98 80 43 10 67 44 19 34 13 57]

 shuffle_repeat = [n.numpy().mean() for n, line_batch in shuffled]
plt.plot(shuffle_repeat, label="shuffle().repeat()")
plt.ylabel("Mean item ID")
plt.legend()
 
<matplotlib.legend.Legend at 0x7fe0a00a1d68>

png

אבל חזרה לפני ערבוב מערבבת את גבולות העידן יחד:

 dataset = tf.data.Dataset.zip((counter, lines))
shuffled = dataset.repeat(2).shuffle(buffer_size=100).batch(10)

print("Here are the item ID's near the epoch boundary:\n")
for n, line_batch in shuffled.skip(55).take(15):
  print(n.numpy())
 
Here are the item ID's near the epoch boundary:

[440  15   8 599 567  18 550   5  19  17]
[ 12 501 571 473 466  21 531 596 580 555]
[  3 573  38 563  25 416 595  29  46 602]
[485 566 561  16 331 615 386  28 609  41]
[611 622 575  10 589  61 598 527  52  35]
[ 55 597  42  23  13  47  11 505  68 582]
[612 613  75  43   7 392  74 452  82 509]
[  9  44  62 491  71 343  51 590  60  98]
[  6  95 619  86 625 537 617  85 465   0]
[ 88  27  92 101 109 111 104  24  36 113]
[103 118  79  53  70  40 121 100  65  33]
[562 588 124 125  64  84  83  67 610 130]
[  4 142 131  90 518 129 143 112   2 551]
[377  91 140  76  50  48 526 553 156 591]
[105 128  69 114  93 520 154  56 145 115]

 repeat_shuffle = [n.numpy().mean() for n, line_batch in shuffled]

plt.plot(shuffle_repeat, label="shuffle().repeat()")
plt.plot(repeat_shuffle, label="repeat().shuffle()")
plt.ylabel("Mean item ID")
plt.legend()
 
<matplotlib.legend.Legend at 0x7fe0582fbb70>

png

עיבוד נתונים מראש

Dataset.map(f) מייצרת מערך נתונים חדש על ידי החלת פונקציה נתונה f על כל אלמנט של מערך הקלט. זה מבוסס על פונקציית map() המופעלת לרוב על רשימות (ומבנים אחרים) בשפות תכנות פונקציונליות. הפונקציה f לוקחת את האובייקטים tf.Tensor המייצגים אלמנט יחיד בכניסה, ומחזירה את האובייקטים tf.Tensor שייצגו אלמנט יחיד במערך הנתונים החדש. יישומה משתמש בפעולות TensorFlow סטנדרטיות כדי להפוך אלמנט אחד לאחר.

פרק זה מכסה דוגמאות נפוצות לשימוש ב- Dataset.map() .

פענוח נתוני תמונה ושינוי גודל

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

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

 list_ds = tf.data.Dataset.list_files(str(flowers_root/'*/*'))
 

כתוב פונקציה שמתפעלת את רכיבי הנתונים.

 # Reads an image from a file, decodes it into a dense tensor, and resizes it
# to a fixed shape.
def parse_image(filename):
  parts = tf.strings.split(filename, os.sep)
  label = parts[-2]

  image = tf.io.read_file(filename)
  image = tf.image.decode_jpeg(image)
  image = tf.image.convert_image_dtype(image, tf.float32)
  image = tf.image.resize(image, [128, 128])
  return image, label
 

תבדוק שזה עובד.

 file_path = next(iter(list_ds))
image, label = parse_image(file_path)

def show(image, label):
  plt.figure()
  plt.imshow(image)
  plt.title(label.numpy().decode('utf-8'))
  plt.axis('off')

show(image, label)
 

png

מיפוי דרך מערך הנתונים.

 images_ds = list_ds.map(parse_image)

for image, label in images_ds.take(2):
  show(image, label)
 

png

png

החלת היגיון פיתון שרירותי

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

לדוגמה, אם אתה רוצה להחיל רוטציה אקראית, tf.image מודול יש רק tf.image.rot90 , אשר אינו שימושי מאוד עבור הגדלת התמונה.

כדי להדגים את tf.py_function , נסה להשתמש בפונקציה scipy.ndimage.rotate במקום:

 import scipy.ndimage as ndimage

def random_rotate_image(image):
  image = ndimage.rotate(image, np.random.uniform(-30, 30), reshape=False)
  return image
 
 image, label = next(iter(images_ds))
image = random_rotate_image(image)
show(image, label)
 
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).

png

כדי להשתמש בפונקציה זו עם Dataset.map אותם אזהרות חלות כמו ב- Dataset.from_generator , עליך לתאר את צורות ההחזרה ואת הסוגים כאשר אתה מיישם את הפונקציה:

 def tf_random_rotate_image(image, label):
  im_shape = image.shape
  [image,] = tf.py_function(random_rotate_image, [image], [tf.float32])
  image.set_shape(im_shape)
  return image, label
 
 rot_ds = images_ds.map(tf_random_rotate_image)

for image, label in rot_ds.take(2):
  show(image, label)
 
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).

png

png

ניתוח tf.Example פרוטוקול

צינורות כניסה רבים מוציאים את tf.train.Example הודעות חיץ פרוטוקול מתבנית TFRecord. כל רשומת tf.train.Example מכילה " tf.train.Example אחת או יותר, וצינור הקלט ממיר בדרך כלל תכונות אלה לטנזורים.

 fsns_test_file = tf.keras.utils.get_file("fsns.tfrec", "https://storage.googleapis.com/download.tensorflow.org/data/fsns-20160927/testdata/fsns-00000-of-00001")
dataset = tf.data.TFRecordDataset(filenames = [fsns_test_file])
dataset
 
<TFRecordDatasetV2 shapes: (), types: tf.string>

אתה יכול לעבוד עם tf.train.Example מחוץ ל- tf.data.Dataset כדי להבין את הנתונים:

 raw_example = next(iter(dataset))
parsed = tf.train.Example.FromString(raw_example.numpy())

feature = parsed.features.feature
raw_img = feature['image/encoded'].bytes_list.value[0]
img = tf.image.decode_png(raw_img)
plt.imshow(img)
plt.axis('off')
_ = plt.title(feature["image/text"].bytes_list.value[0])
 

png

 raw_example = next(iter(dataset))
 
 def tf_parse(eg):
  example = tf.io.parse_example(
      eg[tf.newaxis], {
          'image/encoded': tf.io.FixedLenFeature(shape=(), dtype=tf.string),
          'image/text': tf.io.FixedLenFeature(shape=(), dtype=tf.string)
      })
  return example['image/encoded'][0], example['image/text'][0]
 
 img, txt = tf_parse(raw_example)
print(txt.numpy())
print(repr(img.numpy()[:20]), "...")
 
b'Rue Perreyon'
b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x02X' ...

 decoded = dataset.map(tf_parse)
decoded
 
<MapDataset shapes: ((), ()), types: (tf.string, tf.string)>
 image_batch, text_batch = next(iter(decoded.batch(10)))
image_batch.shape
 
TensorShape([10])

סדרות זמן חלונות

לדוגמה של סדרות זמן מקצה לקצה ראה: חיזוי סדרות זמן .

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

השתמש Dataset.range פשוט כדי להדגים:

 range_ds = tf.data.Dataset.range(100000)
 

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

הגישה הפשוטה ביותר היא לאסוף את הנתונים:

באמצעות batch

 batches = range_ds.batch(10, drop_remainder=True)

for batch in batches.take(5):
  print(batch.numpy())
 
[0 1 2 3 4 5 6 7 8 9]
[10 11 12 13 14 15 16 17 18 19]
[20 21 22 23 24 25 26 27 28 29]
[30 31 32 33 34 35 36 37 38 39]
[40 41 42 43 44 45 46 47 48 49]

או כדי לבצע תחזיות צפופות צעד אחד לעתיד, אתה עשוי לשנות את התכונות והתוויות בצעד אחד ביחס זה לזה:

 def dense_1_step(batch):
  # Shift features and labels one step relative to each other.
  return batch[:-1], batch[1:]

predict_dense_1_step = batches.map(dense_1_step)

for features, label in predict_dense_1_step.take(3):
  print(features.numpy(), " => ", label.numpy())
 
[0 1 2 3 4 5 6 7 8]  =>  [1 2 3 4 5 6 7 8 9]
[10 11 12 13 14 15 16 17 18]  =>  [11 12 13 14 15 16 17 18 19]
[20 21 22 23 24 25 26 27 28]  =>  [21 22 23 24 25 26 27 28 29]

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

 batches = range_ds.batch(15, drop_remainder=True)

def label_next_5_steps(batch):
  return (batch[:-5],   # Take the first 5 steps
          batch[-5:])   # take the remainder

predict_5_steps = batches.map(label_next_5_steps)

for features, label in predict_5_steps.take(3):
  print(features.numpy(), " => ", label.numpy())
 
[0 1 2 3 4 5 6 7 8 9]  =>  [10 11 12 13 14]
[15 16 17 18 19 20 21 22 23 24]  =>  [25 26 27 28 29]
[30 31 32 33 34 35 36 37 38 39]  =>  [40 41 42 43 44]

כדי לאפשר חפיפה מסוימת בין התכונות של אצווה אחת לבין התוויות של אחת אחרת, השתמש ב Dataset.zip :

 feature_length = 10
label_length = 5

features = range_ds.batch(feature_length, drop_remainder=True)
labels = range_ds.batch(feature_length).skip(1).map(lambda labels: labels[:-5])

predict_5_steps = tf.data.Dataset.zip((features, labels))

for features, label in predict_5_steps.take(3):
  print(features.numpy(), " => ", label.numpy())
 
[0 1 2 3 4 5 6 7 8 9]  =>  [10 11 12 13 14]
[10 11 12 13 14 15 16 17 18 19]  =>  [20 21 22 23 24]
[20 21 22 23 24 25 26 27 28 29]  =>  [30 31 32 33 34]

באמצעות window

בזמן השימוש ב- Dataset.batch עובד, ישנם מצבים בהם יתכן שתזדקקו לשליטה עדינה יותר. שיטת Dataset.window מעניקה לך שליטה מלאה, אך דורשת טיפול מסוים: היא מחזירה Dataset של Datasets . ראה מבנה מערך הנתונים לפרטים.

 window_size = 5

windows = range_ds.window(window_size, shift=1)
for sub_ds in windows.take(5):
  print(sub_ds)
 
<_VariantDataset shapes: (), types: tf.int64>
<_VariantDataset shapes: (), types: tf.int64>
<_VariantDataset shapes: (), types: tf.int64>
<_VariantDataset shapes: (), types: tf.int64>
<_VariantDataset shapes: (), types: tf.int64>

שיטת Dataset.flat_map יכולה לקחת מערך נתונים של ערכות נתונים ולשטח אותה למערך נתונים יחיד:

  for x in windows.flat_map(lambda x: x).take(30):
   print(x.numpy(), end=' ')
 
WARNING:tensorflow:AutoGraph could not transform <function <lambda> at 0x7fe0582dbbf8> and will run it as-is.
Cause: could not parse the source code:

for x in windows.flat_map(lambda x: x).take(30):

This error may be avoided by creating the lambda in a standalone statement.

To silence this warning, decorate the function with @tf.autograph.experimental.do_not_convert
WARNING: AutoGraph could not transform <function <lambda> at 0x7fe0582dbbf8> and will run it as-is.
Cause: could not parse the source code:

for x in windows.flat_map(lambda x: x).take(30):

This error may be avoided by creating the lambda in a standalone statement.

To silence this warning, decorate the function with @tf.autograph.experimental.do_not_convert
0 1 2 3 4 1 2 3 4 5 2 3 4 5 6 3 4 5 6 7 4 5 6 7 8 5 6 7 8 9 

כמעט בכל המקרים, אתם תרצו .batch בסיס הנתונים הראשון:

 def sub_to_batch(sub):
  return sub.batch(window_size, drop_remainder=True)

for example in windows.flat_map(sub_to_batch).take(5):
  print(example.numpy())
 
[0 1 2 3 4]
[1 2 3 4 5]
[2 3 4 5 6]
[3 4 5 6 7]
[4 5 6 7 8]

כעת תוכלו לראות כי טיעון shift שולט בכמה מעבר לכל חלון.

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

 def make_window_dataset(ds, window_size=5, shift=1, stride=1):
  windows = ds.window(window_size, shift=shift, stride=stride)

  def sub_to_batch(sub):
    return sub.batch(window_size, drop_remainder=True)

  windows = windows.flat_map(sub_to_batch)
  return windows

 
 ds = make_window_dataset(range_ds, window_size=10, shift = 5, stride=3)

for example in ds.take(10):
  print(example.numpy())
 
[ 0  3  6  9 12 15 18 21 24 27]
[ 5  8 11 14 17 20 23 26 29 32]
[10 13 16 19 22 25 28 31 34 37]
[15 18 21 24 27 30 33 36 39 42]
[20 23 26 29 32 35 38 41 44 47]
[25 28 31 34 37 40 43 46 49 52]
[30 33 36 39 42 45 48 51 54 57]
[35 38 41 44 47 50 53 56 59 62]
[40 43 46 49 52 55 58 61 64 67]
[45 48 51 54 57 60 63 66 69 72]

אז קל לחלץ תוויות, כמו קודם:

 dense_labels_ds = ds.map(dense_1_step)

for inputs,labels in dense_labels_ds.take(3):
  print(inputs.numpy(), "=>", labels.numpy())
 
[ 0  3  6  9 12 15 18 21 24] => [ 3  6  9 12 15 18 21 24 27]
[ 5  8 11 14 17 20 23 26 29] => [ 8 11 14 17 20 23 26 29 32]
[10 13 16 19 22 25 28 31 34] => [13 16 19 22 25 28 31 34 37]

דגימה מחדש

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

 zip_path = tf.keras.utils.get_file(
    origin='https://storage.googleapis.com/download.tensorflow.org/data/creditcard.zip',
    fname='creditcard.zip',
    extract=True)

csv_path = zip_path.replace('.zip', '.csv')
 
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/creditcard.zip
69156864/69155632 [==============================] - 10s 0us/step

 creditcard_ds = tf.data.experimental.make_csv_dataset(
    csv_path, batch_size=1024, label_name="Class",
    # Set the column types: 30 floats and an int.
    column_defaults=[float()]*30+[int()])
 

עכשיו, בדוק את התפלגות הכיתות, זה מוטה מאוד:

 def count(counts, batch):
  features, labels = batch
  class_1 = labels == 1
  class_1 = tf.cast(class_1, tf.int32)

  class_0 = labels == 0
  class_0 = tf.cast(class_0, tf.int32)

  counts['class_0'] += tf.reduce_sum(class_0)
  counts['class_1'] += tf.reduce_sum(class_1)

  return counts
 
 counts = creditcard_ds.take(10).reduce(
    initial_state={'class_0': 0, 'class_1': 0},
    reduce_func = count)

counts = np.array([counts['class_0'].numpy(),
                   counts['class_1'].numpy()]).astype(np.float32)

fractions = counts/counts.sum()
print(fractions)
 
[0.996 0.004]

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

דגימה של ערכות נתונים

גישה אחת sample_from_datasets מחדש של מערך נתונים היא להשתמש sample_from_datasets . זה ישים יותר כאשר יש לך נפרדות data.Dataset עבור כל מחלקה.

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

 negative_ds = (
  creditcard_ds
    .unbatch()
    .filter(lambda features, label: label==0)
    .repeat())
positive_ds = (
  creditcard_ds
    .unbatch()
    .filter(lambda features, label: label==1)
    .repeat())
 
WARNING:tensorflow:AutoGraph could not transform <function <lambda> at 0x7fe0a01fd1e0> and will run it as-is.
Cause: could not parse the source code:

    .filter(lambda features, label: label==0)

This error may be avoided by creating the lambda in a standalone statement.

To silence this warning, decorate the function with @tf.autograph.experimental.do_not_convert
WARNING: AutoGraph could not transform <function <lambda> at 0x7fe0a01fd1e0> and will run it as-is.
Cause: could not parse the source code:

    .filter(lambda features, label: label==0)

This error may be avoided by creating the lambda in a standalone statement.

To silence this warning, decorate the function with @tf.autograph.experimental.do_not_convert
WARNING:tensorflow:AutoGraph could not transform <function <lambda> at 0x7fe058159620> and will run it as-is.
Cause: could not parse the source code:

    .filter(lambda features, label: label==1)

This error may be avoided by creating the lambda in a standalone statement.

To silence this warning, decorate the function with @tf.autograph.experimental.do_not_convert
WARNING: AutoGraph could not transform <function <lambda> at 0x7fe058159620> and will run it as-is.
Cause: could not parse the source code:

    .filter(lambda features, label: label==1)

This error may be avoided by creating the lambda in a standalone statement.

To silence this warning, decorate the function with @tf.autograph.experimental.do_not_convert

 for features, label in positive_ds.batch(10).take(1):
  print(label.numpy())
 
[1 1 1 1 1 1 1 1 1 1]

לשימוש ב- tf.data.experimental.sample_from_datasets עוברים את ערכות הנתונים, והמשקל של כל אחד מהם:

 balanced_ds = tf.data.experimental.sample_from_datasets(
    [negative_ds, positive_ds], [0.5, 0.5]).batch(10)
 

כעת מערך הנתונים מייצר דוגמאות לכל מחלקה עם הסתברות 50/50:

 for features, labels in balanced_ds.take(10):
  print(labels.numpy())
 
[0 0 0 1 0 0 1 1 0 0]
[1 1 0 0 1 1 0 0 0 1]
[1 1 0 0 0 0 0 0 1 1]
[1 0 0 1 1 0 0 0 1 0]
[1 1 0 0 0 1 1 0 0 1]
[0 0 1 1 1 0 0 1 1 0]
[0 0 0 1 0 0 0 1 1 1]
[1 1 0 1 0 0 1 0 1 1]
[0 1 0 0 1 1 0 0 0 1]
[0 1 0 0 1 0 0 1 1 0]

דחיית דגימה מחדש

אחת הבעיות בגישת experimental.sample_from_datasets היא שהיא צריכה tf.data.Dataset נפרד לכל כיתה. השימוש ב Dataset.filter עובד, אך מביא לכך שכל הנתונים נטענים פעמיים.

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

data.experimental.rejection_resample לוקח טיעון class_func . class_func זה מיושם על כל אלמנט מערך נתונים, ומשמש לקביעת איזו מחלקה דוגמא שייכת למטרות איזון.

האלמנטים של creditcard_ds הם כבר זוגות (features, label) . אז class_func רק צריך להחזיר את התוויות האלה:

 def class_func(features, label):
  return label
 

המשווק מחדש דורש גם חלוקת יעד, ואופציונלי אומדן חלוקה ראשוני:

 resampler = tf.data.experimental.rejection_resample(
    class_func, target_dist=[0.5, 0.5], initial_dist=fractions)
 

מהדגם ה- resampler עוסק בדוגמאות בודדות, לכן עליך unbatch את unbatch של מערך הנתונים לפני unbatch את ה- resampler:

 resample_ds = creditcard_ds.unbatch().apply(resampler).batch(10)
 
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/data/experimental/ops/resampling.py:156: Print (from tensorflow.python.ops.logging_ops) is deprecated and will be removed after 2018-08-20.
Instructions for updating:
Use tf.print instead of tf.Print. Note that tf.print returns a no-output operator that directly prints the output. Outside of defuns or eager mode, this operator will not be executed unless it is directly specified in session.run or used as a control dependency for other operators. This is only a concern in graph mode. Below is an example of how to ensure tf.print executes in graph mode:


ה- Resampler מחזיר יוצרים זוגות (class, example) מהפלט של class_func . במקרה זה, example הייתה כבר זוג (feature, label) , אז השתמש map כדי להפיל את העותק הנוסף של התוויות:

 balanced_ds = resample_ds.map(lambda extra_label, features_and_label: features_and_label)
 

כעת מערך הנתונים מייצר דוגמאות לכל מחלקה עם הסתברות 50/50:

 for features, labels in balanced_ds.take(10):
  print(labels.numpy())
 
[1 0 1 1 1 1 0 1 1 1]
[0 0 1 1 1 0 1 0 1 1]
[1 0 0 1 0 0 0 0 0 1]
[1 1 1 1 1 1 0 1 1 1]
[1 1 0 1 0 0 0 0 1 0]
[1 0 0 0 1 0 1 0 1 0]
[0 0 0 0 0 0 1 0 0 0]
[0 0 0 1 1 0 0 1 0 1]
[0 0 1 1 1 1 0 0 1 1]
[0 0 1 1 0 1 0 1 1 0]

איתור איטרטור

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

כדי לכלול את האיטטור שלך במחסום, העביר את tf.train.Checkpoint .

 range_ds = tf.data.Dataset.range(20)

iterator = iter(range_ds)
ckpt = tf.train.Checkpoint(step=tf.Variable(0), iterator=iterator)
manager = tf.train.CheckpointManager(ckpt, '/tmp/my_ckpt', max_to_keep=3)

print([next(iterator).numpy() for _ in range(5)])

save_path = manager.save()

print([next(iterator).numpy() for _ in range(5)])

ckpt.restore(manager.latest_checkpoint)

print([next(iterator).numpy() for _ in range(5)])
 
[0, 1, 2, 3, 4]
[5, 6, 7, 8, 9]
[5, 6, 7, 8, 9]

שימוש בממשקי API ברמה גבוהה

tf.keras

ממשק ה- API של tf.keras מפשט היבטים רבים של יצירה והפעלה של מודלים ללימוד מכונות. ממשקי ה- API שלה .fit() ו- .evaluate() ו- .predict() תומכים במערכות נתונים כקלט. להלן מערך נתונים מהיר והגדרת דגם:

 train, test = tf.keras.datasets.fashion_mnist.load_data()

images, labels = train
images = images/255.0
labels = labels.astype(np.int32)
 
 fmnist_train_ds = tf.data.Dataset.from_tensor_slices((images, labels))
fmnist_train_ds = fmnist_train_ds.shuffle(5000).batch(32)

model = tf.keras.Sequential([
  tf.keras.layers.Flatten(),
  tf.keras.layers.Dense(10)
])

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

העברת מערך נתונים של זוגות (feature, label) הוא כל מה שצריך עבור Model.fit ו- Model.evaluate :

 model.fit(fmnist_train_ds, epochs=2)
 
Epoch 1/2
WARNING:tensorflow:Layer flatten is casting an input tensor from dtype float64 to the layer's dtype of float32, which is new behavior in TensorFlow 2.  The layer has dtype float32 because it's dtype defaults to floatx.

If you intended to run this layer in float32, you can safely ignore this warning. If in doubt, this warning is likely only an issue if you are porting a TensorFlow 1.X model to TensorFlow 2.

To change all layers to have dtype float64 by default, call `tf.keras.backend.set_floatx('float64')`. To change just this layer, pass dtype='float64' to the layer constructor. If you are the author of this layer, you can disable autocasting by passing autocast=False to the base Layer constructor.

1875/1875 [==============================] - 4s 2ms/step - loss: 0.6031 - accuracy: 0.7937
Epoch 2/2
1875/1875 [==============================] - 3s 2ms/step - loss: 0.4620 - accuracy: 0.8416

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

אם אתה מעביר מערך נתונים אינסופי, למשל על ידי חיוג ל- Dataset.repeat() , אתה רק צריך לעבור את הארגומנט של steps_per_epoch :

 model.fit(fmnist_train_ds.repeat(), epochs=2, steps_per_epoch=20)
 
Epoch 1/2
20/20 [==============================] - 0s 2ms/step - loss: 0.4050 - accuracy: 0.8672
Epoch 2/2
20/20 [==============================] - 0s 2ms/step - loss: 0.4077 - accuracy: 0.8703

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

להערכה תוכלו לעבור את מספר שלבי ההערכה:

 loss, accuracy = model.evaluate(fmnist_train_ds)
print("Loss :", loss)
print("Accuracy :", accuracy)
 
1875/1875 [==============================] - 3s 2ms/step - loss: 0.4474 - accuracy: 0.8439
Loss : 0.4474281072616577
Accuracy : 0.843916654586792

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

 loss, accuracy = model.evaluate(fmnist_train_ds.repeat(), steps=10)
print("Loss :", loss)
print("Accuracy :", accuracy)
 
10/10 [==============================] - 0s 2ms/step - loss: 0.5262 - accuracy: 0.8156
Loss : 0.5262183547019958
Accuracy : 0.815625011920929

אין צורך בתוויות כשאתה מתקשר ל- Model.predict .

 predict_ds = tf.data.Dataset.from_tensor_slices(images).batch(32)
result = model.predict(predict_ds, steps = 10)
print(result.shape)
 
(320, 10)

אבל התוויות מתעלמות אם אתה עובר מערך נתונים המכיל אותם:

 result = model.predict(fmnist_train_ds, steps = 10)
print(result.shape)
 
(320, 10)

tf.estimator

כדי להשתמש במערך Dataset בערך input_fn של tf.estimator.Estimator , פשוט החזיר את Dataset מה input_fn והמסגרת תדאג לצרוך את האלמנטים שלו עבורך. לדוגמה:

 import tensorflow_datasets as tfds

def train_input_fn():
  titanic = tf.data.experimental.make_csv_dataset(
      titanic_file, batch_size=32,
      label_name="survived")
  titanic_batches = (
      titanic.cache().repeat().shuffle(500)
      .prefetch(tf.data.experimental.AUTOTUNE))
  return titanic_batches
 
 embark = tf.feature_column.categorical_column_with_hash_bucket('embark_town', 32)
cls = tf.feature_column.categorical_column_with_vocabulary_list('class', ['First', 'Second', 'Third']) 
age = tf.feature_column.numeric_column('age')
 
 import tempfile
model_dir = tempfile.mkdtemp()
model = tf.estimator.LinearClassifier(
    model_dir=model_dir,
    feature_columns=[embark, cls, age],
    n_classes=2
)
 
INFO:tensorflow:Using default config.
INFO:tensorflow:Using config: {'_model_dir': '/tmp/tmpefmfuc4o', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true
graph_options {
  rewrite_options {
    meta_optimizer_iterations: ONE
  }
}
, '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}

 model = model.train(input_fn=train_input_fn, steps=100)
 
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/resource_variable_ops.py:1666: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.
Instructions for updating:
If using Keras pass *_constraint arguments to layers.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/training_util.py:236: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.
Instructions for updating:
Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.
INFO:tensorflow:Calling model_fn.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/feature_column/feature_column_v2.py:540: Layer.add_variable (from tensorflow.python.keras.engine.base_layer_v1) is deprecated and will be removed in a future version.
Instructions for updating:
Please use `layer.add_weight` method instead.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/optimizer_v2/ftrl.py:144: calling Constant.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.
Instructions for updating:
Call initializer instance with the dtype argument instead of passing it to the constructor
INFO:tensorflow:Done calling model_fn.
INFO:tensorflow:Create CheckpointSaverHook.
INFO:tensorflow:Graph was finalized.
INFO:tensorflow:Running local_init_op.
INFO:tensorflow:Done running local_init_op.
INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 0...
INFO:tensorflow:Saving checkpoints for 0 into /tmp/tmpefmfuc4o/model.ckpt.
INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 0...
INFO:tensorflow:loss = 0.6931472, step = 0
INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 100...
INFO:tensorflow:Saving checkpoints for 100 into /tmp/tmpefmfuc4o/model.ckpt.
INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 100...
INFO:tensorflow:Loss for final step: 0.58668363.

 result = model.evaluate(train_input_fn, steps=10)

for key, value in result.items():
  print(key, ":", value)
 
INFO:tensorflow:Calling model_fn.
INFO:tensorflow:Done calling model_fn.
INFO:tensorflow:Starting evaluation at 2020-07-23T01:23:29Z
INFO:tensorflow:Graph was finalized.
INFO:tensorflow:Restoring parameters from /tmp/tmpefmfuc4o/model.ckpt-100
INFO:tensorflow:Running local_init_op.
INFO:tensorflow:Done running local_init_op.
INFO:tensorflow:Evaluation [1/10]
INFO:tensorflow:Evaluation [2/10]
INFO:tensorflow:Evaluation [3/10]
INFO:tensorflow:Evaluation [4/10]
INFO:tensorflow:Evaluation [5/10]
INFO:tensorflow:Evaluation [6/10]
INFO:tensorflow:Evaluation [7/10]
INFO:tensorflow:Evaluation [8/10]
INFO:tensorflow:Evaluation [9/10]
INFO:tensorflow:Evaluation [10/10]
INFO:tensorflow:Inference Time : 0.83507s
INFO:tensorflow:Finished evaluation at 2020-07-23-01:23:30
INFO:tensorflow:Saving dict for global step 100: accuracy = 0.675, accuracy_baseline = 0.58125, auc = 0.71750116, auc_precision_recall = 0.6480325, average_loss = 0.64111984, global_step = 100, label/mean = 0.41875, loss = 0.64111984, precision = 0.85714287, prediction/mean = 0.30204886, recall = 0.26865673
INFO:tensorflow:Saving 'checkpoint_path' summary for global step 100: /tmp/tmpefmfuc4o/model.ckpt-100
accuracy : 0.675
accuracy_baseline : 0.58125
auc : 0.71750116
auc_precision_recall : 0.6480325
average_loss : 0.64111984
label/mean : 0.41875
loss : 0.64111984
precision : 0.85714287
prediction/mean : 0.30204886
recall : 0.26865673
global_step : 100

 for pred in model.predict(train_input_fn):
  for key, value in pred.items():
    print(key, ":", value)
  break
 
INFO:tensorflow:Calling model_fn.
INFO:tensorflow:Done calling model_fn.
INFO:tensorflow:Graph was finalized.
INFO:tensorflow:Restoring parameters from /tmp/tmpefmfuc4o/model.ckpt-100
INFO:tensorflow:Running local_init_op.
INFO:tensorflow:Done running local_init_op.
logits : [-0.5965]
logistic : [0.3551]
probabilities : [0.6449 0.3551]
class_ids : [0]
classes : [b'0']
all_class_ids : [0 1]
all_classes : [b'0' b'1']