Master LLMs with our FREE course in collaboration with Activeloop & Intel Disruptor Initiative. Join now!

Publication

Enhancing Efficiency: 10 Decorators I Use Daily as a Tech MLE
Latest   Machine Learning

Enhancing Efficiency: 10 Decorators I Use Daily as a Tech MLE

Last Updated on November 6, 2023 by Editorial Team

Author(s): MindfulModeler

Originally published on Towards AI.

Introduction:

Machine Learning Engineers (MLEs) are the architects of AI, sculpting intelligent systems that can recognize patterns, make predictions, and automate tasks. In this journey, Python is our trusty companion, and Python decorators are our secret weapons. Decorators are elegant and powerful tools that allow us to modify the behavior of functions or methods. As an MLE in the tech world, I rely on a set of 10 decorators daily to streamline my machine learning workflows. In this blog, I’ll introduce you to these essential decorators with practical code examples, making your journey into the world of machine learning a little more exciting.

Decorator 1: Memoization

Memoization is like having a photographic memory for your functions. It caches the results of expensive function calls and reuses them when the same inputs occur again. This can drastically improve the efficiency of your ML pipelines.

def memoize(func):
cache = {}

def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]

return wrapper

@memoize
def fibonacci(n):
if n <= 1:
return n
else:
return fibonacci(n-1) + fibonacci(n-2)

Decorator 2: Timing

Timing your code is crucial in ML, especially when optimizing algorithms. This decorator calculates the execution time of a function.

import time

def timing(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start} seconds to run.")
return result

return wrapper

@timing
def train_model(data):
# Training code here
pass

Decorator 3: Validation

Validation is a cornerstone of machine learning. This decorator adds input validation to your functions, ensuring that you’re working with the right data types.

def validate_input(*types):
def decorator(func):
def wrapper(*args, **kwargs):
for i, arg in enumerate(args):
if not isinstance(arg, types[i]):
raise TypeError(f"Argument {i+1} should be of type {types[i]}")
return func(*args, **kwargs)
return wrapper
return decorator

@validate_input(int, list)
def train_model(iterations, data):
# Training code here
pass

Decorator 4: Retry

In ML, we often deal with flaky data sources or external APIs. This decorator retries a function a specified number of times if it fails.

import random

def retry(max_retries):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(max_retries):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"Error: {e}")
wait_time = random.uniform(0.1, 1.0)
time.sleep(wait_time)
raise Exception(f"Max retries ({max_retries}) exceeded.")
return wrapper
return decorator

@retry(max_retries=3)
def fetch_data():
# Data fetching code here
pass

Decorator 5: Logging

Logging is your best friend when debugging ML models. This decorator logs function inputs, outputs, and exceptions.

import logging

def log_function(func):
logging.basicConfig(filename='ml_engineer.log', level=logging.INFO)

def wrapper(*args, **kwargs):
try:
result = func(*args, **kwargs)
logging.info(f"{func.__name__}({args}, {kwargs}) returned {result}")
return result
except Exception as e:
logging.error(f"{func.__name__}({args}, {kwargs}) raised an exception: {e}")
raise

return wrapper

@log_function
def train_model(data, epochs=10):
# Training code here
pass

Decorator 6: Parameter Validation

Machine learning models often have numerous hyperparameters. This decorator ensures that the hyperparameters passed to your functions are within acceptable ranges.

def validate_hyperparameters(param_ranges):
def decorator(func):
def wrapper(*args, **kwargs):
for param, value in kwargs.items():
if param in param_ranges:
min_val, max_val = param_ranges[param]
if not (min_val <= value <= max_val):
raise ValueError(f"{param} should be between {min_val} and {max_val}")
return func(*args, **kwargs)
return wrapper
return decorator

@param_validate({'learning_rate': (0.001, 0.1), 'batch_size': (16, 128)})
def train_model(data, learning_rate=0.01, batch_size=32):
# Training code here
pass

Decorator 7: Data Preprocessing

Data preprocessing is a crucial step in ML pipelines. This decorator handles data preprocessing tasks, such as scaling and feature extraction, before passing the data to your functions.

def preprocess_data(func):
def wrapper(*args, **kwargs):
data = args[0] # Assuming the first argument is the data
# Data preprocessing code here
preprocessed_data = data # Replace with actual preprocessing logic
return func(preprocessed_data, **kwargs)
return wrapper

@preprocess_data
def train_model(data, learning_rate=0.01):
# Training code here
pass

Decorator 8: Model Persistance

Once you’ve trained a model, you’ll want to save it for later use. This decorator automatically saves the trained model to a specified file path.

import joblib

def save_model(model_path):
def decorator(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
model = args[0] # Assuming the first argument is the trained model
joblib.dump(model, model_path)
return result
return wrapper
return decorator

@save_model('my_model.pkl')
def train_model(data, epochs=10):
# Training code here
pass

Decorator 9: Performance Profiling

Understanding the performance of your ML code is crucial for optimization. This decorator profiles your code and provides insights into its execution.

import cProfile

def profile_performance(func):
def wrapper(*args, **kwargs):
profiler = cProfile.Profile()
result = profiler.runcall(func, *args, **kwargs)
profiler.print_stats()
return result
return wrapper

@profile_performance
def train_model(data, epochs=10):
# Training code here
pass

Decorator 10: Experiment Tracking

Keeping track of experiments is essential in machine learning research. This decorator logs experiment details, including hyperparameters and performance metrics.

def track_experiment(experiment_name):
def decorator(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
experiment_details = {
'name': experiment_name,
'hyperparameters': kwargs,
'performance_metrics': result # Replace with actual metrics
}
# Log experiment details to a tracking system (e.g., MLflow)
print(f"Logged experiment: {experiment_details}")
return result
return wrapper
return decorator

@track_experiment('experiment_1')
def train_model(data, learning_rate=0.01, batch_size=32):
# Training code here
pass

Conclusion

These ten Python decorators are indispensable companions for any Machine Learning Engineer. They streamline your code, enhance efficiency, and provide valuable insights, making your journey in the realm of machine learning not only more productive but also incredibly rewarding. With these decorators at your disposal, you’re well-equipped to tackle the complexities and challenges of modern AI with confidence and ease. Happy coding, and may your algorithms shine brighter than ever!

Join thousands of data leaders on the AI newsletter. Join over 80,000 subscribers and keep up to date with the latest developments in AI. From research to projects and ideas. If you are building an AI startup, an AI-related product, or a service, we invite you to consider becoming a sponsor.

Published via Towards AI

Feedback ↓