tf.data: خطوط لوله ورودی TensorFlow را بسازید

مشاهده در TensorFlow.org در Google Colab اجرا کنید مشاهده منبع در GitHubدانلود دفترچه یادداشت

tf.data API شما را قادر به ساخت خطوط لوله پیچیده ورودی از ساده، قطعات قابل استفاده مجدد. به عنوان مثال ، خط لوله برای یک مدل تصویر ممکن است داده های پرونده ها را در یک سیستم فایل توزیع شده جمع آوری کند ، اختلالات تصادفی را روی هر تصویر اعمال کند و تصاویر انتخاب شده به طور تصادفی را در یک دسته برای آموزش ادغام کند. خط لوله برای یک مدل متن ممکن است شامل استخراج نمادها از داده های متن خام ، تبدیل آنها به تعبیه شناسه ها با یک جدول جستجو و دسته بندی توالی هایی با طول های مختلف باشد. tf.data API را ممکن می سازد که مسئولیت رسیدگی به مقادیر زیادی از داده ها، به عنوان خوانده شده از فرمت های مختلف داده ها، و انجام تحولات پیچیده است.

tf.data API معرفی 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 شی، شما می توانید آن را به یک جدید تبدیل Dataset با زنجیری شدن تماس های روش در tf.data.Dataset شی. به عنوان مثال، شما می توانید در هر عنصر تحولات مانند اعمال Dataset.map() و تحولات چند عنصر مانند Dataset.batch() . اسناد و مدارک برای مشاهده tf.data.Dataset برای یک لیست کامل از تحولات.

Dataset شی پایتون iterable است. به این ترتیب می توان عناصر آن را با استفاده از حلقه 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

ساختار مجموعه داده

یک مجموعه داده دنباله ای از عناصر، که در آن هر عنصر همان (تو در تو) ساختار قطعات است تولید می کند. اجزاء منحصر به فرد از ساختار، از هر نوع representable به توسط شود tf.TypeSpec ، از جمله tf.Tensor ، tf.sparse.SparseTensor ، tf.RaggedTensor ، tf.TensorArray یا tf.data.Dataset .

سازه پایتون است که می تواند مورد استفاده قرار گیرد به بیان (تو در تو) ساختار عناصر عبارتند از tuple ، dict ، NamedTuple و OrderedDict . به طور خاص، list است یک ساختار معتبر برای بیان ساختار مجموعه داده عناصر نیست. دلیل این است که کاربران tf.data اوایل احساس به شدت در مورد list ورودی (به عنوان مثال به تصویب رسید به tf.data.Dataset.from_tensors ) به طور خودکار به عنوان تانسورها و بسته بندی شده list خروجی (به عنوان مثال مقادیر بازگشتی از توابع تعریف شده توسط کاربر) از اینکه به یک اجباری tuple . در نتیجه، اگر شما می خواهم یک list ورودی به عنوان یک ساختار درمان می شود، شما نیاز به تبدیل آن به tuple و اگر شما یک دوست list خروجی به تک جزئی، سپس شما نیاز به صراحت آن را بسته با استفاده از tf.stack به

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())
[5 4 1 7 2 7 2 7 5 5]
[9 5 9 2 4 5 8 9 7 7]
[5 4 3 9 8 4 8 2 8 2]
[2 4 1 6 1 1 5 5 8 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

مشاهده در حال بارگذاری آرایه نامپای برای نمونه بیشتر.

اگر تمام متناسب با داده های ورودی خود را در حافظه، ساده ترین راه برای ایجاد یک 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
40960/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
26435584/26421880 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
16384/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
4431872/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 ژنراتور پایتون است.

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 استدلال، که به عنوان استدلال قابل فراخوانی گذشت.

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 استدلال مورد نیاز نمی باشد اما بسیار به عنوان بسیاری از عملیات tensorflow توصیه نمی تانسورها با رتبه ناشناخته پشتیبانی نمی کند. اگر در طول یک محور خاص ناشناخته و یا متغیر است، آن را به عنوان مجموعه ای None در output_shapes .

این نیز مهم است که توجه داشته باشید که 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 : [0.018]
2 : [-0.1799 -0.4538 -1.7808  1.2569 -0.77  ]
3 : [ 0.1728  1.0476 -1.3632  1.2148  1.0274  0.7899]
4 : [-1.5616  0.8348 -0.7334]
5 : [ 0.0777  1.5326 -2.0872]
6 : [-0.0785 -0.3282  0.2665]

خروجی اول است 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())
[13  7  5 17 19 14 12 26 16  6]

[[ 0.976   0.2999  1.1758 -0.7253  0.5655  0.    ]
 [-0.5697 -0.3878 -0.8197  0.8563  1.1502 -0.3431]
 [ 0.      0.      0.      0.      0.      0.    ]
 [ 0.      0.      0.      0.      0.      0.    ]
 [-0.415   1.5524  0.0372  0.8279  0.      0.    ]
 [-2.1594  0.3963  0.5639 -0.1209  0.6403  0.7756]
 [-2.0557  0.3314  0.      0.      0.      0.    ]
 [-0.3955  0.0811 -0.4472  0.      0.      0.    ]
 [-0.7648 -0.8468  0.      0.      0.      0.    ]
 [ 0.3892  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
228827136/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(
    lambda: img_gen.flow_from_directory(flowers), 
    output_types=(tf.float32, tf.float32), 
    output_shapes=([32,256,256,3], [32,5])
)

ds.element_spec
(TensorSpec(shape=(32, 256, 256, 3), dtype=tf.float32, name=None),
 TensorSpec(shape=(32, 5), dtype=tf.float32, name=None))
for images, label in ds.take(1):
  print('images.shape: ', images.shape)
  print('labels.shape: ', labels.shape)
Found 3670 images belonging to 5 classes.
images.shape:  (32, 256, 256, 3)
labels.shape:  (32, 5)

مصرف داده های TFRecord

مشاهده در حال بارگذاری TFRecords برای مثال پایان به پایان.

tf.data API پشتیبانی از انواع فرمت های فایل به طوری که شما می توانید مجموعه داده های بزرگ که در حافظه مناسب نبود را پردازش کند. به عنوان مثال ، قالب فایل 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
7913472/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 رکوردها در فایل های 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
827392/815980 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/derby.txt
811008/809730 [==============================] - 0s 0us/step
819200/809730 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/butler.txt
811008/807992 [==============================] - 0s 0us/step
819200/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 . این باعث می شود تا فایل ها را با هم جمع کنید راحت تر. در اینجا سطرهای اول ، دوم و سوم از هر ترجمه آمده است:

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
40960/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 فایل ، و در حال بارگذاری پانداها DataFrames برای نمونه بیشتر.

فرمت فایل 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)
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 است که با رعایت فراهم می کند 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'               : [30. 26. 31. 20.]
  'n_siblings_spouses': [1 0 0 0]
  'parch'             : [0 0 0 0]
  'fare'              : [24.      7.925  10.5     7.8542]
  'class'             : [b'Second' b'Third' b'Second' b'Third']
  'deck'              : [b'unknown' b'unknown' b'unknown' b'unknown']
  'embark_town'       : [b'Cherbourg' b'Southampton' b'Southampton' b'Southampton']
  'alone'             : [b'n' 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 1 0 0]
  'fare'              : [110.8833  41.5792   8.05    10.5   ]
  'class'             : [b'First' b'Second' b'Third' b'Second']

همچنین یک سطح پایین تر وجود دارد experimental.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/4560613196_91a04f8dcf_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/8223949_2928d3f6f6_n.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/roses/15951588433_c0713cbfc6_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/sunflowers/2960610406_b61930727f_n.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/roses/4809566219_88f9a1aea3.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'daisy'

دسته بندی عناصر مجموعه داده

دسته بندی ساده

ساده ترین شکل جداجدا سفارش داده شده پشته 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 در شکل:

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 یا ثابت طول در مثال بالا). همچنین ممکن است مقدار padding را که به طور پیش فرض 0 است نادیده بگیرید.

گردش کارهای آموزشی

پردازش چند دوره

tf.data API پیشنهادات دو راه اصلی برای پردازش دوره های متعدد از داده های مشابه.

ساده ترین راه برای تکرار بیش از یک مجموعه داده در دوره های متعدد است که استفاده از 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

اگر می خواهید یک محاسبه سفارشی (به عنوان مثال جمع آوری آمار) در پایان هر دوره انجام دهید ، راه اندازی مجدد مجموعه داده در هر دوره ساده تر است:

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
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/data/experimental/ops/counter.py:66: scan (from tensorflow.python.data.experimental.ops.scan_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.data.Dataset.scan(...) instead
<BatchDataset shapes: ((None,), (None,)), types: (tf.int64, tf.string)>

از آنجا که buffer_size 100 است، و به اندازه دسته ای 20 است، دسته اول شامل هیچ یک از عناصر با شاخص بیش از 120.

n,line_batch = next(iter(dataset))
print(n.numpy())
[75  3 49 55 24 30  8 10 91 94 88 40 52 67 89 41 33 63 79 26]

همانطور که با Dataset.batch ترتیب نسبی به Dataset.repeat مسائل.

Dataset.shuffle می کند در پایان هر عصر، سیگنال نیست تا زمانی که بافر زدن خالی است. بنابراین یک 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:

[502 531 625 473 532 600 585 607 381 598]
[544 341 583 586 577 603 377 468 492 512]
[588 562 584 602 596 592 550 286]
[ 40  94   2  58  53  95  77 105  79  20]
[ 16  19  46  72   9 114 108  30  50 103]
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 0x7fd46416f590>

png

اما یک تکرار قبل از یک shuffle مرزهای دوره را با هم مخلوط می کند:

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:

[579 613 483 542 370   9 622 201  26  30]
[595 431 584  20 419 406  18 606 626 562]
[603  39  10 464  25  12 601  43 620  40]
[598  13 581 540  44  15  54 568 478 591]
[ 36  52  53  34   8  62 597 550 599 614]
[593  68 565 617 520 619 604 611  28 572]
[546  71 233  45  29  38  83  80 605 434]
[556  77 575 526  51 552  55  33  56  58]
[559 502  91  61 618  23  66 103 498 609]
[ 76  81   4  83 446 113   1 100 107 474]
[ 21 596 578 104 602  69 126 588  78 102]
[112  19  64  93 131 531 580  60 409   0]
[  7  97  14 138 115  42 141  17 623  95]
[117   2  70  16 114  31 157 151 133 499]
[111 140 144  30 544 105 135 148 506 167]
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 0x7fd42079ab10>

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 رکورد شامل یک یا چند "ویژگی های" و خط لوله ورودی به طور معمول به تانسورها تبدیل این ویژگی ها.

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 خارج PROTOS یک 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],   # Inputs: All except the last 5 steps
          batch[-5:])   # Labels: The last 5 steps

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 = 3

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

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

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

با استفاده از 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 روش می تواند یک مجموعه داده از مجموعه داده را و پهن آن را به یک DataSet به:

for x in windows.flat_map(lambda x: x).take(30):
   print(x.numpy(), end=' ')
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 [==============================] - 1s 0us/step
69165056/69155632 [==============================] - 1s 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.9958 0.0042]

یک رویکرد رایج برای آموزش با مجموعه داده نامتعادل ، ایجاد تعادل بین آن است. tf.data شامل چند روش که فعال کردن این گردش کار:

نمونه گیری مجموعه داده ها

یک رویکرد برای بازنمونه گیری یک مجموعه داده است که استفاده از 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())
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)
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/data/experimental/ops/interleave_ops.py:260: RandomDataset.__init__ (from tensorflow.python.data.ops.dataset_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.data.Dataset.random(...)`.

اکنون مجموعه داده نمونه هایی از هر کلاس را با احتمال 50/50 تولید می کند:

for features, labels in balanced_ds.take(10):
  print(labels.numpy())
[0 1 1 1 0 1 0 1 0 1]
[1 0 0 0 1 0 1 0 0 0]
[1 1 1 0 0 0 1 0 1 0]
[0 0 1 0 0 0 0 1 0 1]
[0 1 0 0 0 1 1 1 1 1]
[0 1 0 1 0 1 1 0 0 1]
[1 0 0 1 1 1 0 0 1 1]
[1 0 0 1 1 0 1 1 0 1]
[1 1 1 0 1 1 0 1 1 1]
[1 1 0 0 0 1 1 1 0 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)

معاملات نمونهبردار مجدد با نمونه های منحصر به فرد، بنابراین شما باید unbatch مجموعه داده قبل از استفاده از نمونهبردار مجدد:

resample_ds = creditcard_ds.unbatch().apply(resampler).batch(10)
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/data/experimental/ops/resampling.py:159: 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:

بازده نمونهبردار مجدد ایجاد (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())
Proportion of examples rejected by sampler is high: [0.995800793][0.995800793 0.00419921894][0 1]
Proportion of examples rejected by sampler is high: [0.995800793][0.995800793 0.00419921894][0 1]
Proportion of examples rejected by sampler is high: [0.995800793][0.995800793 0.00419921894][0 1]
Proportion of examples rejected by sampler is high: [0.995800793][0.995800793 0.00419921894][0 1]
Proportion of examples rejected by sampler is high: [0.995800793][0.995800793 0.00419921894][0 1]
Proportion of examples rejected by sampler is high: [0.995800793][0.995800793 0.00419921894][0 1]
Proportion of examples rejected by sampler is high: [0.995800793][0.995800793 0.00419921894][0 1]
Proportion of examples rejected by sampler is high: [0.995800793][0.995800793 0.00419921894][0 1]
Proportion of examples rejected by sampler is high: [0.995800793][0.995800793 0.00419921894][0 1]
Proportion of examples rejected by sampler is high: [0.995800793][0.995800793 0.00419921894][0 1]
[1 0 0 1 0 1 0 0 1 1]
[0 1 0 1 0 0 0 1 0 1]
[0 0 0 0 0 0 1 1 0 0]
[1 0 0 1 0 1 0 1 0 1]
[1 0 1 1 1 0 1 1 0 0]
[0 1 0 0 0 0 1 1 0 1]
[1 1 1 1 1 0 0 1 0 0]
[0 0 1 1 0 1 1 1 0 0]
[0 0 1 1 1 0 1 0 0 0]
[0 0 1 1 0 1 0 0 0 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]

استفاده از tf.data با tf.keras

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
1875/1875 [==============================] - 4s 2ms/step - loss: 0.5909 - accuracy: 0.8016
Epoch 2/2
1875/1875 [==============================] - 3s 2ms/step - loss: 0.4605 - accuracy: 0.8416
<keras.callbacks.History at 0x7fd4203a1790>

اگر شما یک مجموعه داده بی نهایت، برای مثال عبور از طریق تماس با 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.5205 - accuracy: 0.8109
Epoch 2/2
20/20 [==============================] - 0s 2ms/step - loss: 0.4074 - accuracy: 0.8641
<keras.callbacks.History at 0x7fd420221350>

برای ارزیابی می توانید تعدادی از مراحل ارزیابی را پشت سر بگذارید:

loss, accuracy = model.evaluate(fmnist_train_ds)
print("Loss :", loss)
print("Accuracy :", accuracy)
1875/1875 [==============================] - 4s 2ms/step - loss: 0.4352 - accuracy: 0.8516
Loss : 0.4352473020553589
Accuracy : 0.8516333103179932

برای مجموعه داده های طولانی ، تعداد مراحل ارزیابی را تعیین کنید:

loss, accuracy = model.evaluate(fmnist_train_ds.repeat(), steps=10)
print("Loss :", loss)
print("Accuracy :", accuracy)
10/10 [==============================] - 0s 2ms/step - loss: 0.3755 - accuracy: 0.8875
Loss : 0.3755129277706146
Accuracy : 0.887499988079071

برچسب در هنگام فراخوانی لازم نیست 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)