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