class_docstr: Graph Transformer.
  
  This is the bulk of the AutoDist backend logic, taking a single-node,
  single-GPU graph and transforming it into a distributed
  graph. This all happens based on the `Strategy` provided.
  
  The transformation occurs over several steps:
  
  1. Partitions the necessary variables
  2. Replicates the graph the desired number of times
  3. Within the graph, synchronizes gradients with in-graph logic
  4. Adds between-graph gradient synchronization logic
code: def _initialize_synchronizers(self):
      self._synchronizers = {}
      for node in self._strategy.node_config:
          partitioner = getattr(node, "partitioner")
          if partitioner:
              for part in node.part_config:
                  self._synchronizers[
                      part.var_name
                  ] = Synchronizer.create(
                      part.WhichOneof("synchronizer"),
                      getattr(part, part.WhichOneof("synchronizer")),
                  )
          else:
              self._synchronizers[node.var_name] = Synchronizer.create(
                  node.WhichOneof("synchronizer"),
                  getattr(node, node.WhichOneof("synchronizer")),
              )
  
      config = self._strategy.graph_config.replicas
      replica_devices = {
          device_spec.DeviceSpecV2.from_string(s) for s in config
      }
      replica_hosts = {
          self._cluster.get_address_from_task(d.job, d.task)
          for d in replica_devices
      }
      self._num_workers = len(replica_hosts)
  
      local_canonical_replica_devices = sorted(
          {
              d.to_string()
              for d in replica_devices
              if self._cluster.get_local_address()
              == self._cluster.get_address_from_task(d.job, d.task)
          }
      )
      logging.debug(
          "Local replica devices: {}".format(
              local_canonical_replica_devices
          )
      )
      self._num_local_replicas = len(local_canonical_replica_devices)
  
      local_worker_id = self._cluster.get_local_worker_task_index()
      local_worker_device = "/job:worker/task:{}".format(
          local_worker_id
      )
  
      for synchronizer in self._synchronizers.values():
          synchronizer.assign_cluster_information(
              num_workers=self._num_workers,
              num_replicas=self._num_local_replicas,
              worker_device=local_worker_device,
              worker_id=local_worker_id,
              canonical_replica_devices=sorted(
                  {d.to_string() for d in replica_devices}
              ),
              is_chief=self._cluster.is_chief(),
          )
  
file_path: autodist/autodist/kernel/graph_transformer.py
imports: from tensorflow.core.framework.attr_value_pb2 import AttrValue
  from tensorflow.python.eager import context
  from tensorflow.python.framework import device_spec
  from autodist.graph_item import GraphItem
  from autodist.kernel.partitioner import VariablePartitioner
  from autodist.kernel.replicator import Replicator
  from autodist.kernel.synchronization.synchronizer import Synchronizer
  from autodist.utils import logging, visualization_util
label_desc: Use image node_id 3 for calling the GraphTransformer obj's underlying member method code with example usage: obj._initialize_synchronizers() without return types