Red Hot Cyber
Condividi la tua difesa. Incoraggia l'eccellenza. La vera forza della cybersecurity risiede nell'effetto moltiplicatore della conoscenza.
Cerca
Banner Ransomfeed 970x120 1
Enterprise BusinessLog 320x200 1
Inheritance in Python: la chiave per scrivere codice pulito e collaborativo nel Machine Learning

Inheritance in Python: la chiave per scrivere codice pulito e collaborativo nel Machine Learning

Marcello Politi : 17 Aprile 2025 22:22

Molte persone che si avvicinano al machine learning non hanno un forte background in ingegneria del software, e quando devono lavorare su un prodotto reale, il loro codice può risultare disordinato e difficile da gestire. Per questo motivo, raccomando sempre vivamente di imparare a usare le coding best practices, che ti permetteranno di lavorare senza problemi all’interno di un team e di migliorare il livello del progetto su cui stai lavorando. Oggi voglio parlare dell’inheritance di Python e mostrare alcuni semplici esempi di come utilizzarla nel campo del machine learning.

Nello sviluppo software e in altri ambiti dell’informatica, il technical debt (noto anche come design debt o code debt) rappresenta il costo implicito di future rielaborazioni dovuto a una soluzione che privilegia la rapidità rispetto a un design a lungo termine.

Se sei interessato a saperne di più sui design patterns, potresti trovare utili alcuni dei miei articoli precedenti.

Python Inheritance


Byte The Silence

Scarica Gratuitamente Byte The Silence, il fumetto sul Cyberbullismo di Red Hot Cyber
"Il cyberbullismo è una delle minacce più insidiose e silenziose che colpiscono i nostri ragazzi. Non si tratta di semplici "bravate online", ma di veri e propri atti di violenza digitale, capaci di lasciare ferite profonde e spesso irreversibili nell’animo delle vittime. 
Non possiamo più permetterci di chiudere gli occhi". Così si apre la prefazione del fumetto di Massimiliano Brolli, fondatore di Red Hot Cyber, un’opera che affronta con sensibilità e realismo uno dei temi più urgenti della nostra epoca.
 Distribuito gratuitamente, questo fumetto nasce con l'obiettivo di sensibilizzare e informare. È uno strumento pensato per scuole, insegnanti, genitori e vittime, ma anche per chi, per qualsiasi ragione, si è ritrovato nel ruolo del bullo, affinché possa comprendere, riflettere e cambiare. 
Con la speranza che venga letto, condiviso e discusso, Red Hot Cyber è orgogliosa di offrire un contributo concreto per costruire una cultura digitale più consapevole, empatica e sicura. Contattaci tramite WhatsApp al numero 375 593 1011 per richiedere ulteriori informazioni oppure alla casella di posta [email protected]


Supporta Red Hot Cyber attraverso: 

  1. L'acquisto del fumetto sul Cybersecurity Awareness
  2. Ascoltando i nostri Podcast
  3. Seguendo RHC su WhatsApp
  4. Seguendo RHC su Telegram
  5. Scarica gratuitamente “Byte The Silence”, il fumetto sul Cyberbullismo di Red Hot Cyber

Se ti piacciono le novità e gli articoli riportati su di Red Hot Cyber, iscriviti immediatamente alla newsletter settimanale per non perdere nessun articolo. La newsletter generalmente viene inviata ai nostri lettori ad inizio settimana, indicativamente di lunedì.

L’Inheritance non è solo un concetto di Python, ma un concetto generale nell’Object Oriented Programming. Quindi, in questo tutorial, dovremo lavorare con classei e oggetti, che rappresentano un paradigma di sviluppo non molto utilizzato in Python rispetto ad altri linguaggi come Java.

Nell’OOP, possiamo definire una classe generale che rappresenta qualcosa nel mondo, ad esempio una Persona, che definiamo semplicemente con un nome, un cognome e un’età nel seguente modo.

class Person:
    def __init__(self, name, surname, age):
        self.name = name
        self.surname = surname
        self.age = age
        
    def __str__(self):
        return f"Name: {self.name}, surname: {self.surname}, age: {self.age}"
    
    def grow(self):
        self.age +=1

In questa classe, abbiamo definito un semplice costruttore (init). Poi abbiamo definito il method str, che si occuperà di stampare l’oggetto nel modo che desideriamo. Infine, abbiamo il method grow() per rendere la persona di un anno più vecchia.

Ora possiamo instanziare un oggetto e utilizzare questa classe.

person = Person("Marcello", "Politi", 28)
person.grow()
print(person)


# output wiil be
# Name: Marcello, surname: Politi, age: 29

E se volessimo definire un particolare tipo di persona, ad esempio un operaio? Possiamo fare la stessa cosa di prima, ma aggiungiamo un’altra variabile di input per aggiungere il suo stipendio.

class Worker:
    def __init__(self, name, surname, age, salary):
        self.name = name
        self.surname = surname
        self.age = age
        self.salary = salary
        
    def __str__(self):
        return f"Name: {self.name}, surname: {self.surname}, age: {self.age}, salary: {self.salary}"
    
    def grow(self):
        self.age +=1

Tutto qui. Ma è questo il modo migliore per implementarlo? Vedete che la maggior parte del codice del lavoratore è uguale a quello della persona, perché un lavoratore è una persona particolare e condivide molte cose in comune con una persona.

Quello che possiamo fare è dire a Python che il lavoratore deve ereditare tutto da Persona, e poi aggiungere manualmente tutte le cose di cui abbiamo bisogno, che una persona generica non ha.

class Worker(Person):
    def __init__(self, name, surname, age, salary):
        super().__init__(name, surname, age)
        self.salary = salary
    
    def __str__(self):
        text = super().__str__()
        return text + f",salary: {self.salary}"

Nella classe worker, il costruttore richiama il costruttore della classe person sfruttando la parola chiave super() e poi aggiunge anche la variabile salary.

La stessa cosa avviene quando si definisce il metodo str. Utilizziamo lo stesso testo restituito da Person usando la parola chiave super e aggiungiamo il salario quando stampiamo l’oggetto.

Ereditarietà nel Machine Learning

Non ci sono regole su quando usare l’ereditarietà nell’machine learning . Non so a quale progetto stiate lavorando, né come sia il vostro codice. Voglio solo sottolineare il fatto che dovreste adottare un paradigma OOP nel vostro codice. Tuttavia, vediamo alcuni esempi di utilizzo dell’ereditarietà.

Definire un BaseModel

Sviluppiamo una classe di base per il modello di ML, definita da alcune variabili standard. Questa classe avrà un metodo per caricare i dati, uno per addestrare, un altro per valutare e uno per preelaborare i dati. Tuttavia, ogni modello specifico preelaborerà i dati in modo diverso, quindi le sottoclassi che erediteranno il modello di base dovranno riscrivere il metodo di preelaborazione.
Attenzione, il modello BaseMLModel stesso eredita la classe ABC. Questo è un modo per dire a Python che questa classe è una classe astratta e non deve essere usata, ma è solo un modello per costruire sottoclassi.

Lo stesso vale per il metodo preprocess_train_data, che è contrassegnato come @abstactmethod. Ciò significa che le sottoclassi devono reimplementare questo metodo.

Guardate questo video per saperne di più su classi e metodi astratti:

from abc import ABC, abstractmethod
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris
import numpy as np

class BaseMLModel(ABC):
    def __init__(self, test_size=0.2, random_state=42):
        self.model = None  # This will be set in subclasses
        self.test_size = test_size
        self.random_state = random_state
        self.X_train = None
        self.X_test = None
        self.y_train = None
        self.y_test = None

    def load_data(self, X, y):
        self.X_train, self.X_test, self.y_train, self.y_test = train_test_split(
            X, y, test_size=self.test_size, random_state=self.random_state
        )

    @abstractmethod
    def preprocess_train_data(self):
        """Each model can define custom preprocessing for training data."""
        pass

    def train(self):
        self.X_train, self.y_train = self.preprocess_train_data()
        self.model.fit(self.X_train, self.y_train)

    def evaluate(self):
        predictions = self.model.predict(self.X_test)
        return accuracy_score(self.y_test, predictions)

Vediamo ora come ereditare da questa classe. Per prima cosa, possiamo implementare un LogisticRegressionModel. Che avrà il suo algoritmo di preelaborazione.

class LogisticRegressionModel(BaseMLModel):
    def __init__(self, **kwargs):
        super().__init__()
        self.model = LogisticRegression(**kwargs)

    def preprocess_train_data(self):
        #Standardize features for Logistic Regression
        mean = self.X_train.mean(axis=0)
        std = self.X_train.std(axis=0)
        X_train_scaled = (self.X_train - mean) / std
        return X_train_scaled, self.y_train

Poi possiamo definire tutte le sottoclassi che vogliamo. Qui ne definisco una per una Random Forest.

class RandomForestModel(BaseMLModel):
    def __init__(self, n_important_features=2, **kwargs):
        super().__init__()
        self.model = RandomForestClassifier(**kwargs)
        self.n_important_features = n_important_features

    def preprocess_train_data(self):
        #Select top `n_important_features` features based on variance
        feature_variances = np.var(self.X_train, axis=0)
        top_features_indices = np.argsort(feature_variances)[-self.n_important_features:]
        X_train_selected = self.X_train[:, top_features_indices]
        return X_train_selected, self.y_train

Ora usiamo tutto nella funzione main.

if __name__ == "__main__":
    # Load dataset
    data = load_iris()
    X, y = data.data, data.target

    # Logistic Regression
    log_reg_model = LogisticRegressionModel(max_iter=200)
    log_reg_model.load_data(X, y)
    log_reg_model.train()
    print(f"Logistic Regression Accuracy: {log_reg_model.evaluate()}")

    # Random Forest
    rf_model = RandomForestModel(n_estimators=100, n_important_features=3)
    rf_model.load_data(X, y)
    rf_model.train()
    print(f"Random Forest Accuracy: {rf_model.evaluate()}")


Conclusioni

Uno dei principali vantaggi dell’ereditarietà di Python nei progetti ML è nella progettazione di codici modulari, mantenibili e scalabili. L’ereditarietà aiuta a evitare codice ridondante, scrivendo la logica comune in una classe base, come BaseMLModel, riducendo quindi la duplicazione del codice. L’inheritance rende anche facile incapsulare comportamenti comuni in una classe base, permettendo alle subclasses di definire dettagli specifici.


Il principale vantaggio, a mio avviso, è che una codebase ben organizzata e orientata agli oggetti consente a più sviluppatori all’interno di un team di lavorare indipendentemente su parti separate. Nel nostro esempio, un ingegnere capo potrebbe definire il modello base, e poi ogni sviluppatore potrebbe concentrarsi su un singolo algoritmo e scrivere la subclass.
Prima di immergerti in design patterns complessi, concentrati sull’utilizzo delle best practices nell’OOP. Farlo ti renderà un programmatore migliore rispetto a molti altri nel campo dell’AI!

Immagine del sitoMarcello Politi
Esperto di intelligenza artificiale con una grande passione per l'esplorazione spaziale. Ho avuto la fortuna di lavorare presso l'Agenzia Spaziale Europea, contribuendo a progetti di ottimizzazione del flusso di dati e di architettura del software. Attualmente, sono AI Scientist & Coach presso la PiSchool, dove mi dedico alla prototipazione rapida di prodotti basati sull'intelligenza artificiale. Mi piace scrivere articoli riguardo la data science e recentemente sono stato riconosciuto come uno dei blogger più prolifici su Towards Data Science.

Lista degli articoli

Articoli in evidenza

Immagine del sito
Furto al Louvre: la password “LOUVRE” del sistema di sorveglianza ha messo in crisi il museo
Di Redazione RHC - 02/11/2025

Abbiamo recentemente pubblicato un approfondimento sul “furto del secolo” al Louvre, nel quale sottolineavamo come la sicurezza fisica – accessi, controllo ambientale, vigilanza – sia oggi str...

Immagine del sito
Allarme phishing in Lombardia: usano dati sanitari reali per chiedere pagamenti
Di Redazione RHC - 02/11/2025

Una nuova e insidiosa campagna di phishing sta colpendo i cittadini lombardi. I truffatori inviano e-mail che sembrano provenire da una presunta agenzia di recupero crediti, chiedendo il pagamento di ...

Immagine del sito
Scopri cos’è il ransomware. Tecniche, tattiche e procedure del cybercrime da profitto
Di Massimiliano Brolli - 02/11/2025

Capire davvero cos’è il ransomware non è semplice: tra notizie frammentate e articoli tecnici, chi cerca risposte rischia di perdersi in un mare di informazioni confuse. Questo articolo nasce per ...

Immagine del sito
Scopri il Dark Web: accesso, segreti e link utili della rete onion
Di Redazione RHC - 02/11/2025

Hai sempre pensato che il Dark Web sia frequentato dai criminali informatici? Hai sempre pensato che il Dark Web sia una rete pericolosa e piena di insidie? Oggi vogliamo sfatare questo mito e creare ...

Immagine del sito
OpenAI potrebbe aver perso 12 miliardi di dollari nell’ultimo trimestre fiscale
Di Redazione RHC - 01/11/2025

Il rapporto finanziario di Microsoft indica che OpenAI potrebbe aver perso 12 miliardi di dollari nell’ultimo trimestre fiscale. Una spesa nel rapporto sugli utili di Microsoft (517.81, -7.95, -1.51...