Hem tancat el mòdul 5 amb una constatació incòmoda: les xarxes neuronals i els grans models de llenguatge són subsimbòlics i opacs; ningú no pot llegir-ne els pesos ni demanar-los comptes d'una decisió. I NovaMarket té decisions que exigeixen poder-se llegir i justificar: si procedeix o no un reemborsament (cas 8) o quina és la causa probable d'una incidència (cas 9). Aquesta lliçó obre l'altra meitat de la intel·ligència artificial, la simbòlica de les primeres dècades de 01-01, començant pel seu fonament: la lògica, el llenguatge formal per representar coneixement de manera explícita i per raonar-hi de manera verificable. Veurem la lògica proposicional (proposicions, connectius i taules de veritat, equivalències, regles d'inferència, bases de coneixement i la noció d'implicació lògica), la lògica de primer ordre (objectes, predicats, quantificadors), que permet escriure les polítiques de devolució de NovaMarket amb precisió, i el mecanisme de treball de gairebé tots els sistemes basats en regles: l'encadenament cap endavant i cap enrere amb clàusules de Horn. Tot amb codi en Python pur que pots executar i llegir, línia a línia. És important perquè la lògica és el vocabulari que compartiran les tres lliçons següents: els sistemes experts (06-02) són lògica més enginyeria, les xarxes bayesianes (06-03) són la resposta a allò que la lògica no sap fer, i les aplicacions de 06-04 són lògica en producció.
Contingut
- Per què la lògica: coneixement explícit i verificable
- Lògica proposicional: proposicions i connectius
- Tautologies, contradiccions, satisfactibilitat i equivalències útils
- Regles d'inferència: modus ponens, modus tollens i resolució
- Base de coneixement i consulta: KB ⊨ α per enumeració de models
- Lògica de primer ordre: objectes, predicats, funcions i quantificadors
- Clàusules de Horn: encadenament cap endavant i cap enrere
- Prolog, el llenguatge de la lògica
- Limitacions: món tancat, monotonia i incertesa
- Errors Comuns i Consells
- Exercicis
- Conclusió
- Per què la lògica: coneixement explícit i verificable
A 02-02 vam distingir la IA simbòlica (símbols i regles que un humà pot llegir) de la subsimbòlica (nombres i pesos que ningú no llegeix). Els mòduls 4 i 5 han estat subsimbòlics: la regressió logística de 04-04 i l'MLP de 05-03 aprenen pesos de comandes.csv, i l'LLM de 05-05 genera text sense poder explicar per què. Per a moltes tasques és el més adequat. Però considera què necessita el Diego per a les devolucions:
- La regla existeix abans que les dades. El termini de desistiment de 14 dies o la garantia legal de 3 anys no s'aprenen d'un històric: els fixa la normativa i la política comercial de NovaMarket. Un model que "aprengués" 13 o 16 dies estaria senzillament equivocat.
- La decisió s'ha de poder justificar davant del client, davant d'un auditor i, com vam veure a 02-04, davant de l'AI Act i el RGPD (decisions automatitzades amb efectes significatius). "El model va donar 0,71" no és una justificació; "no procedeix perquè el producte està usat i han passat 20 dies, regla R3" sí que ho és.
- Ha de ser verificable: el Diego ha de poder llegir les regles, detectar que dues es contradiuen i corregir-les, sense reentrenar res.
- Hi ha poques dades i molts casos límit (un producte d'higiene obert, una garantia ampliada), justament on l'ML rendeix pitjor.
La lògica ofereix exactament això: un llenguatge formal amb sintaxi precisa (quines frases estan ben formades), semàntica precisa (què vol dir que una frase sigui vertadera) i regles d'inferència que deriven conclusions noves de manera mecànica i correcta. És l'eina que Aristòtil va esbossar, Boole i Frege van formalitzar i el Logic Theorist de 1956 va portar a l'ordinador (01-01). Comencem per la versió més simple.
- Lògica proposicional: proposicions i connectius
Una proposició és una afirmació que és vertadera o falsa, sense terme mitjà: "el producte està usat", "han passat més de 14 dies des del lliurament", "procedeix el reemborsament". Les representem amb lletres (P, Q, R) o amb noms llegibles (usat, mes14, reemborsament). Un model (o interpretació) és una assignació de vertader/fals a cada proposició: descriu un "món possible". Amb 3 proposicions hi ha 2³ = 8 mons possibles.
Els connectius combinen proposicions en fórmules compostes. El seu significat es defineix amb taules de veritat, que diuen el valor de la fórmula en cada combinació de valors de les seves parts:
| P | Q | ¬P (no P) | P ∧ Q (i) | P ∨ Q (o) | P → Q (si P aleshores Q) | P ↔ Q (si i només si) |
|---|---|---|---|---|---|---|
| V | V | F | V | V | V | V |
| V | F | F | F | V | F | F |
| F | V | V | F | V | V | F |
| F | F | V | F | F | V | V |
Dues observacions importants per a principiants:
- L'o lògic és inclusiu: "P ∨ Q" és vertadera també quan totes dues ho són. "Producte danyat o equivocat" admet que sigui les dues coses.
- La implicació P → Q només és falsa quan P és vertadera i Q falsa. Si P és falsa, la implicació és vertadera sigui quin sigui el valor de Q: la regla "si està usat i han passat 14 dies, no procedeix reemborsament" no diu res dels productes sense usar; no s'incompleix amb ells. Això sorprèn al principi, però és exactament el que volem d'una regla: només la viola el cas que compleix la condició i contradiu la conclusió.
La regla del Diego amb què va tancar el mòdul 5 s'escriu (usat ∧ mes14) → ¬reemborsament. La seva taula de veritat té 8 files i només una és falsa: la del món en què el producte està usat, han passat més de 14 dies i tot i així es reemborsa. Aquest món és el que la regla prohibeix. Generarem taules com aquesta en codi.
2.1 Codi: avaluar fórmules i generar taules de veritat
Representem una fórmula com un nom de variable (cadena) o una tupla el primer element de la qual és l'operador: ("i", "P", "Q"), ("no", "R"), ("->", ("i", "P", "Q"), ("no", "R")). Un avaluador recursiu baixa per la tupla i aplica la taula de veritat de cada connectiu; itertools.product genera totes les combinacions de V/F.
from itertools import product
def avaluar(formula, model):
"""Avalua una formula proposicional donat un model (dict variable -> bool).
Una formula es un nom de variable (str) o una tupla (operador, operands...)."""
if isinstance(formula, str):
return model[formula] # una proposicio: el seu valor en el model
op = formula[0]
if op == "no":
return not avaluar(formula[1], model)
if op == "i":
return avaluar(formula[1], model) and avaluar(formula[2], model)
if op == "o":
return avaluar(formula[1], model) or avaluar(formula[2], model)
if op == "->": # P -> Q equival a (no P) o Q
return (not avaluar(formula[1], model)) or avaluar(formula[2], model)
if op == "<->":
return avaluar(formula[1], model) == avaluar(formula[2], model)
raise ValueError(f"operador desconegut: {op}")
def variables_de(formula, acc=None):
"""Recull el conjunt de proposicions que apareixen a la formula."""
acc = set() if acc is None else acc
if isinstance(formula, str):
acc.add(formula)
else:
for sub in formula[1:]:
variables_de(sub, acc)
return acc
def taula_veritat(formula, nom="formula"):
vars_ = sorted(variables_de(formula))
print(" | ".join(f"{v:^5}" for v in vars_) + " | " + nom)
resultats = []
for valors in product([True, False], repeat=len(vars_)): # totes les combinacions V/F
model = dict(zip(vars_, valors))
r = avaluar(formula, model)
resultats.append(r)
print(" | ".join(f"{'V' if x else 'F':^5}" for x in valors) + " | " + ("V" if r else "F"))
if all(resultats):
print("-> tautologia")
elif not any(resultats):
print("-> contradiccio")
else:
print(f"-> satisfactible ({sum(resultats)} de {len(resultats)} models)")
# P: "el producte esta usat", Q: "han passat mes de 14 dies", R: "procedeix el reemborsament"
regla = ("->", ("i", "P", "Q"), ("no", "R"))
taula_veritat(regla, "(P ∧ Q) → ¬R")Sortida:
P | Q | R | (P ∧ Q) → ¬R V | V | V | F V | V | F | V V | F | V | V V | F | F | V F | V | V | V F | V | F | V F | F | V | V F | F | F | V -> satisfactible (7 de 8 models)
Explicació: avaluar és una funció recursiva: si la fórmula és una cadena retorna el seu valor en el model; si és una tupla, avalua els operands i combina els resultats amb la taula del connectiu. Fixa't en la línia de "->": implementem la implicació com (no P) o Q, que és la seva definició. product([True, False], repeat=3) genera les 8 files. L'única fila falsa és la primera (usat, més de 14 dies i reemborsament), com havíem raonat.
- Tautologies, contradiccions, satisfactibilitat i equivalències útils
Segons com es comporti una fórmula en tots els models, la classifiquem:
| Tipus | Definició | Exemple | Ús pràctic |
|---|---|---|---|
| Tautologia | Vertadera en tots els models | P ∨ ¬P | Les equivalències i les lleis lògiques són tautologies |
| Contradicció | Falsa en tots els models | P ∧ ¬P | Una base de regles que la implica és inconsistent |
| Satisfactible | Vertadera en almenys un model | (P ∧ Q) → ¬R | La majoria de les regles de negoci |
Dues fórmules són equivalents (≡) si tenen la mateixa taula de veritat, és a dir, si el seu bicondicional és una tautologia. Les equivalències permeten reescriure regles sense canviar-ne el significat, cosa que farà el motor de 06-02 i que faràs tu quan depuris regles del Diego:
| Equivalència | Fórmula | Lectura amb NovaMarket |
|---|---|---|
| De Morgan | ¬(P ∧ Q) ≡ ¬P ∨ ¬Q | "No és cert que estigui usat i fora de termini" = "no està usat o està dins del termini" |
| De Morgan | ¬(P ∨ Q) ≡ ¬P ∧ ¬Q | "No està ni danyat ni equivocat" = "no danyat i no equivocat" |
| Contrapositiva | P → Q ≡ ¬Q → ¬P | "Si és defectuós, reemborsament" = "si no hi ha reemborsament, no era defectuós" |
| Implicació | P → Q ≡ ¬P ∨ Q | És el que hem programat a avaluar |
| Doble negació | ¬¬P ≡ P | |
| Distributiva | P ∧ (Q ∨ R) ≡ (P ∧ Q) ∨ (P ∧ R) | Desdoblar una regla amb "o" en dues regles |
Compte amb la conversa: P → Q no equival a Q → P. "Si és defectuós, procedeix reemborsament" no vol dir "si procedeix reemborsament, és defectuós" (pot procedir per desistiment). És l'error lògic més freqüent en regles de negoci mal escrites.
Comprovem De Morgan i una contradicció amb el codi anterior:
taula_veritat(("<->", ("no", ("i", "P", "Q")), ("o", ("no", "P"), ("no", "Q"))), "¬(P∧Q) ↔ (¬P∨¬Q)")
taula_veritat(("i", "P", ("no", "P")), "P ∧ ¬P")Sortida (resumida):
P | Q | ¬(P∧Q) ↔ (¬P∨¬Q) V | V | V V | F | V F | V | V F | F | V -> tautologia P | P ∧ ¬P V | F F | F -> contradiccio
- Regles d'inferència: modus ponens, modus tollens i resolució
Les taules de veritat decideixen qualsevol pregunta, però amb n proposicions tenen 2ⁿ files: amb les 40 proposicions que pot manejar un sistema de devolucions real són un bilió de files. Les regles d'inferència eviten enumerar: són patrons que, aplicats a fórmules vertaderes, produeixen fórmules vertaderes (són correctes, sound).
| Regla | Premisses | Conclusió | Exemple NovaMarket |
|---|---|---|---|
| Modus ponens | P → Q, P | Q | "Si és defectuós i està en garantia, enviament gratis"; és defectuós i està en garantia; per tant enviament gratis |
| Modus tollens | P → Q, ¬Q | ¬P | "Si la comanda va sortir de Getafe, l'albarà porta la lletra G"; no porta G; per tant no va sortir de Getafe |
| Eliminació de ∧ | P ∧ Q | P | De "danyat i amb retard" se segueix "danyat" |
| Introducció de ∧ | P, Q | P ∧ Q | |
| Sil·logisme hipotètic | P → Q, Q → R | P → R | Encadenar regles |
| Resolució | P ∨ Q, ¬P ∨ R | Q ∨ R | Vegeu més avall |
El modus ponens és la regla estrella dels sistemes experts: donat "si condicions aleshores conclusió" i les condicions, afegeix la conclusió. És literalment el que farà el motor de la secció 7.
La resolució mereix una intuïció: si sabem que "el paquet va arribar danyat o el producte era l'equivocat" (P ∨ Q) i també que "no va arribar danyat o la causa és el transport" (¬P ∨ R), aleshores, sigui quin sigui el valor de P, alguna cosa se salva: si P és certa, la segona clàusula obliga a R; si és falsa, la primera obliga a Q. Per tant Q ∨ R. La resolució és una única regla que, aplicada sobre fórmules convertides a una forma normal de clàusules, és completa: si alguna cosa se segueix lògicament, la resolució la troba (sovint demostrant que la negació porta a la clàusula buida, una contradicció). És el motor intern de Prolog (secció 8) i dels demostradors automàtics; aquí n'hi ha prou amb la idea.
- Base de coneixement i consulta: KB ⊨ α per enumeració de models
Una base de coneixement (KB) és un conjunt de fórmules que donem per vertaderes: regles generals més fets del cas. Una consulta α és una fórmula sobre la qual preguntem. Diem que la KB implica lògicament α, escrit KB ⊨ α, quan α és vertadera en tots els models en què la KB és vertadera. És la definició precisa de "se segueix del que sabem": no queda cap món compatible amb el que sabem en què α sigui falsa.
La manera més directa de comprovar-ho és l'enumeració de models: recórrer totes les assignacions, quedar-se amb les que satisfan la KB i mirar si α és certa en totes. És exponencial, però per a bases petites és perfecta i, sobretot, és la definició feta codi.
def implica(kb, alfa):
"""Retorna True si KB ⊨ alfa: en tot model on totes les formules de KB
son vertaderes, alfa tambe ho es. Enumera tots els models."""
vars_ = set()
for f in kb + [alfa]:
variables_de(f, vars_)
vars_ = sorted(vars_)
models_kb = 0
for valors in product([True, False], repeat=len(vars_)):
model = dict(zip(vars_, valors))
if all(avaluar(f, model) for f in kb): # el model satisfa la KB?
models_kb += 1
if not avaluar(alfa, model): # ...i en ell alfa es falsa: contraexemple
print(f" contraexemple: {model}")
return False
print(f" ({models_kb} models de la KB, alfa certa en tots)")
return True
# Comanda 48377: auriculars NovaSound, retornats usats al cap de 20 dies
KB = [
("->", ("i", "usat", "mes14"), ("no", "reemborsament")), # regla del Diego
("->", "defectuos", "reemborsament"), # si es defectuos, reemborsament (garantia)
"usat", # fet
"mes14", # fet
]
print("KB ⊨ ¬reemborsament ?", implica(KB, ("no", "reemborsament")))
print("KB ⊨ ¬defectuos ?", implica(KB, ("no", "defectuos")))
print("KB ⊨ reemborsament ?", implica(KB, "reemborsament"))
KB2 = KB[:2] + ["usat"] # ja no sabem si han passat 14 dies
print("KB2 ⊨ ¬reemborsament ?", implica(KB2, ("no", "reemborsament")))Sortida:
(1 models de la KB, alfa certa en tots)
KB ⊨ ¬reemborsament ? True
(1 models de la KB, alfa certa en tots)
KB ⊨ ¬defectuos ? True
contraexemple: {'defectuos': False, 'mes14': True, 'reemborsament': False, 'usat': True}
KB ⊨ reemborsament ? False
contraexemple: {'defectuos': True, 'mes14': False, 'reemborsament': True, 'usat': True}
KB2 ⊨ ¬reemborsament ? FalseExplicació, línia a línia del resultat:
- KB ⊨ ¬reemborsament: amb
usatimes14com a fets, la regla del Diego obliga a¬reemborsamenten l'únic model compatible. Modus ponens fet per força bruta. - KB ⊨ ¬defectuos: això no ho havíem escrit i, tanmateix, se segueix: si fos defectuós hi hauria reemborsament (regla 2), però no n'hi ha (regla 1); per tant no és defectuós. És modus tollens, i és una alarma útil: la KB tal com està escrita afirma que un producte usat retornat al cap de 20 dies no pot ser defectuós, cosa que és absurda. La lògica ha detectat que les dues regles, juntes, són incompatibles amb un cas real (usat, tardà i defectuós: la KB seria inconsistent, sense cap model, i d'una KB inconsistent se segueix qualsevol cosa). A 06-02 ho resoldrem amb prioritats entre regles; aquí n'hi ha prou de notar que la verificació formal ha trobat un error de disseny que un model opac no mostraria mai.
- KB ⊨ reemborsament és fals, amb un contraexemple explícit: el món en què no és defectuós i no hi ha reemborsament és compatible amb la KB.
- KB2: en treure el fet
mes14, ja no es pot concloure¬reemborsament; el contraexemple mostra un món ambmes14fals, defectuós i reemborsat. La consulta no està decidida: cal més informació. Aquesta "pregunta que falta" serà el motor de la interfície de preguntes de 06-02.
- Lògica de primer ordre: objectes, predicats, funcions i quantificadors
La lògica proposicional tracta "el producte està usat" com un àtom indivisible. Però NovaMarket té 12.000 productes i 3.000 comandes al dia: no escriurem una proposició usat_comanda_48377, usat_comanda_48378... La lògica de primer ordre (LPO) afegeix estructura interna a les afirmacions:
- Objectes: coses del món: la comanda 48377, l'aspirador NovaClean, la clienta Ana López, el magatzem de Getafe. S'anomenen amb constants (
p48377,getafe). - Predicats: propietats i relacions entre objectes, vertaderes o falses:
Producte(x),Precintat(x),Defectuos(x),LliuratDes(p, magatzem),Client(c, p). - Funcions: retornen un objecte a partir d'altres:
DiesDesLliurament(p)retorna un nombre,Magatzem(p)retorna un magatzem. - Variables i quantificadors: ∀x ("per a tot x") i ∃x ("existeix algun x").
- Els mateixos connectius ¬, ∧, ∨, →, ↔ d'abans.
Ara la política de devolucions de NovaMarket es pot escriure d'una vegada per a tots els productes (les dades de negoci que fixem i que reapareixeran en tot el mòdul: desistiment 14 dies, garantia legal 3 anys per a producte nou, precintats/higiene no retornables si s'obren, etiqueta original, enviament de devolució gratuït si és defectuós; la normativa concreta de consum sempre l'ha de revisar un professional legal, aquí és un exemple docent):
∀p Producte(p) ∧ ¬Precintat(p) ∧ EtiquetaOriginal(p) ∧ DiesDesLliurament(p) ≤ 14 → Retornable(p)
∀p Producte(p) ∧ Higiene(p) ∧ Obert(p) → ¬Retornable(p)
∀p Producte(p) ∧ Nou(p) ∧ Defectuos(p) ∧ DiesDesLliurament(p) ≤ 1095 → EnGarantia(p)
∀p EnGarantia(p) → EnviamentDevolucioGratis(p)
∃p Producte(p) ∧ Defectuos(p) ∧ Magatzem(p) = getafe ("algun producte defectuos va sortir de Getafe")La primera regla és la que anuncia l'enunciat d'aquesta lliçó: Retornable(p) ← Producte(p) ∧ ¬Precintat(p) ∧ DiesDesLliurament(p) ≤ 14, escrita amb la fletxa cap a l'esquerra ("Retornable si..."), la notació habitual de les regles. Dues precisions de lectura:
- ∀ va amb →: "per a tot p, si p és un producte no precintat dins del termini, aleshores és retornable". Escriure ∀p (Producte(p) ∧ Retornable(p)) diria que tot objecte és un producte retornable: fals.
- ∃ va amb ∧: "existeix p tal que p és producte i defectuós i de Getafe". Escriure ∃p (Producte(p) → Defectuos(p)) seria trivialment cert així que existís alguna cosa que no fos producte (una implicació amb antecedent fals és vertadera).
Les dues afirmacions es relacionen per De Morgan quantificat: ¬∀x P(x) ≡ ∃x ¬P(x) ("no tots els paquets arriben bé" = "algun paquet no arriba bé").
6.1 Unificació i inferència, a nivell intuïtiu
Per aplicar modus ponens a una regla amb variables cal aparellar-la amb fets concrets: unificar Producte(p) amb Producte(p48377) substituint p = p48377, comprovar ¬Precintat(p48377), EtiquetaOriginal(p48377) i DiesDesLliurament(p48377) ≤ 14 amb la mateixa substitució, i concloure Retornable(p48377). La unificació és l'algorisme que troba la substitució de variables que fa iguals dues expressions (Client(c, p48377) unifica amb Client(ana, p48377) amb c = ana; Client(ana, p) no unifica amb Client(luis, p48377)). És la peça que fa que una regla general s'apliqui a milers de casos, i el cor de Prolog. En el nostre codi ens estalviarem la unificació amb un truc habitual a la pràctica: instanciar les regles per a una comanda concreta, és a dir, convertir els predicats sobre la comanda en proposicions (dins_termini, sense_usar), amb la qual cosa tornem a la lògica proposicional, on el modus ponens és un simple all(...).
- Clàusules de Horn: encadenament cap endavant i cap enrere
La inferència general en LPO és cara (fins i tot indecidible en general). Els sistemes basats en regles restringeixen les fórmules a clàusules de Horn: implicacions amb una conjunció de premisses positives i una sola conclusió positiva, P₁ ∧ P₂ ∧ ... ∧ Pₙ → Q (un fet és el cas n = 0). Amb aquesta restricció hi ha dos algorismes eficients i correctes:
- Encadenament cap endavant (forward chaining, dirigit per les dades): parteixes dels fets coneguts, apliques modus ponens a tota regla les premisses de la qual ja estiguin satisfetes, hi afegeixes la conclusió i repeteixes fins que no aparegui res de nou. Deriva tot el que és derivable. Adequat quan arriben dades i vols totes les conseqüències (una comanda arriba retornada i el sistema calcula reemborsament, enviament gratuït, avís al proveïdor...).
- Encadenament cap enrere (backward chaining, dirigit per l'objectiu): parteixes de la pregunta ("procedeix reemborsament?"), busques regles la conclusió de les quals sigui aquest objectiu, i converteixes les seves premisses en nous subobjectius, recursivament, fins a arribar a fets. Només explora el que és rellevant per a la pregunta i, si un fet no hi és, el pot preguntar a l'usuari: així funcionen els sistemes de diagnòstic interactius de 06-02.
Regles de devolució de NovaMarket com a clàusules de Horn (versió petita, sense conflictes entre regles; els conflictes els tracta 06-02):
| Regla | Premisses | Conclusió |
|---|---|---|
| R1 | dies_le_14 | dins_termini |
| R2 | dies_le_1095 | en_garantia (3 anys ≈ 1095 dies, producte nou) |
| R3 | dins_termini ∧ etiqueta_original ∧ sense_usar | desistiment_ok |
| R4 | defectuos ∧ en_garantia | garantia_ok |
| R5 | desistiment_ok | procedeix_reemborsament |
| R6 | garantia_ok | procedeix_reemborsament |
| R7 | garantia_ok | enviament_devolucio_gratis |
Traça cap endavant per a la comanda 48213 (cafetera NovaBrew lliurada fa 9 dies, amb etiqueta, sense usar, no defectuosa). Fets inicials: dies_le_14, dies_le_1095, etiqueta_original, sense_usar.
| Pas | Regla disparada | Premisses satisfetes | Fet nou |
|---|---|---|---|
| 1 | R1 | dies_le_14 | dins_termini |
| 2 | R2 | dies_le_1095 | en_garantia |
| 3 | R3 | dins_termini, etiqueta_original, sense_usar | desistiment_ok |
| 4 | R5 | desistiment_ok | procedeix_reemborsament |
| 5 | (R4, R6, R7 no es disparen: falta defectuos) |
fi |
Traça cap enrere per a la comanda 47102 (aspirador NovaClean, 40 dies, usat, sense etiqueta, defectuós), objectiu procedeix_reemborsament:
| Objectiu | Regla provada | Subobjectius | Resultat |
|---|---|---|---|
| procedeix_reemborsament | R5 | desistiment_ok | → |
| desistiment_ok | R3 | dins_termini, etiqueta_original, sense_usar | → |
| dins_termini | R1 | dies_le_14 | falla (no és fet ni té regla) → R3 falla → R5 falla |
| procedeix_reemborsament | R6 | garantia_ok | → |
| garantia_ok | R4 | defectuos, en_garantia | defectuos és fet; → |
| en_garantia | R2 | dies_le_1095 | és fet → R2 ok → R4 ok → R6 ok → demostrat |
Observa com l'encadenament cap enrere retrocedeix (backtracking) quan una branca falla i prova la següent regla amb la mateixa conclusió.
7.1 Codi: motor d'encadenament cap endavant i cap enrere
# Regles de Horn: (nom, [premisses], conclusio)
REGLES = [
("R1", ["dies_le_14"], "dins_termini"),
("R2", ["dies_le_1095"], "en_garantia"), # 3 anys ≈ 1095 dies
("R3", ["dins_termini", "etiqueta_original", "sense_usar"], "desistiment_ok"),
("R4", ["defectuos", "en_garantia"], "garantia_ok"),
("R5", ["desistiment_ok"], "procedeix_reemborsament"),
("R6", ["garantia_ok"], "procedeix_reemborsament"),
("R7", ["garantia_ok"], "enviament_devolucio_gratis"),
]
def fets_de(comanda):
"""Tradueix les dades de la comanda a fets (atoms vertaders). El que no apareix
es considera fals (suposicio de mon tancat)."""
f = set()
if comanda["dies_des_lliurament"] <= 14:
f.add("dies_le_14")
if comanda["dies_des_lliurament"] <= 1095:
f.add("dies_le_1095")
if comanda["etiqueta_original"]:
f.add("etiqueta_original")
if not comanda["usat"]:
f.add("sense_usar")
if comanda["defectuos"]:
f.add("defectuos")
return f
def encadenar_endavant(fets, regles, verbose=True):
fets = set(fets)
aplicades = []
canvi = True
while canvi: # repetir fins que no es derivi res de nou
canvi = False
for nom, premisses, conclusio in regles:
if conclusio not in fets and all(p in fets for p in premisses): # modus ponens
fets.add(conclusio)
aplicades.append(nom)
canvi = True
if verbose:
print(f" {nom}: {' ∧ '.join(premisses)} → {conclusio}")
return fets, aplicades
comandes = {
48213: dict(producte="Cafetera NovaBrew", dies_des_lliurament=9, etiqueta_original=True, usat=False, defectuos=False),
48377: dict(producte="Auriculars NovaSound", dies_des_lliurament=20, etiqueta_original=True, usat=True, defectuos=False),
47102: dict(producte="Aspirador NovaClean", dies_des_lliurament=40, etiqueta_original=False, usat=True, defectuos=True),
}
for id_comanda, dades in comandes.items():
print(f"Comanda {id_comanda} ({dades['producte']}):")
fets = fets_de(dades)
print(f" fets inicials: {sorted(fets)}")
finals, aplicades = encadenar_endavant(fets, REGLES)
print(f" procedeix_reemborsament: {'procedeix_reemborsament' in finals} regles: {aplicades}\n")
def demostrar(objectiu, fets, regles, nivell=0):
"""Encadenament cap enrere: es pot demostrar l'objectiu?"""
sagnat = " " * nivell
if objectiu in fets:
print(f"{sagnat}{objectiu}: es un fet")
return True
for nom, premisses, conclusio in regles:
if conclusio == objectiu: # regla que conclou l'objectiu
print(f"{sagnat}{objectiu}: provo {nom} (necessito {premisses})")
if all(demostrar(p, fets, regles, nivell + 1) for p in premisses):
return True # totes les premisses demostrades
print(f"{sagnat}{objectiu}: no demostrable")
return False
print("Cap enrere, comanda 47102:")
print(demostrar("procedeix_reemborsament", fets_de(comandes[47102]), REGLES))Sortida:
Comanda 48213 (Cafetera NovaBrew):
fets inicials: ['dies_le_1095', 'dies_le_14', 'etiqueta_original', 'sense_usar']
R1: dies_le_14 → dins_termini
R2: dies_le_1095 → en_garantia
R3: dins_termini ∧ etiqueta_original ∧ sense_usar → desistiment_ok
R5: desistiment_ok → procedeix_reemborsament
procedeix_reemborsament: True regles: ['R1', 'R2', 'R3', 'R5']
Comanda 48377 (Auriculars NovaSound):
fets inicials: ['dies_le_1095', 'etiqueta_original']
R2: dies_le_1095 → en_garantia
procedeix_reemborsament: False regles: ['R2']
Comanda 47102 (Aspirador NovaClean):
fets inicials: ['defectuos', 'dies_le_1095']
R2: dies_le_1095 → en_garantia
R4: defectuos ∧ en_garantia → garantia_ok
R6: garantia_ok → procedeix_reemborsament
R7: garantia_ok → enviament_devolucio_gratis
procedeix_reemborsament: True regles: ['R2', 'R4', 'R6', 'R7']
Cap enrere, comanda 47102:
procedeix_reemborsament: provo R5 (necessito ['desistiment_ok'])
desistiment_ok: provo R3 (necessito ['dins_termini', 'etiqueta_original', 'sense_usar'])
dins_termini: provo R1 (necessito ['dies_le_14'])
dies_le_14: no demostrable
dins_termini: no demostrable
desistiment_ok: no demostrable
procedeix_reemborsament: provo R6 (necessito ['garantia_ok'])
garantia_ok: provo R4 (necessito ['defectuos', 'en_garantia'])
defectuos: es un fet
en_garantia: provo R2 (necessito ['dies_le_1095'])
dies_le_1095: es un fet
TrueExplicació:
REGLESés una llista de tuples: nom, llista de premisses i conclusió. És tota la "base de coneixement" de regles; els fets de cada comanda els produeixfets_de, que instancia els predicats de primer ordre (DiesDesLliurament(p) ≤ 14) en proposicions (dies_le_14) per a aquella comanda concreta, com hem anunciat a 6.1.encadenar_endavantés un bucle "fins que res no canviï": a cada volta recorre les regles i dispara (modus ponens) les que tenen totes les premisses en el conjunt de fets i la conclusió de les quals encara no hi és. Retorna els fets finals i la llista de regles aplicades, que ja és una explicació rudimentària ("procedeix reemborsament perquè R1, R3, R5"). A 06-02 l'enriquirem.- Compara les tres comandes: la cafetera es reemborsa per desistiment; els auriculars usats i tardans no deriven
procedeix_reemborsament(res no ho demostra, i amb món tancat això vol dir "no"); l'aspirador defectuós al cap de 40 dies es reemborsa per garantia i a més té enviament gratuït (R7), encara que no porti etiqueta ni estigui sense usar: la garantia és una altra via diferent del desistiment, exactament com en la política real. demostrarés l'encadenament cap enrere en deu línies: si l'objectiu és un fet, llestos; si no, prova cada regla que el conclou i demostra recursivament les seves premisses;all(...)s'atura a la primera premissa que falla (retrocés). La traça impresa reprodueix la taula de la secció 7 i mostra que només s'ha explorat el que és rellevant per a la pregunta.
- Prolog, el llenguatge de la lògica
Tot això és tan regular que existeix un llenguatge de programació l'intèrpret del qual és un motor d'encadenament cap enrere amb unificació i resolució sobre clàusules de Horn: Prolog (1972). Les mateixes regles s'escriuen gairebé igual que a la taula, i la consulta és una pregunta:
% Fets i regles (una clausula per linia; ":-" es llegeix "si")
dies_des_lliurament(p48213, 9).
etiqueta_original(p48213).
sense_usar(p48213).
dins_termini(P) :- dies_des_lliurament(P, D), D =< 14.
desistiment_ok(P) :- dins_termini(P), etiqueta_original(P), sense_usar(P).
procedeix_reemborsament(P) :- desistiment_ok(P).
% Consulta interactiva:
% ?- procedeix_reemborsament(p48213). -> true.
% ?- procedeix_reemborsament(X). -> X = p48213.No l'executarem aquí (no forma part de l'entorn del curs); l'esmentem perquè és l'exemple pur de programació declarativa: descrius què és cert i l'intèrpret busca com demostrar-ho. A 07-01 compararem Prolog amb Python i amb Lisp, i a 06-02 i 06-04 veurem les eines modernes (CLIPS, Drools, experta) que hereten aquestes idees.
- Limitacions: món tancat, monotonia i incertesa
La lògica és precisa, però aquesta precisió té un preu. Convé conèixer-ne els tres límits principals abans de construir-hi un sistema:
- Suposició de món tancat: en el nostre motor, el que no és als fets es tracta com a fals. Per a la comanda 48377 vam concloure "no procedeix reemborsament" perquè no vam poder demostrar que procedís, no perquè demostréssim el contrari. En bases de dades i regles de negoci la suposició és còmoda (si un producte no consta com a defectuós, no ho és), però pot ser perillosa: "no consta que sigui defectuós" i "sabem que no és defectuós" són coses diferents. La lògica pura (seccions 2-5) és de món obert: si no se sap, no es conclou res; recorda
KB2a la secció 5. - Monotonia: en lògica clàssica, afegir coneixement mai no elimina conclusions. Però el sentit comú no funciona així: "els productes electrònics tenen 3 anys de garantia" i després "llevat que siguin recondicionats (1 any)" ens obliga a retirar una conclusió quan en sabem més. Els sistemes experts ho resolen de manera pragmàtica amb excepcions i prioritats entre regles (06-02); la recerca en lògiques no monòtones és àmplia i queda fora del curs.
- Incertesa: la lògica és binària. "El paquet arriba abonyegat" no implica amb certesa "dany en transport": també pot ser un error de picking amb un embalatge deficient. Els símptomes d'una incidència són ambigus, i forçar-los a vertader/fals perd informació. Precisament el diagnòstic d'incidències del cas 9 necessita graus de creença, i aquest és el tema de 06-03: probabilitat, teorema de Bayes i xarxes bayesianes.
I una limitació pràctica que no és de la lògica sinó del procés: algú ha d'escriure les regles correctament, mantenir-les i validar-les amb casos. Aquesta feina s'anomena enginyeria del coneixement i és el cor de la propera lliçó.
Errors Comuns i Consells
- Confondre la implicació amb la conversa. "Si defectuós aleshores reemborsament" no autoritza a deduir "defectuós" de "reemborsament". Quan escriguis una regla, llegeix-ne també la conversa en veu alta i comprova que no l'estàs assumint.
- Pensar que P → Q és falsa quan P és falsa. Una regla no s'incompleix en els casos que no compleixen la seva condició. L'única fila falsa de la implicació és V → F.
- Escriure ∀ amb ∧ o ∃ amb →. És l'error de sintaxi més comú en primer ordre: "per a tot p, si Producte(p) aleshores..." i "existeix p tal que Producte(p) i...".
- Fer servir taules de veritat per a tot. Són perfectes per entendre i per verificar bases petites (el nostre
implica), però creixen com 2ⁿ. Per a bases reals, encadenament amb Horn o resolució. - Oblidar la suposició de món tancat. Si el teu motor retorna "no procedeix", pregunta't si és "demostrat que no" o "no demostrat". Per a decisions amb efectes sobre persones (02-04), la segona hauria de portar a "preguntar més" o a revisió humana, no a denegar.
- No comprovar la consistència. La nostra KB de la secció 5 implicava
¬defectuos, senyal que dues regles xocaven en un cas real. Executa consultes "absurdes" (implica la KB que cap producte no és defectuós?) per caçar aquests xocs abans que els caci un client. - Noms de proposicions ambigus.
terminino diu si és "dins" o "fora". Fes servir noms que siguin afirmacions completes i positives (dins_termini,sense_usar): les clàusules de Horn no admeten negacions a les premisses i n'agrairàs la claredat.
Exercicis
Exercici 1. Amb taula_veritat, comprova si són tautologies: (a) la contrapositiva (P → Q) ↔ (¬Q → ¬P); (b) la conversa (P → Q) ↔ (Q → P); (c) el sil·logisme hipotètic ((P → Q) ∧ (Q → R)) → (P → R). Interpreta cada resultat amb proposicions de NovaMarket (P: defectuós, Q: procedeix reemborsament, R: enviament gratuït).
Exercici 2. Amplia la base KB de la secció 5 amb la regla de la garantia tal com hauria de ser: si és defectuós i està en garantia, procedeix reemborsament (en lloc de "si és defectuós, procedeix reemborsament"), i afegeix el fet en_garantia. Amb implica, comprova de nou si KB ⊨ ¬defectuos. Ha canviat res? Què et diu això sobre la incompatibilitat detectada, i què caldria per resoldre-la de debò? (No cal programar la solució; n'hi ha prou de raonar-la: es farà a 06-02.)
Exercici 3. Afegeix a REGLES de la secció 7 la política de productes d'higiene: R8 higiene ∧ obert → no_retornable, i fes que fets_de generi higiene i obert a partir de dues noves claus de la comanda. Executa l'encadenament cap endavant amb un raspall elèctric NovaSmile obert i retornat al cap de 5 dies amb etiqueta. Què deriva el motor? És coherent? Explica per què el motor d'aquesta lliçó no pot resoldre el que veus i quin mecanisme caldrà.
Solucions
Solució 1. (a) Tautologia: les 4 files són V; "si és defectuós procedeix reemborsament" equival a "si no procedeix reemborsament, no era defectuós". (b) No és tautologia (satisfactible: falla quan P i Q difereixen): que procedeixi el reemborsament no implica que sigui defectuós (pot ser desistiment). (c) Tautologia (8 files, totes V): les regles s'encadenen; és la base que l'encadenament cap endavant sigui correcte: si defectuós → reemborsament i reemborsament → avís, aleshores defectuós → avís.
Solució 2. Amb ("->", ("i", "defectuos", "en_garantia"), "reemborsament") i el fet en_garantia, implica(KB, ("no", "defectuos")) continua retornant True (un únic model de la KB, en què defectuos és fals). No ha canviat res, perquè el conflicte de fons no era la formulació de la garantia sinó que les dues regles conclouen coses contràries (¬reemborsament i reemborsament) sobre una mateixa comanda usada, tardana, en garantia i defectuosa; la lògica clàssica no pot tenir totes dues i "sacrifica" la possibilitat que sigui defectuós. Resoldre-ho de debò exigeix que una regla prevalgui sobre l'altra (la garantia sobre el desistiment) o reescriure la regla del Diego amb una excepció (usat ∧ mes14 ∧ ¬defectuos → ¬reemborsament), cosa que en clàusules de Horn no es pot expressar directament (premissa negada). Els sistemes experts ho fan amb prioritats i resolució de conflictes, tema de 06-02.
Solució 3. Amb ("R8", ["higiene", "obert"], "no_retornable") i la comanda dict(dies_des_lliurament=5, etiqueta_original=True, usat=False, defectuos=False, higiene=True, obert=True) (i fets_de afegint higiene/obert quan les claus siguin vertaderes), el motor deriva alhora procedeix_reemborsament (per R1, R3, R5: és dins del termini, amb etiqueta i "sense usar") i no_retornable (per R8). És incoherent: un producte d'higiene obert no s'ha de reemborsar per desistiment. El motor d'aquesta lliçó només afegeix fets i no té cap manera de dir que R8 bloqueja R3/R5: les clàusules de Horn no tenen negació a les premisses ni noció de prioritat. Caldrà un motor amb resolució de conflictes (prioritat o especificitat: R8 és més específica que R3) i una memòria de treball que registri una única decisio, que és el que construirem a 06-02.
Conclusió
En aquesta lliçó hem obert la meitat simbòlica de la IA amb la seva eina fonamental. Hem vist per què NovaMarket necessita coneixement explícit i verificable per a les devolucions (les regles precedeixen les dades, s'han de justificar i auditar); la lògica proposicional amb els seus cinc connectius i les seves taules de veritat, generades amb itertools.product; les nocions de tautologia, contradicció i satisfactibilitat, i les equivalències (De Morgan, contrapositiva) que permeten reescriure regles sense canviar-ne el significat; les regles d'inferència (modus ponens, modus tollens, resolució) que eviten enumerar; la implicació lògica KB ⊨ α, comprovada per enumeració de models, que a més ens ha destapat un xoc entre la regla del Diego i la de la garantia; la lògica de primer ordre, amb què la política de devolucions s'escriu una vegada per als 12.000 productes, i la unificació que l'aplica a cada comanda; i els dos algorismes de treball sobre clàusules de Horn, l'encadenament cap endavant (dirigit per les dades, encadenar_endavant) i cap enrere (dirigit per l'objectiu, demostrar), que han decidit el reemborsament de la cafetera NovaBrew, els auriculars NovaSound i l'aspirador NovaClean amb una traça llegible. Hem tancat amb Prolog i amb els límits de la lògica: món tancat, monotonia i incertesa.
Ja tenim el motor mínim. El que falta és tot allò que converteix un motor en un sistema que el Diego pugui fer servir: regles amb prioritat que resolguin els conflictes que hem deixat oberts (la garantia davant del desistiment, el producte d'higiene obert), una memòria de treball, un mòdul que expliqui "per què" i "com", una interfície que pregunti a l'usuari els fets que falten i un procés per extreure les regles del cap de l'expert i validar-les. Això és un sistema expert, la tecnologia que als anys 80 va treure la IA dels laboratoris (MYCIN, XCON, 01-01), i el construirem en Python pur per a les devolucions i garanties de NovaMarket a la propera lliçó, 06-02.
Fonaments d'Intel·ligència Artificial (IA)
Mòdul 1: Introducció a la Intel·ligència Artificial
Mòdul 2: Principis Bàsics de la IA
- Conceptes Fonamentals: Agents, Entorns i Racionalitat
- Tipus d'Intel·ligència Artificial
- Les Dades com a Matèria Primera de la IA
- Ètica i Consideracions en IA
Mòdul 3: Algorismes en IA
- Introducció als Algorismes
- Algorismes de Cerca
- Cerca amb Adversari: Jocs i Minimax
- Algorismes d'Optimització
Mòdul 4: Aprenentatge Automàtic (Machine Learning)
- Conceptes Bàsics de Machine Learning
- Tipus d'Aprenentatge Automàtic
- Preparació de Dades i Característiques
- Algorismes de Machine Learning
- Avaluació i Validació de Models
- Sobreajust, Regularització i Ajust d'Hiperparàmetres
Mòdul 5: Xarxes Neuronals i Deep Learning
- Introducció a les Xarxes Neuronals
- Arquitectura de Xarxes Neuronals
- Com Aprèn una Xarxa: Descens del Gradient i Retropropagació
- Deep Learning i les seves Aplicacions
- Transformers, Grans Models de Llenguatge i IA Generativa
Mòdul 6: Lògica i Sistemes Experts
- Lògica en IA
- Sistemes Experts
- Raonament amb Incertesa: Probabilitat i Xarxes Bayesianes
- Aplicacions dels Sistemes Experts
Mòdul 7: Eines i Llenguatges de Programació en IA
- Llenguatges de Programació per a IA
- Python Científic: NumPy, pandas i Matplotlib
- Eines i Llibreries Populars
- Entorns de Desenvolupament
Mòdul 8: Projectes i Casos d'Estudi
Mòdul 9: Exercicis i Pràctiques
- Exercicis d'Algorismes
- Pràctiques de Machine Learning
- Projectes de Xarxes Neuronals
- Projecte Integrador: de la Idea al Prototip
