Mẹo về hiệu suất

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ộ đệ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 tập 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ớ nên không nên 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:

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)