# tensorflow 2.x core api import tensorflow as tf from sklearn.datasets import load_diabetes import mlflow from mlflow.models import infer_signature class Normalize(tf.Module): """Data Normalization class""" def __init__(self, x): # Initialize the mean and standard deviation for normalization self.mean = tf.math.reduce_mean(x, axis=0) self.std = tf.math.reduce_std(x, axis=0) def norm(self, x): return (x - self.mean) / self.std def unnorm(self, x): return (x * self.std) + self.mean class LinearRegression(tf.Module): """Linear Regression model class""" def __init__(self): self.built = False @tf.function def __call__(self, x): # Initialize the model parameters on the first call if not self.built: # Randomly generate the weight vector and bias term rand_w = tf.random.uniform(shape=[x.shape[-1], 1]) rand_b = tf.random.uniform(shape=[]) self.w = tf.Variable(rand_w) self.b = tf.Variable(rand_b) self.built = True y = tf.add(tf.matmul(x, self.w), self.b) return tf.squeeze(y, axis=1) class ExportModule(tf.Module): """Exporting TF model""" def __init__(self, model, norm_x, norm_y): # Initialize pre and postprocessing functions self.model = model self.norm_x = norm_x self.norm_y = norm_y @tf.function(input_signature=[tf.TensorSpec(shape=[None, None], dtype=tf.float32)]) def __call__(self, x): # Run the ExportModule for new data points x = self.norm_x.norm(x) y = self.model(x) y = self.norm_y.unnorm(y) return y def mse_loss(y_pred, y): """Calculating Mean Square Error Loss function""" return tf.reduce_mean(tf.square(y_pred - y)) if __name__ == "__main__": # Set a random seed for reproducible results tf.random.set_seed(42) # Load dataset dataset = load_diabetes(as_frame=True)["frame"] # Drop missing values dataset = dataset.dropna() # using only 1500 dataset = dataset[:1500] dataset_tf = tf.convert_to_tensor(dataset, dtype=tf.float32) # Split dataset into train and test dataset_shuffled = tf.random.shuffle(dataset_tf, seed=42) train_data = dataset_shuffled[100:] test_data = dataset_shuffled[:100] x_train = train_data[:, :-1] y_train = train_data[:, -1] x_test = test_data[:, :-1] y_test = test_data[:, -1] # Data normalization norm_x = Normalize(x_train) norm_y = Normalize(y_train) x_train_norm = norm_x.norm(x_train) y_train_norm = norm_y.norm(y_train) x_test_norm = norm_x.norm(x_test) y_test_norm = norm_y.norm(y_test) with mlflow.start_run(): # Initialize linear regression model lin_reg = LinearRegression() # Use mini batches for memory efficiency and faster convergence batch_size = 32 train_dataset = tf.data.Dataset.from_tensor_slices((x_train_norm, y_train_norm)) train_dataset = train_dataset.shuffle(buffer_size=x_train.shape[0]).batch(batch_size) test_dataset = tf.data.Dataset.from_tensor_slices((x_test_norm, y_test_norm)) test_dataset = test_dataset.shuffle(buffer_size=x_test.shape[0]).batch(batch_size) # Set training parameters epochs = 100 learning_rate = 0.01 train_losses = [] test_losses = [] # Format training loop for epoch in range(epochs): batch_losses_train = [] batch_losses_test = [] # Iterate through the training data for x_batch, y_batch in train_dataset: with tf.GradientTape() as tape: y_pred_batch = lin_reg(x_batch) batch_loss = mse_loss(y_pred_batch, y_batch) # Update parameters with respect to the gradient calculations grads = tape.gradient(batch_loss, lin_reg.variables) for g, v in zip(grads, lin_reg.variables): v.assign_sub(learning_rate * g) # Keep track of batch-level training performance batch_losses_train.append(batch_loss) # Iterate through the testing data for x_batch, y_batch in test_dataset: y_pred_batch = lin_reg(x_batch) batch_loss = mse_loss(y_pred_batch, y_batch) # Keep track of batch-level testing performance batch_losses_test.append(batch_loss) # Keep track of epoch-level model performance train_loss = tf.reduce_mean(batch_losses_train) test_loss = tf.reduce_mean(batch_losses_test) train_losses.append(train_loss) test_losses.append(test_loss) if epoch % 10 == 0: mlflow.log_metric(key="train_losses", value=train_loss, step=epoch) mlflow.log_metric(key="test_losses", value=test_loss, step=epoch) print(f"Mean squared error for step {epoch}: {train_loss.numpy():0.3f}") # Log the parameters mlflow.log_params({ "epochs": epochs, "learning_rate": learning_rate, "batch_size": batch_size, }) # Log the final metrics mlflow.log_metrics({ "final_train_loss": train_loss.numpy(), "final_test_loss": test_loss.numpy(), }) print(f"\nFinal train loss: {train_loss:0.3f}") print(f"Final test loss: {test_loss:0.3f}") # Export the tensorflow model lin_reg_export = ExportModule(model=lin_reg, norm_x=norm_x, norm_y=norm_y) # Infer model signature predictions = lin_reg_export(x_test) signature = infer_signature(x_test.numpy(), predictions.numpy()) mlflow.tensorflow.log_model(lin_reg_export, name="model", signature=signature)