TensorFlow (TF) and TensorFlow Federated (TFF) are different layers of functionality that are designed to play well together (as the names implie).
Still, they are different things designed to solve different problems.
I wonder what is the best way to describe computation in a way that can be used by both vanilla TF and in TFF workloads, as well as the kind of pitfalls that one might want to avoid.
Great question. Indeed, there are at least 3 ways to approach composition of TensorFlow code for use with TFF, each with its own merits.
When possible, decorate TensorFlow components using @tf.function, and wrap the entire TensorFlow block as a @tff.tf_computation only at the top level, before embedding it in a @tff.federated_computation. One of the many benefits of this is that it allows you to test components outside of TFF, using standard TensorFlow tools.
So, the following is encouraged and preferred:
# here using TensorFlow's compositional mechanism (defuns)
# rather than TFF's to decorate "foo"
@tf.function(...)
def foo(...):
...
@tff.tf_computation(...)
def bar(...):
# here relying on TensorFlow to embed "foo" as a component of "bar"
...foo(...)...
You may still want to use this pattern to allow your components to be tested outside of TFF, or in situations where neither (1) or (3) works.
So, the following is an alternative you should consider first if (1) doesn't work:
# here composing things in Python, no special TF or TFF mechanism employed
def foo(...):
# keep in mind that in this case, "foo" can access and tamper with
# the internal state of "bar" - you get no isolation benefits
...
@tff.tf_computation(...)
def bar(...):
# here effectively just executing "foo" within "bar" at the
# time "bar" is traced
...foo(...)...
Not encouraged (although currently sometimes necessary):
# here using TFF's compositional mechanism
@tff.tf_computation(...)
def foo(...):
# here you do get isolation benefits - "foo" is traced and
# serialized by TFF, but you can expect that e.g., some
# tf.data.Dataset features won't work
...
@tff.tf_computation(...)
def bar(...):
# here relying on TFF to embed "foo" within "bar"
...foo(...)...