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 เป็น Python iterable สิ่งนี้ทำให้สามารถใช้องค์ประกอบโดยใช้ for loop:

dataset = tf.data.Dataset.from_tensor_slices([8, 3, 0, 8, 2, 1])
dataset
<TensorSliceDataset element_spec=TensorSpec(shape=(), dtype=tf.int32, name=None)>
for elem in dataset:
  print(elem.numpy())
8
3
0
8
2
1

หรือโดยการสร้าง Python iterator อย่างชัดเจนโดยใช้ 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

โครงสร้าง Python ที่ใช้เพื่อแสดงโครงสร้าง (ซ้อนกัน) ขององค์ประกอบ ได้แก่ tuple , dict , NamedTuple และ OrderedDict โดยเฉพาะอย่างยิ่ง list ไม่ใช่โครงสร้างที่ถูกต้องสำหรับการแสดงโครงสร้างขององค์ประกอบชุดข้อมูล นี่เป็นเพราะว่าผู้ใช้ tuple ในยุคแรกๆ รู้สึกหนักใจกับ list อินพุต (เช่น ส่งผ่านไปยัง tf.data.Dataset.from_tensors ) ที่บรรจุโดยอัตโนมัติเป็นเทนเซอร์และ list เอาท์พุต ดังนั้น หากคุณต้องการให้ 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 element_spec=TensorSpec(shape=(10,), dtype=tf.int32, name=None)>
for z in dataset1:
  print(z.numpy())
[3 3 7 5 9 8 4 2 3 7]
[8 9 6 7 5 6 1 6 2 3]
[9 8 4 4 8 7 1 5 6 7]
[5 9 5 4 2 5 7 8 8 8]
dataset2 = tf.data.Dataset.from_tensor_slices(
   (tf.random.uniform([4]),
    tf.random.uniform([4, 100], maxval=100, dtype=tf.int32)))

dataset2
<TensorSliceDataset 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
<ZipDataset element_spec=(TensorSpec(shape=(10,), dtype=tf.int32, name=None), (TensorSpec(shape=(), dtype=tf.float32, name=None), TensorSpec(shape=(100,), dtype=tf.int32, name=None)))>
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
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 element_spec=(TensorSpec(shape=(28, 28), dtype=tf.float64, name=None), TensorSpec(shape=(), dtype=tf.uint8, name=None))>

การใช้เครื่องกำเนิด Python

แหล่งข้อมูลทั่วไปอีกแหล่งหนึ่งที่สามารถนำเข้ามาเป็น 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 ที่ทำงานได้อย่างสมบูรณ์

ตัวสร้างรับ callable เป็นอินพุต ไม่ใช่ตัววนซ้ำ ซึ่งช่วยให้สามารถรีสตาร์ทเครื่องกำเนิดไฟฟ้าเมื่อถึงจุดสิ้นสุด ใช้ args ที่เป็นตัวเลือก ซึ่งส่งผ่านเป็นอาร์กิวเมนต์ของ callable

จำเป็นต้องมีอาร์กิวเมนต์ 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.3939]
1 : [ 0.9282 -0.0158  1.0096  0.7155  0.0491  0.6697 -0.2565  0.487 ]
2 : [-0.4831  0.37   -1.3918 -0.4786  0.7425 -0.3299]
3 : [ 0.1427 -1.0438  0.821  -0.8766 -0.8369  0.4168]
4 : [-1.4984 -1.8424  0.0337  0.0941  1.3286 -1.4938]
5 : [-1.3158 -1.2102  2.6887 -1.2809]
6 : []

เอาต์พุตแรกคือ int32 เอาต์พุตที่สองคือ float32

รายการแรกเป็นสเกลาร์ รูปร่าง () และรายการที่สองคือเวกเตอร์ที่ไม่ทราบความยาว รูปร่าง (None,)

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

ds_series
<FlatMapDataset element_spec=(TensorSpec(shape=(), dtype=tf.int32, name=None), TensorSpec(shape=(None,), dtype=tf.float32, name=None))>

ตอนนี้สามารถใช้งานได้เหมือน 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())
[ 8 10 18  1  5 19 22 17 21 25]

[[-0.6098  0.1366 -2.15   -0.9329  0.      0.    ]
 [ 1.0295 -0.033  -0.0388  0.      0.      0.    ]
 [-0.1137  0.3552  0.4363 -0.2487 -1.1329  0.    ]
 [ 0.      0.      0.      0.      0.      0.    ]
 [-1.0466  0.624  -1.7705  1.4214  0.9143 -0.62  ]
 [-0.9502  1.7256  0.5895  0.7237  1.5397  0.    ]
 [ 0.3747  1.2967  0.      0.      0.      0.    ]
 [-0.4839  0.292  -0.7909 -0.7535  0.4591 -1.3952]
 [-0.0468  0.0039 -1.1185 -1.294   0.      0.    ]
 [-0.1679 -0.3375  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 [==============================] - 10s 0us/step
228827136/228813984 [==============================] - 10s 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 สำหรับตัวอย่างแบบ end-to-end

tf.data API รองรับไฟล์รูปแบบต่างๆ เพื่อให้คุณสามารถประมวลผลชุดข้อมูลขนาดใหญ่ที่ไม่พอดีกับหน่วยความจำ ตัวอย่างเช่น รูปแบบไฟล์ TFRecord เป็นรูปแบบไบนารีเชิงบันทึกอย่างง่ายที่แอปพลิเคชั่น TensorFlow จำนวนมากใช้สำหรับข้อมูลการฝึกอบรม คลาส tf.data.TFRecordDataset ช่วยให้คุณสามารถสตรีมเนื้อหาของไฟล์ TFRecord ตั้งแต่หนึ่งไฟล์ขึ้นไปโดยเป็นส่วนหนึ่งของไปป์ไลน์อินพุต

นี่คือตัวอย่างการใช้ไฟล์ทดสอบจาก French Street Name Signs (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 [==============================] - 1s 0us/step
7913472/7904079 [==============================] - 1s 0us/step
ตัวยึดตำแหน่ง60

อาร์กิวเมนต์ filenames สำหรับตัวเริ่มต้น TFRecordDataset สามารถเป็นสตริง รายการสตริง หรือ tf.Tensor ของสตริง ดังนั้น หากคุณมีไฟล์สองชุดสำหรับการฝึกอบรมและการตรวจสอบ คุณสามารถสร้างวิธีการจากโรงงานที่สร้างชุดข้อมูล โดยใช้ชื่อไฟล์เป็นอาร์กิวเมนต์อินพุต:

dataset = tf.data.TFRecordDataset(filenames = [fsns_test_file])
dataset
<TFRecordDatasetV2 element_spec=TensorSpec(shape=(), dtype=tf.string, name=None)>

โครงการ 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 และ การ โหลด 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 0 0 0]
features:
  'sex'               : [b'female' b'female' b'male' b'male']
  'age'               : [32. 28. 37. 50.]
  'n_siblings_spouses': [0 3 0 0]
  'parch'             : [0 1 1 0]
  'fare'              : [13.     25.4667 29.7    13.    ]
  'class'             : [b'Second' b'Third' b'First' b'Second']
  'deck'              : [b'unknown' b'unknown' b'C' b'unknown']
  'embark_town'       : [b'Southampton' b'Southampton' b'Cherbourg' b'Southampton']
  'alone'             : [b'y' b'n' b'n' 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': [0 1 1 0]
  'fare'              : [ 7.05 15.5  26.25  8.05]
  'class'             : [b'Third' b'Third' b'Second' b'Third']

นอกจากนี้ยังมีคลาส 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 element_spec=TensorSpec(shape=(4,), dtype=tf.int32, name=None)>
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 element_spec=TensorSpec(shape=(2,), dtype=tf.int32, name=None)>
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)

ไดเร็กทอรี root มีไดเร็กทอรีสำหรับแต่ละคลาส:

for item in flowers_root.glob("*"):
  print(item.name)
sunflowers
daisy
LICENSE.txt
roses
tulips
dandelion
ตัวยึดตำแหน่ง103

ไฟล์ในแต่ละไดเร็กทอรีคลาสเป็นตัวอย่าง:

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/5018120483_cc0421b176_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/8642679391_0805b147cb_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/sunflowers/8266310743_02095e782d_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/tulips/13176521023_4d7cc74856_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/19437578578_6ab1b3c984.jpg'
ตัวยึดตำแหน่ง105

อ่านข้อมูลโดยใช้ฟังก์ชัน 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\x00\x00\x01\x00\x01\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])]
ตัวยึดตำแหน่ง110

ขณะที่ tf.data พยายามเผยแพร่ข้อมูลรูปร่าง การตั้งค่าเริ่มต้นของ Dataset.batch ส่งผลให้มีขนาดแบทช์ที่ไม่รู้จักเนื่องจากแบทช์ล่าสุดอาจไม่เต็ม หมายเหตุ None ในรูปร่าง:

batched_dataset
<BatchDataset element_spec=(TensorSpec(shape=(None,), dtype=tf.int64, name=None), TensorSpec(shape=(None,), dtype=tf.int64, name=None))>
ตัวยึดตำแหน่ง112

ใช้อาร์กิวเมนต์ drop_remainder เพื่อละเว้นแบทช์สุดท้ายและรับการขยายพันธุ์แบบเต็ม:

batched_dataset = dataset.batch(7, drop_remainder=True)
batched_dataset
<BatchDataset element_spec=(TensorSpec(shape=(7,), dtype=tf.int64, name=None), TensorSpec(shape=(7,), dtype=tf.int64, name=None))>

แบทช์เทนเซอร์พร้อมช่องว่างภายใน

สูตรข้างต้นใช้ได้กับเทนเซอร์ที่มีขนาดเท่ากันทั้งหมด อย่างไรก็ตาม โมเดลจำนวนมาก (เช่น โมเดลตามลำดับ) ทำงานกับข้อมูลอินพุตที่สามารถมีขนาดต่างกันได้ (เช่น ลำดับที่มีความยาวต่างกัน) ในการจัดการกรณีนี้ การแปลง 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]]
ตัวยึดตำแหน่ง116

การแปลง Dataset.padded_batch ช่วยให้คุณสามารถตั้งค่าช่องว่างภายในที่แตกต่างกันสำหรับแต่ละมิติของแต่ละองค์ประกอบ และอาจเป็นความยาวผันแปร (แสดงโดย None ในตัวอย่างด้านบน) หรือความยาวคงที่ นอกจากนี้ยังสามารถแทนที่ค่าช่องว่างภายในซึ่งมีค่าเริ่มต้นเป็น 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')
ตัวยึดตำแหน่ง118

การใช้การ 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
ตัวยึดตำแหน่ง122

สุ่มสับเปลี่ยนข้อมูลเข้า

การ 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 element_spec=(TensorSpec(shape=(None,), dtype=tf.int64, name=None), TensorSpec(shape=(None,), dtype=tf.string, name=None))>
ตัวยึดตำแหน่ง124

เนื่องจาก buffer_size คือ 100 และขนาดชุดงานคือ 20 ชุดแรกจึงไม่มีองค์ประกอบที่มีดัชนีมากกว่า 120

n,line_batch = next(iter(dataset))
print(n.numpy())
[ 52  94  22  70  63  96  56 102  38  16  27 104  89  43  41  68  42  61
 112   8]
ตัวยึดตำแหน่ง126

เช่นเดียวกับ Dataset.batch ลำดับที่เกี่ยวข้องกับ Dataset.repeat

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:

[509 595 537 550 555 591 480 627 482 519]
[522 619 538 581 569 608 531 558 461 496]
[548 489 379 607 611 622 234 525]
[ 59  38   4  90  73  84  27  51 107  12]
[77 72 91 60  7 62 92 47 70 67]
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 0x7f7e7061c650>

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:

[  6   8 528 604  13 492 308 441 569 475]
[  5 626 615 568  20 554 520 454  10 607]
[510 542   0 363  32 446 395 588  35   4]
[  7  15  28  23  39 559 585  49 252 556]
[581 617  25  43  26 548  29 460  48  41]
[ 19  64  24 300 612 611  36  63  69  57]
[287 605  21 512 442  33  50  68 608  47]
[625  90  91 613  67  53 606 344  16  44]
[453 448  89  45 465   2  31 618 368 105]
[565   3 586 114  37 464  12 627  30 621]
[ 82 117  72  75  84  17 571 610  18 600]
[107 597 575  88 623  86 101  81 456 102]
[122  79  51  58  80  61 367  38 537 113]
[ 71  78 598 152 143 620 100 158 133 130]
[155 151 144 135 146 121  83  27 103 134]
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 0x7f7e706013d0>

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.io.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

การใช้ตรรกะ Python โดยพลการ

ด้วยเหตุผลด้านประสิทธิภาพ ให้ใช้การดำเนินการ TensorFlow เพื่อประมวลผลข้อมูลล่วงหน้าทุกครั้งที่ทำได้ อย่างไรก็ตาม บางครั้งการเรียกไลบรารี Python ภายนอกเมื่อแยกวิเคราะห์ข้อมูลอินพุตของคุณก็มีประโยชน์ คุณสามารถใช้การดำเนินการ 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 element_spec=TensorSpec(shape=(), dtype=tf.string, name=None)>
ตัวยึดตำแหน่ง146

คุณสามารถทำงานกับ 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 element_spec=(TensorSpec(shape=(), dtype=tf.string, name=None), TensorSpec(shape=(), dtype=tf.string, name=None))>
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]
ตัวยึดตำแหน่ง158

หรือหากต้องการคาดการณ์อย่างหนาแน่นหนึ่งก้าวสู่อนาคต คุณอาจเปลี่ยนคุณลักษณะและป้ายกำกับทีละขั้นโดยสัมพันธ์กัน:

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())
l10n-ตัวยึดตำแหน่ง
[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 of Datasets ดู โครงสร้างชุดข้อมูล สำหรับรายละเอียด

window_size = 5

windows = range_ds.window(window_size, shift=1)
for sub_ds in windows.take(5):
  print(sub_ds)
<_VariantDataset element_spec=TensorSpec(shape=(), dtype=tf.int64, name=None)>
<_VariantDataset element_spec=TensorSpec(shape=(), dtype=tf.int64, name=None)>
<_VariantDataset element_spec=TensorSpec(shape=(), dtype=tf.int64, name=None)>
<_VariantDataset element_spec=TensorSpec(shape=(), dtype=tf.int64, name=None)>
<_VariantDataset element_spec=TensorSpec(shape=(), dtype=tf.int64, name=None)>
ตัวยึดตำแหน่ง166

เมธอด 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]
ตัวยึดตำแหน่ง170

ในตอนนี้ คุณจะเห็นว่าอาร์กิวเมนต์ 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]
ตัวยึดตำแหน่ง175

สุ่มตัวอย่างใหม่

เมื่อทำงานกับชุดข้อมูลที่มีคลาสไม่สมดุลมาก คุณอาจต้องการสุ่มตัวอย่างชุดข้อมูล 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 [==============================] - 2s 0us/step
69165056/69155632 [==============================] - 2s 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.9956 0.0044]

วิธีทั่วไปในการฝึกอบรมด้วยชุดข้อมูลที่ไม่สมดุลคือการทำให้สมดุล 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.Dataset.sample_from_datasets ให้ส่งชุดข้อมูลและน้ำหนักสำหรับแต่ละชุด:

balanced_ds = tf.data.Dataset.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())
[1 0 1 0 1 0 1 1 1 1]
[0 0 1 1 0 1 1 1 1 1]
[1 1 1 1 0 0 1 0 1 0]
[1 1 1 0 1 0 0 1 1 1]
[0 1 0 1 1 1 0 1 1 0]
[0 1 0 0 0 1 0 0 0 0]
[1 1 1 1 1 0 0 1 1 0]
[0 0 0 1 0 1 1 1 0 0]
[0 0 1 1 1 1 0 1 1 1]
[1 0 0 1 1 1 1 0 1 1]
ตัวยึดตำแหน่ง187

การสุ่มตัวอย่างการปฏิเสธ

ปัญหาอย่างหนึ่งของแนวทาง Dataset.sample_from_datasets ด้านบนคือต้องใช้ tf.data.Dataset แยกกันต่อคลาส คุณสามารถใช้ Dataset.filter เพื่อสร้างชุดข้อมูลทั้งสองชุด แต่นั่นส่งผลให้ข้อมูลทั้งหมดถูกโหลดสองครั้ง

สามารถใช้เมธอด data.Dataset.rejection_resample กับชุดข้อมูลเพื่อปรับสมดุล ในขณะที่โหลดเพียงครั้งเดียว องค์ประกอบจะถูกลบออกจากชุดข้อมูลเพื่อให้เกิดความสมดุล

data.Dataset.rejection_resample รับอาร์กิวเมนต์ class_func class_func นี้ใช้กับองค์ประกอบชุดข้อมูลแต่ละองค์ประกอบ และใช้เพื่อพิจารณาว่าตัวอย่างเป็นของคลาสใดเพื่อวัตถุประสงค์ในการปรับสมดุล

เป้าหมายที่นี่คือการสร้างสมดุลให้กับการกระจายฉลาก และองค์ประกอบของ creditcard_ds เป็นคู่ (features, label) แล้ว ดังนั้น class_func ต้องส่งคืนป้ายกำกับเหล่านั้น:

def class_func(features, label):
  return label

วิธีการสุ่มตัวอย่างจะเกี่ยวข้องกับแต่ละตัวอย่าง ดังนั้นในกรณีนี้ คุณต้อง unbatch ชุดข้อมูลออกก่อนที่จะใช้วิธีนั้น

วิธีการนี้ต้องการการกระจายเป้าหมาย และเลือกประมาณการการกระจายเริ่มต้นเป็นอินพุต

resample_ds = (
    creditcard_ds
    .unbatch()
    .rejection_resample(class_func, target_dist=[0.5,0.5],
                        initial_dist=fractions)
    .batch(10))
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/data/ops/dataset_ops.py:5797: 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:
ตัวยึดตำแหน่ง190

วิธี rejection_resample ตัวอย่างจะคืนค่าคู่ (class, example) โดยที่ class เป็นเอาต์พุตของ 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.995605469][0.995605469 0.00439453125][0 1]
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
Proportion of examples rejected by sampler is high: [0.995605469][0.995605469 0.00439453125][0 1]
[0 1 1 1 0 1 1 0 1 1]
[1 1 0 1 0 0 0 0 1 1]
[1 1 1 1 0 0 0 0 1 1]
[1 0 0 1 0 0 1 0 1 1]
[1 0 0 0 0 1 0 0 0 0]
[1 0 0 1 1 0 1 1 1 0]
[1 1 0 0 0 0 0 0 0 1]
[0 0 1 0 0 0 1 0 1 1]
[0 1 0 1 0 1 0 0 0 1]
[0 0 0 0 0 0 0 0 1 1]

จุดตรวจ Iterator

Tensorflow รองรับ การตรวจสอบจุดตรวจ ดังนั้นเมื่อกระบวนการฝึกอบรมของคุณเริ่มต้นใหม่ จะสามารถกู้คืนจุดตรวจสอบล่าสุดเพื่อกู้คืนความคืบหน้าส่วนใหญ่ได้ นอกจากจุดตรวจสอบตัวแปรโมเดลแล้ว คุณยังสามารถตรวจสอบความคืบหน้าของตัววนซ้ำชุดข้อมูลได้อีกด้วย ซึ่งอาจเป็นประโยชน์หากคุณมีชุดข้อมูลขนาดใหญ่และไม่ต้องการเริ่มชุดข้อมูลตั้งแต่ต้นในการรีสตาร์ทแต่ละครั้ง อย่างไรก็ตาม โปรดทราบว่าจุดตรวจสอบของ iterator อาจมีขนาดใหญ่ เนื่องจากการแปลง เช่น shuffle และ prefetch จำเป็นต้องมีองค์ประกอบบัฟเฟอร์ภายใน iterator

หากต้องการรวม iterator ของคุณในจุดตรวจสอบ ให้ส่ง iterator ไปยังตัวสร้าง 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]
ตัวยึดตำแหน่ง195

การใช้ tf.data กับ tf.keras

tf.keras API ช่วยลดความยุ่งยากในหลายแง่มุมของการสร้างและดำเนินการโมเดลการเรียนรู้ของเครื่อง .fit() และ .evaluate() และ .predict() APIs รองรับชุดข้อมูลเป็นอินพุต นี่คือชุดข้อมูลและการตั้งค่าแบบจำลองอย่างรวดเร็ว:

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.5984 - accuracy: 0.7973
Epoch 2/2
1875/1875 [==============================] - 4s 2ms/step - loss: 0.4607 - accuracy: 0.8430
<keras.callbacks.History at 0x7f7e70283110>
ตัวยึดตำแหน่ง199

หากคุณส่งชุดข้อมูลที่ไม่จำกัด เช่น เรียก 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.4574 - accuracy: 0.8672
Epoch 2/2
20/20 [==============================] - 0s 2ms/step - loss: 0.4216 - accuracy: 0.8562
<keras.callbacks.History at 0x7f7e144948d0>
ตัวยึดตำแหน่ง201

สำหรับการประเมิน คุณสามารถผ่านจำนวนขั้นตอนการประเมิน:

loss, accuracy = model.evaluate(fmnist_train_ds)
print("Loss :", loss)
print("Accuracy :", accuracy)
1875/1875 [==============================] - 4s 2ms/step - loss: 0.4350 - accuracy: 0.8524
Loss : 0.4350026249885559
Accuracy : 0.8524333238601685
ตัวยึดตำแหน่ง203

สำหรับชุดข้อมูลแบบยาว ให้กำหนดจำนวนขั้นตอนในการประเมิน:

loss, accuracy = model.evaluate(fmnist_train_ds.repeat(), steps=10)
print("Loss :", loss)
print("Accuracy :", accuracy)
10/10 [==============================] - 0s 2ms/step - loss: 0.4345 - accuracy: 0.8687
Loss : 0.43447819352149963
Accuracy : 0.8687499761581421
ตัวยึดตำแหน่ง205

ไม่จำเป็นต้องใช้ป้ายกำกับเมื่อเรียก 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)
ตัวยึดตำแหน่ง207

แต่ป้ายกำกับจะถูกละเว้นหากคุณส่งชุดข้อมูลที่มี:

result = model.predict(fmnist_train_ds, steps = 10)
print(result.shape)
(320, 10)
ตัวยึดตำแหน่ง209