Tài liệu này cung cấp các mẹo về hiệu suất dành riêng cho Bộ dữ liệu TensorFlow (TFDS). Lưu ý rằng TFDS cung cấp các tập dữ liệu dưới dạng đối tượng tf.data.Dataset
, vì vậy lời khuyên từ hướng dẫn tf.data
vẫn được áp dụng.
Bộ dữ liệu điểm chuẩn
Sử dụng tfds.benchmark(ds)
để đánh giá bất kỳ đối tượng tf.data.Dataset
nào.
Đảm bảo chỉ ra batch_size=
để bình thường hóa kết quả (ví dụ: 100 iter/giây -> 3200 ex/giây). Điều này hoạt động với bất kỳ lần lặp nào (ví dụ: tfds.benchmark(tfds.as_numpy(ds))
).
ds = tfds.load('mnist', split='train').batch(32).prefetch()
# Display some benchmark statistics
tfds.benchmark(ds, batch_size=32)
# Second iteration is much faster, due to auto-caching
tfds.benchmark(ds, batch_size=32)
Bộ dữ liệu nhỏ (dưới 1 GB)
Tất cả các bộ dữ liệu TFDS lưu trữ dữ liệu trên đĩa ở định dạng TFRecord
. Đối với các tập dữ liệu nhỏ (ví dụ: MNIST, CIFAR-10/-100), việc đọc từ .tfrecord
có thể tăng thêm chi phí đáng kể.
Khi những tập dữ liệu đó vừa với bộ nhớ, có thể cải thiện đáng kể hiệu suất bằng cách lưu vào bộ nhớ đệm hoặc tải trước tập dữ liệu. Lưu ý rằng TFDS tự động lưu trữ các bộ dữ liệu nhỏ (phần sau có thông tin chi tiết).
Bộ nhớ đệm tập dữ liệu
Dưới đây là ví dụ về đường dẫn dữ liệu lưu trữ tập dữ liệu một cách rõ ràng sau khi chuẩn hóa hình ảnh.
def normalize_img(image, label):
"""Normalizes images: `uint8` -> `float32`."""
return tf.cast(image, tf.float32) / 255., label
ds, ds_info = tfds.load(
'mnist',
split='train',
as_supervised=True, # returns `(img, label)` instead of dict(image=, ...)
with_info=True,
)
# Applying normalization before `ds.cache()` to re-use it.
# Note: Random transformations (e.g. images augmentations) should be applied
# after both `ds.cache()` (to avoid caching randomness) and `ds.batch()` (for
# vectorization [1]).
ds = ds.map(normalize_img, num_parallel_calls=tf.data.AUTOTUNE)
ds = ds.cache()
# For true randomness, we set the shuffle buffer to the full dataset size.
ds = ds.shuffle(ds_info.splits['train'].num_examples)
# Batch after shuffling to get unique batches at each epoch.
ds = ds.batch(128)
ds = ds.prefetch(tf.data.experimental.AUTOTUNE)
Khi lặp lại tập dữ liệu này, lần lặp thứ hai sẽ nhanh hơn nhiều so với lần lặp đầu tiên nhờ vào bộ nhớ đệm.
Tự động lưu vào bộ nhớ đệm
Theo mặc định, các bộ dữ liệu TFDS tự động lưu vào bộ đệm (với ds.cache()
) đáp ứng các ràng buộc sau:
- Tổng kích thước tập dữ liệu (tất cả các phần tách) được xác định và < 250 MiB
-
shuffle_files
bị vô hiệu hóa hoặc chỉ đọc một phân đoạn duy nhất
Có thể từ chối tự động lưu vào bộ nhớ đệm bằng cách chuyển try_autocaching=False
tới tfds.ReadConfig
trong tfds.load
. Hãy xem tài liệu danh mục tập dữ liệu để xem liệu một tập dữ liệu cụ thể có sử dụng bộ nhớ đệm tự động hay không.
Đang tải toàn bộ dữ liệu dưới dạng một Tensor
Nếu tập dữ liệu của bạn vừa với bộ nhớ, bạn cũng có thể tải toàn bộ tập dữ liệu dưới dạng một mảng Tensor hoặc NumPy. Có thể làm như vậy bằng cách đặt batch_size=-1
thành lô tất cả các ví dụ trong một tf.Tensor
. Sau đó sử dụng tfds.as_numpy
để chuyển đổi từ tf.Tensor
sang np.array
.
(img_train, label_train), (img_test, label_test) = tfds.as_numpy(tfds.load(
'mnist',
split=['train', 'test'],
batch_size=-1,
as_supervised=True,
))
Bộ dữ liệu lớn
Các bộ dữ liệu lớn được phân chia (chia thành nhiều tệp) và thường không vừa với bộ nhớ, vì vậy chúng không nên được lưu vào bộ nhớ đệm.
Trộn và đào tạo
Trong quá trình huấn luyện, điều quan trọng là phải xáo trộn dữ liệu thật tốt - dữ liệu được xáo trộn kém có thể dẫn đến độ chính xác của huấn luyện thấp hơn.
Ngoài việc sử dụng ds.shuffle
để xáo trộn các bản ghi, bạn cũng nên đặt shuffle_files=True
để có được hành vi xáo trộn tốt cho các tập dữ liệu lớn hơn được phân chia thành nhiều tệp. Nếu không, các kỷ nguyên sẽ đọc các phân đoạn theo cùng một thứ tự và do đó dữ liệu sẽ không thực sự được ngẫu nhiên hóa.
ds = tfds.load('imagenet2012', split='train', shuffle_files=True)
Ngoài ra, khi shuffle_files=True
, TFDS vô hiệu hóa options.deterministic
, điều này có thể giúp tăng hiệu suất một chút. Để có được sự xáo trộn xác định, bạn có thể chọn không tham gia tính năng này bằng tfds.ReadConfig
: bằng cách đặt read_config.shuffle_seed
hoặc ghi đè read_config.options.deterministic
.
Tự động phân chia dữ liệu của bạn trên các công nhân (TF)
Khi đào tạo nhiều công nhân, bạn có thể sử dụng đối số input_context
của tfds.ReadConfig
để mỗi công nhân sẽ đọc một tập hợp con dữ liệu.
input_context = tf.distribute.InputContext(
input_pipeline_id=1, # Worker id
num_input_pipelines=4, # Total number of workers
)
read_config = tfds.ReadConfig(
input_context=input_context,
)
ds = tfds.load('dataset', split='train', read_config=read_config)
Đây là phần bổ sung cho API phân chia. Đầu tiên, API subplit được áp dụng: train[:50%]
được chuyển đổi thành danh sách các tệp cần đọc. Sau đó, op ds.shard()
được áp dụng trên các tệp đó. Ví dụ: khi sử dụng train[:50%]
với num_input_pipelines=2
, mỗi người trong số 2 công nhân sẽ đọc 1/4 dữ liệu.
Khi shuffle_files=True
, các tệp được xáo trộn trong một nhân viên chứ không phải giữa các nhân viên. Mỗi công nhân sẽ đọc cùng một tập hợp con các tệp giữa các kỷ nguyên.
Tự động phân chia dữ liệu của bạn giữa các công nhân (Jax)
Với Jax, bạn có thể sử dụng API tfds.split_for_jax_process
hoặc tfds.even_splits
để phân phối dữ liệu của mình cho các nhân viên. Xem hướng dẫn chia API .
split = tfds.split_for_jax_process('train', drop_remainder=True)
ds = tfds.load('my_dataset', split=split)
tfds.split_for_jax_process
là bí danh đơn giản cho:
# The current `process_index` loads only `1 / process_count` of the data.
splits = tfds.even_splits('train', n=jax.process_count(), drop_remainder=True)
split = splits[jax.process_index()]
Giải mã hình ảnh nhanh hơn
Theo mặc định, TFDS tự động giải mã hình ảnh. Tuy nhiên, có những trường hợp có thể hiệu quả hơn nếu bỏ qua quá trình giải mã hình ảnh bằng tfds.decode.SkipDecoding
và áp dụng tf.io.decode_image
op theo cách thủ công:
- Khi lọc các ví dụ (với
tf.data.Dataset.filter
), để giải mã hình ảnh sau khi các ví dụ đã được lọc. - Khi cắt xén hình ảnh, hãy sử dụng
tf.image.decode_and_crop_jpeg
hợp nhất.
Mã cho cả hai ví dụ đều có sẵn trong hướng dẫn giải mã .
Bỏ qua các tính năng không sử dụng
Nếu bạn chỉ sử dụng một tập hợp con các tính năng thì bạn có thể bỏ qua hoàn toàn một số tính năng. Nếu tập dữ liệu của bạn có nhiều tính năng không được sử dụng thì việc không giải mã chúng có thể cải thiện đáng kể hiệu suất. Xem https://www.tensorflow.org/datasets/decode#only_decode_a_sub-set_of_the_features
tf.data sử dụng hết RAM của tôi!
Nếu bạn bị giới hạn về RAM hoặc nếu bạn đang tải song song nhiều bộ dữ liệu trong khi sử dụng tf.data
thì đây là một số tùy chọn có thể trợ giúp:
Ghi đè kích thước bộ đệm
builder.as_dataset(
read_config=tfds.ReadConfig(
...
override_buffer_size=1024, # Save quite a bit of RAM.
),
...
)
Điều này sẽ ghi đè buffer_size
được truyền cho TFRecordDataset
(hoặc tương đương): https://www.tensorflow.org/api_docs/python/tf/data/TFRecordDataset#args
Sử dụng tf.data.Dataset.with_options để ngăn chặn các hành vi ma thuật
https://www.tensorflow.org/api_docs/python/tf/data/Dataset#with_options
options = tf.data.Options()
# Stop magic stuff that eats up RAM:
options.autotune.enabled = False
options.experimental_distribute.auto_shard_policy = (
tf.data.experimental.AutoShardPolicy.OFF)
options.experimental_optimization.inject_prefetch = False
data = data.with_options(options)