Intelligenza Artificiale applicata al teorema di Bayes: un esempio in Python

Cerca:

Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors
AI e Teorema di Bayes: Automazione con Python

Introduzione

L’Intelligenza Artificiale (AI) sta rivoluzionando il modo in cui affrontiamo e risolviamo problemi complessi. Una delle aree in cui l’AI si rivela particolarmente efficace è l’applicazione del teorema di Bayes. Questo teorema, fondamentale nella teoria della probabilità, permette di aggiornare le nostre conoscenze su un evento alla luce di nuove evidenze. In questo articolo, esploreremo come l’AI, attraverso un esempio pratico in Python, possa automatizzare l’applicazione del teorema di Bayes a diversi tipi di problemi, aprendo nuove frontiere nell’analisi dei dati e nel processo decisionale.

In questo articolo analizzeremo come implementare  un codice Python che automatizza l’applicazione del teorema di Bayes a diversi tipi di problemi, offrendo sia la soluzione numerica che una spiegazione dettagliata del processo.

Obiettivo del modello : automatizzare l’interpretazione e la risoluzione di problemi di probabilità espressi in linguaggio naturale

L’obiettivo principale di questo codice è automatizzare il processo di interpretazione di un problema di probabilità e della sua risoluzione attraverso il teorema di Bayes. Il codice è in grado di:

  • Comprendere il tipo di problema: Analizzando il testo di input, il codice identifica il tipo di problema (medico, spam, ecc.) in base a parole chiave predefinite.
  • Estrarre le informazioni rilevanti: Utilizzando espressioni regolari, il codice estrae le probabilità necessarie per applicare il teorema di Bayes.
  • Applicare il teorema di Bayes: Il codice implementa la formula del teorema di Bayes per calcolare la probabilità a posteriori.
  • Fornire una spiegazione chiara: Il codice non si limita a fornire il risultato numerico, ma genera anche una spiegazione dettagliata del processo, rendendo comprensibili i passaggi matematici e le formule utilizzate.

Il codice  rappresenta un esempio concreto ( didattico ) di come l’AI possa essere utilizzata per automatizzare l’applicazione del teorema di Bayes in diversi contesti, come medicina, spam filtering, controllo qualità e altri. Analizziamo insieme i componenti chiave:

  1. Definizione dei tipi di problemi (ProblemType):

    • Il codice definisce una classe Enum che categorizza i diversi tipi di problemi supportati: MEDICAL, SPAM, DICE, QUALITY e WEATHER. Questa classificazione preliminare è un primo passo verso l’automazione, in quanto permette al sistema di “comprendere” il contesto del problema.
  2. Struttura dati per le probabilità (BayesProbability):

    • Viene definita una classe dataclass che contiene tutti i componenti necessari per il teorema di Bayes:
      • prior: la probabilità a priori P(A)
      • likelihood: la verosimiglianza P(B|A)
      • false_positive: la probabilità di falsi positivi P(B|¬A)
      • marginal: la probabilità marginale P(B)
      • posterior: la probabilità a posteriori P(A|B)
    • L’utilizzo di dataclass semplifica la creazione di istanze di questa classe e migliora la leggibilità del codice.
  3. Il risolutore principale (BayesProblemSolver):

    • Questa è la classe principale che contiene tutta la logica per:
      • Identificare il tipo di problema dal testo
      • Estrarre i numeri e le probabilità dal testo
      • Applicare il teorema di Bayes
      • Generare spiegazioni dettagliate
    • Vediamo le funzionalità chiave:
      • identify_problem_type():
        • Analizza il testo cercando parole chiave specifiche per ogni tipo di problema.
        • Usa un sistema di punteggio per determinare il tipo più probabile.
      • extract_probabilities():
        • Estrae i valori numerici dal testo usando espressioni regolari.
        • Gestisce sia percentuali che frazioni.
        • Adatta l’estrazione al tipo specifico di problema.
      • solve_bayes_problem():
        • Implementa il teorema di Bayes.
        • Calcola la probabilità marginale: P(B) = P(B|A)·P(A) + P(B|¬A)·P(¬A)
        • Calcola la probabilità posteriore: P(A|B) = [P(B|A)·P(A)] / P(B)
      • generate_explanation():
        • Crea una spiegazione dettagliata del processo.
        • Mostra tutti i passaggi intermedi.
        • Include le formule utilizzate.
Forse potrebbe interessarti anche:  Come calcolare la Divisione tra Polinomi in Python (vs Metodo di Ruffini)

Gestione dell’incertezza e dell’ambiguità nel testo naturale

Un aspetto interessante di questo codice è la gestione dell’incertezza e dell’ambiguità nel testo naturale:

  • Usa valori predefiniti ragionevoli quando mancano informazioni.
  • Implementa una robusta estrazione dei numeri che può gestire sia percentuali che frazioni.
  • Utilizza espressioni regolari flessibili per trovare i valori nel testo, anche in presenza di formulazioni diverse.

Esempi pratici di utilizzo (main()):

La funzione main() fornisce esempi pratici di utilizzo, mostrando come il sistema può gestire diversi scenari:

  • Test medici e loro accuratezza
  • Filtri spam
  • Problemi con dadi truccati
  • Controllo qualità nella produzione
  • Previsioni meteorologiche

Il codice Python

import re
import math
from typing import Dict, Any, Optional, List, Tuple
from dataclasses import dataclass
from enum import Enum, auto

class ProblemType(Enum):
    """Enumerazione dei tipi di problemi supportati"""
    MEDICAL = auto()  # Problemi di diagnostica medica
    SPAM = auto()     # Classificazione email spam
    DICE = auto()     # Problemi con dadi
    QUALITY = auto()  # Controllo qualità
    WEATHER = auto()  # Previsioni meteo

@dataclass
class BayesProbability:
    """Classe per gestire le probabilità bayesiane"""
    prior: float              # Probabilità a priori P(A)
    likelihood: float         # Verosimiglianza P(B|A)
    false_positive: float     # Falsi positivi P(B|¬A)
    marginal: float = 0.0     # Probabilità marginale P(B)
    posterior: float = 0.0    # Probabilità a posteriori P(A|B)

class BayesProblemSolver:
    def __init__(self):
        """Inizializzazione del risolutore"""
        self.problem_keywords = {
            ProblemType.MEDICAL: ['malattia', 'test', 'diagnosi', 'paziente', 'sensibilità', 'specificità'],
            ProblemType.SPAM: ['email', 'spam', 'filtro', 'classificazione'],
            ProblemType.DICE: ['dado', 'dadi', 'lancio', 'probabilità', '1/6'],
            ProblemType.QUALITY: ['difettoso', 'qualità', 'produzione', 'controllo'],
            ProblemType.WEATHER: ['pioggia', 'previsioni', 'meteo', 'temporale']
        }

    def identify_problem_type(self, text: str) -> ProblemType:
        """
        Identifica il tipo di problema analizzando le parole chiave nel testo
        Ritorna: ProblemType corrispondente al problema identificato
        """
        text = text.lower()
        type_scores = {
            ptype: sum(text.count(k) for k in keywords)
            for ptype, keywords in self.problem_keywords.items()
        }
        return max(type_scores, key=type_scores.get)

    def extract_number(self, text: str, pattern: str, default: float = 0.0) -> float:
        """
        Estrae un numero dal testo usando il pattern specificato
        Gestisce sia percentuali che frazioni
        """
        match = re.search(pattern, text.lower())
        if not match:
            return default
        
        value = match.group(1)
        if '/' in value:
            num, den = map(float, value.split('/'))
            return num / den
        return float(value) / 100 if '%' in pattern else float(value)

    def extract_probabilities(self, text: str, problem_type: ProblemType) -> BayesProbability:
        """
        Estrae le probabilità dal testo in base al tipo di problema
        Ritorna: Oggetto BayesProbability con le probabilità estratte
        """
        if problem_type == ProblemType.MEDICAL:
            return BayesProbability(
                prior=self.extract_number(text, r'prevalenza\s*[è\:]*\s*(\d+(?:\.\d+)?)\s*%', 0.01),
                likelihood=self.extract_number(text, r'sensibilità\s*[è\:]*\s*(\d+(?:\.\d+)?)\s*%', 0.90),
                false_positive=1 - self.extract_number(text, r'specificità\s*[è\:]*\s*(\d+(?:\.\d+)?)\s*%', 0.95)
            )
        
        elif problem_type == ProblemType.SPAM:
            return BayesProbability(
                prior=self.extract_number(text, r'(\d+(?:\.\d+)?)\s*%\s*delle?\s*email\s*(?:è\s*)?spam', 0.10),
                likelihood=self.extract_number(text, r'classifica\s*come\s*spam\s*(?:il\s*)?(\d+(?:\.\d+)?)\s*%\s*delle?\s*email\s*spam', 0.98),
                false_positive=self.extract_number(text, r'(\d+(?:\.\d+)?)\s*%\s*delle?\s*email\s*non\s*spam', 0.05)
            )
        
        elif problem_type == ProblemType.QUALITY:
            return BayesProbability(
                prior=self.extract_number(text, r'(\d+(?:\.\d+)?)\s*%\s*dei\s*prodotti\s*(?:è\s*)?difettoso', 0.02),
                likelihood=self.extract_number(text, r'identifica\s*(?:il\s*)?(\d+(?:\.\d+)?)\s*%\s*dei\s*difetti', 0.95),
                false_positive=self.extract_number(text, r'falsi\s*positivi\s*(?:al\s*)?(\d+(?:\.\d+)?)\s*%', 0.03)
            )
        
        elif problem_type == ProblemType.WEATHER:
            return BayesProbability(
                prior=self.extract_number(text, r'probabilità\s*di\s*pioggia\s*(?:del\s*)?(\d+(?:\.\d+)?)\s*%', 0.30),
                likelihood=self.extract_number(text, r'previsioni\s*corrette\s*(?:al\s*)?(\d+(?:\.\d+)?)\s*%', 0.80),
                false_positive=self.extract_number(text, r'falsi\s*allarmi\s*(?:al\s*)?(\d+(?:\.\d+)?)\s*%', 0.15)
            )
        
        else:  # ProblemType.DICE
            return BayesProbability(
                prior=0.5,  # Probabilità di scegliere il dado truccato
                likelihood=self.extract_number(text, r'truccato\s*ha\s*probabilità\s*(\d+(?:\/\d+)?)\s*per\s*il\s*6', 0.5),
                false_positive=self.extract_number(text, r'normale\s*ha\s*probabilità\s*(\d+(?:\/\d+)?)', 1/6)
            )

    def solve_bayes_problem(self, prob: BayesProbability) -> BayesProbability:
        """
        Applica il teorema di Bayes per calcolare la probabilità posteriore
        Modifica l'oggetto BayesProbability passato aggiungendo i risultati
        """
        # Calcolo probabilità marginale P(B)
        prob.marginal = (prob.likelihood * prob.prior + 
                        prob.false_positive * (1 - prob.prior))
        
        # Calcolo probabilità posteriore P(A|B)
        if prob.marginal > 0:
            prob.posterior = (prob.likelihood * prob.prior) / prob.marginal
        
        return prob

    def generate_explanation(self, problem_type: ProblemType, prob: BayesProbability, 
                           text: str) -> str:
        """
        Genera una spiegazione dettagliata della soluzione
        Include formule e passaggi intermedi
        """
        type_names = {
            ProblemType.MEDICAL: "Diagnostica Medica",
            ProblemType.SPAM: "Classificazione Spam",
            ProblemType.DICE: "Probabilità Dadi",
            ProblemType.QUALITY: "Controllo Qualità",
            ProblemType.WEATHER: "Previsioni Meteo"
        }

        explanation = f"Analisi del problema: {type_names[problem_type]}\n\n"
        explanation += f"Testo originale: {text}\n\n"
        explanation += "Calcolo del teorema di Bayes:\n"
        explanation += f"1. Probabilità a priori P(A): {prob.prior:.2%}\n"
        explanation += f"2. Verosimiglianza P(B|A): {prob.likelihood:.2%}\n"
        explanation += f"3. Falsi positivi P(B|¬A): {prob.false_positive:.2%}\n"
        explanation += f"4. Probabilità marginale P(B): {prob.marginal:.2%}\n"
        explanation += f"   Calcolata come: P(B) = P(B|A)·P(A) + P(B|¬A)·P(¬A)\n"
        explanation += f"   = {prob.likelihood:.2%}·{prob.prior:.2%} + {prob.false_positive:.2%}·{1-prob.prior:.2%}\n"
        explanation += f"5. Probabilità posteriore P(A|B): {prob.posterior:.2%}\n"
        explanation += f"   Calcolata come: P(A|B) = [P(B|A)·P(A)] / P(B)\n"
        explanation += f"   = [{prob.likelihood:.2%}·{prob.prior:.2%}] / {prob.marginal:.2%}\n"
        
        return explanation

    def process_problem(self, text: str) -> Dict[str, Any]:
        """
        Processo completo di risoluzione del problema
        Ritorna un dizionario con tipo, probabilità e spiegazione
        """
        problem_type = self.identify_problem_type(text)
        probabilities = self.extract_probabilities(text, problem_type)
        solution = self.solve_bayes_problem(probabilities)
        explanation = self.generate_explanation(problem_type, solution, text)
        
        return {
            'problem_type': problem_type,
            'probabilities': solution,
            'explanation': explanation
        }

def main():
    """
    Funzione principale per testare il risolutore con vari esempi
    """
    solver = BayesProblemSolver()
    
    # Lista dei problemi di test
    test_problems = [
        # Problema medico
        """Un test per una malattia ha sensibilità del 90% e specificità del 95%. 
           La malattia ha una prevalenza dell'1%. 
           Qual è la probabilità che un paziente sia malato se il test è positivo?""",
        
        # Problema spam
        """Un filtro spam classifica come spam il 98% delle email spam e il 5% delle email non spam. 
           Il 10% delle email è spam. 
           Qual è la probabilità che un'email classificata come spam lo sia effettivamente?""",
        
        # Problema dadi
        """Hai due dadi: uno normale e uno truccato. 
           Il dado normale ha probabilità 1/6 per ogni faccia. 
           Il dado truccato ha probabilità 1/2 per il 6 e 1/10 per le altre facce. 
           Scegli un dado a caso e ottieni un 6. 
           Qual è la probabilità che sia il dado truccato?""",
        
        # Problema controllo qualità
        """In una linea di produzione, il 2% dei prodotti è difettoso. 
           Il sistema di controllo identifica il 95% dei difetti, 
           ma ha un tasso di falsi positivi del 3%. 
           Se un prodotto viene segnalato come difettoso, 
           qual è la probabilità che lo sia davvero?""",
        
        # Problema previsioni meteo
        """Le previsioni meteo indicano una probabilità di pioggia del 30%.
           Le previsioni sono corrette all'80% quando piove,
           ma danno falsi allarmi nel 15% dei casi quando non piove.
           Se le previsioni indicano pioggia,
           qual è la probabilità che piova effettivamente?"""
    ]
    
    # Processa ogni problema
    for problem in test_problems:
        print("="*50)
        print("PROBLEMA:")
        print(problem.strip())
        print("\nSOLUZIONE:")
        result = solver.process_problem(problem)
        print(result['explanation'])
        print("="*50 + "\n")

if __name__ == "__main__":
    main()

Esempi di Output

Forse potrebbe interessarti anche:  False Completeness: Perché non devi fidarti dell'analisi file delle IA (e il codice per fixarlo)

Intelligenza Artificiale applicata al teorema di Bayes un esempio in Python

Utilizzo delle espressioni regolari (regex) nel codice

Cosa sono le regex

Le regex sono uno strumento potente per la manipolazione di stringhe e vengono ampiamente utilizzate in questo codice per diverse finalità:

1. Estrazione di numeri e probabilità

2. Identificazione del tipo di problema

3. Estrazione di informazioni specifiche.

La funzione principale che gestisce le espressioni regolari è extract_number.

Esaminiamola in dettaglio:

def extract_number(self, text: str, pattern: str, default: float = 0.0) -> float:
    """
    Estrae un numero dal testo usando il pattern specificato
    Gestisce sia percentuali che frazioni
    """
    match = re.search(pattern, text.lower())
    if not match:
        return default
    
    value = match.group(1)
    if '/' in value:
        num, den = map(float, value.split('/'))
        return num / den
    return float(value) / 100 if '%' in pattern else float(value)

Questa funzione è progettata per essere molto flessibile. Vediamo come gestisce diversi casi:

Per i problemi medici, viene utilizzato un pattern come questo:

r'prevalenza\s*[è\:]*\s*(\d+(?:\.\d+)?)\s*%'

Analizziamo ogni componente di questa espressione regolare:

  1. prevalenza: cerca letteralmente la parola “prevalenza”
  2. \s*: permette qualsiasi numero di spazi bianchi (anche zero)
  3. [è\:]*: accetta sia il carattere “è” che i due punti “:”
  4. (\d+(?:\.\d+)?): questa è la parte più complessa:
    • \d+: corrisponde a una o più cifre
    • (?:\.\d+)?: è un gruppo non catturante (grazie a ?:) che trova un punto decimale seguito da cifre, ed è opzionale (grazie al ? finale)
    • Le parentesi tonde esterne () creano un gruppo di cattura

Per i problemi di spam, vengono utilizzati pattern più complessi:

r'(\d+(?:\.\d+)?)\s*%\s*delle?\s*email\s*(?:è\s*)?spam'

Questo pattern è interessante perché:

  1. Inizia cercando direttamente il numero
  2. Usa delle? per catturare sia “delle” che “dell”
  3. Rende opzionale il verbo “è” con (?:è\s*)?

Il codice gestisce anche frazioni, come nel caso dei dadi:

r'truccato\s*ha\s*probabilità\s*(\d+(?:\/\d+)?)'

Questo pattern può catturare sia numeri interi che frazioni come “1/6”:

  1. Cerca prima il contesto (“truccato ha probabilità”)
  2. Poi cattura il numero, che può includere una frazione
  3. Il gruppo (?:\/\d+)? rende opzionale la parte frazionaria
Forse potrebbe interessarti anche:  MAD in Data Science: significato, formule, esempi e applicazioni nel forecasting e nell'anomaly detection

Un esempio pratico di come viene utilizzato:

testo = "La malattia ha una prevalenza del 2.5% nella popolazione"
pattern = r'prevalenza\s*[è\:]*\s*(\d+(?:\.\d+)?)\s*%'
numero = extract_number(testo, pattern, default=0.0)
# numero sarà 0.025 (2.5%)

Il codice utilizza anche regex implicite attraverso il metodo count() per identificare il tipo di problema:

text.count(k) for k in keywords

Questo approccio, sebbene più semplice, è efficace per:

  1. Contare le occorrenze di parole chiave
  2. Determinare il contesto del problema
  3. Assegnare un punteggio a ciascun tipo di problema
Pubblicità