# This example showcases how to use Tensorflow with Ray Train. # Original code: # https://www.tensorflow.org/tutorials/distribute/multi_worker_with_keras import os os.environ["TF_USE_LEGACY_KERAS"] = "1" import argparse import json import numpy as np import tensorflow as tf from filelock import FileLock from ray.air.integrations.keras import ReportCheckpointCallback from ray.train import Result, RunConfig, ScalingConfig from ray.train.tensorflow import TensorflowTrainer def mnist_dataset(batch_size: int) -> tf.data.Dataset: with FileLock(os.path.expanduser("~/.mnist_lock")): (x_train, y_train), _ = tf.keras.datasets.mnist.load_data() # The `x` arrays are in uint8 and have values in the [0, 255] range. # You need to convert them to float32 with values in the [0, 1] range. x_train = x_train / np.float32(255) y_train = y_train.astype(np.int64) train_dataset = ( tf.data.Dataset.from_tensor_slices((x_train, y_train)) .shuffle(60000) .repeat() .batch(batch_size) ) return train_dataset def build_cnn_model() -> tf.keras.Model: model = tf.keras.Sequential( [ tf.keras.Input(shape=(28, 28)), tf.keras.layers.Reshape(target_shape=(28, 28, 1)), tf.keras.layers.Conv2D(32, 3, activation="relu"), tf.keras.layers.Flatten(), tf.keras.layers.Dense(128, activation="relu"), tf.keras.layers.Dense(10), ] ) return model def train_func(config: dict): per_worker_batch_size = config.get("batch_size", 64) epochs = config.get("epochs", 3) steps_per_epoch = config.get("steps_per_epoch", 70) tf_config = json.loads(os.environ["TF_CONFIG"]) num_workers = len(tf_config["cluster"]["worker"]) strategy = tf.distribute.MultiWorkerMirroredStrategy() global_batch_size = per_worker_batch_size * num_workers multi_worker_dataset = mnist_dataset(global_batch_size) with strategy.scope(): # Model building/compiling need to be within `strategy.scope()`. multi_worker_model = build_cnn_model() learning_rate = config.get("lr", 0.001) multi_worker_model.compile( loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), optimizer=tf.keras.optimizers.SGD(learning_rate=learning_rate), metrics=["accuracy"], ) history = multi_worker_model.fit( multi_worker_dataset, epochs=epochs, steps_per_epoch=steps_per_epoch, callbacks=[ReportCheckpointCallback()], ) results = history.history return results def train_tensorflow_mnist( num_workers: int = 2, use_gpu: bool = False, epochs: int = 4, storage_path: str = None, ) -> Result: config = {"lr": 1e-3, "batch_size": 64, "epochs": epochs} trainer = TensorflowTrainer( train_loop_per_worker=train_func, train_loop_config=config, scaling_config=ScalingConfig(num_workers=num_workers, use_gpu=use_gpu), run_config=RunConfig(storage_path=storage_path), ) results = trainer.fit() return results if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument( "--address", required=False, type=str, help="the address to use for Ray" ) parser.add_argument( "--num-workers", "-n", type=int, default=2, help="Sets number of workers for training.", ) parser.add_argument( "--use-gpu", action="store_true", default=False, help="Enables GPU training" ) parser.add_argument( "--epochs", type=int, default=3, help="Number of epochs to train for." ) parser.add_argument( "--smoke-test", action="store_true", default=False, help="Finish quickly for testing.", ) args, _ = parser.parse_known_args() import ray if args.smoke_test: # 2 workers, 1 for trainer, 1 for datasets num_gpus = args.num_workers if args.use_gpu else 0 ray.init(num_cpus=4, num_gpus=num_gpus) train_tensorflow_mnist(num_workers=2, use_gpu=args.use_gpu) else: ray.init(address=args.address) train_tensorflow_mnist( num_workers=args.num_workers, use_gpu=args.use_gpu, epochs=args.epochs )