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:
-
Definizione dei tipi di problemi (
ProblemType):- Il codice definisce una classe
Enumche categorizza i diversi tipi di problemi supportati:MEDICAL,SPAM,DICE,QUALITYeWEATHER. Questa classificazione preliminare è un primo passo verso l’automazione, in quanto permette al sistema di “comprendere” il contesto del problema.
- Il codice definisce una classe
-
Struttura dati per le probabilità (
BayesProbability):- Viene definita una classe
dataclassche 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
dataclasssemplifica la creazione di istanze di questa classe e migliora la leggibilità del codice.
- Viene definita una classe
-
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.
- Questa è la classe principale che contiene tutta la logica per:
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


Utilizzo delle espressioni regolari (regex) nel codice
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:
prevalenza: cerca letteralmente la parola “prevalenza”\s*: permette qualsiasi numero di spazi bianchi (anche zero)[è\:]*: accetta sia il carattere “è” che i due punti “:”(\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é:
- Inizia cercando direttamente il numero
- Usa
delle?per catturare sia “delle” che “dell” - 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”:
- Cerca prima il contesto (“truccato ha probabilità”)
- Poi cattura il numero, che può includere una frazione
- Il gruppo
(?:\/\d+)?rende opzionale la parte frazionaria
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:
- Contare le occorrenze di parole chiave
- Determinare il contesto del problema
- Assegnare un punteggio a ciascun tipo di problema



