tensorflow / tensorflow/probability

LDA example contains data handling bottleneck.

Open
#774 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Jupyter Notebook
Stars
4.4k
Forks
1.1k
PR merge metrics
No merged PRs in 30d

Description

LDA example, latent_dirichlet_allocation_distributions.py, looks like including a bottleneck in the data loading sequence. By modifying the code like below, the performance was improved. (Runtime log are at the bottom this issue.)
Does that make sense? If necessary, I can send a PR on this. Please feel free to let me know.
(BTW, at least TFP v0.7 (and TF 1.14), this slowdown didn't happen... I'm not sure if this performance change is expected.)

diff --git a/tensorflow_probability/examples/latent_dirichlet_allocation_distributions.py b/tensorflow_probability/examples/latent_dirichlet_allocation_distributions.py
index 2d0c33c5..4f4db7dc 100644
--- a/tensorflow_probability/examples/latent_dirichlet_allocation_distributions.py
+++ b/tensorflow_probability/examples/latent_dirichlet_allocation_distributions.py
@@ -409,7 +409,7 @@ def download(directory, filename):
   return filepath


-def newsgroups_dataset(directory, split_name, num_words, shuffle_and_repeat):
+def newsgroups_dataset(directory, split_name, num_words, batch_size, shuffle_and_repeat):
   """Return 20 newsgroups tf.data.Dataset."""
   data = np.load(download(directory, FILE_TEMPLATE.format(split=split_name)),
                  allow_pickle=True, encoding="latin1")
@@ -440,14 +440,16 @@ def newsgroups_dataset(directory, split_name, num_words, shuffle_and_repeat):
   # stored as a sparse matrix outside of the graph.
   def get_row_py_func(idx):
     def get_row_python(idx_py):
-      return np.squeeze(np.array(sparse_matrix[idx_py].todense()), axis=0)
+      return np.array(sparse_matrix[idx_py].todense())

     py_func = tf.compat.v1.py_func(
         get_row_python, [idx], tf.float32, stateful=False)
-    py_func.set_shape((num_words,))
+    py_func.set_shape((None, num_words))
     return py_func
-  dataset = dataset.map(get_row_py_func)
+  dataset = dataset.batch(batch_size).map(
+    get_row_py_func,
+    num_parallel_calls=tf.data.experimental.AUTOTUNE)
   return dataset


@@ -497,16 +499,17 @@ def build_input_fns(data_dir, batch_size):
   # Build an iterator over training batches.
   def train_input_fn():
     dataset = newsgroups_dataset(
-        data_dir, "train", num_words, shuffle_and_repeat=True)
+        data_dir, "train", num_words, batch_size,
+        shuffle_and_repeat=True)
     # Prefetching makes training about 1.5x faster.
-    dataset = dataset.batch(batch_size).prefetch(32)
+    dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE)
     return tf.compat.v1.data.make_one_shot_iterator(dataset).get_next()

   # Build an iterator over the heldout set.
   def eval_input_fn():
     dataset = newsgroups_dataset(
-        data_dir, "test", num_words, shuffle_and_repeat=False)
-    dataset = dataset.batch(batch_size)
+        data_dir, "test", num_words, batch_size,
+        shuffle_and_repeat=False)
     return tf.compat.v1.data.make_one_shot_iterator(dataset).get_next()

   return train_input_fn, eval_input_fn, vocabulary

Runtime log before modification is below.

...(skip)...
INFO:tensorflow:loss = 547.6347, step = 200 (11.816 sec)
I0206 15:08:23.798992 140255794526016 basic_session_run_hooks.py:260] loss = 547.6347, step = 200 (11.816 sec)
INFO:tensorflow:global_step/sec: 8.51187
I0206 15:08:35.536295 140255794526016 basic_session_run_hooks.py:700] global_step/sec: 8.51187
INFO:tensorflow:loss = 632.9415, step = 300 (11.747 sec)
I0206 15:08:35.546317 140255794526016 basic_session_run_hooks.py:260] loss = 632.9415, step = 300 (11.747 sec)
INFO:tensorflow:global_step/sec: 8.53168
I0206 15:08:47.257299 140255794526016 basic_session_run_hooks.py:700] global_step/sec: 8.53168
...(skip)...

Also, runtime log after modification is below.

...(skip)...
INFO:tensorflow:loss = 471.03162, step = 200 (0.661 sec)
I0206 15:13:45.455081 139863353358144 basic_session_run_hooks.py:260] loss = 471.03162, step = 200 (0.661 sec)
INFO:tensorflow:global_step/sec: 146.232
I0206 15:13:46.137598 139863353358144 basic_session_run_hooks.py:700] global_step/sec: 146.232
INFO:tensorflow:loss = 500.88098, step = 300 (0.684 sec)
I0206 15:13:46.139249 139863353358144 basic_session_run_hooks.py:260] loss = 500.88098, step = 300 (0.684 sec)
INFO:tensorflow:global_step/sec: 150.96
I0206 15:13:46.800009 139863353358144 basic_session_run_hooks.py:700] global_step/sec: 150.96
...(skip)...

My environment:

  • Docker container: tensorflow/tensorflow:2.1.0-gpu-py3
    • Docker version: 19.03.5
  • CPU: Intel(R) Core(TM) i7-6850K CPU @ 3.60GHz
  • GPU: RTX 2080 Ti
  • Command: python latent_dirichlet_allocation_distributions.py

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with tensorflow_probability/examples/latent_dirichlet_allocation_distributions.py, especially newsgroups_dataset and build_input_fns. Run the example with the provided command and compare its data-loading and training throughput with the reported logs. Done means determining whether the batching and parallel mapping change addresses the bottleneck without changing the example's behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, tensorflow
Domain
machine-learning, performance
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.