Brief IA

Python: 5 Essential Decorators to Optimize Your AI Code

💻 Code & Dev·Tom Levy·

Python: 5 Essential Decorators to Optimize Your AI Code

Python: 5 Essential Decorators to Optimize Your AI Code
Key Takeaways
1Python decorators allow for the separation of key logic from repetitive tasks, thereby improving the clarity of AI code.
2A concurrency limiting decorator uses semaphores to control the asynchronous execution of functions, preventing limit overflows.
3The logging decorator transforms logs into structured JSON, making debugging easier in machine learning systems.
💡Why it mattersThese tools simplify the management of AI projects, making the code more robust and maintainable in the face of complex challenges.
Le brief IA que lisent les pros

Le brief IA que les pros lisent chaque soir

Les 7 actus IA du jour, décryptées en 5 min. Gratuit.

Inclus dès l'inscription : notre sélection des meilleurs guides & comparatifs IA.

Choisis ton rythme

Gratuit · Pas de spam · Désabonnement en 1 clic

📄
Full Analysis

The Utility of Python Decorators in AI Projects

In the world of developing artificial intelligence and machine learning systems, Python decorators play a crucial role. They allow for the separation of essential logic, such as modeling and data pipeline management, from repetitive tasks like testing, validation, timing, and logging. By decoupling these concerns, developers can write clearer and more maintainable code.

This article explores five Python decorators that have proven particularly effective in improving code cleanliness in AI projects. The provided code examples rely on Python's standard libraries and best practices, such as using functools.wraps. The goal is to demonstrate how each decorator can be utilized, enabling you to easily adapt them to your AI coding projects.

Concurrency Limiter: Controlling Asynchronous Execution

When using third-party large language models (LLMs), developers often encounter limits imposed by free tiers. These limits can be quickly reached if too many asynchronous requests are sent simultaneously. The concurrency limiter decorator introduces a mechanism to safeguard these calls using semaphores, which restrict the number of simultaneous executions of an asynchronous function.

from functools import wraps
import asyncio

def limit_concurrency(limit=5):
    sem = asyncio.Semaphore(limit)
    
    def decorator(func):
        async def wrapper(*args, **kwargs):
            async with sem:
                return await func(*args, **kwargs)
        return wrapper
    return decorator

@limit_concurrency(5)
async def fetch_llm_batch([prompt](/glossaire/prompt)):
    return await async_api_client.generate(prompt)

Structured Logger for Machine Learning

In the complex environments of machine learning, simple print() statements can easily get lost, especially in production. The structured logging decorator captures executions and errors, formatting them into structured JSON logs. These logs are not only easy to search but also essential for quick debugging. The following example shows how to decorate a function that defines a training epoch in a neural network model.

import logging
import json
import time
from functools import wraps

def json_log(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        try:
            res = func(*args, **kwargs)
            logging.info(json.dumps({"step": func.__name__, "status": "success", "time": time.time() - start}))
            return res
        except Exception as e:
            logging.error(json.dumps({"step": func.__name__, "error": str(e)}))
    return wrapper

@json_log
def train_epoch(model, training_data):
    return model.fit(training_data)

Feature Injector: Ensuring Data Consistency

When deploying and inferring models, it is crucial to ensure that raw data from end users undergoes the same transformations as those used during training. The feature injector decorator guarantees this consistency by automating the generation of features from raw data. The example below shows how to automatically add a 'is_weekend' feature to an existing dataframe.

from functools import wraps

def add_weekend_feature(func):
    @wraps(func)
    def wrapper(df, *args, **kwargs):
        df = df.copy()  # Avoids Pandas mutation warnings
        df['is_weekend'] = df['date'].dt.dayofweek.isin([5, 6]).astype(int)
        return func(df, *args, **kwargs)
    return wrapper

@add_weekend_feature
def process_data(df):
    # 'is_weekend' is guaranteed to exist here
    return df.dropna()

Deterministic Seed Setter: Ensuring Reliable Experiments

In the phases of experimentation and hyperparameter tuning, using a random seed is common. However, to isolate variables and make test results more reliable, it is often necessary to lock this seed. This allows for determining whether performance variations are due to changes in hyperparameters or random weight initialization. The deterministic seed decorator facilitates this task.

import random
import numpy as np
from functools import wraps

def lock_seed(seed=42):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            random.seed(seed)
            np.random.seed(seed)
            return func(*args, **kwargs)
        return wrapper
    return decorator

@lock_seed()
def initialize_weights():
    return np.random.randn(10, 10)

Development Mode Fallback: Ensuring Continuity

In local development environments and CI/CD testing, it is common to encounter errors due to external factors, such as connection timeouts or API usage limits. The development mode fallback decorator intercepts these errors and returns a predefined set of "mock test data." This allows the application to continue functioning even if an external service temporarily fails.

from functools import wraps

def fallback_mock(mock_data):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception:  # Captures timeouts and rate limits
                return mock_data
        return wrapper
    return decorator

@fallback_mock(mock_data=[0.01, -0.05, 0.02])
def get_text_embeddings(text):
    return external_api.embed(text)

In conclusion, these five Python decorators offer effective solutions for making AI and machine learning code cleaner and more maintainable. They cover various aspects, from structured logging to random seed management, while ensuring operational continuity in the event of external service failures.

Brief IA — L'actualité IA en français

L'essentiel de l'actualité de l'intelligence artificielle, décrypté et expliqué chaque jour.