Giúp bảo vệ Great Barrier Reef với TensorFlow trên Kaggle Tham Challenge

tf.data: Xây dựng đường ống đầu vào TensorFlow

Xem trên TensorFlow.org Xem nguồn trên GitHubTải xuống sổ ghi chép

Các tf.data API cho phép bạn xây dựng các đường ống đầu vào phức tạp từ đơn giản, miếng tái sử dụng. Ví dụ: đường dẫn cho một mô hình hình ảnh có thể tổng hợp dữ liệu từ các tệp trong hệ thống tệp phân tán, áp dụng nhiễu ngẫu nhiên cho mỗi hình ảnh và hợp nhất các hình ảnh được chọn ngẫu nhiên thành một lô để đào tạo. Quy trình cho mô hình văn bản có thể liên quan đến việc trích xuất các ký hiệu từ dữ liệu văn bản thô, chuyển đổi chúng để nhúng các số nhận dạng với một bảng tra cứu và kết hợp các chuỗi có độ dài khác nhau lại với nhau. Các tf.data API làm cho nó có thể xử lý một lượng lớn dữ liệu, đọc từ các định dạng dữ liệu khác nhau, và thực hiện biến đổi phức tạp.

Các tf.data API giới thiệu một tf.data.Dataset trừu tượng đại diện cho một chuỗi các yếu tố, trong đó mỗi phần tử bao gồm một hoặc nhiều thành phần. Ví dụ: trong một đường ống hình ảnh, một phần tử có thể là một ví dụ huấn luyện đơn lẻ, với một cặp thành phần tensor đại diện cho hình ảnh và nhãn của nó.

Có hai cách riêng biệt để tạo tập dữ liệu:

  • Một nguồn dữ liệu xây dựng một Dataset từ dữ liệu được lưu trữ trong bộ nhớ hoặc trong một hoặc nhiều file.

  • Một chuyển đổi dữ liệu xây dựng một bộ dữ liệu từ một hoặc nhiều tf.data.Dataset đối tượng.

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)

Cơ học cơ bản

Để tạo một đường ống đầu vào, bạn phải bắt đầu với một nguồn dữ liệu. Ví dụ, để xây dựng một Dataset từ dữ liệu trong bộ nhớ, bạn có thể sử dụng tf.data.Dataset.from_tensors() hoặc tf.data.Dataset.from_tensor_slices() . Ngoài ra, nếu dữ liệu đầu vào của bạn được lưu trữ trong một tập tin theo định dạng TFRecord đề nghị, bạn có thể sử dụng tf.data.TFRecordDataset() .

Một khi bạn có một Dataset đối tượng, bạn có thể biến nó thành một mới Dataset bằng cách kết nối các cuộc gọi phương pháp trên tf.data.Dataset đối tượng. Ví dụ, bạn có thể áp dụng biến đổi mỗi yếu tố như Dataset.map() , và sự biến đổi yếu tố-đa như Dataset.batch() . Xem tài liệu cho tf.data.Dataset cho một danh sách đầy đủ của biến đổi.

Các Dataset đối tượng là một Python iterable. Điều này làm cho nó có thể sử dụng các phần tử của nó bằng cách sử dụng vòng lặp 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

Hoặc bằng cách tạo ra một cách rõ ràng Python iterator sử dụng iter và tiêu thụ các yếu tố của nó sử dụng next :

it = iter(dataset)

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

Ngoài ra, bộ dữ liệu các yếu tố có thể được tiêu thụ bằng cách sử dụng reduce chuyển hóa, làm giảm tất cả các yếu tố để tạo ra một kết quả duy nhất. Ví dụ sau minh họa cách sử dụng reduce chuyển đổi để tính tổng của một bộ dữ liệu của các số nguyên.

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

Cấu trúc tập dữ liệu

Một bộ dữ liệu tạo ra một chuỗi các yếu tố, trong đó mỗi phần tử là như nhau (lồng) cấu trúc của các thành phần. Thành phần riêng biệt của cấu trúc có thể là bất kì kiểu biểu diễn bởi tf.TypeSpec , bao gồm tf.Tensor , tf.sparse.SparseTensor , tf.RaggedTensor , tf.TensorArray , hoặc tf.data.Dataset .

Các cấu trúc Python có thể được sử dụng để diễn tả (lồng) cấu trúc của các yếu tố bao gồm tuple , dict , NamedTuple , và OrderedDict . Đặc biệt, list không phải là một cấu trúc có giá trị để thể hiện cấu trúc của bộ dữ liệu các yếu tố. Điều này là do người dùng tf.data đầu cảm thấy mạnh mẽ về list đầu vào (ví dụ như thông qua với tf.data.Dataset.from_tensors ) được tự động đóng gói như tensors và list kết quả đầu ra (ví dụ giá trị trở lại của hàm do người dùng định nghĩa) bị ép buộc vào một tuple . Do đó, nếu bạn muốn có một list đầu vào để được coi là một cấu trúc, bạn cần phải chuyển đổi nó thành tuple và nếu bạn muốn có một list đầu ra là một thành phần duy nhất, sau đó bạn cần để đóng gói một cách rõ ràng bằng tf.stack .

Các Dataset.element_spec sở hữu cho phép bạn kiểm tra kiểu của mỗi thành phần nguyên tố. Tài sản trả về một cấu trúc lồng nhau của tf.TypeSpec đối tượng, phù hợp với cấu trúc của các yếu tố, có thể là một thành phần duy nhất, một tuple của các thành phần, hoặc một tuple lồng nhau của các thành phần. Ví dụ:

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

Các Dataset biến đổi hỗ trợ bộ dữ liệu của bất kỳ cấu trúc. Khi sử dụng Dataset.map() , và Dataset.filter() biến đổi, trong đó áp dụng một chức năng để mỗi phần tử, cấu trúc nguyên tố xác định các đối số của hàm:

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

Đọc dữ liệu đầu vào

Tiêu thụ mảng NumPy

Xem tải mảng NumPy để biết thêm ví dụ.

Nếu tất cả phù hợp dữ liệu đầu vào của bạn trong bộ nhớ, cách đơn giản nhất để tạo ra một Dataset từ họ là để chuyển đổi chúng sang tf.Tensor đối tượng và sử dụng Dataset.from_tensor_slices() .

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

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

Sử dụng trình tạo Python

Một nguồn dữ liệu phổ biến mà có thể dễ dàng được hấp thụ như một tf.data.Dataset là máy phát điện python.

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

Các Dataset.from_generator constructor chuyển đổi máy phát điện python để một đầy đủ chức năng tf.data.Dataset .

Hàm tạo nhận một đầu vào có thể gọi được, không phải là một trình lặp. Điều này cho phép nó khởi động lại máy phát khi kết thúc. Phải mất một tùy chọn args tranh cãi, được thông qua như là đối số của callable.

Các output_types tranh luận là cần thiết vì tf.data xây dựng một tf.Graph nội bộ, và các cạnh biểu đồ đòi hỏi một 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]

Các output_shapes tranh luận không cần thiết nhưng được đánh giá cao được đề nghị như nhiều hoạt động tensorflow không hỗ trợ tensors với chưa biết xếp hạng. Nếu chiều dài của một trục đặc biệt là không biết hoặc thay đổi, thiết lập nó như None trong output_shapes .

Nó cũng quan trọng cần lưu ý rằng output_shapesoutput_types theo các quy tắc làm tổ giống như phương pháp tập dữ liệu khác.

Đây là một trình tạo ví dụ minh họa cả hai khía cạnh, nó trả về các bộ giá trị của mảng, trong đó mảng thứ hai là một vectơ có độ dài chưa biết.

def gen_series():
  i = 0
  while True:
    size = np.random.randint(0, 10)
    yield i, np.random.normal(size=(size,))
    i += 1
for i, series in gen_series():
  print(i, ":", str(series))
  if i > 5:
    break
0 : [-1.2659 -0.0164  0.8822  0.9887 -0.5352 -1.1873  0.6209]
1 : [-0.492  -2.3205 -0.5924 -0.2635  0.6136  1.9545  1.7315  0.4508]
2 : [0.2972]
3 : [ 0.2433  0.0502 -1.0612 -0.1499 -0.0939  0.5694  0.2207]
4 : [-1.8696 -0.4797]
5 : [ 0.3438 -0.4853 -0.36  ]
6 : [-0.9741]

Kết quả đầu tiên là một int32 thứ hai là một float32 .

Mục đầu tiên là một đại lượng vô hướng, hình dạng () , và thứ hai là một vector có độ dài không rõ, hình dạng (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)>

Bây giờ nó có thể được sử dụng như một thường xuyên tf.data.Dataset . Lưu ý rằng khi Trạm trộn một tập dữ liệu với một hình dạng thay đổi, bạn cần phải sử dụng 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())
[ 5  7 10  4  2 13 25 18 17 27]

[[ 0.8387  0.2497  1.7194  0.      0.      0.      0.      0.      0.    ]
 [-2.3122 -1.1238 -1.3219 -0.0437 -0.0156 -1.5395  1.0858  0.      0.    ]
 [-0.8438  0.6191 -0.9143 -0.0623  0.1734 -0.5583  0.8883  0.      0.    ]
 [-0.955   0.3145  0.4488  0.6816  0.      0.      0.      0.      0.    ]
 [ 0.3774  0.      0.      0.      0.      0.      0.      0.      0.    ]
 [ 0.0288  0.5001 -1.4579  0.576  -0.8185  0.1437  0.8146 -0.9032  0.    ]
 [ 0.      0.      0.      0.      0.      0.      0.      0.      0.    ]
 [ 0.      0.      0.      0.      0.      0.      0.      0.      0.    ]
 [-0.7994 -0.8724 -0.4017  0.      0.      0.      0.      0.      0.    ]
 [-0.0224  0.9805  0.0618 -0.0507 -0.3333 -0.4813  0.7181  0.351   0.097 ]]

Đối với một ví dụ thực tế hơn, hãy thử gói preprocessing.image.ImageDataGenerator như một tf.data.Dataset .

Đầu tiên tải xuống dữ liệu:

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 [==============================] - 5s 0us/step
228827136/228813984 [==============================] - 5s 0us/step

Tạo 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)

Sử dụng dữ liệu TFRecord

Xem Đang tải TFRecords cho một ví dụ end-to-end.

Các tf.data API hỗ trợ một loạt các định dạng tập tin để bạn có thể xử lý các tập dữ liệu lớn mà không phù hợp trong bộ nhớ. Ví dụ: định dạng tệp TFRecord là một định dạng nhị phân hướng bản ghi đơn giản mà nhiều ứng dụng TensorFlow sử dụng cho dữ liệu đào tạo. Các tf.data.TFRecordDataset lớp cho phép bạn dòng qua nội dung của một hoặc nhiều file TFRecord như một phần của đường ống đầu vào.

Đây là một ví dụ bằng cách sử dụng tệp thử nghiệm từ Bảng hiệu Tên đường của Pháp (FSNS).

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

Các filenames lập luận cho TFRecordDataset initializer hoặc có thể là một chuỗi, một danh sách các chuỗi, hoặc một tf.Tensor các chuỗi. Do đó, nếu bạn có hai bộ tệp cho mục đích đào tạo và xác thực, bạn có thể tạo một phương thức gốc tạo ra tập dữ liệu, lấy tên tệp làm đối số đầu vào:

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

Nhiều dự án TensorFlow sử dụng serialized tf.train.Example bản ghi trong file TFRecord của họ. Chúng cần được giải mã trước khi có thể được kiểm tra:

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

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

Sử dụng dữ liệu văn bản

Xem tải bản cho dấu chấm hết cho ví dụ kết thúc.

Nhiều bộ dữ liệu được phân phối dưới dạng một hoặc nhiều tệp văn bản. Các tf.data.TextLineDataset cung cấp một cách dễ dàng để trích xuất đường từ một hoặc nhiều tập tin văn bản. Với một hoặc nhiều tên tập tin, một TextLineDataset sẽ tạo ra một yếu tố chuỗi giá trị trên mỗi dòng của các tập tin.

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)

Đây là vài dòng đầu tiên của tệp đầu tiên:

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)'

Để dòng xen kẽ giữa các tập tin sử dụng Dataset.interleave . Điều này giúp việc trộn các tệp với nhau dễ dàng hơn. Đây là dòng đầu tiên, thứ hai và thứ ba từ mỗi bản dịch:

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'

Theo mặc định, một TextLineDataset mang lại mỗi dòng của mỗi tập tin, mà có thể không được mong muốn, ví dụ, nếu tập tin bắt đầu với một dòng tiêu đề, hoặc chứa ý kiến. Những dòng này có thể được loại bỏ bằng cách sử dụng Dataset.skip() hoặc Dataset.filter() biến đổi. Tại đây, bạn bỏ qua dòng đầu tiên, sau đó lọc để chỉ tìm những người sống sót.

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'

Sử dụng dữ liệu CSV

Xem tải tập tin CSV , và tải Pandas DataFrames để biết thêm ví dụ.

Định dạng tệp CSV là một định dạng phổ biến để lưu trữ dữ liệu dạng bảng ở dạng văn bản thuần túy.

Ví dụ:

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()

Nếu co giật dữ liệu của bạn trong bộ nhớ cùng Dataset.from_tensor_slices phương pháp công trình trên từ điển, cho phép dữ liệu này để dễ dàng nhập khẩu:

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'

Một cách tiếp cận có thể mở rộng hơn là tải từ đĩa khi cần thiết.

Các tf.data mô-đun cung cấp phương pháp ghi nhận chiết xuất từ một hoặc nhiều file CSV mà tuân thủ RFC 4180 .

Các experimental.make_csv_dataset chức năng là giao diện cấp cao cho việc đọc bộ file csv. Nó hỗ trợ suy luận kiểu cột và nhiều tính năng khác, như chia lô và xáo trộn, để làm cho việc sử dụng trở nên đơn giản.

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 0]
features:
  'sex'               : [b'male' b'female' b'male' b'male']
  'age'               : [45.  2. 22. 37.]
  'n_siblings_spouses': [0 0 0 1]
  'parch'             : [0 1 0 0]
  'fare'              : [ 8.05   12.2875  7.7958 26.    ]
  'class'             : [b'Third' b'Third' b'Third' b'Second']
  'deck'              : [b'unknown' b'unknown' b'unknown' b'unknown']
  'embark_town'       : [b'Southampton' b'Southampton' b'Southampton' b'Southampton']
  'alone'             : [b'y' b'n' b'y' b'n']

Bạn có thể sử dụng select_columns tranh luận nếu bạn chỉ cần một tập hợp con của các cột.

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'              : [30.      7.0542  8.05   79.2   ]
  'class'             : [b'First' b'Third' b'Third' b'First']

Ngoài ra còn có một cấp dưới experimental.CsvDataset lớp mà cung cấp điều khiển hạt mịn. Nó không hỗ trợ suy luận kiểu cột. Thay vào đó, bạn phải chỉ định loại của mỗi cột.

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']

Nếu một số cột trống, giao diện cấp thấp này cho phép bạn cung cấp các giá trị mặc định thay vì các loại cột.

%%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]

Theo mặc định, một CsvDataset mang lại mỗi cột của mỗi dòng của tập tin, mà có thể không được mong muốn, ví dụ nếu các tập tin bắt đầu với một dòng tiêu đề đó nên được bỏ qua, hoặc nếu một số cột không cần thiết trong đầu vào. Những dòng này và các lĩnh vực có thể được loại bỏ bằng các headerselect_cols đối số tương ứng.

# 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]

Tiêu thụ bộ tệp

Có nhiều tập dữ liệu được phân phối dưới dạng một tập hợp các tệp, trong đó mỗi tệp là một ví dụ.

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)

Thư mục gốc chứa một thư mục cho mỗi lớp:

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

Các tệp trong mỗi thư mục lớp là ví dụ:

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/daisy/2045022175_ad087f5f60_n.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/tulips/19425920580_cdc8f49aed_n.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/160456948_38c3817c6a_m.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/tulips/17012955700_7141d29eee.jpg'
b'/home/kbuilder/.keras/datasets/flower_photos/sunflowers/4933823300_39fd4420b6.jpg'

Đọc dữ liệu bằng cách sử dụng tf.io.read_file chức năng và trích xuất các nhãn từ con đường, trở về (image, label) cặp:

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'dandelion'

Kết hợp các phần tử tập dữ liệu

Lô đơn giản

Hình thức đơn giản nhất của Trạm trộn ngăn xếp n yếu tố liên tiếp của một tập dữ liệu vào một yếu tố duy nhất. Các Dataset.batch() chuyển đổi không chính xác này, với những hạn chế giống như tf.stack() điều hành, áp dụng cho từng thành phần của các nguyên tố: tức là với mỗi i thành phần, tất cả các yếu tố này phải có một tensor của hình dạng chính xác như vậy.

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])]

Trong khi tf.data cố gắng để truyền bá thông tin hình dạng, các thiết lập mặc định của Dataset.batch kết quả trong một kích thước hàng loạt chưa rõ vì đợt cuối cùng có thể không đầy đủ. Lưu ý None s trong hình dạng:

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

Sử dụng các drop_remainder luận bỏ qua rằng đợt cuối cùng, và nhận công tác tuyên truyền hình đầy đủ:

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

Kết hợp các máy căng với lớp đệm

Công thức trên áp dụng cho các tenxơ có cùng kích thước. Tuy nhiên, nhiều mô hình (ví dụ: mô hình trình tự) làm việc với dữ liệu đầu vào có thể có kích thước khác nhau (ví dụ: chuỗi có độ dài khác nhau). Để xử lý trường hợp này, Dataset.padded_batch chuyển đổi cho phép bạn tensors lô hình dạng khác nhau bằng cách xác định một hoặc nhiều khía cạnh, trong đó họ có thể được đệm.

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]]

Các Dataset.padded_batch chuyển đổi cho phép bạn thiết lập đệm khác nhau cho mỗi kích thước của mỗi thành phần, và nó có thể là chiều dài thay đổi (biểu thị bằng None trong ví dụ trên) hoặc không đổi chiều dài. Cũng có thể ghi đè giá trị đệm, giá trị này được mặc định là 0.

Quy trình đào tạo

Xử lý nhiều kỷ nguyên

Các tf.data API cung cấp hai cách chính để xử lý nhiều kỷ nguyên của cùng một dữ liệu.

Cách đơn giản nhất để lặp qua một tập dữ liệu trong nhiều thời đại là sử dụng Dataset.repeat() chuyển đổi. Đầu tiên, tạo một tập dữ liệu về dữ liệu 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')

Áp dụng Dataset.repeat() chuyển đổi không có đối số sẽ lặp lại các đầu vào vô thời hạn.

Các Dataset.repeat chuyển đổi concatenates đối số của nó mà không báo hiệu sự kết thúc của một kỷ nguyên và sự bắt đầu của kỷ nguyên tiếp theo. Bởi vì điều này một Dataset.batch áp dụng sau khi Dataset.repeat sẽ mang lại lô rằng ranh giới kỷ nguyên straddle:

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

png

Nếu bạn cần tách kỷ nguyên rõ ràng, đưa Dataset.batch trước khi lặp lại:

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

plot_batch_sizes(titanic_batches)

png

Nếu bạn muốn thực hiện một phép tính tùy chỉnh (ví dụ: để thu thập số liệu thống kê) vào cuối mỗi kỷ nguyên thì cách đơn giản nhất là khởi động lại quá trình lặp lại tập dữ liệu trên mỗi kỷ nguyên:

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

Ngẫu nhiên trộn dữ liệu đầu vào

Các Dataset.shuffle() chuyển đổi duy trì một bộ đệm kích thước cố định và chọn phần tử tiếp theo thống nhất một cách ngẫu nhiên từ bộ đệm đó.

Thêm chỉ mục vào tập dữ liệu để bạn có thể thấy hiệu ứng:

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)>

Kể từ khi buffer_size là 100, và kích thước lô là 20, lô hàng đầu tiên không chứa các yếu tố với một chỉ số trên 120.

n,line_batch = next(iter(dataset))
print(n.numpy())
[ 83  24   4  14   6  53  37   2  18  71  27  40  77  76 110  11  30  47
  46  28]

Như với Dataset.batch thứ tự tương ứng với Dataset.repeat vấn đề.

Dataset.shuffle không báo hiệu kết thúc của một kỷ nguyên cho đến khi bộ đệm ngẫu nhiên là rỗng. Vì vậy, một lần xáo trộn được đặt trước một lần lặp lại sẽ hiển thị mọi phần tử của một kỷ nguyên trước khi chuyển sang kỷ nguyên tiếp theo:

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:

[612 458 485 439 305 506 624 570 625 523]
[529 544 597 620 610 615 592 487 566 454]
[611 572 585   0 520 601 398 594]
[ 65  22  75  78  73  55 105  86   8  94]
[ 14  52   4  36  85  66  69 100  38  33]
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 0x7fcea41e1a50>

png

Nhưng một lần lặp lại trước khi xáo trộn sẽ trộn các ranh giới kỷ nguyên với nhau:

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:

[593 580 524 619 506 555 617 526  24 520]
[586 445 356  26 600  20  16 528 504  39]
[583 594 576  37 560  13 626 314  41 501]
[571  46 574 542  32  52   4  10  40  35]
[590 541  34 625  56 609 529 446 618  50]
[ 64 611 582 538 443  72 604  23 613  79]
[ 36  14  17 557   2  59 622  25 395 565]
[400 483 588  89  51 599  67 591  12 458]
[ 69  33 373 101  29  98 102 468 104  94]
[548  21  68  30  61  49 581 513  44 360]
[ 75 121  97 603  58  62 577  15 452  80]
[426  48  65  90 105 616 124 100  66 134]
[ 99  74 620 136  82 587  88  96  31 127]
[138  87 151 131 143 471  86 140 146 117]
[  0 514 119 130  45   8   9 110 107  55]
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 0x7fcdd84fd5d0>

png

Tiền xử lý dữ liệu

Các Dataset.map(f) chuyển đổi sản xuất một tập dữ liệu mới bằng cách áp dụng một chức năng nhất định f để mỗi phần tử của tập dữ liệu đầu vào. Nó được dựa trên map() chức năng mà thường được áp dụng cho danh sách (và các cấu trúc khác) trong ngôn ngữ lập trình chức năng. Chức năng f lấy tf.Tensor đối tượng đại diện cho một yếu tố duy nhất trong đầu vào, và trả về tf.Tensor đối tượng sẽ đại diện cho một yếu tố duy nhất trong tập dữ liệu mới. Việc triển khai nó sử dụng các phép toán TensorFlow tiêu chuẩn để chuyển đổi một phần tử này thành một phần tử khác.

Phần này bao gồm ví dụ phổ biến về cách sử dụng Dataset.map() .

Giải mã dữ liệu hình ảnh và thay đổi kích thước

Khi đào tạo mạng nơ-ron trên dữ liệu hình ảnh trong thế giới thực, thông thường cần phải chuyển đổi các hình ảnh có kích thước khác nhau thành một kích thước chung, để chúng có thể được ghép thành một kích thước cố định.

Xây dựng lại tập dữ liệu tên tệp hoa:

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

Viết một hàm thao tác với các phần tử của tập dữ liệu.

# 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

Kiểm tra xem nó hoạt động.

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

Ánh xạ nó qua tập dữ liệu.

images_ds = list_ds.map(parse_image)

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

png

png

Áp dụng logic Python tùy ý

Vì lý do hiệu suất, hãy sử dụng các hoạt động TensorFlow để xử lý trước dữ liệu của bạn bất cứ khi nào có thể. Tuy nhiên, đôi khi sẽ hữu ích khi gọi các thư viện Python bên ngoài khi phân tích cú pháp dữ liệu đầu vào của bạn. Bạn có thể sử dụng tf.py_function() hoạt động trong một Dataset.map() chuyển đổi.

Ví dụ, nếu bạn muốn áp dụng một vòng xoay ngẫu nhiên, tf.image mô-đun chỉ có tf.image.rot90 , mà không phải là rất hữu ích cho tăng thêm hình ảnh.

Để chứng minh tf.py_function , hãy thử sử dụng scipy.ndimage.rotate chức năng thay vì:

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

Để sử dụng chức năng này với Dataset.map hãy cẩn thận tương tự áp dụng như với Dataset.from_generator , bạn cần phải mô tả hình dạng trở lại và các loại khi bạn áp dụng các chức năng:

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

Parsing tf.Example thông điệp giao thức đệm

Nhiều đường ống đầu vào trích xuất tf.train.Example thông điệp giao thức đệm từ một định dạng TFRecord. Mỗi tf.train.Example kỷ lục chứa một hoặc nhiều "tính năng", và các đường ống dẫn đầu vào thường chuyển đổi các tính năng này vào tensors.

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>

Bạn có thể làm việc với tf.train.Example protos bên ngoài của một tf.data.Dataset để hiểu được dữ liệu:

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])

Cửa sổ chuỗi thời gian

Đối với một đầu đến cuối chuỗi thời gian dụ xem: dự báo chuỗi thời gian .

Dữ liệu chuỗi thời gian thường được sắp xếp với trục thời gian nguyên vẹn.

Sử dụng đơn giản Dataset.range để chứng minh:

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

Thông thường, các mô hình dựa trên loại dữ liệu này sẽ muốn có một lát thời gian liền kề.

Cách tiếp cận đơn giản nhất sẽ là gộp dữ liệu:

Sử dụng 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]

Hoặc để đưa ra các dự đoán dày đặc một bước trong tương lai, bạn có thể thay đổi các tính năng và nhãn từng bước một so với nhau:

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]

Để dự đoán toàn bộ cửa sổ thay vì một khoảng chênh lệch cố định, bạn có thể chia các lô thành hai phần:

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

def label_next_5_steps(batch):
  return (batch[:-5],   # Inputs: All except the last 5 steps
          batch[-5:])   # Labels: The last 5 steps

predict_5_steps = batches.map(label_next_5_steps)

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

Để cho phép một số chồng chéo giữa các tính năng của một hàng loạt và các nhãn của người khác, sử dụng 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]

Sử dụng window

Trong khi sử dụng Dataset.batch công trình, có những tình huống mà bạn có thể cần phải kiểm soát tốt hơn. Các Dataset.window phương pháp cung cấp cho bạn kiểm soát hoàn toàn, nhưng đòi hỏi một số dịch vụ chăm sóc: nó trả về một Dataset của Datasets . Xem cấu trúc Dataset để biết chi tiết.

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>

Các Dataset.flat_map phương pháp có thể tham gia một bộ dữ liệu của các tập dữ liệu và làm phẳng nó vào một tập dữ liệu duy nhất:

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

Trong hầu hết các trường hợp, bạn sẽ muốn .batch dataset đầu tiên:

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]

Bây giờ, bạn có thể thấy rằng shift điều khiển lập luận bao nhiêu mỗi cửa sổ di chuyển qua.

Kết hợp điều này lại với nhau, bạn có thể viết hàm này:

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]

Sau đó, thật dễ dàng để trích xuất nhãn, như trước đây:

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]

Lấy mẫu lại

Khi làm việc với tập dữ liệu rất mất cân bằng về lớp, bạn có thể muốn lấy mẫu lại tập dữ liệu. tf.data cung cấp hai phương pháp để làm điều này. Tập dữ liệu gian lận thẻ tín dụng là một ví dụ điển hình cho loại vấn đề này.

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

csv_path = zip_path.replace('.zip', '.csv')
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/creditcard.zip
69156864/69155632 [==============================] - 1s 0us/step
69165056/69155632 [==============================] - 1s 0us/step
creditcard_ds = tf.data.experimental.make_csv_dataset(
    csv_path, batch_size=1024, label_name="Class",
    # Set the column types: 30 floats and an int.
    column_defaults=[float()]*30+[int()])

Bây giờ, hãy kiểm tra sự phân bố của các lớp, nó rất lệch:

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.9973 0.0027]

Một cách tiếp cận phổ biến để đào tạo với một tập dữ liệu không cân bằng là cân bằng nó. tf.data bao gồm một vài phương pháp cho phép công việc này:

Lấy mẫu tập dữ liệu

Một cách tiếp cận để resampling một tập dữ liệu là sử dụng sample_from_datasets . Đây là áp dụng hơn khi bạn có một riêng biệt data.Dataset cho mỗi lớp.

Ở đây, chỉ cần sử dụng bộ lọc để tạo chúng từ dữ liệu gian lận thẻ tín dụng:

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]

Để sử dụng tf.data.experimental.sample_from_datasets vượt qua bộ dữ liệu, và trọng lượng cho mỗi:

balanced_ds = tf.data.experimental.sample_from_datasets(
    [negative_ds, positive_ds], [0.5, 0.5]).batch(10)
WARNING:tensorflow:From /tmp/ipykernel_11206/929671245.py:2: sample_from_datasets_v2 (from tensorflow.python.data.experimental.ops.interleave_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.data.Dataset.sample_from_datasets(...)`.

Bây giờ tập dữ liệu tạo ra các ví dụ về từng lớp với xác suất 50/50:

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

Lấy lại mẫu từ chối

Một vấn đề với trên experimental.sample_from_datasets cách tiếp cận này là nó cần một riêng biệt tf.data.Dataset mỗi lớp. Sử dụng Dataset.filter hoạt động, nhưng kết quả trong tất cả các dữ liệu được nạp hai lần.

Các data.experimental.rejection_resample chức năng có thể được áp dụng cho một tập dữ liệu để cân bằng lại nó, trong khi chỉ tải nó một lần. Các phần tử sẽ bị loại bỏ khỏi tập dữ liệu để đạt được sự cân bằng.

data.experimental.rejection_resample mất một class_func tranh cãi. Đây class_func được áp dụng cho mỗi phần tử dữ liệu, và được sử dụng để xác định lớp một ví dụ thuộc về nhằm mục đích cân bằng.

Các yếu tố của creditcard_ds đã (features, label) cặp. Vì vậy, các class_func chỉ cần trả lại những nhãn:

def class_func(features, label):
  return label

Trình lấy mẫu lại cũng cần phân phối đích và ước tính phân phối ban đầu có thể là tùy chọn:

resampler = tf.data.experimental.rejection_resample(
    class_func, target_dist=[0.5, 0.5], initial_dist=fractions)
WARNING:tensorflow:From /tmp/ipykernel_11206/2462412449.py:2: rejection_resample (from tensorflow.python.data.experimental.ops.resampling) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.data.Dataset.rejection_resample(...)`.

Các giao dịch lấy mẫu lại với ví dụ cá nhân, vì vậy bạn phải unbatch dataset trước khi áp dụng các hình mẫu:

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

Trở về lấy mẫu lại tạo (class, example) cặp từ đầu ra của class_func . Trong trường hợp này, example là đã một (feature, label) cặp, vì vậy sử dụng map để thả các bản sao của các nhãn:

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

Bây giờ tập dữ liệu tạo ra các ví dụ về từng lớp với xác suất 50/50:

for features, labels in balanced_ds.take(10):
  print(labels.numpy())
Proportion of examples rejected by sampler is high: [0.997265637][0.997265637 0.00273437495][0 1]
Proportion of examples rejected by sampler is high: [0.997265637][0.997265637 0.00273437495][0 1]
Proportion of examples rejected by sampler is high: [0.997265637][0.997265637 0.00273437495][0 1]
Proportion of examples rejected by sampler is high: [0.997265637][0.997265637 0.00273437495][0 1]
Proportion of examples rejected by sampler is high: [0.997265637][0.997265637 0.00273437495][0 1]
Proportion of examples rejected by sampler is high: [0.997265637][0.997265637 0.00273437495][0 1]
Proportion of examples rejected by sampler is high: [0.997265637][0.997265637 0.00273437495][0 1]
Proportion of examples rejected by sampler is high: [0.997265637][0.997265637 0.00273437495][0 1]
Proportion of examples rejected by sampler is high: [0.997265637][0.997265637 0.00273437495][0 1]
Proportion of examples rejected by sampler is high: [0.997265637][0.997265637 0.00273437495][0 1]
[1 1 1 0 1 1 1 0 1 1]
[0 1 1 0 1 1 0 0 1 0]
[1 0 1 1 0 1 0 0 0 1]
[0 1 0 1 0 1 1 0 0 0]
[0 0 0 1 0 1 1 0 0 0]
[1 0 1 0 0 1 1 1 1 0]
[0 1 1 1 1 1 0 1 0 0]
[1 1 1 0 0 1 1 1 0 0]
[0 1 1 0 1 0 1 0 0 0]
[0 0 1 0 1 1 1 1 0 1]

Điểm kiểm tra lặp lại

Hỗ trợ Tensorflow lấy điểm kiểm tra để khi khởi động lại quá trình đào tạo của bạn nó có thể khôi phục các trạm kiểm soát mới nhất để phục hồi hầu hết tiến trình của nó. Ngoài việc kiểm tra các biến mô hình, bạn cũng có thể kiểm tra tiến trình của trình lặp tập dữ liệu. Điều này có thể hữu ích nếu bạn có một tập dữ liệu lớn và không muốn khởi động tập dữ liệu từ đầu mỗi lần khởi động lại. Tuy nhiên lưu ý rằng các điểm kiểm tra iterator có thể lớn, vì biến đổi như shuffleprefetch đòi hỏi yếu tố đệm trong iterator.

Để đưa iterator của bạn trong một trạm kiểm soát, vượt qua iterator đến tf.train.Checkpoint constructor.

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]

Sử dụng tf.data với tf.keras

Các tf.keras API đơn giản hoá nhiều khía cạnh của việc tạo ra và thực hiện các mô hình học máy. Nó .fit().evaluate().predict() API hỗ trợ bộ dữ liệu như đầu vào. Đây là tập dữ liệu nhanh và thiết lập mô hình:

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'])

Đi qua một tập dữ liệu của (feature, label) cặp là tất cả những gì cần thiết cho Model.fitModel.evaluate :

model.fit(fmnist_train_ds, epochs=2)
Epoch 1/2
1875/1875 [==============================] - 4s 2ms/step - loss: 0.5979 - accuracy: 0.7986
Epoch 2/2
1875/1875 [==============================] - 4s 2ms/step - loss: 0.4610 - accuracy: 0.8429
<keras.callbacks.History at 0x7fce40120590>

Nếu bạn vượt qua một bộ dữ liệu vô hạn, ví dụ bằng cách gọi Dataset.repeat() , bạn chỉ cần cũng vượt qua steps_per_epoch luận:

model.fit(fmnist_train_ds.repeat(), epochs=2, steps_per_epoch=20)
Epoch 1/2
20/20 [==============================] - 0s 2ms/step - loss: 0.4489 - accuracy: 0.8484
Epoch 2/2
20/20 [==============================] - 0s 2ms/step - loss: 0.4426 - accuracy: 0.8625
<keras.callbacks.History at 0x7fce4029cfd0>

Để đánh giá, bạn có thể vượt qua một số bước đánh giá:

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

Đối với tập dữ liệu dài, hãy đặt số bước để đánh giá:

loss, accuracy = model.evaluate(fmnist_train_ds.repeat(), steps=10)
print("Loss :", loss)
print("Accuracy :", accuracy)
10/10 [==============================] - 0s 2ms/step - loss: 0.4348 - accuracy: 0.8750
Loss : 0.43478837609291077
Accuracy : 0.875

Các nhãn không cần thiết trong khi gọi 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)

Nhưng các nhãn sẽ bị bỏ qua nếu bạn chuyển tập dữ liệu có chứa chúng:

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