TFDS ודטרמיניזם

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

מסמך זה מסביר:

  • ה-TFDS מבטיח לדטרמיניזם
  • באיזה סדר TFDS קורא דוגמאות
  • הסתייגויות שונות והסתייגויות

להכין

מערכי נתונים

יש צורך בהקשר מסוים כדי להבין כיצד TFDS קורא את הנתונים.

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

מדריך זה משתמש ב-imagenet שיש לו 1024 רסיסים:

import re
import tensorflow_datasets as tfds

imagenet = tfds.builder('imagenet2012')

num_shards = imagenet.info.splits['train'].num_shards
num_examples = imagenet.info.splits['train'].num_examples
print(f'imagenet has {num_shards} shards ({num_examples} examples)')
imagenet has 1024 shards (1281167 examples)

מציאת מזהי הדוגמאות של מערך הנתונים

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

כל למשל במערך מזוהה באופן ייחודי על ידי id (למשל 'imagenet2012-train.tfrecord-01023-of-01024__32' ). אתה יכול לשחזר את זה id ידי העברת read_config.add_tfds_id = True אשר יוסיף 'tfds_id' מפתח dict מן tf.data.Dataset .

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

דטרמיניזם בעת קריאה

סעיף זה מסביר ערבות deterministim של tfds.load .

עם shuffle_files=False (ברירת המחדל)

By TFDS מחדל להניב דוגמאות deterministically ( shuffle_files=False )

# Same as: imagenet.as_dataset(split='train').take(20)
print_ex_ids(imagenet, split='train', take=20)
print_ex_ids(imagenet, split='train', take=20)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1251, 1252, 1253, 1254]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1251, 1252, 1253, 1254]

לקבלת ביצועים, TFDS לקרוא שברים מרובים בו זמנית באמצעות tf.data.Dataset.interleave . אנו רואים בדוגמה זו כי TFDS לעבור שבר 2 לאחר קריאת 16 דוגמאות ( ..., 14, 15, 1251, 1252, ... ). עוד על השזירה למטה.

באופן דומה, ממשק ה-API המשנה הוא גם דטרמיניסטי:

print_ex_ids(imagenet, split='train[67%:84%]', take=20)
print_ex_ids(imagenet, split='train[67%:84%]', take=20)
[858382, 858383, 858384, 858385, 858386, 858387, 858388, 858389, 858390, 858391, 858392, 858393, 858394, 858395, 858396, 858397, 859533, 859534, 859535, 859536]
[858382, 858383, 858384, 858385, 858386, 858387, 858388, 858389, 858390, 858391, 858392, 858393, 858394, 858395, 858396, 858397, 859533, 859534, 859535, 859536]

אם האימונים מיועדים לאנשים יותר בעידן אחד, ההתקנה מעל אינו מומלץ כמו כל תקופות יקראו את השברים באותו סדר (כך אקראיות מוגבל ds = ds.shuffle(buffer) buffer בגודל).

עם shuffle_files=True

עם shuffle_files=True , לפיצולים דשדש במשך כל תקופה, כך הקריאה אינה דטרמיניסטית יותר.

print_ex_ids(imagenet, split='train', shuffle_files=True, take=20)
print_ex_ids(imagenet, split='train', shuffle_files=True, take=20)
[568017, 329050, 329051, 329052, 329053, 329054, 329056, 329055, 568019, 568020, 568021, 568022, 568023, 568018, 568025, 568024, 568026, 568028, 568030, 568031]
[43790, 43791, 43792, 43793, 43796, 43794, 43797, 43798, 43795, 43799, 43800, 43801, 43802, 43803, 43804, 43805, 43806, 43807, 43809, 43810]

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

אזהרת דטרמיניזם: ארגומנטים של שזירה

שינוי read_config.interleave_cycle_length , read_config.interleave_block_length נשנה את סדר דוגמאות.

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

מובטח שהסדר לדוגמה יהיה זהה רק עבור ערך קבוע של ארג'ים שזירה. ראה doc interleave להבין מה cycle_length ו block_length להתכתב מדי.

  • cycle_length=16 , block_length=16 (ברירת מחדל, כנ"ל):
print_ex_ids(imagenet, split='train', take=20)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1251, 1252, 1253, 1254]
  • cycle_length=3 , block_length=2 :
read_config = tfds.ReadConfig(
    interleave_cycle_length=3,
    interleave_block_length=2,
)
print_ex_ids(imagenet, split='train', read_config=read_config, take=20)
[0, 1, 1251, 1252, 2502, 2503, 2, 3, 1253, 1254, 2504, 2505, 4, 5, 1255, 1256, 2506, 2507, 6, 7]

בדוגמה השנייה, אנו רואים כי הנתונים לקרוא 2 ( block_length=2 ) דוגמאות בתוך שבר, ולאחר מכן לעבור את הרסיס הבא. כל 2 * 3 ( cycle_length=3 ) דוגמאות, זה חוזר שבר ראשון ( shard0-ex0, shard0-ex1, shard1-ex0, shard1-ex1, shard2-ex0, shard2-ex1, shard0-ex2, shard0-ex3, shard1-ex2, shard1-ex3, shard2-ex2,... ).

פיצול משנה וסדר לדוגמה

לכל לדוגמה מזהה 0, 1, ..., num_examples-1 . API subsplit לבחור פרוסת דוגמאות (למשל train[:x] בחר 0, 1, ..., x-1 ).

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

באופן ספציפי יותר, ds.take(x) ו split='train[:x]' אינן שוות ערך!

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

print_ex_ids(imagenet, split='train', take=25)  # tfds.load(..., split='train').take(25)
print_ex_ids(imagenet, split='train[:25]', take=-1)  # tfds.load(..., split='train[:25]')
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1251, 1252, 1253, 1254, 1255, 1256, 1257, 1258, 1259]
[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]

לאחר 16 (block_length) הדוגמות, .take(25) עוברים את הרסיס הבא בעוד train[:25] להמשיך לקרוא דוגמאות מן הרסיס הראשון.

מתכונים

קבל עירוב קבצים דטרמיניסטי

ישנן 2 דרכים לערבב דטרמיניסטי:

  1. הגדרת shuffle_seed . הערה: זה מחייב לשנות את הזרע בכל תקופה, אחרת רסיסים ייקראו באותו סדר בין תקופה.
read_config = tfds.ReadConfig(
    shuffle_seed=32,
)

# Deterministic order, different from the default shuffle_files=False above
print_ex_ids(imagenet, split='train', shuffle_files=True, read_config=read_config, take=22)
print_ex_ids(imagenet, split='train', shuffle_files=True, read_config=read_config, take=22)
[176411, 176412, 176413, 176414, 176415, 176416, 176417, 176418, 176419, 176420, 176421, 176422, 176423, 176424, 176425, 176426, 710647, 710648, 710649, 710650, 710651, 710652]
[176411, 176412, 176413, 176414, 176415, 176416, 176417, 176418, 176419, 176420, 176421, 176422, 176423, 176424, 176425, 176426, 710647, 710648, 710649, 710650, 710651, 710652]
  1. שימוש experimental_interleave_sort_fn : זה נותן שליטה מלאה על אשר לפיצולים לקרוא ובאיזה סדר, ולא להסתמך על ds.shuffle סדר.
def _reverse_order(file_instructions):
  return list(reversed(file_instructions))

read_config = tfds.ReadConfig(
    experimental_interleave_sort_fn=_reverse_order,
)

# Last shard (01023-of-01024) is read first
print_ex_ids(imagenet, split='train', read_config=read_config, take=5)
[1279916, 1279917, 1279918, 1279919, 1279920]

קבל צינור מונע דטרמיניסטי

זה יותר מסובך. אין פתרון קל ומשביע רצון.

  1. ללא ds.shuffle ועם דשדוש דטרמיניסטית, בתיאוריה זה אמור להיות אפשרי לספור את הדוגמאות אשר נקראו ולהסיק אשר דוגמאות נקראו בתוך בכל רסיס (כפונקציה של cycle_length , block_length וסדר שבר). ואז skip , take עבור כל פיצול יכול להיות מוזרק דרך experimental_interleave_sort_fn .

  2. עם ds.shuffle זה בלתי אפשרי סביר בלי לדוש בצנרת אימון המלאה. זה ידרוש שמירת ds.shuffle מדינת חיץ כדי להסיק אשר דוגמאות נקראו. דוגמאות יכולות להיות לא רציפה (למשל shard5_ex2 , shard5_ex4 לקרוא אבל לא shard5_ex3 ).

  3. עם ds.shuffle , דרך אחת יהיה להציל כל shards_ids / example_ids לקרוא (להסיק tfds_id ), אז והסיק את ההוראות בקובץ מזה.

המקרה הפשוט ביותר עבור 1. היא לקבל .skip(x).take(y) התאמה train[x:x+y] המשחק. זה דורש:

  • סט cycle_length=1 (כך לפיצולים לקרוא ברצף)
  • סט shuffle_files=False
  • אין להשתמש ds.shuffle

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

read_config = tfds.ReadConfig(
    interleave_cycle_length=1,  # Read shards sequentially
)

print_ex_ids(imagenet, split='train', read_config=read_config, skip=40, take=22)
# If the job get pre-empted, using the subsplit API will skip at most `len(shard0)`
print_ex_ids(imagenet, split='train[40:]', read_config=read_config, take=22)
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61]
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61]

מצא אילו רסיסים/דוגמאות נקראים עבור תת-פיצול נתון

עם tfds.core.DatasetInfo , יש לך גישה ישירה קראו את ההוראות.

imagenet.info.splits['train[44%:45%]'].file_instructions
[FileInstruction(filename='imagenet2012-train.tfrecord-00450-of-01024', skip=700, take=-1, num_examples=551),
 FileInstruction(filename='imagenet2012-train.tfrecord-00451-of-01024', skip=0, take=-1, num_examples=1251),
 FileInstruction(filename='imagenet2012-train.tfrecord-00452-of-01024', skip=0, take=-1, num_examples=1251),
 FileInstruction(filename='imagenet2012-train.tfrecord-00453-of-01024', skip=0, take=-1, num_examples=1251),
 FileInstruction(filename='imagenet2012-train.tfrecord-00454-of-01024', skip=0, take=-1, num_examples=1252),
 FileInstruction(filename='imagenet2012-train.tfrecord-00455-of-01024', skip=0, take=-1, num_examples=1251),
 FileInstruction(filename='imagenet2012-train.tfrecord-00456-of-01024', skip=0, take=-1, num_examples=1251),
 FileInstruction(filename='imagenet2012-train.tfrecord-00457-of-01024', skip=0, take=-1, num_examples=1251),
 FileInstruction(filename='imagenet2012-train.tfrecord-00458-of-01024', skip=0, take=-1, num_examples=1251),
 FileInstruction(filename='imagenet2012-train.tfrecord-00459-of-01024', skip=0, take=-1, num_examples=1251),
 FileInstruction(filename='imagenet2012-train.tfrecord-00460-of-01024', skip=0, take=1001, num_examples=1001)]