Google I / O 18 تا 20 مه بازگشت دارد! فضا را ذخیره کنید و برنامه خود را بسازید اکنون ثبت نام کنید

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

شی Dataset یک Python قابل تکرار است. با استفاده از حلقه 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 ،tf.data.Dataset .

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

ویژگی Dataset.element_spec به شما امکان می دهد نوع هر جز component عنصر را بررسی کنید. خاصیت یک ساختار تو در تو از اشیا tf.TypeSpec برمی گرداند ، مطابق با ساختار عنصر ، که ممکن است یک جز single باشد ، یک توله از اجزا یا یک توله تو در تو. مثلا:

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 6 2 1 5 3 8 8 3 6]
[6 8 8 8 5 4 5 1 3 3]
[2 9 9 1 9 2 8 9 4 6]
[1 1 4 1 5 7 1 9 6 7]
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 [==============================] - 1s 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 بلعیده شود تولید کننده پایتون است.

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 کاملاً کاربردی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 : [-0.7193  0.1489  0.3015]
1 : [ 0.9257 -0.8073  1.1848 -0.4203 -0.5188 -1.7014 -1.0934  2.6323]
2 : [-0.2845 -0.3942  0.8429 -0.2441  0.8038]
3 : [ 0.0553 -0.2246]
4 : [ 0.5449 -0.0552 -0.4308 -0.5846  0.2252  0.475  -0.6507  1.3336 -0.6148]
5 : []
6 : [-0.0788 -0.2671 -0.5868 -1.038  -0.145 ]

اولین خروجی 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 معمولی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())
[14  2 16 18  5 13  3  4  0 25]

[[ 2.2655e-03 -1.1356e+00 -6.3269e-01  0.0000e+00  0.0000e+00  0.0000e+00
   0.0000e+00  0.0000e+00  0.0000e+00]
 [ 5.9163e-01  1.1532e-01 -9.4578e-01 -5.7305e-01  5.8756e-01 -8.3086e-01
   1.0705e+00  4.6149e-01 -7.8259e-01]
 [ 8.7261e-01 -1.4031e+00  2.1707e-01  0.0000e+00  0.0000e+00  0.0000e+00
   0.0000e+00  0.0000e+00  0.0000e+00]
 [ 0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00
   0.0000e+00  0.0000e+00  0.0000e+00]
 [ 2.2473e-01 -4.9448e-02 -5.0318e-01 -7.6812e-01 -5.9628e-01  0.0000e+00
   0.0000e+00  0.0000e+00  0.0000e+00]
 [-5.1477e-01  0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00  0.0000e+00
   0.0000e+00  0.0000e+00  0.0000e+00]
 [-1.3577e+00  7.6869e-02  1.3755e+00  1.1951e-01  0.0000e+00  0.0000e+00
   0.0000e+00  0.0000e+00  0.0000e+00]
 [ 1.4727e+00  5.2939e-01  1.1665e-01  6.6326e-01  0.0000e+00  0.0000e+00
   0.0000e+00  0.0000e+00  0.0000e+00]
 [-1.5769e+00  7.4631e-01  8.4916e-01  5.3781e-01 -2.3621e+00  1.0438e-01
   9.3234e-01  0.0000e+00  0.0000e+00]
 [-1.7867e+00 -1.5707e+00  1.7925e-02  0.0000e+00  0.0000e+00  0.0000e+00
   0.0000e+00  0.0000e+00  0.0000e+00]]

برای یک مثال واقعی تر ، سعی کنید بسته بندی 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 [==============================] - 6s 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

آرگومان filenames در TFRecordDataset اولیه TFRecordDataset می تواند یک رشته ، یک لیست از رشته ها یا یک tf.Tensor رشته ها باشد. بنابراین اگر دو مجموعه پرونده برای آموزش و اعتبارسنجی دارید ، می توانید یک روش کارخانه ایجاد کنید که مجموعه داده را تولید می کند و نام پرونده ها را به عنوان آرگومان ورودی در نظر می گیرید:

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

بسیاری از پروژه های tf.train.Example از 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 استفاده کنید. این کار باعث می شود پرونده ها با هم راحت تر شوند. در اینجا سطرهای اول ، دوم و سوم از هر ترجمه آورده شده است:

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 و بارگیری Pandas 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': [1 1 0 1]
features:
  'sex'               : [b'female' b'male' b'male' b'female']
  'age'               : [27. 48. 28. 50.]
  'n_siblings_spouses': [0 1 0 0]
  'parch'             : [2 0 0 0]
  'fare'              : [11.1333 76.7292  7.8958 10.5   ]
  'class'             : [b'Third' b'First' b'Third' b'Second']
  'deck'              : [b'unknown' b'D' b'unknown' b'unknown']
  'embark_town'       : [b'Southampton' b'Cherbourg' b'Southampton' b'Southampton']
  'alone'             : [b'n' b'n' 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 0 0]
  'fare'              : [29.7     7.8958 15.0458  7.75  ]
  'class'             : [b'First' b'Third' b'Second' b'Third']

همچنین یک کلاس سطح پایین 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/sunflowers/7652532108_01ef94c476.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/roses/3276552939_8c31b22d3e.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/roses/4363734507_5cc4ed6e01.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/20754920332_53b995fc63_n.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/15219268336_f2460fca88_m.jpg'

داده ها را با استفاده از تابعtf.io.read_file و برچسب را از مسیر استخراج (image, label) جفت های برگشتی (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\x00\x00\x01\x00\x01\x00\x00\xff\xed\x00\x1cPhotoshop 3.0\x008BIM\x04\x04\x00\x00\x00\x00\x00\x00\xff\xdb\x00C\x00\x03\x02\x02\x03\x02\x02\x03\x03\x03\x03\x04\x03\x03\x04\x05\x08\x05\x05\x04\x04\x05\n\x07\x07\x06\x08\x0c\n\x0c\x0c\x0b\n\x0b\x0b\r\x0e\x12\x10\r\x0e\x11\x0e\x0b\x0b\x10'

b'daisy'

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

دسته ای ساده

ساده ترین شکل دسته ای ، n عنصر متوالی یک مجموعه داده را در یک عنصر واحد قرار می دهد. Dataset.batch() دقیقاً این کار را انجام می دهد ، با همان محدودیت هایی که tf.stack() برای هر م componentلفه عناصر اعمال می شود: یعنی برای هر م componentلفه 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 به شما امکان می دهد برای هر بعد از هر Dataset.padded_batch padding مختلفی تنظیم کنید ، و ممکن است دارای طول متغیر باشد (که توسط None در مثال بالا مشخص نشده است) یا طول ثابت باشد. همچنین می توان مقدار padding را که به طور پیش فرض 0 است ، لغو کرد.

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

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

tf.data API دو روش اصلی برای پردازش چندین دوره از داده های یکسان ارائه می دهد.

ساده ترین راه برای تکرار یک مجموعه داده در چندین دوره استفاده از تغییر شکل Dataset.repeat() . ابتدا یک مجموعه داده از داده های titanic ایجاد کنید:

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
<BatchDataset shapes: ((None,), (None,)), types: (tf.int64, tf.string)>

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

n,line_batch = next(iter(dataset))
print(n.numpy())
[ 36  72  81  39  19  32  70  11  64  18  56  86  30  40 111  80  87  13
 110 116]

همانند 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:

[622 295 617 552 465 394 592 593 442 488]
[469 543 329 600 359 471 554 627 620 456]
[565 550 563 464 556 618 587 449]
[20  0 71  8 25 86 10 57 72  2]
[ 78  99  85  36  62  13 108  46  56  97]
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 0x7fcccbcea860>

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:

[607 519 555 627  23 469   9 501   2 500]
[546 470  14  21 583 552  15 539 340  30]
[620  29 586 570 250   6 599 498 618 610]
[ 16  25 518 505 465 418  12  22  59  51]
[ 10 558  47 553  52   7 616 454 561 597]
[363  68 441 530  75 603  57 339  64  37]
[ 46 388 626  39 600 577 609 467 451  44]
[601  78 537  69 514  54  11 613 502  32]
[ 62  76  85 391  34  65  36 105 541  96]
[548 576 107  61 543  19 527  72 111  38]
[ 98  26  43  70 463 581 115 559 590  86]
[ 50 594  77 490  55 108 370  63  87  94]
[122  27 136  73 617 535  40 144 112  24]
[ 67  99 151 128   5 154 120   4 343 102]
[625 132  83  35 356  80 130  60 156 100]
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 0x7fccb44c5f28>

png

پیش پردازش داده ها

Dataset.map(f) با استفاده از یک تابع داده شده f به هر عنصر از مجموعه داده ، یک مجموعه داده جدید ایجاد می کند. این تابع براساس map() است که معمولاً در زبانهای برنامه نویسی کاربردی برای لیستها (و سایر ساختارها) اعمال می شود. تابع f اشیا tf.Tensor را نشان می دهد که یک عنصر واحد را در ورودی نشان می دهد و 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

تست کنید که م worksثر است.

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() شکل 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 استخراج می کنند. به 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 ، از 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 . برای جزئیات به ساختار Dataset مراجعه کنید.

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=' ')
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 [==============================] - 3s 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.9952 0.0048]

یک روش مشترک برای آموزش با یک مجموعه داده نامتعادل ، متعادل سازی آن است. 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())
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 1 1 0 0 1 1 0 1]
[0 1 1 0 0 1 0 0 1 1]
[0 0 0 1 0 1 1 0 0 1]
[1 0 0 1 1 0 1 1 1 1]
[1 0 0 1 1 1 0 1 1 1]
[0 0 1 0 1 1 0 1 0 0]
[1 0 1 0 0 1 0 1 1 0]
[0 0 0 0 1 0 0 1 0 1]
[0 0 0 0 0 0 1 1 0 1]
[1 0 1 0 0 0 1 0 0 1]

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

یک مشکل در رویکرد فوق experimental.sample_from_datasets .tf.data.Dataset برای هر کلاس به یک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.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:

بازگردانی مجدد جفت (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 0 0 1 1 1 0 0 1]
[0 1 1 0 1 1 1 0 0 0]
[0 0 0 0 1 0 0 1 1 0]
[0 0 1 1 0 1 1 1 0 0]
[0 1 0 1 0 0 0 0 0 0]
[0 0 0 1 0 1 1 0 1 0]
[1 0 0 1 0 1 1 1 0 1]
[0 0 0 0 0 1 0 1 1 1]
[1 0 1 0 1 0 0 0 1 1]
[0 1 0 0 0 1 0 0 0 0]

Iterator Checkpointing

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) جفت (feature, label) تمام آنچه برای Model.fit و Model.evaluate مورد نیاز است ، Model.evaluate :

model.fit(fmnist_train_ds, epochs=2)
Epoch 1/2
1875/1875 [==============================] - 4s 2ms/step - loss: 0.7767 - accuracy: 0.7407
Epoch 2/2
1875/1875 [==============================] - 3s 2ms/step - loss: 0.4709 - accuracy: 0.8387
<tensorflow.python.keras.callbacks.History at 0x7fcca40a2080>

اگر یک مجموعه داده بی نهایت را منتقل کردید ، برای مثال با فراخوانی 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.4301 - accuracy: 0.8609
Epoch 2/2
20/20 [==============================] - 0s 2ms/step - loss: 0.4454 - accuracy: 0.8281
<tensorflow.python.keras.callbacks.History at 0x7fccb416bd30>

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

loss, accuracy = model.evaluate(fmnist_train_ds)
print("Loss :", loss)
print("Accuracy :", accuracy)
1875/1875 [==============================] - 4s 2ms/step - loss: 0.4484 - accuracy: 0.8435
Loss : 0.4483910799026489
Accuracy : 0.843500018119812

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

loss, accuracy = model.evaluate(fmnist_train_ds.repeat(), steps=10)
print("Loss :", loss)
print("Accuracy :", accuracy)
10/10 [==============================] - 0s 2ms/step - loss: 0.4643 - accuracy: 0.8531
Loss : 0.4643370509147644
Accuracy : 0.8531249761581421

هنگام تماس با 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)